Modified the hardcoded CTL formulas in preparation to dispatcher removal

Reviewed By: dulmarod

Differential Revision: D4130612

fbshipit-source-id: d482966
master
Dino Distefano 8 years ago committed by Facebook Github Bot
parent 0085417e0d
commit 6ffaded4be

@ -18,6 +18,7 @@ type context = {
current_objc_impl : Clang_ast_t.decl option;
(** True if inside an objc static factory method (a class-level initializer, like +new) *)
in_objc_static_factory_method : bool;
et_evaluation_node : string option;
}
let empty translation_unit_context = {
@ -27,4 +28,5 @@ let empty translation_unit_context = {
is_ck_translation_unit = false;
current_objc_impl = None;
in_objc_static_factory_method = false;
et_evaluation_node = None;
}

@ -59,13 +59,12 @@ let ivar_name stmt =
|| is_CXXOperatorCallExpr || is_ObjCMessageExpr *)
let ctl_makes_an_expensive_call () =
let open CTL in
let white_list_functions = ["CGPointMake"] in
Or (Or (Or (Or (And (Atomic ("is_statement_kind", ["CallExpr"]),
Not(Atomic("call_function_named", white_list_functions))),
Atomic ("is_statement_kind", ["CXXTemporaryObjectExpr"])),
Atomic ("is_statement_kind", ["CXXMemberCallExpr"])),
Atomic ("is_statement_kind", ["CXXOperatorCallExpr"])),
Atomic ("is_statement_kind", ["ObjCMessageExpr"]))
Or (Or (Or (Or (And (Atomic ("is_stmt", ["CallExpr"]),
Not (Atomic("call_function_named", ["CGPointMake"]))),
Atomic ("is_stmt", ["CXXTemporaryObjectExpr"])),
Atomic ("is_stmt", ["CXXMemberCallExpr"])),
Atomic ("is_stmt", ["CXXOperatorCallExpr"])),
Atomic ("is_stmt", ["ObjCMessageExpr"]))
(*
@ -128,7 +127,8 @@ let ctl_bad_pointer_comparison_warning lctx stmt =
*)
let p = Or (is_expr_with_cleanups, Or (is_implicit_cast_expr, Or (is_binop, is_unop_lnot))) in
let p' = And (Not is_binop_neq, p) in
let condition = EU (None, p', is_nsnumber) in
let condition = ETX (["IfStmt"; "ForStmt"; "WhileStmt"; "ConditionalOperator"], Some Cond,
EU (None, p', is_nsnumber)) in
let issue_desc =
{ CIssue.
issue = CIssue.Bad_pointer_comparison;
@ -150,9 +150,9 @@ let ctl_strong_delegate lctx dec =
Not(Atomic ("property_name_contains_word", ["queue"])) in
let is_strong_property =
Atomic("is_strong_property", []) in
let condition = And (name_contains_delegate,
And (name_does_not_contains_queue,
is_strong_property)) in
let condition = ET (["ObjCPropertyDecl"], None, And (name_contains_delegate,
And (name_does_not_contains_queue,
is_strong_property))) in
let issue_desc = {
CIssue.issue = CIssue.Strong_delegate_warning;
CIssue.description = Printf.sprintf
@ -172,7 +172,8 @@ let ctl_global_var_init_with_calls_warning lctx decl =
Not (Atomic ("is_const_var", []))) in
let ctl_is_initialized_with_expensive_call =
ET(["VarDecl"], Some InitExpr, EF (None, (ctl_makes_an_expensive_call ()))) in
let condition = And (ctl_is_global_var, ctl_is_initialized_with_expensive_call) in
let condition =
ET (["VarDecl"], None, And (ctl_is_global_var, ctl_is_initialized_with_expensive_call)) in
let issue_desc = {
CIssue.issue = CIssue.Global_variable_initialized_with_function_or_method_call;
CIssue.description = Printf.sprintf
@ -187,8 +188,9 @@ let ctl_global_var_init_with_calls_warning lctx decl =
(* is_assign_property AND is_property_pointer_type *)
let ctl_assign_pointer_warning lctx decl =
let open CTL in
let condition =
And (Atomic("is_assign_property", []), Atomic("is_property_pointer_type", [])) in
let condition = ET(["ObjCPropertyDecl"], None,
And (Atomic ("is_assign_property", []),
Atomic ("is_property_pointer_type", []))) in
let issue_desc =
{ CIssue.issue = CIssue.Assign_pointer_warning;
CIssue.description =
@ -206,12 +208,12 @@ let ctl_assign_pointer_warning lctx decl =
*)
let ctl_direct_atomic_property_access_warning lctx stmt =
let open CTL in
let condition =
And (And (And (And (Not (Atomic ("context_in_synchronized_block", [])),
Atomic("is_ivar_atomic", [])),
Not (Atomic ("is_method_property_accessor_of_ivar", []))),
Not (Atomic ("is_objc_constructor", []))),
Not (Atomic ("is_objc_dealloc", []))) in
let condition = ET (["ObjCIvarRefExpr"], None,
And (And (And (And (Not (Atomic ("context_in_synchronized_block", [])),
Atomic("is_ivar_atomic", [])),
Not (Atomic ("is_method_property_accessor_of_ivar", []))),
Not (Atomic ("is_objc_constructor", []))),
Not (Atomic ("is_objc_dealloc", [])))) in
let issue_desc = {
CIssue.issue = CIssue.Direct_atomic_property_access;
CIssue.description = Printf.sprintf
@ -224,7 +226,8 @@ let ctl_direct_atomic_property_access_warning lctx stmt =
let ctl_captured_cxx_ref_in_objc_block_warning lctx stmt =
(* Fire if the list of captured references is not empty *)
let condition = CTL.Atomic ("captures_cxx_references", []) in
let open CTL in
let condition = ET (["BlockDecl"], None, Atomic ("captures_cxx_references", [])) in
let issue_desc = {
CIssue.issue = CIssue.Cxx_reference_captured_in_objc_block;
CIssue.description = Printf.sprintf
@ -232,7 +235,9 @@ let ctl_captured_cxx_ref_in_objc_block_warning lctx stmt =
(Predicates.var_descs_name stmt);
CIssue.suggestion = Some ("C++ References are unmanaged and may be invalid " ^
"by the time the block executes.");
CIssue.loc = location_from_stmt lctx stmt
CIssue.loc = match stmt with
| Clang_ast_t.BlockExpr (_, _ , _, decl) -> location_from_decl lctx decl
| _ -> location_from_stmt lctx stmt;
} in
condition, issue_desc

@ -63,13 +63,6 @@ let var_checker_list = [CFrontend_checkers.global_var_init_with_calls_warning;
let checker_for_var dec checker context =
checker context dec
(* List of checkers on conditional operator *)
let conditional_op_checker_list = [CFrontend_checkers.bad_pointer_comparison_warning]
(* Invocation of checker belonging to conditional_op_checker_list *)
let checker_for_conditional_op first_stmt checker context =
checker context first_stmt
(* List of checkers on if-statement *)
let if_stmt_checker_list = [CFrontend_checkers.bad_pointer_comparison_warning]
@ -77,20 +70,6 @@ let if_stmt_checker_list = [CFrontend_checkers.bad_pointer_comparison_warning]
let checker_for_if_stmt cond checker context =
checker context cond
(* List of checkers on for statement *)
let for_stmt_checker_list = [CFrontend_checkers.bad_pointer_comparison_warning]
(* Invocation of checker belonging to for_stmt_checker_list *)
let checker_for_for_stmt cond checker context =
checker context cond
(* List of checkers on while statement *)
let while_stmt_checker_list = [CFrontend_checkers.bad_pointer_comparison_warning]
(* Invocation of checker belonging to while_stmt_checker_list *)
let checker_for_while_stmt cond checker context =
checker context cond
let function_decl_checker_list = [ComponentKit.component_factory_function_advice]
let checker_for_function_decl decl checker context =
@ -146,26 +125,11 @@ let run_frontend_checkers_on_stmt context instr =
invoke_set_of_checkers call_captured_vars_checker context key
captured_vars_checker_list;
context
| IfStmt (_, _ :: _ :: cond :: _) ->
let call_checker = checker_for_if_stmt cond in
let key = Ast_utils.generate_key_stmt cond in
| IfStmt _ | ConditionalOperator _ | ForStmt _ | WhileStmt _ ->
let call_checker = checker_for_if_stmt instr in
let key = Ast_utils.generate_key_stmt instr in
invoke_set_of_checkers call_checker context key if_stmt_checker_list;
context
| ConditionalOperator (_, first_stmt :: _, _) ->
let call_checker = checker_for_conditional_op first_stmt in
let key = Ast_utils.generate_key_stmt first_stmt in
invoke_set_of_checkers call_checker context key conditional_op_checker_list;
context
| ForStmt (_, [_; _; cond; _; _]) ->
let call_checker = checker_for_for_stmt cond in
let key = Ast_utils.generate_key_stmt cond in
invoke_set_of_checkers call_checker context key for_stmt_checker_list;
context
| WhileStmt (_, [_; cond; _]) ->
let call_checker = checker_for_while_stmt cond in
let key = Ast_utils.generate_key_stmt cond in
invoke_set_of_checkers call_checker context key while_stmt_checker_list;
context
| CallExpr (_, called_func_stmt :: _, _) ->
let call_checker = checkers_for_call_expr called_func_stmt in
let key = Ast_utils.generate_key_stmt called_func_stmt in

@ -22,6 +22,7 @@ type transitions =
| Body (* decl to stmt *)
| InitExpr (* decl to stmt *)
| Super (* decl to decl *)
| Cond
(* In formulas below prefix
"E" means "exists a path"
@ -46,6 +47,7 @@ type t = (* A ctl formula *)
| EU of transitions option * t * t
| EH of string list * t
| ET of string list * transitions option * t
| ETX of string list * transitions option * t
(* the kind of AST nodes where formulas are evaluated *)
type ast_node =
@ -57,19 +59,28 @@ module Debug = struct
let pp_aux fmt trans = match trans with
| Body -> Format.pp_print_string fmt "Body"
| InitExpr -> Format.pp_print_string fmt "InitExpr"
| Super -> Format.pp_print_string fmt "Super" in
| Super -> Format.pp_print_string fmt "Super"
| Cond -> Format.pp_print_string fmt "Cond" in
match trans_opt with
| Some trans -> pp_aux fmt trans
| None -> Format.pp_print_string fmt "_"
(* a flag to print more or less in the dotty graph *)
let full_print = true
let rec pp_formula fmt phi =
match phi with
| True -> Format.fprintf fmt "True"
| False -> Format.fprintf fmt "False"
| Atomic p -> Predicates.pp_predicate fmt p
| Not phi -> Format.fprintf fmt "NOT(%a)" pp_formula phi
| And (phi1, phi2) -> Format.fprintf fmt "(%a AND %a)" pp_formula phi1 pp_formula phi2
| Or (phi1, phi2) -> Format.fprintf fmt "(%a OR %a)" pp_formula phi1 pp_formula phi2
| Not phi -> if full_print then Format.fprintf fmt "NOT(%a)" pp_formula phi
else Format.fprintf fmt "NOT(...)"
| And (phi1, phi2) -> if full_print then
Format.fprintf fmt "(%a AND %a)" pp_formula phi1 pp_formula phi2
else Format.fprintf fmt "(... AND ...)"
| Or (phi1, phi2) -> if full_print then
Format.fprintf fmt "(%a OR %a)" pp_formula phi1 pp_formula phi2
else Format.fprintf fmt "(... OR ...)"
| Implies (phi1, phi2) -> Format.fprintf fmt "(%a ==> %a)" pp_formula phi1 pp_formula phi2
| InNode (nl, phi) -> Format.fprintf fmt "IN-NODE %a: (%a)"
(Utils.pp_comma_seq Format.pp_print_string) nl
@ -86,10 +97,11 @@ module Debug = struct
| EH (arglist, phi) -> Format.fprintf fmt "EH[%a](%a)"
(Utils.pp_comma_seq Format.pp_print_string) arglist
pp_formula phi
| ET (arglist, trans, phi) -> Format.fprintf fmt "ET[%a][%a](%a)"
(Utils.pp_comma_seq Format.pp_print_string) arglist
pp_transition trans
pp_formula phi
| ET (arglist, trans, phi)
| ETX (arglist, trans, phi) -> Format.fprintf fmt "ET[%a][%a](%a)"
(Utils.pp_comma_seq Format.pp_print_string) arglist
pp_transition trans
pp_formula phi
module EvaluationTracker = struct
exception Empty_stack of string
@ -99,6 +111,7 @@ module Debug = struct
type content = {
ast_node: ast_node;
phi: t;
lcxt: CLintersContext.context;
eval_result: eval_result;
}
@ -115,9 +128,9 @@ module Debug = struct
forest: tree list;
}
let create_content ast_node phi = {ast_node; phi; eval_result = Eval_undefined}
let create_content ast_node phi lcxt = {ast_node; phi; eval_result = Eval_undefined; lcxt = lcxt; }
let create () = {next_id = 0; eval_stack = Stack.create(); forest = []}
let create () = {next_id = 0; eval_stack = Stack.create(); forest = [] }
let eval_begin t content =
let node = {id = t.next_id; content} in
@ -169,6 +182,10 @@ module Debug = struct
| Eval_false -> "red"
| _ -> failwith "Tree is not fully evaluated" in
let label =
let string_of_lcxt c =
match c.CLintersContext.et_evaluation_node with
| Some s -> ("et_evaluation_node = "^s)
| _ -> "et_evaluation_node = NONE" in
let string_of_ast_node an =
match an with
| Stmt stmt -> Clang_ast_proj.get_stmt_kind_string stmt
@ -181,9 +198,10 @@ module Debug = struct
| Implies _ when num_children = 2 -> "(...) ==> (...)"
| Not _ -> "NOT(...)"
| _ -> Utils.pp_to_string pp_formula phi in
Format.sprintf "(%d)\\n%s\\n%s"
Format.sprintf "(%d)\\n%s\\n%s\\n%s"
root_node.id
(Escape.escape_dotty (string_of_ast_node root_node.content.ast_node))
(Escape.escape_dotty (string_of_lcxt root_node.content.lcxt))
(Escape.escape_dotty (smart_string_of_formula root_node.content.phi)) in
let edges =
let buf = Buffer.create 16 in
@ -212,9 +230,9 @@ let ctl_evaluation_tracker = match Config.debug_mode with
| true -> Some (ref (Debug.EvaluationTracker.create ()))
| false -> None
let debug_create_payload ast_node phi =
let debug_create_payload ast_node phi lcxt =
match ctl_evaluation_tracker with
| Some _ -> Some (Debug.EvaluationTracker.create_content ast_node phi)
| Some _ -> Some (Debug.EvaluationTracker.create_content ast_node phi lcxt)
| None -> None
let debug_eval_begin payload =
@ -258,6 +276,15 @@ let node_to_string an =
| Decl d -> Clang_ast_proj.get_decl_kind_string d
| Stmt s -> Clang_ast_proj.get_stmt_kind_string s
let node_to_unique_string_id an =
match an with
| Decl d ->
let di = Clang_ast_proj.get_decl_tuple d in
(Clang_ast_proj.get_decl_kind_string d) ^ (string_of_int di.Clang_ast_t.di_pointer)
| Stmt s ->
let si, _ = Clang_ast_proj.get_stmt_tuple s in
Clang_ast_proj.get_stmt_kind_string s ^ (string_of_int si.Clang_ast_t.si_pointer)
(* true iff an ast node is a node of type among the list tl *)
let node_has_type tl an =
let an_str = node_to_string an in
@ -298,12 +325,22 @@ let transition_decl_to_decl_via_super d =
| _ -> None)
| None -> None
let transition_stmt_to_stmt_via_condition st =
let open Clang_ast_t in
match st with
| IfStmt (_, _ :: _ :: cond :: _)
| ConditionalOperator (_, cond:: _, _)
| ForStmt (_, [_; _; cond; _; _])
| WhileStmt (_, [_; cond; _]) -> Some (Stmt cond)
| _ -> None
(* 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 InitExpr
| Decl d, Some Body -> transition_decl_to_stmt d trans
| Stmt st, Some Cond -> transition_stmt_to_stmt_via_condition st
| _, _ -> None
(* Evaluation of formulas *)
@ -318,7 +355,6 @@ let eval_Atomic pred_name params an lcxt =
| "is_global_var", [], Decl d -> Predicates.is_syntactically_global_var d
| "is_const_var", [], Decl d -> Predicates.is_const_expr_var d
| "call_function_named", _, Stmt st -> Predicates.call_function_named st params
| "is_statement_kind", [p1], Stmt st -> Predicates.is_statement_kind st p1
| "is_declaration_kind", [p1], Decl d -> Predicates.is_declaration_kind d p1
| "is_strong_property", [], Decl d -> Predicates.is_strong_property d
| "is_assign_property", [], Decl d -> Predicates.is_assign_property d
@ -329,7 +365,7 @@ let eval_Atomic pred_name params an lcxt =
Predicates.is_method_property_accessor_of_ivar st lcxt
| "is_objc_constructor", [], _ -> Predicates.is_objc_constructor lcxt
| "is_objc_dealloc", [], _ -> Predicates.is_objc_dealloc lcxt
| "captures_cxx_references", [], Stmt st -> Predicates.captures_cxx_references st
| "captures_cxx_references", [], Decl d -> Predicates.captures_cxx_references d
| "is_binop_with_kind", [kind], Stmt st -> Predicates.is_binop_with_kind st kind
| "is_unop_with_kind", [kind], Stmt st -> Predicates.is_unop_with_kind st kind
| "is_stmt", [stmt_name], Stmt st -> Predicates.is_stmt st stmt_name
@ -428,7 +464,14 @@ and eval_AU phi1 phi2 an lcxt =
an is a node of type in node_type_list and an satifies phi
*)
and in_node node_type_list phi an lctx =
(node_has_type node_type_list an) && (eval_formula phi an lctx)
let holds_for_one_node n =
match lctx.CLintersContext.et_evaluation_node with
| Some id ->
(string_equal id (node_to_unique_string_id an)) && (eval_formula phi an lctx)
| None ->
(node_has_type [n] an) && (eval_formula phi an lctx) in
IList.exists holds_for_one_node node_type_list
(* Intuitive meaning: (an,lcxt) satifies EH[Classes] phi
if the node an is among the declaration specified by the list Classes and
@ -458,9 +501,20 @@ and eval_ET tl trs phi an lcxt =
| None -> EF (None, (InNode (tl, phi))) in
eval_formula f an lcxt
and eval_ETX tl trs phi an lcxt =
let lcxt', tl' = match lcxt.CLintersContext.et_evaluation_node, node_has_type tl an with
| None, true ->
let an_str = node_to_string an in
{lcxt with CLintersContext.et_evaluation_node = Some (node_to_unique_string_id an) }, [an_str]
| _, _ -> lcxt, tl in
let f = match trs with
| Some _ -> EF (None, (InNode (tl', EX (trs, phi))))
| None -> EF (None, (InNode (tl', phi))) in
eval_formula f an lcxt'
(* Formulas are evaluated on a AST node an and a linter context lcxt *)
and eval_formula f an lcxt =
debug_eval_begin (debug_create_payload an f);
debug_eval_begin (debug_create_payload an f lcxt);
let res = match f with
| True -> true
| False -> false
@ -483,6 +537,7 @@ and eval_formula f an lcxt =
| EH (cl, phi) -> eval_EH cl phi an lcxt
| EG (trans, f1) -> (* st |= EG f1 <=> st |= f1 /\ EX EG f1 *)
eval_formula (And (f1, EX (trans, (EG (trans, f1))))) an lcxt
| ET (tl, sw, phi) -> eval_ET tl sw phi an lcxt in
| ET (tl, sw, phi) -> eval_ET tl sw phi an lcxt
| ETX (tl, sw, phi) -> eval_ETX tl sw phi an lcxt in
debug_eval_end res;
res

@ -17,6 +17,7 @@ type transitions =
| Body (* decl to stmt *)
| InitExpr (* decl to stmt *)
| Super (* decl to decl *)
| Cond
(* In formulas below prefix
"E" means "exists a path"
@ -49,6 +50,9 @@ type t =
| ET of string list * transitions option * t (** ET[T][l] phi <=>
there exists a descentant an of the current node such that an is of type in set T
making a transition to a node an' via label l, such that in an phi holds. *)
| ETX of string list * transitions option * t (** ET[T][l] phi <=>
there exists a descentant an of the current node such that an is of type in set T
making a transition to a node an' via label l, such that in an phi holds. *)
(** the kind of AST nodes where formulas are evaluated *)
type ast_node =

@ -23,7 +23,7 @@ let get_ivar_attributes ivar_decl =
| _ -> []
(* list of cxx references captured by stmt *)
let captured_variables_cxx_ref stmt =
let captured_variables_cxx_ref dec =
let capture_var_is_cxx_ref reference_captured_vars captured_var =
let decl_ref_opt = captured_var.Clang_ast_t.bcv_variable in
match Ast_utils.get_decl_opt_with_decl_ref decl_ref_opt with
@ -35,14 +35,17 @@ let captured_variables_cxx_ref stmt =
named_decl_info::reference_captured_vars
| _ -> reference_captured_vars)
| _ -> reference_captured_vars in
let captured_vars = match stmt with
| Clang_ast_t.BlockExpr (_, _ , _, Clang_ast_t.BlockDecl (_, bdi)) ->
let captured_vars = match dec with
| Clang_ast_t.BlockDecl (_, bdi) ->
bdi.Clang_ast_t.bdi_captured_variables
| _ -> [] in
IList.fold_left capture_var_is_cxx_ref [] captured_vars
let var_descs_name stmt =
let capt_refs = captured_variables_cxx_ref stmt in
let capt_refs = match stmt with
| Clang_ast_t.BlockExpr (_, _ , _, decl) ->
captured_variables_cxx_ref decl
| _ -> [] in
let var_desc vars var_named_decl_info =
vars ^ "'" ^ var_named_decl_info.Clang_ast_t.ni_name ^ "'" in
IList.fold_left var_desc "" capt_refs
@ -56,9 +59,6 @@ let pp_predicate fmt (name, arglist) =
let is_declaration_kind decl s =
Clang_ast_proj.get_decl_kind_string decl = s
let is_statement_kind stmt s =
Clang_ast_proj.get_stmt_kind_string stmt = s
(* st |= call_method(m) *)
let call_method m st =
match st with

@ -23,8 +23,6 @@ val is_const_expr_var : Clang_ast_t.decl -> bool
val is_declaration_kind : Clang_ast_t.decl -> string -> bool
val is_statement_kind : Clang_ast_t.stmt -> string -> bool
val call_function_named : Clang_ast_t.stmt -> string list -> bool
val is_strong_property : Clang_ast_t.decl -> bool
@ -43,7 +41,7 @@ val is_objc_constructor : CLintersContext.context -> bool
val is_objc_dealloc : CLintersContext.context -> bool
val captures_cxx_references : Clang_ast_t.stmt -> bool
val captures_cxx_references : Clang_ast_t.decl -> bool
val is_binop_with_kind : Clang_ast_t.stmt -> string -> bool

@ -142,4 +142,21 @@ int bad10(NSNumber* number, Simple* simple) {
return 0;
}
int bad11(int i, NSNumber* number) {
if (i > 10) {
return 11;
} else if (number) {
return 0;
}
return 1;
}
int good6(NSNumber* number) { return (number.integerValue > 5 ? 1 : 0); }
NSNumber* good7(NSNumber* number) {
return (number.integerValue > 5 ? number : @0);
}
NSNumber* bad12(NSNumber* number) {
return (number.integerValue > 5 ? (number ? @1 : @0) : @0);
}

@ -10,6 +10,8 @@ atomic_prop.m, A_writeQ:, 82, DIRECT_ATOMIC_PROPERTY_ACCESS
atomic_prop.m, __objc_anonymous_block_______1, 114, DIRECT_ATOMIC_PROPERTY_ACCESS
badpointer.m, bad1, 17, BAD_POINTER_COMPARISON
badpointer.m, bad10, 139, BAD_POINTER_COMPARISON
badpointer.m, bad11, 148, BAD_POINTER_COMPARISON
badpointer.m, bad12, 161, BAD_POINTER_COMPARISON
badpointer.m, bad2, 26, BAD_POINTER_COMPARISON
badpointer.m, bad3, 33, BAD_POINTER_COMPARISON
badpointer.m, bad4, 85, BAD_POINTER_COMPARISON

Loading…
Cancel
Save