From c9670d215d0c5d7b45938401c95883df01d6e242 Mon Sep 17 00:00:00 2001 From: Dulma Churchill Date: Wed, 10 May 2017 08:50:30 -0700 Subject: [PATCH] [linters] Add new predicate has_type and check for implicit cast Reviewed By: ddino Differential Revision: D5025959 fbshipit-source-id: de7eb55 --- infer/lib/linter_rules/linters.al | 2 +- infer/src/clang/cAst_utils.ml | 42 +++++++++++++++ infer/src/clang/cAst_utils.mli | 2 + infer/src/clang/cPredicates.ml | 32 +++++++++-- infer/src/clang/cPredicates.mli | 7 ++- infer/src/clang/cTL.ml | 53 ++++++++----------- .../objc/linters-for-test-only/Makefile | 1 + .../linters-for-test-only/implicit_cast.c | 12 +++++ .../objc/linters-for-test-only/issues.exp | 3 ++ .../linters-for-test-only/linters_example.al | 20 +++++++ 10 files changed, 137 insertions(+), 37 deletions(-) create mode 100644 infer/tests/codetoanalyze/objc/linters-for-test-only/implicit_cast.c diff --git a/infer/lib/linter_rules/linters.al b/infer/lib/linter_rules/linters.al index b1687a3bd..4bf094591 100644 --- a/infer/lib/linter_rules/linters.al +++ b/infer/lib/linter_rules/linters.al @@ -159,7 +159,7 @@ DEFINE-CHECKER GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL = { is_objc_extension() AND is_global_var() AND (NOT is_const_var()); LET makes_an_expensive_call = - (is_node("CallExpr") AND NOT call_function_named("CGPointMake")) + (is_node("CallExpr") AND NOT call_function("CGPointMake")) OR is_node("CXXTemporaryObjectExpr") OR is_node("CXXMemberCallExpr") OR is_node("CXXOperatorCallExpr") diff --git a/infer/src/clang/cAst_utils.ml b/infer/src/clang/cAst_utils.ml index 5037f0d32..0ca8ae9ec 100644 --- a/infer/src/clang/cAst_utils.ml +++ b/infer/src/clang/cAst_utils.ml @@ -401,3 +401,45 @@ let name_of_decl_ref_opt (decl_ref_opt: Clang_ast_t.decl_ref option) = | Some named_decl_info -> Some named_decl_info.ni_name | _ -> None) | _ -> None + +let type_of_decl decl = + let open Clang_ast_t in + match decl with + | ObjCMethodDecl (_, _, obj_c_method_decl_info) -> + Some obj_c_method_decl_info.omdi_result_type.qt_type_ptr + | ObjCPropertyDecl (_, _, obj_c_property_decl_info) -> + Some obj_c_property_decl_info.opdi_qual_type.qt_type_ptr + | EnumDecl (_, _, _, type_ptr, _, _, _) + | RecordDecl (_, _, _, type_ptr, _, _, _) + | CXXRecordDecl(_, _, _, type_ptr, _, _, _, _) + | ClassTemplateSpecializationDecl(_, _, _, type_ptr, _, _, _, _, _) + | ClassTemplatePartialSpecializationDecl(_, _, _, type_ptr, _, _, _, _, _) + | TemplateTypeParmDecl (_, _, _, type_ptr) + | ObjCTypeParamDecl (_, _, _, type_ptr) + | TypeAliasDecl (_, _, _, type_ptr) + | TypedefDecl (_, _, _, type_ptr, _) + | UnresolvedUsingTypenameDecl (_, _, _, type_ptr) -> Some type_ptr + | BindingDecl (_, _, qual_type) + | FieldDecl (_, _, qual_type, _) + | ObjCAtDefsFieldDecl (_, _, qual_type, _) + | ObjCIvarDecl (_, _, qual_type, _, _) + | FunctionDecl (_, _, qual_type, _) + | CXXMethodDecl (_, _, qual_type, _, _) + | CXXConstructorDecl (_, _, qual_type, _, _) + | CXXConversionDecl (_, _, qual_type, _, _) + | CXXDestructorDecl (_, _, qual_type, _, _) + | MSPropertyDecl (_, _, qual_type) + | NonTypeTemplateParmDecl (_, _, qual_type) + | VarDecl (_, _, qual_type, _) + | DecompositionDecl (_, _, qual_type, _) + | ImplicitParamDecl (_, _, qual_type, _) + | OMPCapturedExprDecl (_, _, qual_type, _) + | ParmVarDecl (_, _, qual_type, _) + | VarTemplateSpecializationDecl (_, _, qual_type, _) + | VarTemplatePartialSpecializationDecl (_, _, qual_type, _) + | EnumConstantDecl (_, _, qual_type, _) + | IndirectFieldDecl (_, _, qual_type, _) + | OMPDeclareReductionDecl (_, _, qual_type) + | UnresolvedUsingValueDecl (_, _, qual_type) -> + Some qual_type.qt_type_ptr + | _ -> None diff --git a/infer/src/clang/cAst_utils.mli b/infer/src/clang/cAst_utils.mli index 966e27be9..afb07def8 100644 --- a/infer/src/clang/cAst_utils.mli +++ b/infer/src/clang/cAst_utils.mli @@ -139,3 +139,5 @@ val is_objc_factory_method : Clang_ast_t.decl -> Clang_ast_t.decl -> bool val name_of_decl_ref_opt : Clang_ast_t.decl_ref option -> string option val sil_annot_of_type : Clang_ast_t.qual_type -> Annot.Item.t + +val type_of_decl : Clang_ast_t.decl -> Clang_ast_t.type_ptr option diff --git a/infer/src/clang/cPredicates.ml b/infer/src/clang/cPredicates.ml index 717a67120..af1772add 100644 --- a/infer/src/clang/cPredicates.ml +++ b/infer/src/clang/cPredicates.ml @@ -163,19 +163,29 @@ let is_const_expr_var an = | Ctl_parser_types.Decl d -> CAst_utils.is_const_expr_var d | _ -> false -let decl_ref_is_in names st = +let decl_ref_name ?kind name st = match st with | Clang_ast_t.DeclRefExpr (_, _, _, drti) -> (match drti.drti_decl_ref with | Some dr -> let ndi, _, _ = CAst_utils.get_info_from_decl_ref dr in - List.exists ~f:(compare_str_with_alexp ndi.ni_name) names + let has_right_name = compare_str_with_alexp ndi.ni_name name in + (match kind with + | Some decl_kind -> + has_right_name && PVariant.(=) dr.Clang_ast_t.dr_kind decl_kind + | None -> has_right_name) | _ -> false) | _ -> false -let call_function_named an names = +let declaration_ref_name ?kind an name = match an with | Ctl_parser_types.Stmt st -> - CAst_utils.exists_eventually_st decl_ref_is_in names st + decl_ref_name ?kind name st + | _ -> false + +let call_function an name = + match an with + | Ctl_parser_types.Stmt st -> + CAst_utils.exists_eventually_st (decl_ref_name ~kind:`Function) name st | _ -> false let is_strong_property an = @@ -345,7 +355,6 @@ let decl_unavailable_in_supported_ios_sdk (cxt : CLintersContext.context) an = (Utils.compare_versions available_attr_ios_sdk max_allowed_version) > 0 | _ -> false - (* Check whether a type_ptr and a string denote the same type *) let type_ptr_equal_type type_ptr type_str = let pos_str lexbuf = @@ -369,6 +378,19 @@ let type_ptr_equal_type type_ptr type_str = Ctl_parser_types.tmp_c_type_equal ~name_c_type:name c_type' abs_ctype | _ -> Logging.out "Couldn't find type....\n"; false +let has_type an _typ = + match an, _typ with + | Ctl_parser_types.Stmt stmt, ALVar.Const typ -> + (match Clang_ast_proj.get_expr_tuple stmt with + | Some (_, _, expr_info) -> + type_ptr_equal_type expr_info.ei_qual_type.qt_type_ptr typ + | _ -> false) + | Ctl_parser_types.Decl decl, ALVar.Const typ -> + (match CAst_utils.type_of_decl decl with + | Some type_ptr -> + type_ptr_equal_type type_ptr typ + | _ -> false) + | _ -> false let method_return_type an _typ = Logging.out "\n Executing method_return_type..."; diff --git a/infer/src/clang/cPredicates.mli b/infer/src/clang/cPredicates.mli index c8c6fbacf..be1c959d9 100644 --- a/infer/src/clang/cPredicates.mli +++ b/infer/src/clang/cPredicates.mli @@ -29,7 +29,7 @@ val is_syntactically_global_var : Ctl_parser_types.ast_node -> bool val is_const_expr_var : Ctl_parser_types.ast_node -> bool -val call_function_named : Ctl_parser_types.ast_node -> ALVar.alexp list -> bool +val call_function : Ctl_parser_types.ast_node -> ALVar.alexp -> bool val is_strong_property : Ctl_parser_types.ast_node -> bool @@ -60,6 +60,9 @@ val is_node : Ctl_parser_types.ast_node -> ALVar.alexp -> bool val declaration_has_name : Ctl_parser_types.ast_node -> ALVar.alexp -> bool +val declaration_ref_name : ?kind:Clang_ast_t.decl_kind -> Ctl_parser_types.ast_node -> + ALVar.alexp -> bool + val is_class : Ctl_parser_types.ast_node -> ALVar.alexp -> bool val pp_predicate : Format.formatter -> t -> unit @@ -67,6 +70,8 @@ val pp_predicate : Format.formatter -> t -> unit val decl_unavailable_in_supported_ios_sdk : CLintersContext.context -> Ctl_parser_types.ast_node -> bool +val has_type : Ctl_parser_types.ast_node -> ALVar.alexp -> bool + 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 diff --git a/infer/src/clang/cTL.ml b/infer/src/clang/cTL.ml index 69d1a40e0..3ce5dac3e 100644 --- a/infer/src/clang/cTL.ml +++ b/infer/src/clang/cTL.ml @@ -597,45 +597,38 @@ let next_state_via_transition an trans = let rec eval_Atomic _pred_name args an lcxt = let pred_name = ALVar.formula_id_to_string _pred_name in match pred_name, args, an with + | "call_class_method", [c; m], an -> CPredicates.call_class_method an c m + | "call_function", [m], an -> CPredicates.call_function an m + | "call_instance_method", [c; m], an -> CPredicates.call_instance_method an c m | "call_method", [m], an -> CPredicates.call_method an m - | "call_class_method", [c; m], an -> - CPredicates.call_class_method an c m - | "call_instance_method", [c; m], an -> - CPredicates.call_instance_method an c m - | "is_objc_interface_named", [name], an -> - CPredicates.is_objc_interface_named an name - | "property_named", [word], an -> - CPredicates.property_named an word - | "is_objc_extension", [], _ -> CPredicates.is_objc_extension lcxt - | "is_global_var", [], an -> CPredicates.is_syntactically_global_var an - | "is_const_var", [], an -> CPredicates.is_const_expr_var an - | "call_function_named", args, an -> CPredicates.call_function_named an args - | "is_strong_property", [], an -> CPredicates.is_strong_property an + | "captures_cxx_references", [], _ -> CPredicates.captures_cxx_references an + | "context_in_synchronized_block", [], _ -> CPredicates.context_in_synchronized_block lcxt + | "declaration_has_name", [decl_name], an -> CPredicates.declaration_has_name an decl_name + | "declaration_ref_name", [decl_name], an -> CPredicates.declaration_ref_name an decl_name + | "decl_unavailable_in_supported_ios_sdk", [], an -> + CPredicates.decl_unavailable_in_supported_ios_sdk lcxt an + | "has_type", [typ], an -> CPredicates.has_type an typ + | "isa", [classname], an -> CPredicates.isa an classname | "is_assign_property", [], an -> CPredicates.is_assign_property an - | "is_property_pointer_type", [], an -> CPredicates.is_property_pointer_type an - | "context_in_synchronized_block", [], _ -> - CPredicates.context_in_synchronized_block lcxt + | "is_binop_with_kind", [kind], an -> CPredicates.is_binop_with_kind an kind + | "is_class", [cname], an -> CPredicates.is_class an cname + | "is_const_var", [], an -> CPredicates.is_const_expr_var an + | "is_global_var", [], an -> CPredicates.is_syntactically_global_var an | "is_ivar_atomic", [], an -> CPredicates.is_ivar_atomic an | "is_method_property_accessor_of_ivar", [], an -> CPredicates.is_method_property_accessor_of_ivar an lcxt + | "is_node", [nodename], an -> CPredicates.is_node an nodename | "is_objc_constructor", [], _ -> CPredicates.is_objc_constructor lcxt | "is_objc_dealloc", [], _ -> CPredicates.is_objc_dealloc lcxt - | "captures_cxx_references", [], _ -> CPredicates.captures_cxx_references an - | "is_binop_with_kind", [kind], an -> - CPredicates.is_binop_with_kind an kind - | "is_unop_with_kind", [kind], an -> - CPredicates.is_unop_with_kind an kind - | "is_node", [nodename], an -> CPredicates.is_node an nodename - | "isa", [classname], an -> CPredicates.isa an classname - | "declaration_has_name", [decl_name], an -> - CPredicates.declaration_has_name an decl_name - | "is_class", [cname], an -> CPredicates.is_class an cname - | "decl_unavailable_in_supported_ios_sdk", [], an -> - CPredicates.decl_unavailable_in_supported_ios_sdk lcxt an + | "is_objc_extension", [], _ -> CPredicates.is_objc_extension lcxt + | "is_objc_interface_named", [name], an -> CPredicates.is_objc_interface_named an name + | "is_property_pointer_type", [], an -> CPredicates.is_property_pointer_type an + | "is_strong_property", [], an -> CPredicates.is_strong_property an + | "is_unop_with_kind", [kind], an -> CPredicates.is_unop_with_kind an kind + | "method_return_type", [typ], an -> CPredicates.method_return_type an typ + | "property_named", [word], an -> CPredicates.property_named an word | "within_responds_to_selector_block", [], an -> CPredicates.within_responds_to_selector_block lcxt an - | "method_return_type", [typ], an -> - CPredicates.method_return_type an typ | _ -> failwith ("ERROR: Undefined Predicate or wrong set of arguments: '" ^ pred_name ^ "'") diff --git a/infer/tests/codetoanalyze/objc/linters-for-test-only/Makefile b/infer/tests/codetoanalyze/objc/linters-for-test-only/Makefile index 6f043a511..b176c649c 100644 --- a/infer/tests/codetoanalyze/objc/linters-for-test-only/Makefile +++ b/infer/tests/codetoanalyze/objc/linters-for-test-only/Makefile @@ -15,5 +15,6 @@ INFERPRINT_OPTIONS = --issues-tests SOURCES = \ $(wildcard *.m) \ $(wildcard */*.m) \ + $(wildcard *.c) \ include $(TESTS_DIR)/clang.make diff --git a/infer/tests/codetoanalyze/objc/linters-for-test-only/implicit_cast.c b/infer/tests/codetoanalyze/objc/linters-for-test-only/implicit_cast.c new file mode 100644 index 000000000..377a590ad --- /dev/null +++ b/infer/tests/codetoanalyze/objc/linters-for-test-only/implicit_cast.c @@ -0,0 +1,12 @@ +/* + * 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. + */ +int main() { + long n = 5000000000; + int c = n; +} 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 8dfcdbf4f..eb46c4eb2 100644 --- a/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp +++ b/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp @@ -1,3 +1,6 @@ +codetoanalyze/objc/linters-for-test-only/implicit_cast.c, main, 10, TEST_VAR_TYPE_CHECK, [] +codetoanalyze/objc/linters-for-test-only/implicit_cast.c, main, 11, TEST_IMPLICIT_CAST_CHECK, [] +codetoanalyze/objc/linters-for-test-only/implicit_cast.c, main, 11, TEST_VAR_TYPE_CHECK, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, A_foo:, 13, TEST_BUILTIN_TYPE, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, A_foo:, 13, TEST_RETURN_METHOD, [] codetoanalyze/objc/linters-for-test-only/subclassing.m, A_foo:, 19, 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 aa979a00c..70135b4de 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 @@ -127,5 +127,25 @@ DEFINE-CHECKER TEST_BUILTIN_TYPE = { HOLDS-IN-NODE ObjCMethodDecl; SET message = "Method return....."; +}; + +DEFINE-CHECKER TEST_IMPLICIT_CAST_CHECK = { + + LET has_type_long_expr = has_type("long") HOLDS-EVENTUALLY; + + SET report_when = + WHEN + has_type("int") AND has_type_long_expr + HOLDS-IN-NODE ImplicitCastExpr; + + SET message = "An implicit case from long to int can cause a crash"; +}; + +DEFINE-CHECKER TEST_VAR_TYPE_CHECK = { + + SET report_when = + WHEN has_type("int") OR has_type("long") + HOLDS-IN-NODE VarDecl; + SET message = "Var has type int or long"; };