diff --git a/infer/src/clang/cPredicates.ml b/infer/src/clang/cPredicates.ml index 1ed4280a3..782eddac2 100644 --- a/infer/src/clang/cPredicates.ml +++ b/infer/src/clang/cPredicates.ml @@ -632,3 +632,42 @@ let using_namespace an namespace = -> false ) | _ -> false + +let rec get_decl_attributes_for_callexpr an = + let open Clang_ast_t in + let open Ctl_parser_types in + match an with + | Stmt CallExpr (_, func :: _, _) + -> get_decl_attributes_for_callexpr (Stmt func) + | Stmt ImplicitCastExpr (_, [stmt], _, _) + -> get_decl_attributes_for_callexpr (Stmt stmt) + | Stmt DeclRefExpr (_, _, _, drti) -> ( + match CAst_utils.get_decl_opt_with_decl_ref drti.drti_decl_ref with + | Some decl + -> let decl_info = Clang_ast_proj.get_decl_tuple decl in + decl_info.di_attributes + | None + -> [] ) + | _ + -> [] + +let has_visibility_attribute an visibility = + let open Clang_ast_t in + let rec has_visibility_attr attrs param = + match attrs with + | [] + -> false + | (VisibilityAttr attr_info) :: rest + -> if List.exists ~f:(fun s -> String.equal param (String.strip s)) attr_info.ai_parameters + then true + else has_visibility_attr rest param + | _ :: rest + -> has_visibility_attr rest param + in + let attributes = get_decl_attributes_for_callexpr an in + match visibility with ALVar.Const vis -> has_visibility_attr attributes vis | _ -> false + +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 diff --git a/infer/src/clang/cPredicates.mli b/infer/src/clang/cPredicates.mli index 294e97380..2b5920239 100644 --- a/infer/src/clang/cPredicates.mli +++ b/infer/src/clang/cPredicates.mli @@ -97,3 +97,7 @@ val receiver_method_call : Ctl_parser_types.ast_node -> Clang_ast_t.decl option val is_at_selector_with_name : Ctl_parser_types.ast_node -> ALVar.alexp -> bool (** an is an expression @selector with whose name in the language of re *) + +val has_visibility_attribute : Ctl_parser_types.ast_node -> ALVar.alexp -> bool + +val has_used_attribute : Ctl_parser_types.ast_node -> bool diff --git a/infer/src/clang/cTL.ml b/infer/src/clang/cTL.ml index 02a1870c2..6e61f9689 100644 --- a/infer/src/clang/cTL.ml +++ b/infer/src/clang/cTL.ml @@ -796,6 +796,10 @@ let rec eval_Atomic _pred_name args an lcxt = -> CPredicates.is_at_selector_with_name an name | "has_type_subprotocol_of", [protname], an -> CPredicates.has_type_subprotocol_of an protname + | "has_visibility_attribute", [vis], an + -> CPredicates.has_visibility_attribute an vis + | "has_used_attribute", [], an + -> CPredicates.has_used_attribute an | _ -> failwith ("ERROR: Undefined Predicate or wrong set of arguments: '" ^ pred_name ^ "'") 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 03636a132..bb97b103b 100644 --- a/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp +++ b/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp @@ -87,3 +87,11 @@ codetoanalyze/objc/linters-for-test-only/subclassing.m, TestType_m4, 76, TEST_BU 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/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, [] +codetoanalyze/objc/linters-for-test-only/visibility.c, bar, 34, TEST_HIDDEN_VISIBILITY, [] +codetoanalyze/objc/linters-for-test-only/visibility.c, bar, 35, TEST_USED_ATTRIBUTE, [] +codetoanalyze/objc/linters-for-test-only/visibility.c, bar, 36, TEST_DEFAULT_VISIBILITY, [] +codetoanalyze/objc/linters-for-test-only/visibility.c, bar, 36, TEST_DEFAULT_VISIBILITY_WITH_USED_ATTRIBUTE, [] +codetoanalyze/objc/linters-for-test-only/visibility.c, bar, 36, TEST_USED_ATTRIBUTE, [] 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 9d2ba5501..910a1ccb4 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 @@ -348,3 +348,31 @@ DEFINE-CHECKER ENUM_CONSTANTS = { SET report_when = is_at_selector_with_name("actionButtonTapped:"); SET message = "Found @selector(actionButtonTapped:)"; }; + + DEFINE-CHECKER TEST_DEFAULT_VISIBILITY = { + SET report_when = + WHEN has_visibility_attribute("Default") + HOLDS-IN-NODE CallExpr; + SET message = "%name% has default visibility"; + }; + + DEFINE-CHECKER TEST_HIDDEN_VISIBILITY = { + SET report_when = + WHEN has_visibility_attribute("Hidden") + HOLDS-IN-NODE CallExpr; + SET message = "%name% has hidden visibility"; + }; + + DEFINE-CHECKER TEST_USED_ATTRIBUTE = { + SET report_when = + WHEN has_used_attribute() + HOLDS-IN-NODE CallExpr; + SET message = "%name% has used attribute"; + }; + + DEFINE-CHECKER TEST_DEFAULT_VISIBILITY_WITH_USED_ATTRIBUTE = { + SET report_when = + WHEN has_visibility_attribute("Default") AND has_used_attribute + HOLDS-IN-NODE CallExpr; + SET message = "%name% has default visibility and used attribute"; + }; diff --git a/infer/tests/codetoanalyze/objc/linters-for-test-only/visibility.c b/infer/tests/codetoanalyze/objc/linters-for-test-only/visibility.c new file mode 100644 index 000000000..167fe4ea0 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/linters-for-test-only/visibility.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015 - 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 + +void foo_with_default_visibility() __attribute__((visibility("default"))); + +// protected will appear as Default in the AST +void foo_with_protected_visibility() __attribute__((visibility("protected"))); + +// internal will appear as Hidden in the AST +void foo_with_internal_visibility() __attribute__((visibility("internal"))); +void foo_with_hidden_visibility() __attribute__((visibility("hidden"))); +void foo_with_used_attribute() __attribute__((used)); +void foo() __attribute__((visibility("default"))) __attribute__((used)); + +void foo_with_default_visibility() {} +void foo_with_protected_visibility() {} +void foo_with_internal_visibility() {} +void foo_with_hidden_visibility() {} +void foo_with_used_attribute() {} +void foo() {} + +void bar() { + foo_with_default_visibility(); + foo_with_protected_visibility(); + foo_with_internal_visibility(); + foo_with_hidden_visibility(); + foo_with_used_attribute(); + foo(); +}