diff --git a/facebook-clang-plugins b/facebook-clang-plugins index f5c1bd411..ba3d4b906 160000 --- a/facebook-clang-plugins +++ b/facebook-clang-plugins @@ -1 +1 @@ -Subproject commit f5c1bd411f95ba797e0e0e5c15a562d91eb6a6c9 +Subproject commit ba3d4b9066cc57c413581d2efe909add94b4366f diff --git a/infer/src/clang/ast_expressions.ml b/infer/src/clang/ast_expressions.ml index 5d01f48cb..d22aa3da9 100644 --- a/infer/src/clang/ast_expressions.ml +++ b/infer/src/clang/ast_expressions.ml @@ -55,6 +55,7 @@ let empty_var_decl_info = { vdi_is_static_local = false; vdi_is_module_private = false; vdi_is_nrvo_variable = false; + vdi_is_const_expr = false; vdi_init_expr = None; vdi_parm_index_in_function = None; } diff --git a/infer/src/clang/cFrontend_checkers.ml b/infer/src/clang/cFrontend_checkers.ml index 69ae40aaf..a01e05ccc 100644 --- a/infer/src/clang/cFrontend_checkers.ml +++ b/infer/src/clang/cFrontend_checkers.ml @@ -59,12 +59,33 @@ let rec is_self s = | _ -> false) | _ -> false -let is_function_or_method_call _ st = +let decl_ref_is_in names st = match st with - | Clang_ast_t.CallExpr _ (* for C *) + | Clang_ast_t.DeclRefExpr (_, _, _, drti) -> + (match drti.drti_decl_ref with + | Some dr -> let ndi, _, _ = CFrontend_utils.Ast_utils.get_info_from_decl_ref dr in + IList.exists (fun n -> n = ndi.ni_name) names + | _ -> false) + | _ -> false + +let call_function_named st names = + Ast_utils.exists_eventually_st decl_ref_is_in names st + +let rec eventually_makes_a_call exp = + CFrontend_utils.Ast_utils.exists_eventually_st is_expensive_function_or_method_call () exp + +and is_expensive_function_or_method_call _ st = + let white_list_functions = ["CGPointMake"] in + let open Clang_ast_t in + match st with + | CallExpr (_, st :: _, _) -> (* for C *) + not (call_function_named st white_list_functions) + | CXXConstructExpr (_, stmt_list, _, _) -> + (* Assumption: constructor is expensive iff it has a call inside *) + (IList.exists eventually_makes_a_call) stmt_list + | CXXTemporaryObjectExpr _ (* for C++ *) | CXXMemberCallExpr _ | CXXOperatorCallExpr _ - | CXXConstructExpr _ | CXXTemporaryObjectExpr _ (* for C++ *) - | Clang_ast_t.ObjCMessageExpr _ -> true (* for ObjC *) + | ObjCMessageExpr _ -> true (* for ObjC *) | _ -> false (* Call method m and on the pn parameter pred holds *) @@ -88,17 +109,15 @@ let dec_body_eventually atomic_pred param dec = (* true if a variable is initialized with a method/function call.*) (* implemented as decl |= EF is_function_or_method_call *) -let is_initialized_with_call decl = +let is_initialized_with_expensive_call decl = match decl with | Clang_ast_t.VarDecl (_, _ ,_, vdi) -> (match vdi.vdi_init_expr with | Some init_expr -> - CFrontend_utils.Ast_utils.exists_eventually_st is_function_or_method_call () init_expr + eventually_makes_a_call init_expr | _ -> false) | _ -> false - - (* === Warnings on properties === *) (* Assing Pointer Warning: a property with a pointer type should not be declared `assign` *) @@ -149,7 +168,8 @@ let global_var_init_with_calls_warning decl = | None -> assert false (* we cannot be here *) in let condition = (CFrontend_utils.Ast_utils.is_objc () || CFrontend_utils.Ast_utils.is_objcpp ()) && CFrontend_utils.Ast_utils.is_global_var decl - && is_initialized_with_call decl in + && (not (CFrontend_utils.Ast_utils.is_const_expr_var decl)) + && is_initialized_with_expensive_call decl in if condition then Some { name = "GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL"; diff --git a/infer/src/clang/cFrontend_utils.ml b/infer/src/clang/cFrontend_utils.ml index 6e5ccb3d5..f36282ca6 100644 --- a/infer/src/clang/cFrontend_utils.ml +++ b/infer/src/clang/cFrontend_utils.ml @@ -404,6 +404,11 @@ struct | Clang_ast_t.VarDecl (_, _ ,_, vdi) -> vdi.vdi_is_global | _ -> false + let is_const_expr_var decl = + match decl with + | Clang_ast_t.VarDecl (_, _ ,_, vdi) -> vdi.vdi_is_const_expr + | _ -> false + let is_objc () = match Config.clang_lang with | Config.OBJC -> true diff --git a/infer/src/clang/cFrontend_utils.mli b/infer/src/clang/cFrontend_utils.mli index 6103d97c8..4c9f07208 100644 --- a/infer/src/clang/cFrontend_utils.mli +++ b/infer/src/clang/cFrontend_utils.mli @@ -138,6 +138,9 @@ sig (* true if a declaration is a global variable *) val is_global_var : Clang_ast_t.decl -> bool + (* true if a declaration is a constexpr variable *) + val is_const_expr_var : Clang_ast_t.decl -> bool + (* true if CFrontend_config.language is set ot ObjC *) val is_objc : unit -> bool diff --git a/infer/tests/codetoanalyze/objcpp/frontend/global-var/B.mm b/infer/tests/codetoanalyze/objcpp/frontend/global-var/B.mm index 5c7825693..47543f2ec 100644 --- a/infer/tests/codetoanalyze/objcpp/frontend/global-var/B.mm +++ b/infer/tests/codetoanalyze/objcpp/frontend/global-var/B.mm @@ -7,7 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import +#import @interface A : NSObject + (int)bar; @@ -34,6 +35,19 @@ static const float kLineSize = 1 / [A scale]; // Error static const float ok = 37; +const NSRange NSNotFoundRange = + (NSRange){.location = NSNotFound, .length = 0}; // OK + +const static CFRange FBCFNotFoundRange = + (CFRange){.location = kCFNotFound, .length = 7}; // OK + +const CGPoint offset = CGPointMake(0, 0); // OK + +constexpr double square(double x) { return x * x; } + +const int dmv = 17; +constexpr double max1 = 1.4 * square(dmv); // OK + void bla() { static const int kInsets = foo(); // Error diff --git a/infer/tests/endtoend/objc/GlobalVarTest.java b/infer/tests/endtoend/objcpp/GlobalVarTest.java similarity index 95% rename from infer/tests/endtoend/objc/GlobalVarTest.java rename to infer/tests/endtoend/objcpp/GlobalVarTest.java index 9e8804d37..d1ae21d7f 100644 --- a/infer/tests/endtoend/objc/GlobalVarTest.java +++ b/infer/tests/endtoend/objcpp/GlobalVarTest.java @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -package endtoend.objc; +package endtoend.objcpp; import static org.hamcrest.MatcherAssert.assertThat; import static utils.matchers.ResultContainsLineNumbers.containsLines; @@ -52,7 +52,7 @@ public class GlobalVarTest { assertThat( "Results should contain " + GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL, inferResults, - containsLines(new int[]{29, 31, 33})); + containsLines(new int[]{30, 32, 34})); } } diff --git a/infer/tests/utils/InferRunner.java b/infer/tests/utils/InferRunner.java index 231b091f4..ca47ddad2 100644 --- a/infer/tests/utils/InferRunner.java +++ b/infer/tests/utils/InferRunner.java @@ -195,6 +195,9 @@ public class InferRunner { case CPP: stdParam = "-std=c++11"; break; + case ObjCPP: + stdParam = "-std=c++11"; + break; } return stdParam; }