From bdd2395be6a12e3d5f63a3f9a408e05cbe050f87 Mon Sep 17 00:00:00 2001 From: Dino Distefano Date: Fri, 19 May 2017 02:47:56 -0700 Subject: [PATCH] Adding ability to get type of paramters. Reviewed By: dulmarod Differential Revision: D5069844 fbshipit-source-id: c591f76 --- infer/src/clang/cFrontend_checkers.ml | 6 +-- infer/src/clang/cPredicates.ml | 16 +++++++ infer/src/clang/cPredicates.mli | 6 ++- infer/src/clang/cTL.ml | 46 ++++++++++++------- infer/src/clang/cTL.mli | 3 +- infer/src/clang/ctl_parser.mly | 1 + .../objc/linters-for-test-only/issues.exp | 3 ++ .../linters-for-test-only/linters_example.al | 32 ++++++++++++- .../objc/linters-for-test-only/subclassing.m | 4 ++ 9 files changed, 94 insertions(+), 23 deletions(-) diff --git a/infer/src/clang/cFrontend_checkers.ml b/infer/src/clang/cFrontend_checkers.ml index b9e5bfaf2..3131dc45b 100644 --- a/infer/src/clang/cFrontend_checkers.ml +++ b/infer/src/clang/cFrontend_checkers.ml @@ -47,9 +47,9 @@ let tag_name_of_node an = let decl_ref_or_selector_name an = match CTL.next_state_via_transition an (Some CTL.PointerToDecl) with - | Some (Ctl_parser_types.Decl ObjCMethodDecl _ as decl_an) -> + | [Ctl_parser_types.Decl ObjCMethodDecl _ as decl_an] -> "The selector " ^ (Ctl_parser_types.ast_node_name decl_an) - | Some (Ctl_parser_types.Decl _ as decl_an) -> + | [Ctl_parser_types.Decl _ as decl_an] -> "The reference " ^ (Ctl_parser_types.ast_node_name decl_an) | _ -> failwith("decl_ref_or_selector_name must be called with a DeclRefExpr \ or an ObjCMessageExpr, but got " ^ (tag_name_of_node an)) @@ -62,7 +62,7 @@ let iphoneos_target_sdk_version _ = let available_ios_sdk an = let open Ctl_parser_types in match CTL.next_state_via_transition an (Some CTL.PointerToDecl) with - | Some Decl decl -> + | [Decl decl] -> (match CPredicates.get_available_attr_ios_sdk (Decl decl) with | Some version -> version | None -> "") diff --git a/infer/src/clang/cPredicates.ml b/infer/src/clang/cPredicates.ml index e612e914e..faf7b0135 100644 --- a/infer/src/clang/cPredicates.ml +++ b/infer/src/clang/cPredicates.ml @@ -393,3 +393,19 @@ let within_responds_to_selector_block (cxt:CLintersContext.context) an = List.mem ~equal:String.equal in_selector_block named_decl_info.ni_name | None -> false) | _ -> 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 diff --git a/infer/src/clang/cPredicates.mli b/infer/src/clang/cPredicates.mli index db2dfc629..d4ed0e508 100644 --- a/infer/src/clang/cPredicates.mli +++ b/infer/src/clang/cPredicates.mli @@ -74,4 +74,8 @@ val method_return_type : Ctl_parser_types.ast_node -> ALVar.alexp -> bool val get_available_attr_ios_sdk : Ctl_parser_types.ast_node -> string option -val within_responds_to_selector_block : CLintersContext.context -> Ctl_parser_types.ast_node -> bool +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 diff --git a/infer/src/clang/cTL.ml b/infer/src/clang/cTL.ml index 09e192e8f..652436310 100644 --- a/infer/src/clang/cTL.ml +++ b/infer/src/clang/cTL.ml @@ -20,6 +20,7 @@ type transitions = | Body (** decl to stmt *) | InitExpr (** decl to stmt *) | Super (** decl to decl *) + | Parameters (** decl to decl *) | Cond | PointerToDecl (** stmt to decl *) @@ -96,6 +97,7 @@ module Debug = struct | Body -> Format.pp_print_string fmt "Body" | InitExpr -> Format.pp_print_string fmt "InitExpr" | Super -> Format.pp_print_string fmt "Super" + | Parameters -> Format.pp_print_string fmt "Parameters" | Cond -> Format.pp_print_string fmt "Cond" | PointerToDecl -> Format.pp_print_string fmt "PointerToDecl" in match trans_opt with @@ -537,25 +539,25 @@ let transition_decl_to_stmt d trs = | Some InitExpr, EnumConstantDecl (_, _, _, ecdi) -> ecdi.ecdi_init_expr | _, _ -> None in match temp_res with - | Some st -> Some (Stmt st) - | _ -> None + | Some st -> [Stmt st] + | _ -> [] let transition_decl_to_decl_via_super d = let decl_opt_to_ast_node_opt d_opt = match d_opt with - | Some d' -> Some (Decl d') - | None -> None in + | Some d' -> [Decl d'] + | None -> [] in let do_ObjCImplementationDecl d = match CAst_utils.get_impl_decl_info d with | Some idi -> decl_opt_to_ast_node_opt (CAst_utils.get_super_ObjCImplementationDecl idi) - | None -> None in + | None -> [] in match d with | Clang_ast_t.ObjCImplementationDecl _ -> do_ObjCImplementationDecl d | Clang_ast_t.ObjCInterfaceDecl (_, _, _, _, idi) -> decl_opt_to_ast_node_opt (CAst_utils.get_decl_opt_with_decl_ref idi.otdi_super) - | _ -> None + | _ -> [] let transition_stmt_to_stmt_via_condition st = let open Clang_ast_t in @@ -563,32 +565,40 @@ let transition_stmt_to_stmt_via_condition st = | IfStmt (_, _ :: _ :: cond :: _) | ConditionalOperator (_, cond:: _, _) | ForStmt (_, [_; _; cond; _; _]) - | WhileStmt (_, [_; cond; _]) -> Some (Stmt cond) - | _ -> None + | WhileStmt (_, [_; cond; _]) -> [Stmt cond] + | _ -> [] let transition_stmt_to_decl_via_pointer stmt = let open Clang_ast_t in match stmt with | ObjCMessageExpr (_, _, _, obj_c_message_expr_info) -> (match CAst_utils.get_decl_opt obj_c_message_expr_info.Clang_ast_t.omei_decl_pointer with - | Some decl -> Some (Decl decl) - | None -> None) + | Some decl -> [Decl decl] + | None -> []) | DeclRefExpr (_, _, _, decl_ref_expr_info) -> (match CAst_utils.get_decl_opt_with_decl_ref decl_ref_expr_info.Clang_ast_t.drti_decl_ref with - | Some decl -> Some (Decl decl) - | None -> None) - | _ -> None + | Some decl -> [Decl decl] + | None -> []) + | _ -> [] + +let transition_decl_to_decl_via_parameters dec = + let open Clang_ast_t in + match dec with + | ObjCMethodDecl (_, _, omdi) -> + List.map ~f:(fun d -> Decl d) omdi.omdi_parameters + | _ -> [] (* given a node an returns the node an' such that an transition to an' via label trans *) let next_state_via_transition an trans = match an, trans with | Decl d, Some Super -> transition_decl_to_decl_via_super d + | Decl d, Some Parameters -> transition_decl_to_decl_via_parameters d | Decl d, Some InitExpr | Decl d, Some Body -> transition_decl_to_stmt d trans | Stmt st, Some Cond -> transition_stmt_to_stmt_via_condition st | Stmt st, Some PointerToDecl -> transition_stmt_to_decl_via_pointer st - | _, _ -> None + | _, _ -> [] (* Evaluation of formulas *) @@ -628,6 +638,8 @@ let rec eval_Atomic _pred_name args an lcxt = | "method_return_type", [typ], an -> 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 | _ -> failwith ("ERROR: Undefined Predicate or wrong set of arguments: '" ^ pred_name ^ "'") @@ -651,9 +663,9 @@ and eval_EF phi an lcxt trans = (* Evaluate phi on node an' such that an -l-> an'. False if an' does not exists *) and evaluate_on_transition phi an lcxt l = - match next_state_via_transition an l with - | Some succ -> eval_formula phi succ lcxt - | None -> false + let succs = next_state_via_transition an l in + List.exists ~f:(fun an' -> eval_formula phi an' lcxt) succs + (* an, lcxt |= EX phi <=> exists an' in Successors(st): an', lcxt |= phi diff --git a/infer/src/clang/cTL.mli b/infer/src/clang/cTL.mli index 2c737a876..53a546230 100644 --- a/infer/src/clang/cTL.mli +++ b/infer/src/clang/cTL.mli @@ -20,6 +20,7 @@ type transitions = | Body (* decl to stmt *) | InitExpr (* decl to stmt *) | Super (* decl to decl *) + | Parameters (* decl to decl *) | Cond | PointerToDecl (* stmt to decl *) @@ -97,7 +98,7 @@ val eval_formula : t -> ast_node -> CLintersContext.context -> bool val save_dotty_when_in_debug_mode : SourceFile.t -> unit -val next_state_via_transition : ast_node -> transitions option -> ast_node option +val next_state_via_transition : ast_node -> transitions option -> ast_node list val create_ctl_evaluation_tracker : SourceFile.t -> unit diff --git a/infer/src/clang/ctl_parser.mly b/infer/src/clang/ctl_parser.mly index 4cbc4522d..61b9c110a 100644 --- a/infer/src/clang/ctl_parser.mly +++ b/infer/src/clang/ctl_parser.mly @@ -192,6 +192,7 @@ transition_label: | "Body" | "body" -> Some CTL.Body | "InitExpr" | "initexpr" -> Some CTL.InitExpr | "Cond" | "cond" -> Some CTL.Cond + | "Parameters" | "parameters" -> Some CTL.Parameters | "PointerToDecl" | "pointertodecl" -> Some CTL.PointerToDecl | _ -> None } ; 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 9fd5c7cd0..ea04607fb 100644 --- a/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp +++ b/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp @@ -47,6 +47,9 @@ codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m23, 99, TEST_B codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m24, 100, TEST_BUILTIN_TYPE, [] 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:pname2:pname3:pname4:, 103, TEST_BUILTIN_TYPE, [] +codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m26:pname2:pname3:pname4:, 103, TEST_NTH_PARAM_TYPE_CHECK, [] +codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m26:pname2:pname3:pname4:, 103, 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, [] 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 3405cf17f..9cee3a5d4 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 @@ -5,7 +5,17 @@ GLOBAL-MACROS { LET global_is_subclass_of(x) = is_class(x) HOLDS-IN-SOME-SUPERCLASS-OF ObjCInterfaceDecl; -}; + LET parameter_of_type(x) = + WHEN + has_type(x) + HOLDS-IN-NODE ParmVarDecl; + + LET method_has_a_parameter_with_type(x) = + WHEN + HOLDS-NEXT WITH-TRANSITION Parameters + (has_type(x)) + HOLDS-IN-NODE ObjCMethodDecl; + }; //Check that class A is not subclassed. @@ -153,3 +163,23 @@ DEFINE-CHECKER TEST_VAR_TYPE_CHECK = { SET message = "Var has type int or long"; }; + +DEFINE-CHECKER TEST_PARAM_TYPE_CHECK = { + + SET report_when = + method_has_a_parameter_with_type("REGEXP('This.+')*" ); + + SET message = "Found a method with a parameter of type...."; + +}; + +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; + + SET message = "Found a method with nth parameter of type...."; + +}; 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 cb8a0e6ce..eb3f0220b 100644 --- a/infer/tests/codetoanalyze/objc/linters-for-test-only/subclassing.m +++ b/infer/tests/codetoanalyze/objc/linters-for-test-only/subclassing.m @@ -100,4 +100,8 @@ - (unsigned int**)m24; - (A*)m25; - (ThisIsAVeryLongName*)m26; +- (void)m26:(int)p1 + pname2:(float)p2 + pname3:(ThisIsAVeryLongName*)p3 + pname4:(A*)p4; @end