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 | AST_MATCHER(Expr, hasUnevaluatedContext) { |
53 | if (isa<CXXNoexceptExpr>(Val: Node) || isa<RequiresExpr>(Val: Node)) |
54 | return true; |
55 | if (const auto *UnaryExpr = dyn_cast<UnaryExprOrTypeTraitExpr>(Val: &Node)) { |
56 | switch (UnaryExpr->getKind()) { |
57 | case UETT_SizeOf: |
58 | case UETT_AlignOf: |
59 | return true; |
60 | default: |
61 | return false; |
62 | } |
63 | } |
64 | if (const auto *TypeIDExpr = dyn_cast<CXXTypeidExpr>(Val: &Node)) |
65 | return !TypeIDExpr->isPotentiallyEvaluated(); |
66 | return false; |
67 | } |
68 | |
69 | // A matcher implementation that matches a list of type name regular expressions |
70 | // against a NamedDecl. If a regular expression contains the substring "::" |
71 | // matching will occur against the qualified name, otherwise only the typename. |
72 | class MatchesAnyListedNameMatcher |
73 | : public ast_matchers::internal::MatcherInterface<NamedDecl> { |
74 | public: |
75 | explicit MatchesAnyListedNameMatcher(llvm::ArrayRef<StringRef> NameList) { |
76 | std::transform( |
77 | first: NameList.begin(), last: NameList.end(), result: std::back_inserter(x&: NameMatchers), |
78 | unary_op: [](const llvm::StringRef Name) { return NameMatcher(Name); }); |
79 | } |
80 | bool matches( |
81 | const NamedDecl &Node, ast_matchers::internal::ASTMatchFinder *Finder, |
82 | ast_matchers::internal::BoundNodesTreeBuilder *Builder) const override { |
83 | return llvm::any_of(Range: NameMatchers, P: [&Node](const NameMatcher &NM) { |
84 | return NM.match(ND: Node); |
85 | }); |
86 | } |
87 | |
88 | private: |
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 | std::vector<NameMatcher> NameMatchers; |
132 | }; |
133 | |
134 | // Returns a matcher that matches NamedDecl's against a list of provided regular |
135 | // expressions. If a regular expression contains starts ':' the NamedDecl's |
136 | // qualified name will be used for matching, otherwise its name will be used. |
137 | inline ::clang::ast_matchers::internal::Matcher<NamedDecl> |
138 | matchesAnyListedName(llvm::ArrayRef<StringRef> NameList) { |
139 | return ::clang::ast_matchers::internal::makeMatcher( |
140 | Implementation: new MatchesAnyListedNameMatcher(NameList)); |
141 | } |
142 | |
143 | // Predicate that verify if statement is not identical to one bound to ID node. |
144 | struct NotIdenticalStatementsPredicate { |
145 | bool |
146 | operator()(const clang::ast_matchers::internal::BoundNodesMap &Nodes) const; |
147 | |
148 | std::string ID; |
149 | ::clang::DynTypedNode Node; |
150 | ASTContext *Context; |
151 | }; |
152 | |
153 | // Checks if statement is identical (utils::areStatementsIdentical) to one bound |
154 | // to ID node. |
155 | AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID) { |
156 | NotIdenticalStatementsPredicate Predicate{ |
157 | ID, ::clang::DynTypedNode::create(Node), &(Finder->getASTContext())}; |
158 | return Builder->removeBindings(Predicate); |
159 | } |
160 | |
161 | // A matcher implementation that matches a list of type name regular expressions |
162 | // against a QualType. |
163 | class MatchesAnyListedTypeNameMatcher |
164 | : public ast_matchers::internal::MatcherInterface<QualType> { |
165 | public: |
166 | explicit MatchesAnyListedTypeNameMatcher(llvm::ArrayRef<StringRef> NameList); |
167 | ~MatchesAnyListedTypeNameMatcher() override; |
168 | bool matches( |
169 | const QualType &Node, ast_matchers::internal::ASTMatchFinder *Finder, |
170 | ast_matchers::internal::BoundNodesTreeBuilder *Builder) const override; |
171 | |
172 | private: |
173 | std::vector<llvm::Regex> NameMatchers; |
174 | }; |
175 | |
176 | // Returns a matcher that matches QualType against a list of provided regular. |
177 | inline ::clang::ast_matchers::internal::Matcher<QualType> |
178 | matchesAnyListedTypeName(llvm::ArrayRef<StringRef> NameList) { |
179 | return ::clang::ast_matchers::internal::makeMatcher( |
180 | Implementation: new MatchesAnyListedTypeNameMatcher(NameList)); |
181 | } |
182 | |
183 | } // namespace clang::tidy::matchers |
184 | |
185 | #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MATCHERS_H |
186 | |