From 0253608fab2f9ca32a811796367e8640b175ff37 Mon Sep 17 00:00:00 2001 From: Dulma Churchill Date: Wed, 13 Jul 2016 02:52:02 -0700 Subject: [PATCH] Separate the flow of the frontend checks form the translation Reviewed By: akotulski Differential Revision: D3543519 fbshipit-source-id: 3493bbe --- infer/src/IR/Procname.re | 16 ++- infer/src/IR/Procname.rei | 10 +- infer/src/clang/cContext.ml | 13 --- infer/src/clang/cContext.mli | 4 - infer/src/clang/cFrontend.ml | 1 + infer/src/clang/cFrontend_checkers.ml | 33 ++++-- infer/src/clang/cFrontend_checkers.mli | 4 +- infer/src/clang/cFrontend_checkers_main.ml | 42 +++++++ infer/src/clang/cFrontend_checkers_main.mli | 10 ++ infer/src/clang/cFrontend_decl.ml | 3 - infer/src/clang/cFrontend_errors.ml | 41 +++---- infer/src/clang/cFrontend_errors.mli | 2 +- infer/src/clang/cTrans.ml | 2 - .../AutoreleaseExample.dot | 106 +++++++++--------- .../frontend/property/PropertyAttributes.dot | 64 +++++------ .../codetoanalyze/objc/warnings/atomic_prop.m | 1 + .../endtoend/objc/AtomicPropertyTest.java | 22 ++-- .../endtoend/objc/BlockCaptureCXXRefTest.java | 18 ++- 18 files changed, 217 insertions(+), 175 deletions(-) create mode 100644 infer/src/clang/cFrontend_checkers_main.ml create mode 100644 infer/src/clang/cFrontend_checkers_main.mli diff --git a/infer/src/IR/Procname.re b/infer/src/IR/Procname.re index 9693fdbec..2b54034d8 100644 --- a/infer/src/IR/Procname.re +++ b/infer/src/IR/Procname.re @@ -421,19 +421,25 @@ let java_is_vararg = } | _ => false; +let is_obj_constructor method_name => method_name == "new" || string_is_prefix "init" method_name; -/** [is_constructor pname] returns true if [pname] is a constructor */ + +/** [is_constructor pname] returns true if [pname] is a constructor + TODO: add case for C++ */ let is_constructor = fun | Java js => js.method_name == "" - | ObjC_Cpp name => name.method_name == "new" || string_is_prefix "init" name.method_name + | ObjC_Cpp name => is_obj_constructor name.method_name | _ => false; +let is_objc_dealloc method_name => method_name == "dealloc"; + -/** [is_objc_dealloc pname] returns true if [pname] is the dealloc method in Objective-C */ -let is_objc_dealloc = +/** [is_dealloc pname] returns true if [pname] is the dealloc method in Objective-C + TODO: add case for C++ */ +let is_destructor = fun - | ObjC_Cpp name => name.method_name == "dealloc" + | ObjC_Cpp name => is_objc_dealloc name.method_name | _ => false; let java_is_close = diff --git a/infer/src/IR/Procname.rei b/infer/src/IR/Procname.rei index 69c7b98ef..c84d5dda1 100644 --- a/infer/src/IR/Procname.rei +++ b/infer/src/IR/Procname.rei @@ -97,6 +97,10 @@ let is_anonymous_inner_class_name: string => bool; let is_c_method: t => bool; +/** Check if this is a constructor method in Objective-C. */ +let is_obj_constructor: string => bool; + + /** Check if this is a constructor. */ let is_constructor: t => bool; @@ -106,7 +110,11 @@ let is_java: t => bool; /** Check if this is a dealloc method in Objective-C. */ -let is_objc_dealloc: t => bool; +let is_objc_dealloc: string => bool; + + +/** Check if this is a dealloc method. */ +let is_destructor: t => bool; /** Create a Java procedure name from its diff --git a/infer/src/clang/cContext.ml b/infer/src/clang/cContext.ml index a0f2c9a41..c618df8b4 100644 --- a/infer/src/clang/cContext.ml +++ b/infer/src/clang/cContext.ml @@ -159,16 +159,3 @@ let rec get_outer_procname context = | Some outer_context -> get_outer_procname outer_context | None -> Cfg.Procdesc.get_proc_name context.procdesc -let is_curr_proc_objc_getter context field_name = - let attrs = Cfg.Procdesc.get_attributes context.procdesc in - match attrs.ProcAttributes.objc_accessor with - | Some ProcAttributes.Objc_getter field -> - (Ident.fieldname_to_string field) = field_name - | _ -> false - -let is_curr_proc_objc_setter context field_name = - let attrs = Cfg.Procdesc.get_attributes context.procdesc in - match attrs.ProcAttributes.objc_accessor with - | Some ProcAttributes.Objc_setter field -> - (Ident.fieldname_to_string field) = field_name - | _ -> false diff --git a/infer/src/clang/cContext.mli b/infer/src/clang/cContext.mli index 46306ff25..ad5f221a2 100644 --- a/infer/src/clang/cContext.mli +++ b/infer/src/clang/cContext.mli @@ -70,7 +70,3 @@ val static_vars_for_block : t -> Procname.t -> (Pvar.t * Typ.t) list val is_objc_instance : t -> bool val get_outer_procname : t -> Procname.t - -val is_curr_proc_objc_getter : t -> string -> bool - -val is_curr_proc_objc_setter : t -> string -> bool diff --git a/infer/src/clang/cFrontend.ml b/infer/src/clang/cFrontend.ml index 6964d527e..d946aff1a 100644 --- a/infer/src/clang/cFrontend.ml +++ b/infer/src/clang/cFrontend.ml @@ -60,6 +60,7 @@ let do_source_file source_file ast = let call_graph, cfg = compute_icfg tenv ast in Printing.log_out "\n End building call/cfg graph for '%s'.\n" (DB.source_file_to_string source_file); + CFrontend_checkers_main.do_frontend_checks cfg call_graph ast; (* This part below is a boilerplate in every frontends. *) (* This could be moved in the cfg_infer module *) let source_dir = DB.source_dir_from_source_file !DB.current_source in diff --git a/infer/src/clang/cFrontend_checkers.ml b/infer/src/clang/cFrontend_checkers.ml index 60d929485..07a299616 100644 --- a/infer/src/clang/cFrontend_checkers.ml +++ b/infer/src/clang/cFrontend_checkers.ml @@ -41,6 +41,20 @@ let get_ivar_attributes ivar_decl = | _ -> []) | _ -> [] +let is_method_property_accessor_of_ivar method_decl ivar_pointer = + let open Clang_ast_t in + match method_decl with + | ObjCMethodDecl (_, _, obj_c_method_decl_info) -> + if obj_c_method_decl_info.Clang_ast_t.omdi_is_property_accessor then + let prperty_opt = obj_c_method_decl_info.Clang_ast_t.omdi_property_decl in + match Ast_utils.get_decl_opt_with_decl_ref prperty_opt with + | Some ObjCPropertyDecl (_, _, obj_c_property_decl_info) -> + (match obj_c_property_decl_info.Clang_ast_t.opdi_ivar_decl with + | Some decl_ref -> decl_ref.Clang_ast_t.dr_decl_pointer = ivar_pointer + | None -> false) + | _ -> false + else false + | _ -> false (* checks if ivar is defined among a set of fields and if it is atomic *) let is_ivar_atomic attributes = @@ -58,9 +72,6 @@ let location_from_sinfo info = let location_from_dinfo info = CLocation.get_sil_location_from_range info.Clang_ast_t.di_source_range true -let proc_name_from_context context = - Cfg.Procdesc.get_proc_name (CContext.get_procdesc context) - let rec is_self s = let open Clang_ast_t in match s with @@ -210,11 +221,14 @@ 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 stmt_info ivar_decl_ref = - match Ast_utils.get_decl ivar_decl_ref.Clang_ast_t.dr_decl_pointer with +let direct_atomic_property_access_warning method_decl 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) -> + 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 let ivar_name = named_decl_info.Clang_ast_t.ni_name in - let mname = proc_name_from_context context in let condition = (* We give the warning when: (1) the property has the atomic attribute and @@ -222,10 +236,9 @@ let direct_atomic_property_access_warning context stmt_info ivar_decl_ref = (3) the access of the ivar is not in the init method Last two conditions avoids false positives *) is_ivar_atomic (get_ivar_attributes d) - && not (CContext.is_curr_proc_objc_getter context ivar_name) - && not (CContext.is_curr_proc_objc_setter context ivar_name) - && not (Procname.is_constructor mname) - && not (Procname.is_objc_dealloc mname) in + && not (is_method_property_accessor_of_ivar method_decl ivar_pointer) + && not (Procname.is_obj_constructor method_name) + && not (Procname.is_objc_dealloc method_name) in if condition then Some { name = "DIRECT_ATOMIC_PROPERTY_ACCESS"; diff --git a/infer/src/clang/cFrontend_checkers.mli b/infer/src/clang/cFrontend_checkers.mli index 0ee535dad..efa809dc1 100644 --- a/infer/src/clang/cFrontend_checkers.mli +++ b/infer/src/clang/cFrontend_checkers.mli @@ -28,8 +28,8 @@ val assign_pointer_warning : Clang_ast_t.decl_info -> Clang_ast_t.named_decl_inf (* Direct Atomic Property access: a property declared atomic should not be accesses directly via its iva *) -val direct_atomic_property_access_warning : - CContext.t -> Clang_ast_t.stmt_info -> Clang_ast_t.decl_ref -> warning_desc option +val direct_atomic_property_access_warning : Clang_ast_t.decl -> Clang_ast_t.stmt_info -> + Clang_ast_t.decl_ref -> warning_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 new file mode 100644 index 000000000..2514a7eec --- /dev/null +++ b/infer/src/clang/cFrontend_checkers_main.ml @@ -0,0 +1,42 @@ +(* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +let rec do_frontend_checks_stmt cfg cg method_decl stmt = + CFrontend_errors.run_frontend_checkers_on_stmt cfg cg method_decl stmt; + let _, stmts = Clang_ast_proj.get_stmt_tuple stmt in + IList.iter (do_frontend_checks_stmt cfg cg method_decl) stmts + +let rec do_frontend_checks_decl cfg cg decl = + let open Clang_ast_t in + let info = Clang_ast_proj.get_decl_tuple decl in + CLocation.update_curr_file info; + (match decl with + | FunctionDecl(_, _, _, fdi) + | CXXMethodDecl (_, _, _, fdi, _) + | CXXConstructorDecl (_, _, _, fdi, _) + | CXXConversionDecl (_, _, _, fdi, _) + | CXXDestructorDecl (_, _, _, fdi, _) -> + (match fdi.Clang_ast_t.fdi_body with + | Some stmt -> do_frontend_checks_stmt cfg cg decl stmt + | None -> ()) + | ObjCMethodDecl (_, _, mdi) -> + (match mdi.Clang_ast_t.omdi_body with + | Some stmt -> do_frontend_checks_stmt cfg cg decl stmt + | None -> ()) + | _ -> ()); + CFrontend_errors.run_frontend_checkers_on_decl cfg cg decl; + match Clang_ast_proj.get_decl_context_tuple decl with + | Some (decls, _) -> IList.iter (do_frontend_checks_decl cfg cg) decls + | None -> () + +let do_frontend_checks cfg cg ast = + match ast with + | Clang_ast_t.TranslationUnitDecl(_, decl_list, _, _) -> + IList.iter (do_frontend_checks_decl cfg cg) decl_list + | _ -> assert false (* NOTE: Assumes that an AST alsways starts with a TranslationUnitDecl *) diff --git a/infer/src/clang/cFrontend_checkers_main.mli b/infer/src/clang/cFrontend_checkers_main.mli new file mode 100644 index 000000000..f0ed943d5 --- /dev/null +++ b/infer/src/clang/cFrontend_checkers_main.mli @@ -0,0 +1,10 @@ +(* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +val do_frontend_checks : Cfg.cfg -> Cg.t -> Clang_ast_t.decl -> unit diff --git a/infer/src/clang/cFrontend_decl.ml b/infer/src/clang/cFrontend_decl.ml index 1eeac779e..137c7413a 100644 --- a/infer/src/clang/cFrontend_decl.ml +++ b/infer/src/clang/cFrontend_decl.ml @@ -166,9 +166,6 @@ struct (* Translate one global declaration *) let rec translate_one_declaration tenv cg cfg decl_trans_context dec = let open Clang_ast_t in - (* Run the frontend checkers on this declaration *) - if decl_trans_context = `DeclTraversal then - CFrontend_errors.run_frontend_checkers_on_decl cfg cg dec; (* each procedure has different scope: start names from id 0 *) Ident.NameGenerator.reset (); diff --git a/infer/src/clang/cFrontend_errors.ml b/infer/src/clang/cFrontend_errors.ml index 2a0e40cbc..fa248ccfa 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 = 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 context stmt_info dr_name checker = - checker context stmt_info dr_name +let checkers_for_ivar stmt_info method_decl ivar_decl_ref checker = + checker stmt_info method_decl 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] @@ -48,9 +48,10 @@ let checker_for_global_var dec checker = checker dec (* Add a frontend warning with a description desc at location loc to the errlog of a proc desc *) -let log_frontend_warning pdesc warn_desc = +let log_frontend_warning cfg cg warn_desc = let open CFrontend_checkers in let loc = warn_desc.loc in + let pdesc = CMethod_trans.get_method_for_frontend_checks cfg cg loc in let errlog = Cfg.Procdesc.get_err_log pdesc in let err_desc = Errdesc.explain_frontend_warning warn_desc.description warn_desc.suggestion loc in @@ -68,17 +69,10 @@ let log_frontend_warning pdesc warn_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 cfg cg pdesc_opt checkers = +let invoke_set_of_checkers f cfg cg checkers = IList.iter (fun checker -> match f checker with - | Some warning_desc -> - let pdesc = - match pdesc_opt with - | Some pdesc -> pdesc - | None -> - let loc = warning_desc.CFrontend_checkers.loc in - CMethod_trans.get_method_for_frontend_checks cfg cg loc in - log_frontend_warning pdesc warning_desc + | Some warning_desc -> log_frontend_warning cfg cg warning_desc | None -> ()) checkers (* Call all checkers on properties of class c *) @@ -86,7 +80,7 @@ let rec check_for_property_errors cfg cg decl_list = let open Clang_ast_t in let do_one_property decl_info pname_info pdi = let call_property_checker = checkers_for_property decl_info pname_info pdi in - invoke_set_of_checkers call_property_checker cfg cg None property_checkers_list in + invoke_set_of_checkers call_property_checker cfg cg property_checkers_list in match decl_list with | [] -> () | ObjCPropertyDecl (decl_info, pname_info, pdi) :: rest -> @@ -101,22 +95,17 @@ let get_categories_decls decl_ref = | Some ObjCInterfaceDecl (_, _, decls, _, _) -> decls | _ -> [] -let run_frontend_checkers_on_stmt trans_state instr = +let run_frontend_checkers_on_stmt cfg cg method_decl instr = let open Clang_ast_t in - let context = trans_state.CTrans_utils.context in - let pdesc = CContext.get_procdesc context in - let cg = context.CContext.cg in - let cfg = context.CContext.cfg 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 context stmt_info dr_ref in - invoke_set_of_checkers call_checker_for_ivar cfg cg (Some pdesc) ivar_access_checker_list + let call_checker_for_ivar = checkers_for_ivar method_decl stmt_info dr_ref in + invoke_set_of_checkers call_checker_for_ivar cfg cg ivar_access_checker_list | 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 pdesc_opt = Some pdesc in - invoke_set_of_checkers call_captured_vars_checker cfg cg pdesc_opt captured_vars_checker_list + let call_captured_vars_checker = checkers_for_capture_vars stmt_info captured_block_vars in + invoke_set_of_checkers call_captured_vars_checker cfg cg captured_vars_checker_list | _ -> () let rec run_frontend_checkers_on_decl cfg cg dec = @@ -132,14 +121,14 @@ let rec run_frontend_checkers_on_decl cfg cg dec = let decls = (get_categories_decls idi.Clang_ast_t.oidi_class_interface) @ decl_list in check_for_property_errors cfg cg decls; let call_ns_checker = checkers_for_ns decl_info decl_list in - invoke_set_of_checkers call_ns_checker cfg cg None ns_notification_checker_list; + invoke_set_of_checkers call_ns_checker cfg cg ns_notification_checker_list; IList.iter (run_frontend_checkers_on_decl cfg cg) decl_list | ObjCProtocolDecl (decl_info, _, decl_list, _, _) -> check_for_property_errors cfg cg decl_list; let call_ns_checker = checkers_for_ns decl_info decl_list in - invoke_set_of_checkers call_ns_checker cfg cg None ns_notification_checker_list; + invoke_set_of_checkers call_ns_checker cfg cg ns_notification_checker_list; IList.iter (run_frontend_checkers_on_decl cfg cg) decl_list | VarDecl _ -> let call_global_checker = checker_for_global_var dec in - invoke_set_of_checkers call_global_checker cfg cg None global_var_checker_list + invoke_set_of_checkers call_global_checker cfg cg global_var_checker_list | _ -> () diff --git a/infer/src/clang/cFrontend_errors.mli b/infer/src/clang/cFrontend_errors.mli index 60c52576f..47c94dcd6 100644 --- a/infer/src/clang/cFrontend_errors.mli +++ b/infer/src/clang/cFrontend_errors.mli @@ -13,7 +13,7 @@ open! Utils (* Module for warnings detected at translation time by the frontend *) (* Run frontend checkers on a statement *) -val run_frontend_checkers_on_stmt : CTrans_utils.trans_state -> Clang_ast_t.stmt -> unit +val run_frontend_checkers_on_stmt : Cfg.cfg -> Cg.t -> Clang_ast_t.decl -> Clang_ast_t.stmt -> unit (* Run frontend checkers on a declaration *) val run_frontend_checkers_on_decl : Cfg.cfg -> Cg.t -> Clang_ast_t.decl -> unit diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index e42fc74a6..d7167cfea 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -2314,8 +2314,6 @@ struct (* a trans_state containing current info on the translation and it returns *) (* a result_state.*) and instruction trans_state instr = - (* Run the frontend checkers on this instruction *) - CFrontend_errors.run_frontend_checkers_on_stmt trans_state instr; let stmt_kind = Clang_ast_proj.get_stmt_kind_string instr in let stmt_info, _ = Clang_ast_proj.get_stmt_tuple instr in let stmt_pointer = stmt_info.Clang_ast_t.si_pointer in diff --git a/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/AutoreleaseExample.dot b/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/AutoreleaseExample.dot index 61d009d09..69f7656c3 100644 --- a/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/AutoreleaseExample.dot +++ b/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/AutoreleaseExample.dot @@ -1,145 +1,145 @@ /* @generated */ digraph iCFG { -37 [label="37: DeclStmt \n n$4=_fun___objc_alloc_no_fail(sizeof(class NSAutoreleasePool ):unsigned long ) [line 60]\n n$5=_fun_NSObject_init(n$4:class NSAutoreleasePool *) virtual [line 60]\n *&pool:class NSAutoreleasePool *=n$5 [line 60]\n " shape="box"] +37 [label="37: Exit frontend_checks_260aba1187e75a8c3340e4b361681569 \n " color=yellow style=filled] - 37 -> 36 ; -36 [label="36: DeclStmt \n n$2=_fun___objc_alloc_no_fail(sizeof(class NSString ):unsigned long ) [line 61]\n n$3=_fun___set_autorelease_attribute(n$2:class NSString *) [line 61]\n *&string:class NSString *=n$3 [line 61]\n " shape="box"] +36 [label="36: Start frontend_checks_260aba1187e75a8c3340e4b361681569\nFormals: \nLocals: \n " color=yellow style=filled] - 36 -> 35 ; -35 [label="35: Message Call: release \n n$1=*&pool:class NSAutoreleasePool * [line 63]\n _fun___objc_release_autorelease_pool(n$1:class NSAutoreleasePool *) [line 63]\n " shape="box"] + 36 -> 37 ; +35 [label="35: DeclStmt \n n$4=_fun___objc_alloc_no_fail(sizeof(class NSAutoreleasePool ):unsigned long ) [line 60]\n n$5=_fun_NSObject_init(n$4:class NSAutoreleasePool *) virtual [line 60]\n *&pool:class NSAutoreleasePool *=n$5 [line 60]\n " shape="box"] 35 -> 34 ; -34 [label="34: DeclStmt \n n$0=*&string:class NSString * [line 64]\n *&c:class NSString *=n$0 [line 64]\n " shape="box"] +34 [label="34: DeclStmt \n n$2=_fun___objc_alloc_no_fail(sizeof(class NSString ):unsigned long ) [line 61]\n n$3=_fun___set_autorelease_attribute(n$2:class NSString *) [line 61]\n *&string:class NSString *=n$3 [line 61]\n " shape="box"] 34 -> 33 ; -33 [label="33: Exit test3 \n " color=yellow style=filled] +33 [label="33: Message Call: release \n n$1=*&pool:class NSAutoreleasePool * [line 63]\n _fun___objc_release_autorelease_pool(n$1:class NSAutoreleasePool *) [line 63]\n " shape="box"] -32 [label="32: Start test3\nFormals: \nLocals: c:class NSString * string:class NSString * pool:class NSAutoreleasePool * \n DECLARE_LOCALS(&return,&c,&string,&pool); [line 59]\n " color=yellow style=filled] + 33 -> 32 ; +32 [label="32: DeclStmt \n n$0=*&string:class NSString * [line 64]\n *&c:class NSString *=n$0 [line 64]\n " shape="box"] - 32 -> 37 ; -31 [label="31: DeclStmt \n *&s1:class A *=0 [line 48]\n " shape="box"] + 32 -> 31 ; +31 [label="31: Exit test3 \n " color=yellow style=filled] - 31 -> 30 ; -30 [label="30: DeclStmt \n *&s2:class A *=0 [line 49]\n " shape="box"] +30 [label="30: Start test3\nFormals: \nLocals: c:class NSString * string:class NSString * pool:class NSAutoreleasePool * \n DECLARE_LOCALS(&return,&c,&string,&pool); [line 59]\n " color=yellow style=filled] - 30 -> 29 ; -29 [label="29: DeclStmt \n *&s3:class A *=0 [line 50]\n " shape="box"] + 30 -> 35 ; +29 [label="29: DeclStmt \n *&s1:class A *=0 [line 48]\n " shape="box"] 29 -> 28 ; -28 [label="28: BinaryOperatorStmt: Assign \n n$3=_fun_createA() [line 52]\n *&s1:class A *=n$3 [line 52]\n " shape="box"] +28 [label="28: DeclStmt \n *&s2:class A *=0 [line 49]\n " shape="box"] 28 -> 27 ; -27 [label="27: BinaryOperatorStmt: Assign \n n$2=_fun_createA() [line 53]\n *&s2:class A *=n$2 [line 53]\n " shape="box"] +27 [label="27: DeclStmt \n *&s3:class A *=0 [line 50]\n " shape="box"] 27 -> 26 ; -26 [label="26: BinaryOperatorStmt: Assign \n n$1=_fun_createA() [line 54]\n *&s3:class A *=n$1 [line 54]\n " shape="box"] +26 [label="26: BinaryOperatorStmt: Assign \n n$3=_fun_createA() [line 52]\n *&s1:class A *=n$3 [line 52]\n " shape="box"] 26 -> 25 ; -25 [label="25: Release the autorelease pool \n n$0=_fun___objc_release_autorelease_pool(&s1:class A *,&s3:class A *,&s2:class A *) [line 51]\n " shape="box"] +25 [label="25: BinaryOperatorStmt: Assign \n n$2=_fun_createA() [line 53]\n *&s2:class A *=n$2 [line 53]\n " shape="box"] 25 -> 24 ; -24 [label="24: Return Stmt \n *&return:int =0 [line 56]\n " shape="box"] +24 [label="24: BinaryOperatorStmt: Assign \n n$1=_fun_createA() [line 54]\n *&s3:class A *=n$1 [line 54]\n " shape="box"] 24 -> 23 ; -23 [label="23: Exit test2 \n " color=yellow style=filled] +23 [label="23: Release the autorelease pool \n n$0=_fun___objc_release_autorelease_pool(&s1:class A *,&s3:class A *,&s2:class A *) [line 51]\n " shape="box"] -22 [label="22: Start test2\nFormals: \nLocals: s3:class A * s2:class A * s1:class A * \n DECLARE_LOCALS(&return,&s3,&s2,&s1); [line 47]\n " color=yellow style=filled] + 23 -> 22 ; +22 [label="22: Return Stmt \n *&return:int =0 [line 56]\n " shape="box"] - 22 -> 31 ; -21 [label="21: DeclStmt \n *&s1:class A *=0 [line 35]\n " shape="box"] + 22 -> 21 ; +21 [label="21: Exit test2 \n " color=yellow style=filled] - 21 -> 20 ; -20 [label="20: DeclStmt \n *&s2:class A *=0 [line 36]\n " shape="box"] +20 [label="20: Start test2\nFormals: \nLocals: s3:class A * s2:class A * s1:class A * \n DECLARE_LOCALS(&return,&s3,&s2,&s1); [line 47]\n " color=yellow style=filled] - 20 -> 19 ; -19 [label="19: DeclStmt \n *&s3:class A *=0 [line 37]\n " shape="box"] + 20 -> 29 ; +19 [label="19: DeclStmt \n *&s1:class A *=0 [line 35]\n " shape="box"] 19 -> 18 ; -18 [label="18: BinaryOperatorStmt: Assign \n n$5=_fun_createA() [line 39]\n *&s1:class A *=n$5 [line 39]\n " shape="box"] +18 [label="18: DeclStmt \n *&s2:class A *=0 [line 36]\n " shape="box"] 18 -> 17 ; -17 [label="17: Message Call: retain \n n$3=*&s1:class A * [line 40]\n n$4=_fun___objc_retain(n$3:class A *) [line 40]\n " shape="box"] +17 [label="17: DeclStmt \n *&s3:class A *=0 [line 37]\n " shape="box"] 17 -> 16 ; -16 [label="16: BinaryOperatorStmt: Assign \n n$2=_fun_createA() [line 41]\n *&s2:class A *=n$2 [line 41]\n " shape="box"] +16 [label="16: BinaryOperatorStmt: Assign \n n$5=_fun_createA() [line 39]\n *&s1:class A *=n$5 [line 39]\n " shape="box"] 16 -> 15 ; -15 [label="15: BinaryOperatorStmt: Assign \n n$1=_fun_createA() [line 42]\n *&s3:class A *=n$1 [line 42]\n " shape="box"] +15 [label="15: Message Call: retain \n n$3=*&s1:class A * [line 40]\n n$4=_fun___objc_retain(n$3:class A *) [line 40]\n " shape="box"] 15 -> 14 ; -14 [label="14: Release the autorelease pool \n n$0=_fun___objc_release_autorelease_pool(&s1:class A *,&s2:class A *,&s3:class A *) [line 38]\n " shape="box"] +14 [label="14: BinaryOperatorStmt: Assign \n n$2=_fun_createA() [line 41]\n *&s2:class A *=n$2 [line 41]\n " shape="box"] 14 -> 13 ; -13 [label="13: Return Stmt \n *&return:int =0 [line 44]\n " shape="box"] +13 [label="13: BinaryOperatorStmt: Assign \n n$1=_fun_createA() [line 42]\n *&s3:class A *=n$1 [line 42]\n " shape="box"] 13 -> 12 ; -12 [label="12: Exit test1 \n " color=yellow style=filled] +12 [label="12: Release the autorelease pool \n n$0=_fun___objc_release_autorelease_pool(&s1:class A *,&s2:class A *,&s3:class A *) [line 38]\n " shape="box"] -11 [label="11: Start test1\nFormals: \nLocals: s3:class A * s2:class A * s1:class A * \n DECLARE_LOCALS(&return,&s3,&s2,&s1); [line 34]\n " color=yellow style=filled] + 12 -> 11 ; +11 [label="11: Return Stmt \n *&return:int =0 [line 44]\n " shape="box"] - 11 -> 21 ; -10 [label="10: DeclStmt \n n$2=_fun___objc_alloc_no_fail(sizeof(class A ):unsigned long ) [line 30]\n n$3=_fun_NSObject_init(n$2:class A *) virtual [line 30]\n *&s1:class A *=n$3 [line 30]\n " shape="box"] + 11 -> 10 ; +10 [label="10: Exit test1 \n " color=yellow style=filled] - 10 -> 9 ; -9 [label="9: Return Stmt \n n$0=*&s1:class A * [line 31]\n n$1=_fun___set_autorelease_attribute(n$0:class A *) [line 31]\n *&return:class A *=n$1 [line 31]\n " shape="box"] +9 [label="9: Start test1\nFormals: \nLocals: s3:class A * s2:class A * s1:class A * \n DECLARE_LOCALS(&return,&s3,&s2,&s1); [line 34]\n " color=yellow style=filled] - 9 -> 8 ; -8 [label="8: Exit createA \n " color=yellow style=filled] + 9 -> 19 ; +8 [label="8: DeclStmt \n n$2=_fun___objc_alloc_no_fail(sizeof(class A ):unsigned long ) [line 30]\n n$3=_fun_NSObject_init(n$2:class A *) virtual [line 30]\n *&s1:class A *=n$3 [line 30]\n " shape="box"] -7 [label="7: Start createA\nFormals: \nLocals: s1:class A * \n DECLARE_LOCALS(&return,&s1); [line 29]\n " color=yellow style=filled] + 8 -> 7 ; +7 [label="7: Return Stmt \n n$0=*&s1:class A * [line 31]\n n$1=_fun___set_autorelease_attribute(n$0:class A *) [line 31]\n *&return:class A *=n$1 [line 31]\n " shape="box"] - 7 -> 10 ; -6 [label="6: DeclStmt \n n$2=_fun___objc_alloc_no_fail(sizeof(class NSString ):unsigned long ) [line 23]\n *&s:class NSString *=n$2 [line 23]\n " shape="box"] + 7 -> 6 ; +6 [label="6: Exit createA \n " color=yellow style=filled] - 6 -> 5 ; -5 [label="5: Return Stmt \n n$0=*&s:class NSString * [line 24]\n n$1=_fun___set_autorelease_attribute(n$0:class NSString *) [line 24]\n *&return:class NSString *=n$1 [line 24]\n " shape="box"] +5 [label="5: Start createA\nFormals: \nLocals: s1:class A * \n DECLARE_LOCALS(&return,&s1); [line 29]\n " color=yellow style=filled] - 5 -> 4 ; -4 [label="4: Exit A_main \n " color=yellow style=filled] + 5 -> 8 ; +4 [label="4: DeclStmt \n n$2=_fun___objc_alloc_no_fail(sizeof(class NSString ):unsigned long ) [line 23]\n *&s:class NSString *=n$2 [line 23]\n " shape="box"] -3 [label="3: Start A_main\nFormals: self:class A *\nLocals: s:class NSString * \n DECLARE_LOCALS(&return,&s); [line 22]\n " color=yellow style=filled] + 4 -> 3 ; +3 [label="3: Return Stmt \n n$0=*&s:class NSString * [line 24]\n n$1=_fun___set_autorelease_attribute(n$0:class NSString *) [line 24]\n *&return:class NSString *=n$1 [line 24]\n " shape="box"] - 3 -> 6 ; -2 [label="2: Exit frontend_checks_260aba1187e75a8c3340e4b361681569 \n " color=yellow style=filled] + 3 -> 2 ; +2 [label="2: Exit A_main \n " color=yellow style=filled] -1 [label="1: Start frontend_checks_260aba1187e75a8c3340e4b361681569\nFormals: \nLocals: \n " color=yellow style=filled] +1 [label="1: Start A_main\nFormals: self:class A *\nLocals: s:class NSString * \n DECLARE_LOCALS(&return,&s); [line 22]\n " color=yellow style=filled] - 1 -> 2 ; + 1 -> 4 ; } diff --git a/infer/tests/codetoanalyze/objc/frontend/property/PropertyAttributes.dot b/infer/tests/codetoanalyze/objc/frontend/property/PropertyAttributes.dot index 04defd792..51924bf40 100644 --- a/infer/tests/codetoanalyze/objc/frontend/property/PropertyAttributes.dot +++ b/infer/tests/codetoanalyze/objc/frontend/property/PropertyAttributes.dot @@ -1,73 +1,73 @@ /* @generated */ digraph iCFG { -18 [label="18: DeclStmt \n n$3=_fun___objc_alloc_no_fail(sizeof(class A ):unsigned long ) [line 39]\n n$4=_fun_NSObject_init(n$3:class A *) virtual [line 39]\n *&a:class A *=n$4 [line 39]\n " shape="box"] +18 [label="18: Exit frontend_checks_23a4fcc8f25cc8087aa9202ac0edfbf5 \n " color=yellow style=filled] - 18 -> 17 ; -17 [label="17: Message Call: setLast_name: \n n$1=*&a:class A * [line 40]\n n$2=*&a2:class A * [line 40]\n _fun_A_setLast_name:(n$1:class A *,n$2:class A *) [line 40]\n " shape="box"] +17 [label="17: Start frontend_checks_23a4fcc8f25cc8087aa9202ac0edfbf5\nFormals: \nLocals: \n " color=yellow style=filled] - 17 -> 16 ; -16 [label="16: Message Call: release \n n$0=*&a:class A * [line 41]\n _fun___objc_release(n$0:class A *) [line 41]\n " shape="box"] + 17 -> 18 ; +16 [label="16: DeclStmt \n n$3=_fun___objc_alloc_no_fail(sizeof(class A ):unsigned long ) [line 39]\n n$4=_fun_NSObject_init(n$3:class A *) virtual [line 39]\n *&a:class A *=n$4 [line 39]\n " shape="box"] 16 -> 15 ; -15 [label="15: Return Stmt \n *&return:int =0 [line 42]\n " shape="box"] +15 [label="15: Message Call: setLast_name: \n n$1=*&a:class A * [line 40]\n n$2=*&a2:class A * [line 40]\n _fun_A_setLast_name:(n$1:class A *,n$2:class A *) [line 40]\n " shape="box"] 15 -> 14 ; -14 [label="14: Exit test \n " color=yellow style=filled] +14 [label="14: Message Call: release \n n$0=*&a:class A * [line 41]\n _fun___objc_release(n$0:class A *) [line 41]\n " shape="box"] -13 [label="13: Start test\nFormals: a2:class A *\nLocals: a:class A * \n DECLARE_LOCALS(&return,&a); [line 38]\n " color=yellow style=filled] + 14 -> 13 ; +13 [label="13: Return Stmt \n *&return:int =0 [line 42]\n " shape="box"] - 13 -> 18 ; -12 [label="12: DeclStmt \n n$11=_fun___objc_alloc_no_fail(sizeof(class A ):unsigned long ) [line 27]\n n$12=_fun_NSObject_init(n$11:class A *) virtual [line 27]\n *&other:class A *=n$12 [line 27]\n " shape="box"] + 13 -> 12 ; +12 [label="12: Exit test \n " color=yellow style=filled] - 12 -> 7 ; - 12 -> 8 ; -11 [label="11: BinaryOperatorStmt: Assign \n n$8=*&other:class A * [line 29]\n n$9=*&self:class A * [line 29]\n n$10=*n$9._name:class A * [line 29]\n *n$8._name:class A *=n$10 [line 29]\n " shape="box"] +11 [label="11: Start test\nFormals: a2:class A *\nLocals: a:class A * \n DECLARE_LOCALS(&return,&a); [line 38]\n " color=yellow style=filled] - 11 -> 10 ; -10 [label="10: BinaryOperatorStmt: Assign \n n$5=*&other:class A * [line 30]\n n$6=*&self:class A * [line 30]\n n$7=*n$6._last_name:class A * [line 30]\n *n$5._last_name:class A *=n$7 [line 30]\n " shape="box"] + 11 -> 16 ; +10 [label="10: DeclStmt \n n$11=_fun___objc_alloc_no_fail(sizeof(class A ):unsigned long ) [line 27]\n n$12=_fun_NSObject_init(n$11:class A *) virtual [line 27]\n *&other:class A *=n$12 [line 27]\n " shape="box"] - 10 -> 9 ; -9 [label="9: BinaryOperatorStmt: Assign \n n$2=*&other:class A * [line 31]\n n$3=*&self:class A * [line 31]\n n$4=*n$3._child:class A * [line 31]\n *n$2._child:class A *=n$4 [line 31]\n " shape="box"] + 10 -> 5 ; + 10 -> 6 ; +9 [label="9: BinaryOperatorStmt: Assign \n n$8=*&other:class A * [line 29]\n n$9=*&self:class A * [line 29]\n n$10=*n$9._name:class A * [line 29]\n *n$8._name:class A *=n$10 [line 29]\n " shape="box"] - 9 -> 6 ; -8 [label="8: Prune (false branch) \n n$1=*&other:class A * [line 28]\n PRUNE((n$1 == 0), false); [line 28]\n " shape="invhouse"] + 9 -> 8 ; +8 [label="8: BinaryOperatorStmt: Assign \n n$5=*&other:class A * [line 30]\n n$6=*&self:class A * [line 30]\n n$7=*n$6._last_name:class A * [line 30]\n *n$5._last_name:class A *=n$7 [line 30]\n " shape="box"] - 8 -> 6 ; -7 [label="7: Prune (true branch) \n n$1=*&other:class A * [line 28]\n PRUNE((n$1 != 0), true); [line 28]\n " shape="invhouse"] + 8 -> 7 ; +7 [label="7: BinaryOperatorStmt: Assign \n n$2=*&other:class A * [line 31]\n n$3=*&self:class A * [line 31]\n n$4=*n$3._child:class A * [line 31]\n *n$2._child:class A *=n$4 [line 31]\n " shape="box"] - 7 -> 11 ; -6 [label="6: + \n " ] + 7 -> 4 ; +6 [label="6: Prune (false branch) \n n$1=*&other:class A * [line 28]\n PRUNE((n$1 == 0), false); [line 28]\n " shape="invhouse"] - 6 -> 5 ; -5 [label="5: Return Stmt \n n$0=*&other:class A * [line 33]\n *&return:class A *=n$0 [line 33]\n " shape="box"] + 6 -> 4 ; +5 [label="5: Prune (true branch) \n n$1=*&other:class A * [line 28]\n PRUNE((n$1 != 0), true); [line 28]\n " shape="invhouse"] - 5 -> 4 ; -4 [label="4: Exit A_copy \n " color=yellow style=filled] + 5 -> 9 ; +4 [label="4: + \n " ] -3 [label="3: Start A_copy\nFormals: self:class A *\nLocals: other:class A * \n DECLARE_LOCALS(&return,&other); [line 26]\n " color=yellow style=filled] + 4 -> 3 ; +3 [label="3: Return Stmt \n n$0=*&other:class A * [line 33]\n *&return:class A *=n$0 [line 33]\n " shape="box"] - 3 -> 12 ; -2 [label="2: Exit frontend_checks_23a4fcc8f25cc8087aa9202ac0edfbf5 \n " color=yellow style=filled] + 3 -> 2 ; +2 [label="2: Exit A_copy \n " color=yellow style=filled] -1 [label="1: Start frontend_checks_23a4fcc8f25cc8087aa9202ac0edfbf5\nFormals: \nLocals: \n " color=yellow style=filled] +1 [label="1: Start A_copy\nFormals: self:class A *\nLocals: other:class A * \n DECLARE_LOCALS(&return,&other); [line 26]\n " color=yellow style=filled] - 1 -> 2 ; + 1 -> 10 ; } diff --git a/infer/tests/codetoanalyze/objc/warnings/atomic_prop.m b/infer/tests/codetoanalyze/objc/warnings/atomic_prop.m index c70380631..d602f03d7 100644 --- a/infer/tests/codetoanalyze/objc/warnings/atomic_prop.m +++ b/infer/tests/codetoanalyze/objc/warnings/atomic_prop.m @@ -74,6 +74,7 @@ } - (int)readP { + int i = _q; // Bad access return _p; // Good access } diff --git a/infer/tests/endtoend/objc/AtomicPropertyTest.java b/infer/tests/endtoend/objc/AtomicPropertyTest.java index 0c1d977ba..81e2b428e 100644 --- a/infer/tests/endtoend/objc/AtomicPropertyTest.java +++ b/infer/tests/endtoend/objc/AtomicPropertyTest.java @@ -11,6 +11,7 @@ package endtoend.objc; import static org.hamcrest.MatcherAssert.assertThat; import static utils.matchers.ResultContainsExactly.containsExactly; +import static utils.matchers.ResultContainsLineNumbers.containsLines; import com.google.common.collect.ImmutableList; @@ -46,22 +47,19 @@ public class AtomicPropertyTest { } @Test - public void whenInferRunsOnAtomicProperty() + public void matchErrors() throws InterruptedException, IOException, InferException { InferResults inferResults = InferRunner.runInferObjC(inferCmd); assertThat( - "Results should contain a direct atomic property access", + "Results should contain the correct " + DIRECT_ATOMIC_PROPERTY_ACCESS, inferResults, - containsExactly( - DIRECT_ATOMIC_PROPERTY_ACCESS, - FILE, - new String[]{ - "writeQ:", - "readQ", - "bla" - } - ) - ); + containsLines(new int[]{ + 77, + 82, + 86, + 98, + 99 + })); } } diff --git a/infer/tests/endtoend/objc/BlockCaptureCXXRefTest.java b/infer/tests/endtoend/objc/BlockCaptureCXXRefTest.java index cbb6a88a3..0bd75f94a 100644 --- a/infer/tests/endtoend/objc/BlockCaptureCXXRefTest.java +++ b/infer/tests/endtoend/objc/BlockCaptureCXXRefTest.java @@ -11,6 +11,7 @@ package endtoend.objc; import static org.hamcrest.MatcherAssert.assertThat; import static utils.matchers.ResultContainsExactly.containsExactly; +import static utils.matchers.ResultContainsLineNumbers.containsLines; import com.google.common.collect.ImmutableList; @@ -43,20 +44,15 @@ public class BlockCaptureCXXRefTest { } @Test - public void whenInferRunsOnNavigateToURLInBackgroundThenNPEIsFound() + public void matchErrors() throws InterruptedException, IOException, InferException { InferResults inferResults = InferRunner.runInferObjC(inferCmd); - String[] procedures = { - "foo:", "foo3:param2:" - }; assertThat( - "Results should contain the expected C++ reference captured in block", + "Results should contain the correct " + REFERENCE_CAPTURED, inferResults, - containsExactly( - REFERENCE_CAPTURED, - FILE, - procedures - ) - ); + containsLines(new int[]{ + 20, + 37 + })); } }