diff --git a/infer/src/IR/QualifiedCppName.ml b/infer/src/IR/QualifiedCppName.ml index 8e70388ee..e40b323bf 100644 --- a/infer/src/IR/QualifiedCppName.ml +++ b/infer/src/IR/QualifiedCppName.ml @@ -27,7 +27,9 @@ let strip_template_args quals = let append_template_args_to_last quals ~args = match quals with | [last; _] when String.contains last '<' - -> failwith "expected qualified name without template args" + -> failwithf + "expected qualified name without template args, but got %s, the last qualifier of %s" last + (String.concat ~sep:", " quals) | last :: rest -> (last ^ args) :: rest | [] diff --git a/infer/src/clang/CType_decl.ml b/infer/src/clang/CType_decl.ml index 9798a32b5..0e9a9215e 100644 --- a/infer/src/clang/CType_decl.ml +++ b/infer/src/clang/CType_decl.ml @@ -177,9 +177,10 @@ and get_record_as_typevar (definition_decl: Clang_ast_t.decl) = as it defaults to Typ.NoTemplate *) and get_record_typename ?tenv decl = let open Clang_ast_t in + let linters_mode = match tenv with Some _ -> false | None -> true in match (decl, tenv) with | RecordDecl (_, name_info, opt_type, _, _, _, _), _ - -> CAst_utils.get_qualified_name name_info |> create_c_record_typename opt_type + -> CAst_utils.get_qualified_name ~linters_mode name_info |> create_c_record_typename opt_type | ClassTemplateSpecializationDecl (_, _, _, _, _, _, _, _, spec_info), Some tenv -> let tname = match CAst_utils.get_decl spec_info.tsi_template_decl with @@ -200,7 +201,8 @@ and get_record_typename ?tenv decl = | ClassTemplateSpecializationDecl (_, name_info, _, _, _, _, _, _, _), _ -> (* we use Typ.CppClass for C++ because we expect Typ.CppClass from *) (* types that have methods. And in C++ struct/class/union can have methods *) - Typ.Name.Cpp.from_qual_name Typ.NoTemplate (CAst_utils.get_qualified_name name_info) + Typ.Name.Cpp.from_qual_name Typ.NoTemplate + (CAst_utils.get_qualified_name ~linters_mode name_info) | ObjCInterfaceDecl (_, name_info, _, _, _), _ | ObjCImplementationDecl (_, name_info, _, _, _), _ | ObjCProtocolDecl (_, name_info, _, _, _), _ diff --git a/infer/src/clang/cAst_utils.ml b/infer/src/clang/cAst_utils.ml index 9ef84a2e0..26dc0da51 100644 --- a/infer/src/clang/cAst_utils.ml +++ b/infer/src/clang/cAst_utils.ml @@ -22,7 +22,16 @@ let sanitize_name = Str.global_replace (Str.regexp "[/ ]") "_" let get_qual_name qual_name_list = List.map ~f:sanitize_name qual_name_list |> QualifiedCppName.of_rev_list -let get_qualified_name name_info = get_qual_name name_info.Clang_ast_t.ni_qual_name +let get_qualified_name ?(linters_mode= false) name_info = + if not linters_mode then get_qual_name name_info.Clang_ast_t.ni_qual_name + else + (* Because we are in linters mode, we can't get precise info about templates, + so we strip the template characters to not upset invariants in the system. *) + let replace_template_chars qual_name = + String.tr ~target:'<' ~replacement:'_' qual_name |> String.tr ~target:'>' ~replacement:'_' + in + let qual_names = List.map ~f:replace_template_chars name_info.Clang_ast_t.ni_qual_name in + get_qual_name qual_names let get_unqualified_name name_info = let name = diff --git a/infer/src/clang/cAst_utils.mli b/infer/src/clang/cAst_utils.mli index 34ee1e897..797a918bd 100644 --- a/infer/src/clang/cAst_utils.mli +++ b/infer/src/clang/cAst_utils.mli @@ -38,7 +38,7 @@ val add_enum_constant : Clang_ast_t.pointer -> Clang_ast_t.pointer option -> uni val get_enum_constant_exp : Clang_ast_t.pointer -> Clang_ast_t.pointer option * Exp.t option -val get_qualified_name : Clang_ast_t.named_decl_info -> QualifiedCppName.t +val get_qualified_name : ?linters_mode:bool -> Clang_ast_t.named_decl_info -> QualifiedCppName.t (** returns sanitized, fully qualified name given name info *) val get_unqualified_name : Clang_ast_t.named_decl_info -> string diff --git a/infer/tests/codetoanalyze/objcpp/linters-for-test-only/hash_test.mm b/infer/tests/codetoanalyze/objcpp/linters-for-test-only/hash_test.mm new file mode 100644 index 000000000..021134eae --- /dev/null +++ b/infer/tests/codetoanalyze/objcpp/linters-for-test-only/hash_test.mm @@ -0,0 +1,15 @@ +/* + * 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. + */ +#import +#import + +template <> +struct std::hash { + size_t operator()(const NSObject* const& obj1) const { return [obj1 hash]; } +}; diff --git a/infer/tests/codetoanalyze/objcpp/linters-for-test-only/issues.exp b/infer/tests/codetoanalyze/objcpp/linters-for-test-only/issues.exp index f55f73fca..b96659641 100644 --- a/infer/tests/codetoanalyze/objcpp/linters-for-test-only/issues.exp +++ b/infer/tests/codetoanalyze/objcpp/linters-for-test-only/issues.exp @@ -12,3 +12,4 @@ codetoanalyze/objcpp/linters-for-test-only/TestParamterLabelsChecks.mm, buttonCo codetoanalyze/objcpp/linters-for-test-only/TestParamterLabelsChecks.mm, buttonComponent, 31, TEST_PARAMETER_LABEL_REGEXP, [] codetoanalyze/objcpp/linters-for-test-only/TestParamterLabelsChecks.mm, foo, 54, TEST_PARAMETER_SELECTOR, [] codetoanalyze/objcpp/linters-for-test-only/TestParamterLabelsChecks.mm, foo, 56, TEST_PARAMETER_SELECTOR, [] +codetoanalyze/objcpp/linters-for-test-only/hash_test.mm, std::hash_NSObject_*__operator(), 14, DISCOURAGED_HASH_METHOD_INVOCATION, [] diff --git a/infer/tests/codetoanalyze/objcpp/linters-for-test-only/linters_example.al b/infer/tests/codetoanalyze/objcpp/linters-for-test-only/linters_example.al index 67f464edf..216e29d0e 100644 --- a/infer/tests/codetoanalyze/objcpp/linters-for-test-only/linters_example.al +++ b/infer/tests/codetoanalyze/objcpp/linters-for-test-only/linters_example.al @@ -102,3 +102,8 @@ DEFINE-CHECKER TEST_PARAMETER_SELECTOR = { HOLDS-IN-NODE ObjCMessageExpr; SET message = "Do not construct the Component action with a selector only...`"; }; + +DEFINE-CHECKER DISCOURAGED_HASH_METHOD_INVOCATION = { + SET report_when = call_method("hash"); + SET message = "Don't use the hash method"; +};