diff --git a/clang-tools-extra/clang-tidy/utils/Matchers.h b/clang-tools-extra/clang-tidy/utils/Matchers.h --- a/clang-tools-extra/clang-tidy/utils/Matchers.h +++ b/clang-tools-extra/clang-tidy/utils/Matchers.h @@ -49,11 +49,76 @@ return pointerType(pointee(qualType(isConstQualified()))); } -AST_MATCHER_P(NamedDecl, matchesAnyListedName, std::vector, - NameList) { - return llvm::any_of(NameList, [&Node](const std::string &Name) { - return llvm::Regex(Name).match(Node.getName()); +// A matcher implementation that matches a list of type name regular expressions +// against a NamedDecl. If a regular expression contains the substring "::" +// matching will occur against the qualified name, otherwise only the typename. +class MatchesAnyListedNameMatcher + : public ast_matchers::internal::MatcherInterface { +public: + explicit MatchesAnyListedNameMatcher(llvm::ArrayRef NameList) { + std::transform( + NameList.begin(), NameList.end(), std::back_inserter(NameMatchers), + [](const llvm::StringRef Name) { return NameMatcher(Name); }); + } + bool matches( + const NamedDecl &Node, ast_matchers::internal::ASTMatchFinder *Finder, + ast_matchers::internal::BoundNodesTreeBuilder *Builder) const override { + return llvm::any_of(NameMatchers, [&Node](const NameMatcher &NM) { + return NM.match(Node); }); + } + +private: + class NameMatcher { + llvm::Regex Regex; + enum class MatchMode { + // Match against the unqualified name because the regular expression + // does not contain ":". + MatchUnqualified, + // Match against the qualified name because the regular expression + // contains ":" suggesting name and namespace should be matched. + MatchQualified, + // Match against the fully qualified name because the regular expression + // starts with ":". + MatchFullyQualified, + }; + MatchMode Mode; + + public: + NameMatcher(const llvm::StringRef Regex) + : Regex(Regex), Mode(determineMatchMode(Regex)) {} + + bool match(const NamedDecl &ND) const { + switch (Mode) { + case MatchMode::MatchQualified: + return Regex.match(ND.getQualifiedNameAsString()); + case MatchMode::MatchFullyQualified: + return Regex.match("::" + ND.getQualifiedNameAsString()); + default: + return Regex.match(ND.getName()); + } + } + + private: + MatchMode determineMatchMode(llvm::StringRef Regex) { + if (Regex.startswith(":") || Regex.startswith("^:")) { + return MatchMode::MatchFullyQualified; + } + return Regex.contains(":") ? MatchMode::MatchQualified + : MatchMode::MatchUnqualified; + } + }; + + std::vector NameMatchers; +}; + +// Returns a matcher that matches NamedDecl's against a list of provided regular +// expressions. If a regular expression contains starts ':' the NamedDecl's +// qualified name will be used for matching, otherwise its name will be used. +inline ::clang::ast_matchers::internal::Matcher +matchesAnyListedName(llvm::ArrayRef NameList) { + return ::clang::ast_matchers::internal::makeMatcher( + new MatchesAnyListedNameMatcher(NameList)); } } // namespace matchers diff --git a/clang-tools-extra/docs/clang-tidy/checks/performance-for-range-copy.rst b/clang-tools-extra/docs/clang-tidy/checks/performance-for-range-copy.rst --- a/clang-tools-extra/docs/clang-tidy/checks/performance-for-range-copy.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/performance-for-range-copy.rst @@ -31,4 +31,6 @@ A semicolon-separated list of names of types allowed to be copied in each iteration. Regular expressions are accepted, e.g. `[Rr]ef(erence)?$` matches every type with suffix `Ref`, `ref`, `Reference` and `reference`. The default - is empty. + is empty. If a name in the list contains the sequence `::` it is matched + against the qualified typename (i.e. `namespace::Type`, otherwise it is + matched against only the type name (i.e. `Type`). diff --git a/clang-tools-extra/docs/clang-tidy/checks/performance-unnecessary-copy-initialization.rst b/clang-tools-extra/docs/clang-tidy/checks/performance-unnecessary-copy-initialization.rst --- a/clang-tools-extra/docs/clang-tidy/checks/performance-unnecessary-copy-initialization.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/performance-unnecessary-copy-initialization.rst @@ -43,5 +43,7 @@ A semicolon-separated list of names of types allowed to be initialized by copying. Regular expressions are accepted, e.g. `[Rr]ef(erence)?$` matches - every type with suffix `Ref`, `ref`, `Reference` and `reference`. The - default is empty. + every type with suffix `Ref`, `ref`, `Reference` and `reference`. The default + is empty. If a name in the list contains the sequence `::` it is matched + against the qualified typename (i.e. `namespace::Type`, otherwise it is + matched against only the type name (i.e. `Type`). diff --git a/clang-tools-extra/docs/clang-tidy/checks/performance-unnecessary-value-param.rst b/clang-tools-extra/docs/clang-tidy/checks/performance-unnecessary-value-param.rst --- a/clang-tools-extra/docs/clang-tidy/checks/performance-unnecessary-value-param.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/performance-unnecessary-value-param.rst @@ -66,4 +66,7 @@ A semicolon-separated list of names of types allowed to be passed by value. Regular expressions are accepted, e.g. `[Rr]ef(erence)?$` matches every type - with suffix `Ref`, `ref`, `Reference` and `reference`. The default is empty. + with suffix `Ref`, `ref`, `Reference` and `reference`. The default is + empty. If a name in the list contains the sequence `::` it is matched against + the qualified typename (i.e. `namespace::Type`, otherwise it is matched + against only the type name (i.e. `Type`). diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance-for-range-copy-allowed-types.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance-for-range-copy-allowed-types.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/performance-for-range-copy-allowed-types.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/performance-for-range-copy-allowed-types.cpp @@ -1,5 +1,5 @@ // RUN: %check_clang_tidy %s performance-for-range-copy %t -- \ -// RUN: -config="{CheckOptions: [{key: performance-for-range-copy.AllowedTypes, value: '[Pp]ointer$;[Pp]tr$;[Rr]ef(erence)?$'}]}" \ +// RUN: -config="{CheckOptions: [{key: performance-for-range-copy.AllowedTypes, value: '[Pp]ointer$;[Pp]tr$;[Rr]ef(erence)?$;qualified::Type;::fully::QualifiedType'}]}" \ // RUN: -- -fno-delayed-template-parsing template @@ -63,6 +63,18 @@ typedef SomeComplexTemplate NotTooComplexRef; +namespace qualified { +struct Type { + ~Type(); +}; +} // namespace qualified + +namespace fully { +struct QualifiedType { + ~QualifiedType(); +}; +} // namespace fully + void negativeSmartPointer() { for (auto P : View>()) { auto P2 = P; @@ -124,3 +136,23 @@ auto R2 = R; } } + +void negativeQualified() { + for (auto Q : View>()) { + auto Q2 = Q; + } + using qualified::Type; + for (auto Q : View>()) { + auto Q2 = Q; + } +} + +void negativeFullyQualified() { + for (auto Q : View>()) { + auto Q2 = Q; + } + using fully::QualifiedType; + for (auto Q : View>()) { + auto Q2 = Q; + } +}