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 LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MATCHERS_H
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 
AST_MATCHER(BinaryOperator,isRelationalOperator)19 AST_MATCHER(BinaryOperator, isRelationalOperator) {
20   return Node.isRelationalOp();
21 }
22 
AST_MATCHER(BinaryOperator,isEqualityOperator)23 AST_MATCHER(BinaryOperator, isEqualityOperator) { return Node.isEqualityOp(); }
24 
AST_MATCHER(QualType,isExpensiveToCopy)25 AST_MATCHER(QualType, isExpensiveToCopy) {
26   std::optional<bool> IsExpensive =
27       utils::type_traits::isExpensiveToCopy(Node, Finder->getASTContext());
28   return IsExpensive && *IsExpensive;
29 }
30 
AST_MATCHER(RecordDecl,isTriviallyDefaultConstructible)31 AST_MATCHER(RecordDecl, isTriviallyDefaultConstructible) {
32   return utils::type_traits::recordIsTriviallyDefaultConstructible(
33       Node, Finder->getASTContext());
34 }
35 
AST_MATCHER(QualType,isTriviallyDestructible)36 AST_MATCHER(QualType, isTriviallyDestructible) {
37   return utils::type_traits::isTriviallyDestructible(Node);
38 }
39 
40 // Returns QualType matcher for references to const.
AST_MATCHER_FUNCTION(ast_matchers::TypeMatcher,isReferenceToConst)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.
AST_MATCHER_FUNCTION(ast_matchers::TypeMatcher,isPointerToConst)47 AST_MATCHER_FUNCTION(ast_matchers::TypeMatcher, isPointerToConst) {
48   using namespace ast_matchers;
49   return pointerType(pointee(qualType(isConstQualified())));
50 }
51 
AST_MATCHER(Expr,hasUnevaluatedContext)52 AST_MATCHER(Expr, hasUnevaluatedContext) {
53   if (isa<CXXNoexceptExpr>(Node) || isa<RequiresExpr>(Node))
54     return true;
55   if (const auto *UnaryExpr = dyn_cast<UnaryExprOrTypeTraitExpr>(&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>(&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:
MatchesAnyListedNameMatcher(llvm::ArrayRef<StringRef> NameList)75   explicit MatchesAnyListedNameMatcher(llvm::ArrayRef<StringRef> NameList) {
76     std::transform(
77         NameList.begin(), NameList.end(), std::back_inserter(NameMatchers),
78         [](const llvm::StringRef Name) { return NameMatcher(Name); });
79   }
matches(const NamedDecl & Node,ast_matchers::internal::ASTMatchFinder * Finder,ast_matchers::internal::BoundNodesTreeBuilder * Builder)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(NameMatchers, [&Node](const NameMatcher &NM) {
84       return NM.match(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:
NameMatcher(const llvm::StringRef Regex)105     NameMatcher(const llvm::StringRef Regex)
106         : Regex(Regex), Mode(determineMatchMode(Regex)) {}
107 
match(const NamedDecl & ND)108     bool match(const NamedDecl &ND) const {
109       switch (Mode) {
110       case MatchMode::MatchQualified:
111         return Regex.match(ND.getQualifiedNameAsString());
112       case MatchMode::MatchFullyQualified:
113         return Regex.match("::" + ND.getQualifiedNameAsString());
114       default:
115         if (const IdentifierInfo *II = ND.getIdentifier())
116           return Regex.match(II->getName());
117         return false;
118       }
119     }
120 
121   private:
determineMatchMode(llvm::StringRef Regex)122     MatchMode determineMatchMode(llvm::StringRef Regex) {
123       if (Regex.starts_with(":") || Regex.starts_with("^:")) {
124         return MatchMode::MatchFullyQualified;
125       }
126       return Regex.contains(":") ? 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>
matchesAnyListedName(llvm::ArrayRef<StringRef> NameList)138 matchesAnyListedName(llvm::ArrayRef<StringRef> NameList) {
139   return ::clang::ast_matchers::internal::makeMatcher(
140       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.
AST_MATCHER_P(Stmt,isStatementIdenticalToBoundNode,std::string,ID)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>
matchesAnyListedTypeName(llvm::ArrayRef<StringRef> NameList)178 matchesAnyListedTypeName(llvm::ArrayRef<StringRef> NameList) {
179   return ::clang::ast_matchers::internal::makeMatcher(
180       new MatchesAnyListedTypeNameMatcher(NameList));
181 }
182 
183 } // namespace clang::tidy::matchers
184 
185 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MATCHERS_H
186