From 6bf091d8c0ad483cfd39480504fb8e7a6b4af6c8 Mon Sep 17 00:00:00 2001 From: Sungkeun Cho Date: Wed, 28 Oct 2020 02:21:17 -0700 Subject: [PATCH] [frontend] Revise translation of message expression in ObjC frontend Summary: This diff revises the translation of message expression's arguments in ObjC frontend. In the frontend, it massages the arguments when calling a static method, so the class or object value is not given to the static method as the first parameter. The problem is that it used a raise-exception-and-catch way to detect where we remove the first parameter. This way of using an exception is not only hard to understand, but also incorrectly removed the first parameter, with breaking abstract semantics sometimes. (See the added test.) This diff avoids using the exception. Reviewed By: jvillard Differential Revision: D24565513 fbshipit-source-id: 0a84ca394 --- infer/src/clang/cTrans.ml | 52 +++++++------------ infer/src/clang/cTrans_utils.ml | 34 ++++++++++-- infer/src/clang/cTrans_utils.mli | 8 +-- .../objc/frontend/self_static/Self.m.dot | 44 ++++++++-------- .../codetoanalyze/objc/performance/NSString.m | 11 ++++ .../objc/performance/cost-issues.exp | 2 + .../codetoanalyze/objc/performance/issues.exp | 1 + 7 files changed, 90 insertions(+), 62 deletions(-) diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index c8528f586..5eddb7f41 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -838,14 +838,7 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s in let return = if Self.is_var_self pvar (CContext.is_objc_method context) && CType.is_class typ then - let class_name = CContext.get_curr_class_typename stmt_info context in - if trans_state.is_fst_arg_objc_instance_method_call then - raise - (Self.SelfClassException - {class_name; position= __POS__; source_range= stmt_info.Clang_ast_t.si_source_range}) - else - let exp_typ = sizeof_expr_class class_name in - exp_typ + sizeof_expr_class (CContext.get_curr_class_typename stmt_info context) else (var_exp, typ) in L.(debug Capture Verbose) "@\n@\n PVAR ='%s'@\n@\n" (Pvar.to_string pvar) ; @@ -1289,13 +1282,7 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s CMethod_trans.get_class_name_method_call_from_receiver_kind context obj_c_message_expr_info act_params in - if trans_state.is_fst_arg_objc_instance_method_call && is_receiver_instance receiver_kind then - raise - (Self.SelfClassException - {class_name; position= __POS__; source_range= si.Clang_ast_t.si_source_range}) - else - let exp, typ = sizeof_expr_class class_name in - Some (mk_trans_result (exp, typ) empty_control) + Some (mk_trans_result (sizeof_expr_class class_name) empty_control) else if (* alloc or new *) String.equal selector CFrontend_config.alloc @@ -1347,23 +1334,24 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s let param_trans_results = List.mapi ~f:(exec_instruction_with_trans_state trans_state_param callee_ms_opt) rest in - try - let trans_state_param' = - if is_receiver_instance obj_c_message_expr_info.Clang_ast_t.omei_receiver_kind then - {trans_state_param with is_fst_arg_objc_instance_method_call= true} - else {trans_state_param with is_fst_arg_objc_instance_method_call= false} - in - let fst_res_trans = - exec_instruction_with_trans_state trans_state_param' callee_ms_opt 0 stmt - in - (obj_c_message_expr_info, fst_res_trans :: param_trans_results) - with Self.SelfClassException e -> - let pointer = obj_c_message_expr_info.Clang_ast_t.omei_decl_pointer in - let selector = obj_c_message_expr_info.Clang_ast_t.omei_selector in - let obj_c_message_expr_info = - Ast_expressions.make_obj_c_message_expr_info_class selector e.class_name pointer - in - (obj_c_message_expr_info, param_trans_results) ) + let trans_state_param' = + { trans_state_param with + is_fst_arg_objc_instance_method_call= + is_receiver_instance obj_c_message_expr_info.Clang_ast_t.omei_receiver_kind } + in + match CTrans_utils.should_remove_first_param trans_state_param' stmt with + | Some class_name -> + let pointer = obj_c_message_expr_info.Clang_ast_t.omei_decl_pointer in + let selector = obj_c_message_expr_info.Clang_ast_t.omei_selector in + let obj_c_message_expr_info = + Ast_expressions.make_obj_c_message_expr_info_class selector class_name pointer + in + (obj_c_message_expr_info, param_trans_results) + | None -> + let fst_res_trans = + exec_instruction_with_trans_state trans_state_param' callee_ms_opt 0 stmt + in + (obj_c_message_expr_info, fst_res_trans :: param_trans_results) ) | [] -> (obj_c_message_expr_info, []) diff --git a/infer/src/clang/cTrans_utils.ml b/infer/src/clang/cTrans_utils.ml index 92bd2dc0e..4a11cd90e 100644 --- a/infer/src/clang/cTrans_utils.ml +++ b/infer/src/clang/cTrans_utils.ml @@ -581,10 +581,6 @@ let extract_stmt_from_singleton stmt_list source_range warning_string = module Self = struct - exception - SelfClassException of - {class_name: Typ.Name.t; position: Logging.ocaml_pos; source_range: Clang_ast_t.source_range} - let add_self_parameter_for_super_instance stmt_info context procname loc mei = if is_superinstance mei then let typ, self_expr, instrs = @@ -644,3 +640,33 @@ let last_or_mk_fresh_void_exp_typ exp_typs = last_exp_typ | None -> mk_fresh_void_exp_typ () + + +let should_remove_first_param {context= {tenv} as context; is_fst_arg_objc_instance_method_call} + stmt = + let some_class_name stmt_info = Some (CContext.get_curr_class_typename stmt_info context) in + match (stmt : Clang_ast_t.stmt) with + | ImplicitCastExpr + ( _ + , [ DeclRefExpr + ( stmt_info + , _ + , _ + , {drti_decl_ref= Some {dr_name= Some {ni_name= name}; dr_qual_type= Some qual_type}} ) + ] + , _ + , {cei_cast_kind= `LValueToRValue} ) + when is_fst_arg_objc_instance_method_call && String.equal name "self" + && CType.is_class (CType_decl.qual_type_to_sil_type tenv qual_type) -> + some_class_name stmt_info + | ObjCMessageExpr + ( _ + , [ ImplicitCastExpr + (_, [DeclRefExpr (stmt_info, _, _, _)], _, {cei_cast_kind= `LValueToRValue}) ] + , _ + , {omei_selector= selector} ) + when is_fst_arg_objc_instance_method_call && String.equal selector CFrontend_config.class_method + -> + some_class_name stmt_info + | _ -> + None diff --git a/infer/src/clang/cTrans_utils.mli b/infer/src/clang/cTrans_utils.mli index f3dabfc96..2283185e2 100644 --- a/infer/src/clang/cTrans_utils.mli +++ b/infer/src/clang/cTrans_utils.mli @@ -231,10 +231,6 @@ end (** This module handles the translation of the variable self which is challenging because self is used both as a variable in instance method calls and also as a type in class method calls. *) module Self : sig - exception - SelfClassException of - {class_name: Typ.Name.t; position: Logging.ocaml_pos; source_range: Clang_ast_t.source_range} - val add_self_parameter_for_super_instance : Clang_ast_t.stmt_info -> CContext.t @@ -256,3 +252,7 @@ val mk_fresh_void_id_typ : unit -> Ident.t * Typ.t val mk_fresh_void_return : unit -> (Ident.t * Typ.t) * (Exp.t * Typ.t) val last_or_mk_fresh_void_exp_typ : (Exp.t * Typ.t) list -> Exp.t * Typ.t + +val should_remove_first_param : trans_state -> Clang_ast_t.stmt -> Typ.name option +(** Return a class name when the first parameter should be removed according to its context, for + example, when [self] or [\[x class\]] is given as the first parameter for a class method. *) diff --git a/infer/tests/codetoanalyze/objc/frontend/self_static/Self.m.dot b/infer/tests/codetoanalyze/objc/frontend/self_static/Self.m.dot index 950cfce8a..b839c8f02 100644 --- a/infer/tests/codetoanalyze/objc/frontend/self_static/Self.m.dot +++ b/infer/tests/codetoanalyze/objc/frontend/self_static/Self.m.dot @@ -61,18 +61,18 @@ digraph cfg { "calling_super#A#class.0edc1d1d1c4ade7cd9adaa77e7322ad1_2" [label="2: Exit A.calling_super \n " color=yellow style=filled] -"calling_super#A#class.0edc1d1d1c4ade7cd9adaa77e7322ad1_3" [label="3: Message Call: test_class \n n$18=_fun_C.test_class() [line 82, column 3]\n " shape="box"] +"calling_super#A#class.0edc1d1d1c4ade7cd9adaa77e7322ad1_3" [label="3: Message Call: test_class \n n$15=_fun_C.test_class() [line 82, column 3]\n " shape="box"] "calling_super#A#class.0edc1d1d1c4ade7cd9adaa77e7322ad1_3" -> "calling_super#A#class.0edc1d1d1c4ade7cd9adaa77e7322ad1_2" ; -"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_1" [label="1: Start A.class_method_fst_arg_of_class_method_inside_instance_method\nFormals: \nLocals: 0$?%__sil_tmpSIL_temp_conditional___n$30:NSBundle* stringsBundlePath:NSString* \n " color=yellow style=filled] +"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_1" [label="1: Start A.class_method_fst_arg_of_class_method_inside_instance_method\nFormals: \nLocals: 0$?%__sil_tmpSIL_temp_conditional___n$27:NSBundle* stringsBundlePath:NSString* \n " color=yellow style=filled] "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_1" -> "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_11" ; "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_2" [label="2: Exit A.class_method_fst_arg_of_class_method_inside_instance_method \n " color=yellow style=filled] -"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_3" [label="3: Return Stmt \n n$27=*&#GB$A.class_method_fst_arg_of_class_method_inside_instance_method.bundle:NSBundle* [line 120, column 10]\n *&return:NSBundle*=n$27 [line 120, column 3]\n " shape="box"] +"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_3" [label="3: Return Stmt \n n$24=*&#GB$A.class_method_fst_arg_of_class_method_inside_instance_method.bundle:NSBundle* [line 120, column 10]\n *&return:NSBundle*=n$24 [line 120, column 3]\n " shape="box"] "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_3" -> "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_2" ; @@ -80,32 +80,32 @@ digraph cfg { "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_4" -> "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_10" ; -"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_5" [label="5: Prune (true branch, boolean exp) \n PRUNE(n$29, true); [line 119, column 12]\n " shape="invhouse"] +"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_5" [label="5: Prune (true branch, boolean exp) \n PRUNE(n$26, true); [line 119, column 12]\n " shape="invhouse"] "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_5" -> "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_7" ; -"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_6" [label="6: Prune (false branch, boolean exp) \n PRUNE(!n$29, false); [line 119, column 12]\n " shape="invhouse"] +"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_6" [label="6: Prune (false branch, boolean exp) \n PRUNE(!n$26, false); [line 119, column 12]\n " shape="invhouse"] "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_6" -> "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_8" ; -"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_7" [label="7: ConditionalStmt Branch \n *&0$?%__sil_tmpSIL_temp_conditional___n$30:NSBundle*=n$29 [line 119, column 12]\n " shape="box"] +"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_7" [label="7: ConditionalStmt Branch \n *&0$?%__sil_tmpSIL_temp_conditional___n$27:NSBundle*=n$26 [line 119, column 12]\n " shape="box"] "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_7" -> "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_4" ; -"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_8" [label="8: ConditionalStmt Branch \n n$31=_fun_NSBundle.mainBundle() [line 119, column 59]\n *&0$?%__sil_tmpSIL_temp_conditional___n$30:NSBundle*=n$31 [line 119, column 12]\n " shape="box"] +"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_8" [label="8: ConditionalStmt Branch \n n$28=_fun_NSBundle.mainBundle() [line 119, column 59]\n *&0$?%__sil_tmpSIL_temp_conditional___n$27:NSBundle*=n$28 [line 119, column 12]\n " shape="box"] "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_8" -> "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_4" ; -"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_9" [label="9: BinaryConditionalStmt Init \n n$28=*&stringsBundlePath:NSString* [line 119, column 37]\n n$29=_fun_NSBundle.bundleWithPath:(n$28:NSString*) [line 119, column 12]\n " shape="box"] +"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_9" [label="9: BinaryConditionalStmt Init \n n$25=*&stringsBundlePath:NSString* [line 119, column 37]\n n$26=_fun_NSBundle.bundleWithPath:(n$25:NSString*) [line 119, column 12]\n " shape="box"] "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_9" -> "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_5" ; "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_9" -> "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_6" ; -"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_10" [label="10: BinaryOperatorStmt: Assign \n n$32=*&0$?%__sil_tmpSIL_temp_conditional___n$30:NSBundle* [line 119, column 12]\n *&#GB$A.class_method_fst_arg_of_class_method_inside_instance_method.bundle:NSBundle*=n$32 [line 119, column 3]\n " shape="box"] +"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_10" [label="10: BinaryOperatorStmt: Assign \n n$29=*&0$?%__sil_tmpSIL_temp_conditional___n$27:NSBundle* [line 119, column 12]\n *&#GB$A.class_method_fst_arg_of_class_method_inside_instance_method.bundle:NSBundle*=n$29 [line 119, column 3]\n " shape="box"] "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_10" -> "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_3" ; -"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_11" [label="11: DeclStmt \n VARIABLE_DECLARED(stringsBundlePath:NSString*); [line 116, column 3]\n n$35=_fun_NSBundle.bundleForClass:(sizeof(t=B):unsigned long) [line 117, column 8]\n n$33=_fun_NSString.stringWithUTF8String:(\"Strings\":char* const ) [line 117, column 60]\n n$34=_fun_NSString.stringWithUTF8String:(\"bundle\":char* const ) [line 118, column 60]\n n$36=_fun_NSBundle.pathForResource:ofType:(n$35:NSBundle*,n$33:NSString*,n$34:NSString*) virtual [line 117, column 7]\n *&stringsBundlePath:NSString*=n$36 [line 116, column 3]\n " shape="box"] +"class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_11" [label="11: DeclStmt \n VARIABLE_DECLARED(stringsBundlePath:NSString*); [line 116, column 3]\n n$32=_fun_NSBundle.bundleForClass:(sizeof(t=B):unsigned long) [line 117, column 8]\n n$30=_fun_NSString.stringWithUTF8String:(\"Strings\":char* const ) [line 117, column 60]\n n$31=_fun_NSString.stringWithUTF8String:(\"bundle\":char* const ) [line 118, column 60]\n n$33=_fun_NSBundle.pathForResource:ofType:(n$32:NSBundle*,n$30:NSString*,n$31:NSString*) virtual [line 117, column 7]\n *&stringsBundlePath:NSString*=n$33 [line 116, column 3]\n " shape="box"] "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_11" -> "class_method_fst_arg_of_class_method_inside_instance_method#A#class.7bda69c598fb7e024d776cec3122e2a6_9" ; @@ -131,16 +131,16 @@ digraph cfg { "used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_4" -> "used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_2" ; -"used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_5" [label="5: BinaryOperatorStmt: NE \n n$23=*sizeof(t=A):objc_class* [line 94, column 7]\n n$24=*&c:objc_class* [line 94, column 15]\n " shape="box"] +"used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_5" [label="5: BinaryOperatorStmt: NE \n n$20=*sizeof(t=A):objc_class* [line 94, column 7]\n n$21=*&c:objc_class* [line 94, column 15]\n " shape="box"] "used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_5" -> "used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_6" ; "used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_5" -> "used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_7" ; -"used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_6" [label="6: Prune (true branch, if) \n PRUNE((n$23 != n$24), true); [line 94, column 7]\n " shape="invhouse"] +"used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_6" [label="6: Prune (true branch, if) \n PRUNE((n$20 != n$21), true); [line 94, column 7]\n " shape="invhouse"] "used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_6" -> "used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_8" ; -"used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_7" [label="7: Prune (false branch, if) \n PRUNE(!(n$23 != n$24), false); [line 94, column 7]\n " shape="invhouse"] +"used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_7" [label="7: Prune (false branch, if) \n PRUNE(!(n$20 != n$21), false); [line 94, column 7]\n " shape="invhouse"] "used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_7" -> "used_in_binary_op:#A(struct objc_class)#class.da9fc6494d494952f5246c6cf4478263_9" ; @@ -159,7 +159,7 @@ digraph cfg { "call_alloc_instance#A#instance.70a20314d55f22fb46408deb70d9aabb_2" [label="2: Exit A.call_alloc_instance \n " color=yellow style=filled] -"call_alloc_instance#A#instance.70a20314d55f22fb46408deb70d9aabb_3" [label="3: Call alloc \n n$7=_fun___objc_alloc_no_fail(sizeof(t=A):unsigned long) [line 61, column 3]\n " shape="box"] +"call_alloc_instance#A#instance.70a20314d55f22fb46408deb70d9aabb_3" [label="3: Call alloc \n n$6=_fun___objc_alloc_no_fail(sizeof(t=A):unsigned long) [line 61, column 3]\n " shape="box"] "call_alloc_instance#A#instance.70a20314d55f22fb46408deb70d9aabb_3" -> "call_alloc_instance#A#instance.70a20314d55f22fb46408deb70d9aabb_2" ; @@ -170,7 +170,7 @@ digraph cfg { "call_class_instance#A#instance.eb1ae02cd94582eb1fc7cb426794f9f0_2" [label="2: Exit A.call_class_instance \n " color=yellow style=filled] -"call_class_instance#A#instance.eb1ae02cd94582eb1fc7cb426794f9f0_3" [label="3: Message Call: test_class \n n$9=_fun_C.test_class() [line 65, column 3]\n " shape="box"] +"call_class_instance#A#instance.eb1ae02cd94582eb1fc7cb426794f9f0_3" [label="3: Message Call: test_class \n n$7=_fun_C.test_class() [line 65, column 3]\n " shape="box"] "call_class_instance#A#instance.eb1ae02cd94582eb1fc7cb426794f9f0_3" -> "call_class_instance#A#instance.eb1ae02cd94582eb1fc7cb426794f9f0_2" ; @@ -181,7 +181,7 @@ digraph cfg { "call_class_instance_with_class_name#A#instance.1baf88c0fb5549c04909fab0bed63c39_2" [label="2: Exit A.call_class_instance_with_class_name \n " color=yellow style=filled] -"call_class_instance_with_class_name#A#instance.1baf88c0fb5549c04909fab0bed63c39_3" [label="3: Message Call: test_class \n n$10=_fun_A.test_class() [line 69, column 3]\n " shape="box"] +"call_class_instance_with_class_name#A#instance.1baf88c0fb5549c04909fab0bed63c39_3" [label="3: Message Call: test_class \n n$8=_fun_A.test_class() [line 69, column 3]\n " shape="box"] "call_class_instance_with_class_name#A#instance.1baf88c0fb5549c04909fab0bed63c39_3" -> "call_class_instance_with_class_name#A#instance.1baf88c0fb5549c04909fab0bed63c39_2" ; @@ -203,7 +203,7 @@ digraph cfg { "class_method_fst_arg_of_class_method#A#instance.cf9f3087f45649c74ef1f7ca002450f2_2" [label="2: Exit A.class_method_fst_arg_of_class_method \n " color=yellow style=filled] -"class_method_fst_arg_of_class_method#A#instance.cf9f3087f45649c74ef1f7ca002450f2_3" [label="3: Return Stmt \n n$26=_fun_NSBundle.bundleForClass:(sizeof(t=A):unsigned long) [line 111, column 10]\n *&return:NSBundle*=n$26 [line 111, column 3]\n " shape="box"] +"class_method_fst_arg_of_class_method#A#instance.cf9f3087f45649c74ef1f7ca002450f2_3" [label="3: Return Stmt \n n$23=_fun_NSBundle.bundleForClass:(sizeof(t=A):unsigned long) [line 111, column 10]\n *&return:NSBundle*=n$23 [line 111, column 3]\n " shape="box"] "class_method_fst_arg_of_class_method#A#instance.cf9f3087f45649c74ef1f7ca002450f2_3" -> "class_method_fst_arg_of_class_method#A#instance.cf9f3087f45649c74ef1f7ca002450f2_2" ; @@ -225,7 +225,7 @@ digraph cfg { "init#A#instance.eee79aaaddd644404e17691a7e7d809a_2" [label="2: Exit A.init \n " color=yellow style=filled] -"init#A#instance.eee79aaaddd644404e17691a7e7d809a_3" [label="3: Message Call: init \n n$19=*&self:A* [line 86, column 3]\n n$20=_fun_NSObject.init(n$19:A*) [line 86, column 3]\n " shape="box"] +"init#A#instance.eee79aaaddd644404e17691a7e7d809a_3" [label="3: Message Call: init \n n$16=*&self:A* [line 86, column 3]\n n$17=_fun_NSObject.init(n$16:A*) [line 86, column 3]\n " shape="box"] "init#A#instance.eee79aaaddd644404e17691a7e7d809a_3" -> "init#A#instance.eee79aaaddd644404e17691a7e7d809a_2" ; @@ -236,7 +236,7 @@ digraph cfg { "loggerName#A#instance.36b9a42412bcf7d8d3f8397eb2bcb555_2" [label="2: Exit A.loggerName \n " color=yellow style=filled] -"loggerName#A#instance.36b9a42412bcf7d8d3f8397eb2bcb555_3" [label="3: Return Stmt \n n$22=_fun_NSStringFromClass(sizeof(t=A):unsigned long) [line 90, column 10]\n *&return:NSString*=n$22 [line 90, column 3]\n " shape="box"] +"loggerName#A#instance.36b9a42412bcf7d8d3f8397eb2bcb555_3" [label="3: Return Stmt \n n$19=_fun_NSStringFromClass(sizeof(t=A):unsigned long) [line 90, column 10]\n *&return:NSString*=n$19 [line 90, column 3]\n " shape="box"] "loggerName#A#instance.36b9a42412bcf7d8d3f8397eb2bcb555_3" -> "loggerName#A#instance.36b9a42412bcf7d8d3f8397eb2bcb555_2" ; @@ -247,11 +247,11 @@ digraph cfg { "t#A#instance.e31b9a7bced712626784e2860af1a31b_2" [label="2: Exit A.t \n " color=yellow style=filled] -"t#A#instance.e31b9a7bced712626784e2860af1a31b_3" [label="3: Message Call: b_m \n n$12=_fun_B.b_m() [line 74, column 3]\n " shape="box"] +"t#A#instance.e31b9a7bced712626784e2860af1a31b_3" [label="3: Message Call: b_m \n n$9=_fun_B.b_m() [line 74, column 3]\n " shape="box"] "t#A#instance.e31b9a7bced712626784e2860af1a31b_3" -> "t#A#instance.e31b9a7bced712626784e2860af1a31b_2" ; -"t#A#instance.e31b9a7bced712626784e2860af1a31b_4" [label="4: DeclStmt \n VARIABLE_DECLARED(b:B*); [line 73, column 3]\n n$13=_fun___objc_alloc_no_fail(sizeof(t=B):unsigned long) [line 73, column 10]\n n$14=_fun_NSObject.init(n$13:B*) virtual [line 73, column 10]\n *&b:B*=n$14 [line 73, column 3]\n " shape="box"] +"t#A#instance.e31b9a7bced712626784e2860af1a31b_4" [label="4: DeclStmt \n VARIABLE_DECLARED(b:B*); [line 73, column 3]\n n$10=_fun___objc_alloc_no_fail(sizeof(t=B):unsigned long) [line 73, column 10]\n n$11=_fun_NSObject.init(n$10:B*) virtual [line 73, column 10]\n *&b:B*=n$11 [line 73, column 3]\n " shape="box"] "t#A#instance.e31b9a7bced712626784e2860af1a31b_4" -> "t#A#instance.e31b9a7bced712626784e2860af1a31b_3" ; @@ -269,7 +269,7 @@ digraph cfg { "use_class_in_other_ways:#A(class B)#instance.7a96604c2c855db834d214f72f83a306_2" [label="2: Exit A.use_class_in_other_ways: \n " color=yellow style=filled] -"use_class_in_other_ways:#A(class B)#instance.7a96604c2c855db834d214f72f83a306_3" [label="3: Return Stmt \n n$16=*&object:B* [line 78, column 11]\n n$17=_fun_B.isC:(n$16:B*,sizeof(t=A):unsigned long) virtual [line 78, column 10]\n *&return:_Bool=n$17 [line 78, column 3]\n " shape="box"] +"use_class_in_other_ways:#A(class B)#instance.7a96604c2c855db834d214f72f83a306_3" [label="3: Return Stmt \n n$13=*&object:B* [line 78, column 11]\n n$14=_fun_B.isC:(n$13:B*,sizeof(t=A):unsigned long) virtual [line 78, column 10]\n *&return:_Bool=n$14 [line 78, column 3]\n " shape="box"] "use_class_in_other_ways:#A(class B)#instance.7a96604c2c855db834d214f72f83a306_3" -> "use_class_in_other_ways:#A(class B)#instance.7a96604c2c855db834d214f72f83a306_2" ; diff --git a/infer/tests/codetoanalyze/objc/performance/NSString.m b/infer/tests/codetoanalyze/objc/performance/NSString.m index ead87a3da..25be4dab7 100644 --- a/infer/tests/codetoanalyze/objc/performance/NSString.m +++ b/infer/tests/codetoanalyze/objc/performance/NSString.m @@ -123,3 +123,14 @@ NSString* string_by_appending_path_component_linear(NSString* path, bool string_has_prefix_linear(NSString* str, NSString* prefix) { return [str hasPrefix:prefix]; } + +@interface DummyClass : NSObject +@end + +@implementation DummyClass + ++ (void)call_string_by_appending_string_constant_FP { + NSString* s = [NSStringFromClass(self) stringByAppendingString:@"abc"]; +} + +@end diff --git a/infer/tests/codetoanalyze/objc/performance/cost-issues.exp b/infer/tests/codetoanalyze/objc/performance/cost-issues.exp index 8e05a1372..78dbf90b3 100644 --- a/infer/tests/codetoanalyze/objc/performance/cost-issues.exp +++ b/infer/tests/codetoanalyze/objc/performance/cost-issues.exp @@ -81,6 +81,8 @@ codetoanalyze/objc/performance/NSSet.m, nsset_init_with_array_linear, 7 + 3 ⋅ codetoanalyze/objc/performance/NSSet.m, nsset_init_with_set_constant, 6, OnUIThread:false, [] codetoanalyze/objc/performance/NSSet.m, nsset_iterate_linear, 6 + 8 ⋅ (set->elements.length.ub + 1), OnUIThread:false, [{set->elements.length.ub + 1},Loop] codetoanalyze/objc/performance/NSSet.m, nsset_next_object_linear, 5 + 5 ⋅ (set->elements.length.ub + 1), OnUIThread:false, [{set->elements.length.ub + 1},Loop] +codetoanalyze/objc/performance/NSString.m, DummyClass.call_string_by_appending_string_constant_FP, ⊤, OnUIThread:false, [Unbounded loop,Modeled call to NSString.stringByAppendingString:] +codetoanalyze/objc/performance/NSString.m, DummyClass.dealloc, 1, OnUIThread:false, [] codetoanalyze/objc/performance/NSString.m, call_component_separated_by_char_constant, 46, OnUIThread:false, [] codetoanalyze/objc/performance/NSString.m, call_init_with_string_constant, 15, OnUIThread:false, [] codetoanalyze/objc/performance/NSString.m, component_seperated_by_char_linear, 6 + m.length.ub + 3 ⋅ (-1+max(2, m.length.ub)) + 3 ⋅ (max(2, m.length.ub)), OnUIThread:false, [{max(2, m.length.ub)},Loop,{-1+max(2, m.length.ub)},Loop,{m.length.ub},Modeled call to NSString.componentsSeparatedByString:] diff --git a/infer/tests/codetoanalyze/objc/performance/issues.exp b/infer/tests/codetoanalyze/objc/performance/issues.exp index 8baaa5fbe..bbe760e79 100644 --- a/infer/tests/codetoanalyze/objc/performance/issues.exp +++ b/infer/tests/codetoanalyze/objc/performance/issues.exp @@ -12,6 +12,7 @@ codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_set_linear, 2, INTEGER codetoanalyze/objc/performance/NSSet.m, nsset_enumerator_linear, 7, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,,Assignment,Binary operation: ([0, +oo] + [1, set->elements.length.ub + 1]):signed64] codetoanalyze/objc/performance/NSSet.m, nsset_init_constant, 3, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] codetoanalyze/objc/performance/NSSet.m, nsset_iterate_linear, 3, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,,Assignment,Binary operation: ([0, +oo] + [1, set->elements.length.ub + 1]):signed64] +codetoanalyze/objc/performance/NSString.m, DummyClass.call_string_by_appending_string_constant_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Modeled call to NSString.stringByAppendingString:] codetoanalyze/objc/performance/NSString.m, init_string_constant, 2, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] codetoanalyze/objc/performance/NSString.m, replace_linear_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSString.m, replace_linear_FP, 2, INTEGER_OVERFLOW_U5, no_bucket, ERROR, [,Unknown value from: NSString.stringByReplacingOccurrencesOfString:withString:,Binary operation: ([0, +oo] + 1):signed32]