diff --git a/infer/src/clang/cPredicates.ml b/infer/src/clang/cPredicates.ml index d47fb9039..bfd1f7a35 100644 --- a/infer/src/clang/cPredicates.ml +++ b/infer/src/clang/cPredicates.ml @@ -690,28 +690,6 @@ let within_available_class_block (cxt: CLintersContext.context) an = | _ -> false -let objc_method_has_nth_parameter_of_type an _num _typ = - let open Clang_ast_t in - let num = - match _num with - | ALVar.Const n -> ( - try int_of_string n - with Failure _ -> -1 ) - | _ - -> -1 - in - match (num, an, _typ) with - | -1, _, _ - -> false - | _, Ctl_parser_types.Decl ObjCMethodDecl (_, _, omdi), ALVar.Const typ -> ( - match List.nth omdi.omdi_parameters num with - | Some ParmVarDecl (_, _, qt, _) - -> type_ptr_equal_type qt.qt_type_ptr typ - | _ - -> false ) - | _, _, _ - -> false - let using_namespace an namespace = let open Clang_ast_t in match an with @@ -766,3 +744,15 @@ let has_used_attribute an = let open Clang_ast_t in let attributes = get_decl_attributes_for_callexpr an in List.exists ~f:(fun attr -> match attr with UsedAttr _ -> true | _ -> false) attributes + +let has_value an al_exp = + let open Clang_ast_t in + let open Ctl_parser_types in + match an with + | Stmt IntegerLiteral (_, _, _, integer_literal_info) + -> let value = integer_literal_info.Clang_ast_t.ili_value in + ALVar.compare_str_with_alexp value al_exp + | Stmt StringLiteral (_, _, _, s) + -> ALVar.compare_str_with_alexp s al_exp + | _ + -> false diff --git a/infer/src/clang/cPredicates.mli b/infer/src/clang/cPredicates.mli index be4f763aa..46a796a58 100644 --- a/infer/src/clang/cPredicates.mli +++ b/infer/src/clang/cPredicates.mli @@ -95,6 +95,8 @@ val class_unavailable_in_supported_ios_sdk : val has_type : Ctl_parser_types.ast_node -> ALVar.alexp -> bool +val has_value : Ctl_parser_types.ast_node -> ALVar.alexp -> bool + val method_return_type : Ctl_parser_types.ast_node -> ALVar.alexp -> bool val has_type_subprotocol_of : Ctl_parser_types.ast_node -> ALVar.alexp -> bool @@ -106,9 +108,6 @@ val get_selector : Ctl_parser_types.ast_node -> string option val within_responds_to_selector_block : CLintersContext.context -> Ctl_parser_types.ast_node -> bool -val objc_method_has_nth_parameter_of_type : - Ctl_parser_types.ast_node -> ALVar.alexp -> ALVar.alexp -> bool - val using_namespace : Ctl_parser_types.ast_node -> ALVar.alexp -> bool val receiver_class_method_call : Ctl_parser_types.ast_node -> Clang_ast_t.decl option diff --git a/infer/src/clang/cTL.ml b/infer/src/clang/cTL.ml index 9c07f07b8..b143841f5 100644 --- a/infer/src/clang/cTL.ml +++ b/infer/src/clang/cTL.ml @@ -24,6 +24,7 @@ type transitions = | InitExpr (** decl to stmt *) | Super (** decl to decl *) | ParameterName of ALVar.alexp (** stmt to stmt, decl to decl *) + | ParameterPos of ALVar.alexp (** stmt to stmt, decl to decl *) | Parameters (** stmt to stmt, decl to decl *) | Cond | PointerToDecl (** stmt to decl *) @@ -32,7 +33,7 @@ type transitions = let is_transition_to_successor trans = match trans with - | Body | InitExpr | FieldName _ | Fields | ParameterName _ | Parameters | Cond + | Body | InitExpr | FieldName _ | Fields | ParameterName _ | ParameterPos _ | Parameters | Cond -> true | Super | PointerToDecl | Protocol -> false @@ -143,6 +144,8 @@ module Debug = struct -> Format.pp_print_string fmt "Super" | ParameterName name -> Format.pp_print_string fmt ("ParameterName " ^ ALVar.alexp_to_string name) + | ParameterPos pos + -> Format.pp_print_string fmt ("ParameterPos " ^ ALVar.alexp_to_string pos) | Parameters -> Format.pp_print_string fmt "Parameters" | Cond @@ -708,21 +711,62 @@ let parameter_of_corresp_name method_name args name = | None -> None -let transition_via_parameter_name an name = +let parameter_of_corresp_pos args pos = + let pos_int = + match pos with + | ALVar.Const n -> ( + try int_of_string n + with Failure _ -> -1 ) + | _ + -> -1 + in + List.nth args pos_int + +let transition_via_specified_parameter ~pos an key = + let invalid_param_name_use () = + Logging.die InternalError "Transition ParameterName is only available for ObjC methods" + in + let node_opt_to_ast_node_list f arg_stmt_opt = + match arg_stmt_opt with Some arg -> [f arg] | None -> [] + in + let apply_decl arg = Decl arg in + let apply_stmt arg = Stmt arg in match an with | Stmt ObjCMessageExpr (_, stmt_list, _, omei) - -> ( - let arg_stmt_opt = parameter_of_corresp_name omei.omei_selector stmt_list name in - match arg_stmt_opt with Some arg -> [Stmt arg] | None -> [] ) + -> let method_name = omei.omei_selector in + let parameter_of_corresp_key = + if pos then parameter_of_corresp_pos else parameter_of_corresp_name method_name + in + let arg_stmt_opt = parameter_of_corresp_key stmt_list key in + node_opt_to_ast_node_list apply_stmt arg_stmt_opt + | Stmt CallExpr (_, _ :: args, _) + -> let parameter_of_corresp_key = + if pos then parameter_of_corresp_pos else invalid_param_name_use () + in + let arg_stmt_opt = parameter_of_corresp_key args key in + node_opt_to_ast_node_list apply_stmt arg_stmt_opt | Decl ObjCMethodDecl (_, named_decl_info, omdi) - -> ( - let arg_decl_opt = - parameter_of_corresp_name named_decl_info.ni_name omdi.omdi_parameters name + -> let method_name = named_decl_info.ni_name in + let parameter_of_corresp_key = + if pos then parameter_of_corresp_pos else parameter_of_corresp_name method_name in - match arg_decl_opt with Some arg -> [Decl arg] | None -> [] ) + let arg_decl_opt = parameter_of_corresp_key omdi.omdi_parameters key in + node_opt_to_ast_node_list apply_decl arg_decl_opt + | Decl FunctionDecl (_, _, _, fdi) + | Decl CXXMethodDecl (_, _, _, fdi, _) + | Decl CXXConstructorDecl (_, _, _, fdi, _) + -> let parameter_of_corresp_key = + if pos then parameter_of_corresp_pos else invalid_param_name_use () + in + let arg_decl_opt = parameter_of_corresp_key fdi.fdi_parameters key in + node_opt_to_ast_node_list apply_decl arg_decl_opt | _ -> [] +let transition_via_parameter_name an name = transition_via_specified_parameter an name ~pos:false + +let transition_via_parameter_pos an pos = transition_via_specified_parameter an pos ~pos:true + let transition_via_fields an = let open Clang_ast_t in match an with @@ -789,6 +833,8 @@ let next_state_via_transition an trans = -> transition_stmt_to_decl_via_pointer st | an, ParameterName name -> transition_via_parameter_name an name + | an, ParameterPos pos + -> transition_via_parameter_pos an pos | _, _ -> [] @@ -836,6 +882,8 @@ let rec eval_Atomic _pred_name args an lcxt = -> CPredicates.has_cast_kind an name | "has_type", [typ], an -> CPredicates.has_type an typ + | "has_value", [typ], an + -> CPredicates.has_value an typ | "isa", [classname], an -> CPredicates.isa an classname | "is_assign_property", [], an @@ -890,8 +938,6 @@ let rec eval_Atomic _pred_name args an lcxt = -> CPredicates.method_return_type an typ | "within_responds_to_selector_block", [], an -> CPredicates.within_responds_to_selector_block lcxt an - | "objc_method_has_nth_parameter_of_type", [num; typ], an - -> CPredicates.objc_method_has_nth_parameter_of_type an num typ | "using_namespace", [namespace], an -> CPredicates.using_namespace an namespace | "is_at_selector_with_name", [name], an diff --git a/infer/src/clang/cTL.mli b/infer/src/clang/cTL.mli index c731c3aa5..cf375cd63 100644 --- a/infer/src/clang/cTL.mli +++ b/infer/src/clang/cTL.mli @@ -22,6 +22,7 @@ type transitions = | InitExpr (** decl to stmt *) | Super (** decl to decl *) | ParameterName of ALVar.alexp (** stmt to stmt, decl to decl *) + | ParameterPos of ALVar.alexp (** stmt to stmt, decl to decl *) | Parameters (** stmt to stmt, decl to decl *) | Cond | PointerToDecl (** stmt to decl *) diff --git a/infer/src/clang/ctl_lexer.mll b/infer/src/clang/ctl_lexer.mll index b01dceefa..b4c3d3573 100644 --- a/infer/src/clang/ctl_lexer.mll +++ b/infer/src/clang/ctl_lexer.mll @@ -75,6 +75,7 @@ rule token = parse | "FieldName" { FIELD_NAME } | "Parameters" { PARAMETERS } | "ParameterName" { PARAMETER_NAME } + | "ParameterPos" { PARAMETER_POS } | "Body" {BODY} | "Protocol" {PROTOCOL} | "InitExpr" {INIT_EXPR} diff --git a/infer/src/clang/ctl_parser.mly b/infer/src/clang/ctl_parser.mly index 0d211e878..921ab7a67 100644 --- a/infer/src/clang/ctl_parser.mly +++ b/infer/src/clang/ctl_parser.mly @@ -83,6 +83,7 @@ %token FIELD_NAME %token PARAMETERS %token PARAMETER_NAME +%token PARAMETER_POS %token POINTER_TO_DECL %token PROTOCOL %token EOF @@ -244,6 +245,7 @@ transition_label: | INIT_EXPR { Some CTL.InitExpr } | PARAMETERS { Some CTL.Parameters } | PARAMETER_NAME alexp { Some (CTL.ParameterName $2) } + | PARAMETER_POS alexp { Some (CTL.ParameterPos $2) } | POINTER_TO_DECL { Some CTL.PointerToDecl } | PROTOCOL { Some CTL.Protocol } ; diff --git a/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp b/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp index 608d88ee8..48b97dab6 100644 --- a/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp +++ b/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp @@ -73,7 +73,7 @@ codetoanalyze/objc/linters-for-test-only/subclassing.m, Linters_dummy_method, 53 codetoanalyze/objc/linters-for-test-only/subclassing.m, Linters_dummy_method, 53, SUBCLASSING_TEST_EXAMPLE, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, Linters_dummy_method, 111, TEST_TYPEDEF_CHECK, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, Linters_dummy_method, 119, TEST_TYPEDEF_CHECK, [] -codetoanalyze/objc/linters-for-test-only/subclassing.m, Linters_dummy_method, 123, TEST_TYPEDEF_CHECK, [] +codetoanalyze/objc/linters-for-test-only/subclassing.m, Linters_dummy_method, 125, TEST_TYPEDEF_CHECK, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m1, 73, TEST_BUILTIN_TYPE, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m10, 82, TEST_BUILTIN_TYPE, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m11, 83, TEST_BUILTIN_TYPE, [] @@ -93,13 +93,14 @@ codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m24, 100, TEST_ codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m25, 101, TEST_BUILTIN_TYPE, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m26, 102, TEST_BUILTIN_TYPE, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m26, 103, TEST_BUILTIN_TYPE, [] -codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m26, 103, TEST_NTH_PARAM_TYPE_CHECK, [] +codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m26, 105, TEST_NTH_PARAM_TYPE_CHECK, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m26, 105, TEST_PARAM_TYPE_CHECK, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m3, 75, TEST_BUILTIN_TYPE, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m4, 76, TEST_BUILTIN_TYPE, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m7, 79, TEST_BUILTIN_TYPE, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m8, 80, TEST_BUILTIN_TYPE, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m9, 81, TEST_BUILTIN_TYPE, [] +codetoanalyze/objc/linters-for-test-only/subclassing.m, bar, 129, TEST_NTH_PARAM_TYPE_CHECK_FUNCTION, [] codetoanalyze/objc/linters-for-test-only/visibility.c, bar, 31, TEST_DEFAULT_VISIBILITY, [] codetoanalyze/objc/linters-for-test-only/visibility.c, bar, 32, TEST_DEFAULT_VISIBILITY, [] codetoanalyze/objc/linters-for-test-only/visibility.c, bar, 33, TEST_HIDDEN_VISIBILITY, [] diff --git a/infer/tests/codetoanalyze/objc/linters-for-test-only/linters_example.al b/infer/tests/codetoanalyze/objc/linters-for-test-only/linters_example.al index da943f191..7de51a390 100644 --- a/infer/tests/codetoanalyze/objc/linters-for-test-only/linters_example.al +++ b/infer/tests/codetoanalyze/objc/linters-for-test-only/linters_example.al @@ -211,12 +211,21 @@ DEFINE-CHECKER TEST_PARAM_TYPE_CHECK2 = { }; DEFINE-CHECKER TEST_NTH_PARAM_TYPE_CHECK = { - SET report_when = - WHEN - objc_method_has_nth_parameter_of_type("2", "REGEXP('This.+')*") - HOLDS-IN-NODE ObjCMethodDecl; + WHEN + HOLDS-NEXT WITH-TRANSITION ParameterPos "2" + (has_type("REGEXP('This.+')*")) + HOLDS-IN-NODE ObjCMethodDecl; + SET message = "Found a method with nth parameter of type...."; + SET severity = "LIKE"; +}; +DEFINE-CHECKER TEST_NTH_PARAM_TYPE_CHECK_FUNCTION = { + SET report_when = + WHEN + HOLDS-NEXT WITH-TRANSITION ParameterPos "1" + (has_value("2") HOLDS-EVENTUALLY) + HOLDS-IN-NODE CallExpr; SET message = "Found a method with nth parameter of type...."; SET severity = "LIKE"; }; diff --git a/infer/tests/codetoanalyze/objc/linters-for-test-only/subclassing.m b/infer/tests/codetoanalyze/objc/linters-for-test-only/subclassing.m index 667c7c3da..4d747329a 100644 --- a/infer/tests/codetoanalyze/objc/linters-for-test-only/subclassing.m +++ b/infer/tests/codetoanalyze/objc/linters-for-test-only/subclassing.m @@ -118,6 +118,12 @@ typedef struct { my_pS p; -typedef struct Node { struct my_listNode* next; } my_listNode; +typedef struct Node { + struct my_listNode* next; +} my_listNode; my_listNode ln; + +void foo(int n, int m) {} + +void bar() { foo(0, 2); }