diff --git a/infer/src/clang/cPredicates.ml b/infer/src/clang/cPredicates.ml index 7ba6c7026..7a6dfe050 100644 --- a/infer/src/clang/cPredicates.ml +++ b/infer/src/clang/cPredicates.ml @@ -1487,6 +1487,19 @@ let is_init_expr_cxx11_constant an = false +let call_cxx_method an name = + let open Clang_ast_t in + match an with + | Ctl_parser_types.Stmt (CXXMemberCallExpr (_, member :: _, _)) -> ( + match member with + | MemberExpr (_, _, _, memberExprInfo) -> + ALVar.compare_str_with_alexp memberExprInfo.mei_name.ni_name name + | _ -> + false ) + | _ -> + false + + let source_file_matches src_file path_re = Option.value_map ~f:(fun sf -> diff --git a/infer/src/clang/cPredicates.mli b/infer/src/clang/cPredicates.mli index eaef52594..6c2fe5829 100644 --- a/infer/src/clang/cPredicates.mli +++ b/infer/src/clang/cPredicates.mli @@ -20,6 +20,9 @@ val objc_block_is_capturing_values : Ctl_parser_types.ast_node -> bool val call_method : Ctl_parser_types.ast_node -> ALVar.alexp -> bool (** 'call_method an m an' is true iff node an is a call to an ObjC method with name containing string m *) +val call_cxx_method : Ctl_parser_types.ast_node -> ALVar.alexp -> bool +(** 'call_cxx_method an m an' is true iff node an is a call to a C++ method with name containing string m *) + val call_class_method : Ctl_parser_types.ast_node -> ALVar.alexp -> bool (** 'call_class_method an mname' is true iff node an is a call to an ObjC class method with name containing mname *) diff --git a/infer/src/clang/cTL.ml b/infer/src/clang/cTL.ml index 8b8b99614..49073415c 100644 --- a/infer/src/clang/cTL.ml +++ b/infer/src/clang/cTL.ml @@ -968,6 +968,8 @@ let rec eval_Atomic pred_name_ args an lcxt = CPredicates.call_instance_method an m | "call_method", [m], an -> CPredicates.call_method an m + | "call_cxx_method", [m], an -> + CPredicates.call_cxx_method an m | "captures_cxx_references", [], _ -> CPredicates.captures_cxx_references an | "objc_block_is_capturing_values", [], _ -> diff --git a/infer/tests/codetoanalyze/cpp/linters/Makefile b/infer/tests/codetoanalyze/cpp/linters/Makefile index e1aa932e4..114456b1d 100644 --- a/infer/tests/codetoanalyze/cpp/linters/Makefile +++ b/infer/tests/codetoanalyze/cpp/linters/Makefile @@ -6,7 +6,7 @@ TESTS_DIR = ../../.. CLANG_OPTIONS = -std=c++11 -c -INFER_OPTIONS = --no-capture --linters-only --linters-def-file extracopy.al --linters-def-file call_function.al --no-filtering --debug-exceptions --project-root $(TESTS_DIR) \ +INFER_OPTIONS = --no-capture --linters-only --linters-def-file extracopy.al --linters-def-file call_function.al --linters-def-file call_cxx_method.al --no-filtering --debug-exceptions --project-root $(TESTS_DIR) \ --enable-issue-type GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL INFERPRINT_OPTIONS = --issues-tests diff --git a/infer/tests/codetoanalyze/cpp/linters/call_cxx_method.al b/infer/tests/codetoanalyze/cpp/linters/call_cxx_method.al new file mode 100644 index 000000000..700467713 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/linters/call_cxx_method.al @@ -0,0 +1,15 @@ +// Copyright (c) 2019-present, Facebook, Inc. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. +DEFINE-CHECKER CALL_CXX_METHOD = { + SET report_when = + WHEN call_cxx_method("fooBar") OR call_cxx_method("bar") + HOLDS-IN-NODE CXXMemberCallExpr; + + SET message = "Do not call fooBar or bar."; + SET suggestion = "Call something else."; + SET severity = "WARNING"; + SET mode = "OFF"; + +}; diff --git a/infer/tests/codetoanalyze/cpp/linters/call_cxx_method.cpp b/infer/tests/codetoanalyze/cpp/linters/call_cxx_method.cpp new file mode 100644 index 000000000..68a1da714 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/linters/call_cxx_method.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2019-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +namespace { +struct Foo { + void bar(){}; + void toUnsafeFuture(){}; +}; +struct Bar { + virtual void fooBar() { f.bar(); }; + Foo f; +}; +} // anonymous namespace + +int main() { + Foo{}.bar(); + Foo f; + f.bar(); + Bar{}.fooBar(); + return 0; +} diff --git a/infer/tests/codetoanalyze/cpp/linters/issues.exp b/infer/tests/codetoanalyze/cpp/linters/issues.exp index cd6f32ecb..887757e08 100644 --- a/infer/tests/codetoanalyze/cpp/linters/issues.exp +++ b/infer/tests/codetoanalyze/cpp/linters/issues.exp @@ -1,3 +1,7 @@ +codetoanalyze/cpp/linters/call_cxx_method.cpp, anonymous_namespace_call_cxx_method.cpp::Bar::fooBar, 13, CALL_CXX_METHOD, no_bucket, WARNING, [] +codetoanalyze/cpp/linters/call_cxx_method.cpp, main, 19, CALL_CXX_METHOD, no_bucket, WARNING, [] +codetoanalyze/cpp/linters/call_cxx_method.cpp, main, 21, CALL_CXX_METHOD, no_bucket, WARNING, [] +codetoanalyze/cpp/linters/call_cxx_method.cpp, main, 22, CALL_CXX_METHOD, no_bucket, WARNING, [] codetoanalyze/cpp/linters/call_function.cpp, main, 20, CALL_FUNCTION, no_bucket, WARNING, [] codetoanalyze/cpp/linters/call_function.cpp, main, 22, CALL_FUNCTION, no_bucket, WARNING, [] codetoanalyze/cpp/linters/call_function.cpp, main, 23, CALL_FUNCTION, no_bucket, WARNING, []