| 1 | //===--- Matchers.h - clang-tidy-------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MATCHERS_H |
| 10 | #define |
| 11 | |
| 12 | #include "TypeTraits.h" |
| 13 | #include "clang/AST/ExprConcepts.h" |
| 14 | #include "clang/ASTMatchers/ASTMatchers.h" |
| 15 | #include <optional> |
| 16 | |
| 17 | namespace clang::tidy::matchers { |
| 18 | |
| 19 | AST_MATCHER(BinaryOperator, isRelationalOperator) { |
| 20 | return Node.isRelationalOp(); |
| 21 | } |
| 22 | |
| 23 | AST_MATCHER(BinaryOperator, isEqualityOperator) { return Node.isEqualityOp(); } |
| 24 | |
| 25 | AST_MATCHER(QualType, isExpensiveToCopy) { |
| 26 | std::optional<bool> IsExpensive = |
| 27 | utils::type_traits::isExpensiveToCopy(Type: Node, Context: Finder->getASTContext()); |
| 28 | return IsExpensive && *IsExpensive; |
| 29 | } |
| 30 | |
| 31 | AST_MATCHER(RecordDecl, isTriviallyDefaultConstructible) { |
| 32 | return utils::type_traits::recordIsTriviallyDefaultConstructible( |
| 33 | RecordDecl: Node, Context: Finder->getASTContext()); |
| 34 | } |
| 35 | |
| 36 | AST_MATCHER(QualType, isTriviallyDestructible) { |
| 37 | return utils::type_traits::isTriviallyDestructible(Type: Node); |
| 38 | } |
| 39 | |
| 40 | // Returns QualType matcher for references to const. |
| 41 | AST_MATCHER_FUNCTION(ast_matchers::TypeMatcher, isReferenceToConst) { |
| 42 | using namespace ast_matchers; |
| 43 | return referenceType(pointee(qualType(isConstQualified()))); |
| 44 | } |
| 45 | |
| 46 | // Returns QualType matcher for pointers to const. |
| 47 | AST_MATCHER_FUNCTION(ast_matchers::TypeMatcher, isPointerToConst) { |
| 48 | using namespace ast_matchers; |
| 49 | return pointerType(pointee(qualType(isConstQualified()))); |
| 50 | } |
| 51 | |
| 52 | // Returns QualType matcher for target char type only. |
| 53 | AST_MATCHER(QualType, isSimpleChar) { |
| 54 | const auto ActualType = Node.getTypePtr(); |
| 55 | return ActualType && |
| 56 | (ActualType->isSpecificBuiltinType(K: BuiltinType::Char_S) || |
| 57 | ActualType->isSpecificBuiltinType(K: BuiltinType::Char_U)); |
| 58 | } |
| 59 | |
| 60 | AST_MATCHER(Expr, hasUnevaluatedContext) { |
| 61 | if (isa<CXXNoexceptExpr>(Val: Node) || isa<RequiresExpr>(Val: Node)) |
| 62 | return true; |
| 63 | if (const auto *UnaryExpr = dyn_cast<UnaryExprOrTypeTraitExpr>(Val: &Node)) { |
| 64 | switch (UnaryExpr->getKind()) { |
| 65 | case UETT_SizeOf: |
| 66 | case UETT_AlignOf: |
| 67 | return true; |
| 68 | default: |
| 69 | return false; |
| 70 | } |
| 71 | } |
| 72 | if (const auto *TypeIDExpr = dyn_cast<CXXTypeidExpr>(Val: &Node)) |
| 73 | return !TypeIDExpr->isPotentiallyEvaluated(); |
| 74 | return false; |
| 75 | } |
| 76 | |
| 77 | // A matcher implementation that matches a list of type name regular expressions |
| 78 | // against a NamedDecl. If a regular expression contains the substring "::" |
| 79 | // matching will occur against the qualified name, otherwise only the typename. |
| 80 | class MatchesAnyListedNameMatcher |
| 81 | : public ast_matchers::internal::MatcherInterface<NamedDecl> { |
| 82 | public: |
| 83 | explicit MatchesAnyListedNameMatcher(llvm::ArrayRef<StringRef> NameList) { |
| 84 | std::transform( |
| 85 | first: NameList.begin(), last: NameList.end(), result: std::back_inserter(x&: NameMatchers), |
| 86 | unary_op: [](const llvm::StringRef Name) { return NameMatcher(Name); }); |
| 87 | } |
| 88 | |
| 89 | class NameMatcher { |
| 90 | llvm::Regex Regex; |
| 91 | enum class MatchMode { |
| 92 | // Match against the unqualified name because the regular expression |
| 93 | // does not contain ":". |
| 94 | MatchUnqualified, |
| 95 | // Match against the qualified name because the regular expression |
| 96 | // contains ":" suggesting name and namespace should be matched. |
| 97 | MatchQualified, |
| 98 | // Match against the fully qualified name because the regular expression |
| 99 | // starts with ":". |
| 100 | MatchFullyQualified, |
| 101 | }; |
| 102 | MatchMode Mode; |
| 103 | |
| 104 | public: |
| 105 | NameMatcher(const llvm::StringRef Regex) |
| 106 | : Regex(Regex), Mode(determineMatchMode(Regex)) {} |
| 107 | |
| 108 | bool match(const NamedDecl &ND) const { |
| 109 | switch (Mode) { |
| 110 | case MatchMode::MatchQualified: |
| 111 | return Regex.match(String: ND.getQualifiedNameAsString()); |
| 112 | case MatchMode::MatchFullyQualified: |
| 113 | return Regex.match(String: "::" + ND.getQualifiedNameAsString()); |
| 114 | default: |
| 115 | if (const IdentifierInfo *II = ND.getIdentifier()) |
| 116 | return Regex.match(String: II->getName()); |
| 117 | return false; |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | private: |
| 122 | MatchMode determineMatchMode(llvm::StringRef Regex) { |
| 123 | if (Regex.starts_with(Prefix: ":" ) || Regex.starts_with(Prefix: "^:" )) { |
| 124 | return MatchMode::MatchFullyQualified; |
| 125 | } |
| 126 | return Regex.contains(Other: ":" ) ? MatchMode::MatchQualified |
| 127 | : MatchMode::MatchUnqualified; |
| 128 | } |
| 129 | }; |
| 130 | |
| 131 | bool matches( |
| 132 | const NamedDecl &Node, ast_matchers::internal::ASTMatchFinder *Finder, |
| 133 | ast_matchers::internal::BoundNodesTreeBuilder *Builder) const override { |
| 134 | return llvm::any_of(Range: NameMatchers, P: [&Node](const NameMatcher &NM) { |
| 135 | return NM.match(ND: Node); |
| 136 | }); |
| 137 | } |
| 138 | |
| 139 | private: |
| 140 | std::vector<NameMatcher> NameMatchers; |
| 141 | }; |
| 142 | |
| 143 | // Returns a matcher that matches NamedDecl's against a list of provided regular |
| 144 | // expressions. If a regular expression contains starts ':' the NamedDecl's |
| 145 | // qualified name will be used for matching, otherwise its name will be used. |
| 146 | inline ::clang::ast_matchers::internal::Matcher<NamedDecl> |
| 147 | matchesAnyListedName(llvm::ArrayRef<StringRef> NameList) { |
| 148 | return ::clang::ast_matchers::internal::makeMatcher( |
| 149 | Implementation: new MatchesAnyListedNameMatcher(NameList)); |
| 150 | } |
| 151 | |
| 152 | // Predicate that verify if statement is not identical to one bound to ID node. |
| 153 | struct NotIdenticalStatementsPredicate { |
| 154 | bool |
| 155 | operator()(const clang::ast_matchers::internal::BoundNodesMap &Nodes) const; |
| 156 | |
| 157 | std::string ID; |
| 158 | ::clang::DynTypedNode Node; |
| 159 | ASTContext *Context; |
| 160 | }; |
| 161 | |
| 162 | // Checks if statement is identical (utils::areStatementsIdentical) to one bound |
| 163 | // to ID node. |
| 164 | AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID) { |
| 165 | NotIdenticalStatementsPredicate Predicate{ |
| 166 | ID, ::clang::DynTypedNode::create(Node), &(Finder->getASTContext())}; |
| 167 | return Builder->removeBindings(Predicate); |
| 168 | } |
| 169 | |
| 170 | // A matcher implementation that matches a list of type name regular expressions |
| 171 | // against a QualType. |
| 172 | class MatchesAnyListedTypeNameMatcher |
| 173 | : public ast_matchers::internal::MatcherInterface<QualType> { |
| 174 | public: |
| 175 | explicit MatchesAnyListedTypeNameMatcher(llvm::ArrayRef<StringRef> NameList, |
| 176 | bool CanonicalTypes); |
| 177 | ~MatchesAnyListedTypeNameMatcher() override; |
| 178 | bool matches( |
| 179 | const QualType &Node, ast_matchers::internal::ASTMatchFinder *Finder, |
| 180 | ast_matchers::internal::BoundNodesTreeBuilder *Builder) const override; |
| 181 | |
| 182 | private: |
| 183 | std::vector<llvm::Regex> NameMatchers; |
| 184 | bool CanonicalTypes; |
| 185 | }; |
| 186 | |
| 187 | // Returns a matcher that matches QualType against a list of provided regular. |
| 188 | inline ::clang::ast_matchers::internal::Matcher<QualType> |
| 189 | matchesAnyListedTypeName(llvm::ArrayRef<StringRef> NameList, |
| 190 | bool CanonicalTypes) { |
| 191 | return ::clang::ast_matchers::internal::makeMatcher( |
| 192 | Implementation: new MatchesAnyListedTypeNameMatcher(NameList, CanonicalTypes)); |
| 193 | } |
| 194 | inline ::clang::ast_matchers::internal::Matcher<QualType> |
| 195 | matchesAnyListedTypeName(llvm::ArrayRef<StringRef> NameList) { |
| 196 | return matchesAnyListedTypeName(NameList, CanonicalTypes: true); |
| 197 | } |
| 198 | |
| 199 | } // namespace clang::tidy::matchers |
| 200 | |
| 201 | #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MATCHERS_H |
| 202 | |