From 07bf714d9120c29b659a0957ed4a93195debe2e3 Mon Sep 17 00:00:00 2001 From: Dulma Churchill Date: Fri, 19 Aug 2016 06:23:40 -0700 Subject: [PATCH] Put current method in context Reviewed By: jvillard Differential Revision: D3741728 fbshipit-source-id: 6710016 --- infer/src/clang/CLintersContext.ml | 2 + infer/src/clang/cFrontend_checkers.ml | 6 +-- infer/src/clang/cFrontend_checkers.mli | 4 +- infer/src/clang/cFrontend_checkers_main.ml | 29 +++++++++--- infer/src/clang/cFrontend_errors.ml | 44 +++++++++---------- infer/src/clang/cFrontend_errors.mli | 2 +- .../MutableLocalVariableTest.java | 11 +++++ 7 files changed, 63 insertions(+), 35 deletions(-) diff --git a/infer/src/clang/CLintersContext.ml b/infer/src/clang/CLintersContext.ml index fc5e1bc5b..a6a63e5dd 100644 --- a/infer/src/clang/CLintersContext.ml +++ b/infer/src/clang/CLintersContext.ml @@ -8,6 +8,7 @@ *) type context = { + current_method : Clang_ast_t.decl option; in_synchronized_block: bool; is_ck_translation_unit: bool; (** True if the translation unit contains an ObjC class impl that's a subclass @@ -15,6 +16,7 @@ type context = { } let empty = { + current_method = None; in_synchronized_block = false; is_ck_translation_unit = false; } diff --git a/infer/src/clang/cFrontend_checkers.ml b/infer/src/clang/cFrontend_checkers.ml index 8e00d5c87..2c4db592c 100644 --- a/infer/src/clang/cFrontend_checkers.ml +++ b/infer/src/clang/cFrontend_checkers.ml @@ -217,10 +217,10 @@ let global_var_init_with_calls_warning _ decl = (* Direct Atomic Property access: a property declared atomic should not be accessed directly via its ivar *) -let direct_atomic_property_access_warning context method_decl stmt_info ivar_decl_ref = +let direct_atomic_property_access_warning context stmt_info ivar_decl_ref = let ivar_pointer = ivar_decl_ref.Clang_ast_t.dr_decl_pointer in - match Ast_utils.get_decl ivar_pointer with - | Some (ObjCIvarDecl (_, named_decl_info, _, _, _) as d) -> + match Ast_utils.get_decl ivar_pointer, context.CLintersContext.current_method with + | Some (ObjCIvarDecl (_, named_decl_info, _, _, _) as d), Some method_decl -> let method_name = match Clang_ast_proj.get_named_decl_tuple method_decl with | Some (_, method_named_decl) -> method_named_decl.Clang_ast_t.ni_name | _ -> "" in diff --git a/infer/src/clang/cFrontend_checkers.mli b/infer/src/clang/cFrontend_checkers.mli index 872257c08..f94c75cd1 100644 --- a/infer/src/clang/cFrontend_checkers.mli +++ b/infer/src/clang/cFrontend_checkers.mli @@ -24,8 +24,8 @@ val assign_pointer_warning : (* Direct Atomic Property access: a property declared atomic should not be accesses directly via its iva *) val direct_atomic_property_access_warning : - CLintersContext.context -> Clang_ast_t.decl -> Clang_ast_t.stmt_info -> - Clang_ast_t.decl_ref -> CIssue.issue_desc option + CLintersContext.context -> Clang_ast_t.stmt_info -> Clang_ast_t.decl_ref -> + CIssue.issue_desc option (* CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK: C++ references should not be captured in blocks. *) diff --git a/infer/src/clang/cFrontend_checkers_main.ml b/infer/src/clang/cFrontend_checkers_main.ml index d29fb2606..8fc75bd0b 100644 --- a/infer/src/clang/cFrontend_checkers_main.ml +++ b/infer/src/clang/cFrontend_checkers_main.ml @@ -7,15 +7,20 @@ * of patent rights can be found in the PATENTS file in the same directory. *) -let rec do_frontend_checks_stmt (context:CLintersContext.context) cfg cg method_decl stmt = - let context' = CFrontend_errors.run_frontend_checkers_on_stmt context cfg cg method_decl stmt in - let stmts = CFrontend_utils.Ast_utils.get_stmts_from_stmt stmt in +open CFrontend_utils + +let rec do_frontend_checks_stmt (context:CLintersContext.context) cfg cg stmt = + let open Clang_ast_t in + let context' = CFrontend_errors.run_frontend_checkers_on_stmt context cfg cg stmt in let do_all_checks_on_stmts stmt = (match stmt with - | Clang_ast_t.DeclStmt (_, _, decl_list) -> + | DeclStmt (_, _, decl_list) -> IList.iter (do_frontend_checks_decl context' cfg cg) decl_list + | BlockExpr (_, _, _, decl) -> + IList.iter (do_frontend_checks_decl context' cfg cg) [decl] | _ -> ()); - do_frontend_checks_stmt context' cfg cg method_decl stmt in + do_frontend_checks_stmt context' cfg cg stmt in + let stmts = Ast_utils.get_stmts_from_stmt stmt in IList.iter (do_all_checks_on_stmts) stmts and do_frontend_checks_decl context cfg cg decl = @@ -29,11 +34,21 @@ and do_frontend_checks_decl context cfg cg decl = | CXXConversionDecl (_, _, _, fdi, _) | CXXDestructorDecl (_, _, _, fdi, _) -> (match fdi.Clang_ast_t.fdi_body with - | Some stmt -> do_frontend_checks_stmt context cfg cg decl stmt + | Some stmt -> + let context = {context with CLintersContext.current_method = Some decl } in + do_frontend_checks_stmt context cfg cg stmt | None -> ()) | ObjCMethodDecl (_, _, mdi) -> (match mdi.Clang_ast_t.omdi_body with - | Some stmt -> do_frontend_checks_stmt context cfg cg decl stmt + | Some stmt -> + let context = {context with CLintersContext.current_method = Some decl } in + do_frontend_checks_stmt context cfg cg stmt + | None -> ()) + | BlockDecl (_, block_decl_info) -> + (match block_decl_info.Clang_ast_t.bdi_body with + | Some stmt -> + let context = {context with CLintersContext.current_method = Some decl } in + do_frontend_checks_stmt context cfg cg stmt | None -> ()) | _ -> ()); let context' = CFrontend_errors.run_frontend_checkers_on_decl context cfg cg decl in diff --git a/infer/src/clang/cFrontend_errors.ml b/infer/src/clang/cFrontend_errors.ml index eebd2c6fa..632b253b2 100644 --- a/infer/src/clang/cFrontend_errors.ml +++ b/infer/src/clang/cFrontend_errors.ml @@ -23,8 +23,8 @@ let checkers_for_property decl_info pname_info pdi checker context = let ivar_access_checker_list = [CFrontend_checkers.direct_atomic_property_access_warning] (* Invocation of checker belonging to ivar_access_checker_list *) -let checkers_for_ivar stmt_info method_decl ivar_decl_ref checker context = - checker context stmt_info method_decl ivar_decl_ref +let checkers_for_ivar stmt_info ivar_decl_ref checker context = + checker context stmt_info ivar_decl_ref (* List of checkers for captured vars in objc blocks *) let captured_vars_checker_list = [CFrontend_checkers.captured_cxx_ref_in_objc_block_warning] @@ -40,12 +40,12 @@ let ns_notification_checker_list = [CFrontend_checkers.checker_NSNotificationCen let checkers_for_ns decl_info impl_decl_info decls checker context = checker context decl_info impl_decl_info decls -(* List of checkers on global variables *) -let global_var_checker_list = [CFrontend_checkers.global_var_init_with_calls_warning; - ComponentKit.mutable_local_vars_advice] +(* List of checkers on variables *) +let var_checker_list = [CFrontend_checkers.global_var_init_with_calls_warning; + ComponentKit.mutable_local_vars_advice] -(* Invocation of checker belonging to global_var_checker_list *) -let checker_for_global_var dec checker context = +(* Invocation of checker belonging to var_checker_list *) +let checker_for_var dec checker context = checker context dec (* List of checkers on conditional operator *) @@ -111,49 +111,49 @@ let log_frontend_issue cfg cg method_decl_opt key issue_desc = 1. f a particular way to apply a checker, it's a partial function 2. context 3. the list of checkers to be applied *) -let invoke_set_of_checkers f context cfg cg method_decl_opt key checkers = +let invoke_set_of_checkers f context cfg cg key checkers = IList.iter (fun checker -> match f checker context with - | Some issue_desc -> log_frontend_issue cfg cg method_decl_opt key issue_desc + | Some issue_desc -> + log_frontend_issue cfg cg context.CLintersContext.current_method key issue_desc | None -> ()) checkers -let run_frontend_checkers_on_stmt context cfg cg method_decl instr = +let run_frontend_checkers_on_stmt context cfg cg instr = let open Clang_ast_t in - let decl_opt = Some method_decl in match instr with | ObjCIvarRefExpr (stmt_info, _, _, obj_c_ivar_ref_expr_info) -> let dr_ref = obj_c_ivar_ref_expr_info.Clang_ast_t.ovrei_decl_ref in - let call_checker_for_ivar = checkers_for_ivar method_decl stmt_info dr_ref in + let call_checker_for_ivar = checkers_for_ivar stmt_info dr_ref in let key = Ast_utils.generate_key_stmt instr in invoke_set_of_checkers - call_checker_for_ivar context cfg cg decl_opt key ivar_access_checker_list; + call_checker_for_ivar context cfg cg key ivar_access_checker_list; context | BlockExpr (stmt_info, _ , _, Clang_ast_t.BlockDecl (_, block_decl_info)) -> let captured_block_vars = block_decl_info.Clang_ast_t.bdi_captured_variables in let call_captured_vars_checker = checkers_for_capture_vars stmt_info captured_block_vars in let key = Ast_utils.generate_key_stmt instr in - invoke_set_of_checkers call_captured_vars_checker context cfg cg decl_opt key + invoke_set_of_checkers call_captured_vars_checker context cfg cg key captured_vars_checker_list; context | IfStmt (stmt_info, _ :: cond :: _) -> let call_checker = checker_for_if_stmt stmt_info [cond] in let key = Ast_utils.generate_key_stmt cond in - invoke_set_of_checkers call_checker context cfg cg decl_opt key if_stmt_checker_list; + invoke_set_of_checkers call_checker context cfg cg key if_stmt_checker_list; context | ConditionalOperator (stmt_info, first_stmt :: _, _) -> let call_checker = checker_for_conditional_op stmt_info [first_stmt] in let key = Ast_utils.generate_key_stmt first_stmt in - invoke_set_of_checkers call_checker context cfg cg decl_opt key conditional_op_checker_list; + invoke_set_of_checkers call_checker context cfg cg key conditional_op_checker_list; context | ForStmt (stmt_info, [_; _; cond; _; _]) -> let call_checker = checker_for_for_stmt stmt_info [cond] in let key = Ast_utils.generate_key_stmt cond in - invoke_set_of_checkers call_checker context cfg cg decl_opt key for_stmt_checker_list; + invoke_set_of_checkers call_checker context cfg cg key for_stmt_checker_list; context | WhileStmt (stmt_info, [_; cond; _]) -> let call_checker = checker_for_while_stmt stmt_info [cond] in let key = Ast_utils.generate_key_stmt cond in - invoke_set_of_checkers call_checker context cfg cg decl_opt key while_stmt_checker_list; + invoke_set_of_checkers call_checker context cfg cg key while_stmt_checker_list; context | ObjCAtSynchronizedStmt _ -> { context with CLintersContext.in_synchronized_block = true } @@ -171,17 +171,17 @@ let run_frontend_checkers_on_decl context cfg cg dec = | _ -> None in let call_ns_checker = checkers_for_ns decl_info idi decl_list in let key = Ast_utils.generate_key_decl dec in - invoke_set_of_checkers call_ns_checker context cfg cg None key ns_notification_checker_list; + invoke_set_of_checkers call_ns_checker context cfg cg key ns_notification_checker_list; context | VarDecl _ -> - let call_global_checker = checker_for_global_var dec in + let call_var_checker = checker_for_var dec in let key = Ast_utils.generate_key_decl dec in - invoke_set_of_checkers call_global_checker context cfg cg None key global_var_checker_list; + invoke_set_of_checkers call_var_checker context cfg cg key var_checker_list; context | ObjCPropertyDecl (decl_info, pname_info, pdi) -> let call_property_checker = checkers_for_property decl_info pname_info pdi in let key = Ast_utils.generate_key_decl dec in - invoke_set_of_checkers call_property_checker context cfg cg None key property_checkers_list; + invoke_set_of_checkers call_property_checker context cfg cg key property_checkers_list; context | _ -> context else context diff --git a/infer/src/clang/cFrontend_errors.mli b/infer/src/clang/cFrontend_errors.mli index 04b79e092..7547ede99 100644 --- a/infer/src/clang/cFrontend_errors.mli +++ b/infer/src/clang/cFrontend_errors.mli @@ -15,7 +15,7 @@ open! Utils (* Run frontend checkers on a statement *) val run_frontend_checkers_on_stmt : CLintersContext.context -> - Cfg.cfg -> Cg.t -> Clang_ast_t.decl -> Clang_ast_t.stmt -> CLintersContext.context + Cfg.cfg -> Cg.t -> Clang_ast_t.stmt -> CLintersContext.context (* Run frontend checkers on a declaration *) val run_frontend_checkers_on_decl : CLintersContext.context -> Cfg.cfg -> Cg.t -> diff --git a/infer/tests/endtoend/objcpp/componentkit/MutableLocalVariableTest.java b/infer/tests/endtoend/objcpp/componentkit/MutableLocalVariableTest.java index ad14b2af5..08f056550 100644 --- a/infer/tests/endtoend/objcpp/componentkit/MutableLocalVariableTest.java +++ b/infer/tests/endtoend/objcpp/componentkit/MutableLocalVariableTest.java @@ -11,6 +11,7 @@ package endtoend.objcpp.componentkit; import static org.hamcrest.MatcherAssert.assertThat; import static utils.matchers.ResultContainsLineNumbers.containsLines; +import static utils.matchers.ResultContainsErrorInMethod.contains; import com.google.common.collect.ImmutableList; @@ -52,4 +53,14 @@ public class MutableLocalVariableTest { inferResults, containsLines(new int[]{58, 69, 74, 76, 80, 85})); } + + @Test + public void MLVsInNewWithString() + throws InterruptedException, IOException, InferException { + InferResults inferResults = InferRunner.runInferObjC(inferCmd); + assertThat( + "Results should contain " + MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE, + inferResults, + contains(MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE, FILE, "newWithString:")); + } }