From 652af47da7d708562ca724a3068c6e3f4bccb0f9 Mon Sep 17 00:00:00 2001
From: Dulma Churchill <dulmarod@fb.com>
Date: Wed, 27 Sep 2017 07:55:22 -0700
Subject: [PATCH] [linters] Add transition to parameters by position in the
 parameters list

Reviewed By: ddino

Differential Revision: D5911586

fbshipit-source-id: 263b7dc
---
 infer/src/clang/cPredicates.ml                | 34 ++++------
 infer/src/clang/cPredicates.mli               |  5 +-
 infer/src/clang/cTL.ml                        | 68 ++++++++++++++++---
 infer/src/clang/cTL.mli                       |  1 +
 infer/src/clang/ctl_lexer.mll                 |  1 +
 infer/src/clang/ctl_parser.mly                |  2 +
 .../objc/linters-for-test-only/issues.exp     |  5 +-
 .../linters-for-test-only/linters_example.al  | 17 +++--
 .../objc/linters-for-test-only/subclassing.m  |  8 ++-
 9 files changed, 98 insertions(+), 43 deletions(-)

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); }