[linters] Add transition parameter with label for ObjC method declarations or method calls

Reviewed By: ddino

Differential Revision: D5652850

fbshipit-source-id: 9174343
master
Dulma Churchill 7 years ago committed by Facebook Github Bot
parent 29aea2d61d
commit a3309926b3

@ -117,7 +117,7 @@ DEFINE-CHECKER REGISTERED_OBSERVER_BEING_DEALLOCATED = {
HOLDS-EVENTUALLY;
LET eventually_removeObserver =
IN-NODE ObjCImplementationDecl, ObjCProtocolDecl WITH-TRANSITION _
IN-NODE ObjCImplementationDecl, ObjCProtocolDecl WITH-TRANSITION Any
(remove_observer_in_method OR
remove_observer_in_method HOLDS-IN-SOME-SUPERCLASS-OF ObjCImplementationDecl)
HOLDS-EVENTUALLY;

@ -20,6 +20,7 @@ type transitions =
| Body (** decl to stmt *)
| InitExpr (** decl to stmt *)
| Super (** decl to decl *)
| ParameterName of ALVar.alexp (** stmt to stmt, decl to decl *)
| Parameters (** decl to decl *)
| Cond
| PointerToDecl (** stmt to decl *)
@ -124,6 +125,8 @@ module Debug = struct
-> Format.pp_print_string fmt "InitExpr"
| Super
-> Format.pp_print_string fmt "Super"
| ParameterName name
-> Format.pp_print_string fmt ("ParameterName " ^ ALVar.alexp_to_string name)
| Parameters
-> Format.pp_print_string fmt "Parameters"
| Cond
@ -702,6 +705,35 @@ let transition_decl_to_decl_via_parameters dec =
| _
-> []
let parameter_of_corresp_name method_name args name =
let names =
List.filter (String.split ~on:':' method_name) ~f:(fun label -> not (String.is_empty label))
in
match List.zip names args with
| Some names_args
-> (
let names_arg_opt =
List.find names_args ~f:(fun (arg_label, _) -> ALVar.compare_str_with_alexp arg_label name)
in
match names_arg_opt with Some (_, arg) -> Some arg | None -> None )
| None
-> None
let transition_via_parameter_name an name =
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 -> [] )
| Decl ObjCMethodDecl (_, named_decl_info, omdi)
-> (
let arg_decl_opt =
parameter_of_corresp_name named_decl_info.ni_name omdi.omdi_parameters name
in
match arg_decl_opt with Some arg -> [Decl arg] | None -> [] )
| _
-> []
(* given a node an returns a list of nodes an' such that an transition to an' via label trans *)
let next_state_via_transition an trans =
match (an, trans) with
@ -717,6 +749,8 @@ let next_state_via_transition an trans =
-> transition_stmt_to_stmt_via_condition st
| Stmt st, PointerToDecl
-> transition_stmt_to_decl_via_pointer st
| an, ParameterName name
-> transition_via_parameter_name an name
| _, _
-> []

@ -23,6 +23,8 @@ type transitions =
(* decl to stmt *)
| Super
(* decl to decl *)
| ParameterName of ALVar.alexp
(* stmt to stmt, decl to decl *)
| Parameters
(* decl to decl *)
| Cond
@ -122,4 +124,6 @@ val create_ctl_evaluation_tracker : SourceFile.t -> unit
module Debug : sig
val pp_formula : Format.formatter -> t -> unit
val pp_transition : Format.formatter -> transitions option -> unit
end

@ -71,6 +71,14 @@ rule token = parse
| "NOT" { NOT }
| "IMPLIES" { IMPLIES }
| "REGEXP" { REGEXP }
| "Any" {ANY}
| "Parameters" { PARAMETERS }
| "ParameterName" { PARAMETER_NAME }
| "Body" {BODY}
| "Protocol" {PROTOCOL}
| "InitExpr" {INIT_EXPR}
| "Cond" {COND}
| "PointerToDecl" {POINTER_TO_DECL}
| id { IDENTIFIER (Lexing.lexeme lexbuf) }
| file_id { FILE_IDENTIFIER (Lexing.lexeme lexbuf) }
| '"' { read_string (Buffer.create 80) lexbuf }

@ -71,6 +71,14 @@
%token <string> STRING
%token WHITELIST_PATH
%token BLACKLIST_PATH
%token ANY
%token BODY
%token COND
%token INIT_EXPR
%token PARAMETERS
%token PARAMETER_NAME
%token POINTER_TO_DECL
%token PROTOCOL
%token EOF
/* associativity and priority (lower to higher) of operators */
@ -221,14 +229,14 @@ actual_params:
;
transition_label:
| identifier { match $1 with
| "Body" | "body" -> "Body", Some CTL.Body
| "Protocol" | "protocol" -> "Protocol", Some CTL.Protocol
| "InitExpr" | "initexpr" -> "InitExpr", Some CTL.InitExpr
| "Cond" | "cond" -> "Cond", Some CTL.Cond
| "Parameters" | "parameters" -> "Parameters", Some CTL.Parameters
| "PointerToDecl" | "pointertodecl" -> "PointerToDecl", Some CTL.PointerToDecl
| _ -> "None", None }
| ANY { None }
| BODY { Some CTL.Body }
| COND { Some CTL.Cond }
| INIT_EXPR { Some CTL.InitExpr }
| PARAMETERS { Some CTL.Parameters }
| PARAMETER_NAME alexp { Some (CTL.ParameterName $2) }
| POINTER_TO_DECL { Some CTL.PointerToDecl }
| PROTOCOL { Some CTL.Protocol }
;
formula_EF:
@ -248,33 +256,34 @@ formula:
| formula AF { L.(debug Linters Verbose) "\tParsed AF@\n"; CTL.AF (None,$1) }
| formula EX { L.(debug Linters Verbose) "\tParsed EX@\n"; CTL.EX (None, $1) }
| formula EX WITH_TRANSITION transition_label
{ L.(debug Linters Verbose) "\tParsed EX WITH-TRANSITION '%s'@\n" (fst $4);
CTL.EX (snd $4, $1) }
{ L.(debug Linters Verbose) "\tParsed EX WITH-TRANSITION '%a'@\n" CTL.Debug.pp_transition $4;
CTL.EX ($4, $1) }
| formula AX { L.(debug Linters Verbose) "\tParsed AX@\n"; CTL.AX (None, $1) }
| formula AX WITH_TRANSITION transition_label
{ L.(debug Linters Verbose) "\tParsed AX WITH-TRANSITION '%s'@\n" (fst $4);
CTL.AX (snd $4, $1) }
{ L.(debug Linters Verbose) "\tParsed AX WITH-TRANSITION '%a'@\n" CTL.Debug.pp_transition $4;
CTL.AX ($4, $1) }
| formula EG { L.(debug Linters Verbose) "\tParsed EG@\n"; CTL.EG (None, $1) }
| formula AG { L.(debug Linters Verbose) "\tParsed AG@\n"; CTL.AG (None, $1) }
| formula EH node_list { L.(debug Linters Verbose) "\tParsed EH@\n"; CTL.EH ($3, $1) }
| formula EF { L.(debug Linters Verbose) "\tParsed EF@\n"; CTL.EF (None, $1) }
| formula EF WITH_TRANSITION transition_label
{ L.(debug Linters Verbose) "\tParsed EF WITH-TRANSITION '%s'@\n" (fst $4);
CTL.EF(snd $4, $1) }
{ L.(debug Linters Verbose) "\tParsed EF WITH-TRANSITION '%a'@\n" CTL.Debug.pp_transition $4;
CTL.EF($4, $1) }
| WHEN formula HOLDS_IN_NODE node_list
{ L.(debug Linters Verbose) "\tParsed InNode@\n"; CTL.InNode ($4, $2)}
| ET node_list WITH_TRANSITION transition_label formula_EF
{ L.(debug Linters Verbose) "\tParsed ET with transition '%s'@\n" (fst $4);
CTL.ET ($2, snd $4, $5)}
{ L.(debug Linters Verbose) "\tParsed ET with transition '%a'@\n" CTL.Debug.pp_transition $4;
CTL.ET ($2, $4, $5)}
| ETX node_list WITH_TRANSITION transition_label formula_EF
{ L.(debug Linters Verbose) "\tParsed ETX ith transition '%s'@\n" (fst $4);
CTL.ETX ($2, snd $4, $5)}
{ L.(debug Linters Verbose) "\tParsed ETX ith transition '%a'@\n" CTL.Debug.pp_transition $4;
CTL.ETX ($2, $4, $5)}
| EX WITH_TRANSITION transition_label formula_with_paren
{ L.(debug Linters Verbose) "\tParsed EX with transition '%s'@\n" (fst $3);
CTL.EX (snd $3, $4)}
{ L.(debug Linters Verbose) "\tParsed EX with transition '%a'@\n" CTL.Debug.pp_transition $3;
CTL.EX ($3, $4)}
| AX WITH_TRANSITION transition_label formula_with_paren
{ L.(debug Linters Verbose) "\tParsed AX with transition '%s'@\n" (fst $3);
CTL.AX (snd $3, $4)}
{ L.(debug Linters Verbose)
"\tParsed AX with transition '%a'@\n" CTL.Debug.pp_transition $3;
CTL.AX ($3, $4)}
| formula AND formula { L.(debug Linters Verbose) "\tParsed AND@\n"; CTL.And ($1, $3) }
| formula OR formula { L.(debug Linters Verbose) "\tParsed OR@\n"; CTL.Or ($1, $3) }
| formula IMPLIES formula

@ -0,0 +1,39 @@
/*
* Copyright (c) 2017 - 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.
*/
#include <unordered_map>
#import <Foundation/NSObject.h>
struct SomeStruct {
NSString* someLabel;
};
@interface SomeButton : NSObject
+ (instancetype)newWithStruct:(SomeStruct)aStruct
map:(const std::unordered_map<int, NSString*>&)aMap
object:(id)anObject
number:(int)n;
@end
SomeButton* buttonComponent(void);
SomeButton* buttonComponent(void) {
return [SomeButton newWithStruct:{} map:{} object:nil number:0];
};
SomeButton* anotherButtonComponent(void);
SomeButton* anotherButtonComponent(void) {
return [SomeButton newWithStruct:{.someLabel = @"hi"}
map:{
{ 1, @"some title" }
}
object:@"a string object"
number:5];
};

@ -4,3 +4,7 @@ codetoanalyze/objcpp/linters-for-test-only/ReferenceTest.mm, ReferenceTest_newWi
codetoanalyze/objcpp/linters-for-test-only/ReferenceTest.mm, ReferenceTest_newWithActionRef:, 40, TEST_REFERENCE, []
codetoanalyze/objcpp/linters-for-test-only/ReferenceTest.mm, ReferenceTest_newWithConstAction:, 21, TEST_REFERENCE, []
codetoanalyze/objcpp/linters-for-test-only/ReferenceTest.mm, ReferenceTest_newWithConstAction:, 36, TEST_REFERENCE, []
codetoanalyze/objcpp/linters-for-test-only/TestParamterLabelsChecks.mm, anotherButtonComponent, 33, TEST_PARAMETER_LABEL, []
codetoanalyze/objcpp/linters-for-test-only/TestParamterLabelsChecks.mm, anotherButtonComponent, 33, TEST_PARAMETER_LABEL_REGEXP, []
codetoanalyze/objcpp/linters-for-test-only/TestParamterLabelsChecks.mm, buttonComponent, 28, TEST_PARAMETER_LABEL, []
codetoanalyze/objcpp/linters-for-test-only/TestParamterLabelsChecks.mm, buttonComponent, 28, TEST_PARAMETER_LABEL_REGEXP, []

@ -7,3 +7,31 @@ DEFINE-CHECKER TEST_REFERENCE = {
HOLDS-IN-NODE ObjCMethodDecl;
SET message = "Found reference in parameter of method new";
};
DEFINE-CHECKER TEST_PARAMETER_LABEL = {
LET method_has_parameter_type =
WHEN
HOLDS-NEXT WITH-TRANSITION ParameterName "number"
(has_type("int"))
HOLDS-IN-NODE ObjCMessageExpr;
SET report_when =
WHEN
method_has_parameter_type
AND call_method(REGEXP("^new.*:$"))
HOLDS-IN-NODE ObjCMessageExpr;
SET message = "Found method with parameter labeled with `number` and with type `int`";
};
DEFINE-CHECKER TEST_PARAMETER_LABEL_REGEXP = {
LET method_has_parameter_type =
WHEN
HOLDS-NEXT WITH-TRANSITION ParameterName REGEXP("num.*")
(has_type("int"))
HOLDS-IN-NODE ObjCMessageExpr;
SET report_when =
WHEN
method_has_parameter_type
AND call_method(REGEXP("^new.*:$"))
HOLDS-IN-NODE ObjCMessageExpr;
SET message = "Found method with parameter labeled with `number` and with type `int`";
};

Loading…
Cancel
Save