xref: /llvm-project/clang-tools-extra/clang-tidy/utils/ASTUtils.cpp (revision d48d4805f792adbdac58d480f890449def4964ea)
1 //===---------- ASTUtils.cpp - 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 #include "ASTUtils.h"
10 
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/Lex/Lexer.h"
14 
15 namespace clang::tidy::utils {
16 using namespace ast_matchers;
17 
18 const FunctionDecl *getSurroundingFunction(ASTContext &Context,
19                                            const Stmt &Statement) {
20   return selectFirst<const FunctionDecl>(
21       "function", match(stmt(hasAncestor(functionDecl().bind("function"))),
22                         Statement, Context));
23 }
24 
25 bool isBinaryOrTernary(const Expr *E) {
26   const Expr *EBase = E->IgnoreImpCasts();
27   if (isa<BinaryOperator>(EBase) || isa<ConditionalOperator>(EBase)) {
28     return true;
29   }
30 
31   if (const auto *Operator = dyn_cast<CXXOperatorCallExpr>(EBase)) {
32     return Operator->isInfixBinaryOp();
33   }
34 
35   return false;
36 }
37 
38 bool exprHasBitFlagWithSpelling(const Expr *Flags, const SourceManager &SM,
39                                 const LangOptions &LangOpts,
40                                 StringRef FlagName) {
41   // If the Flag is an integer constant, check it.
42   if (isa<IntegerLiteral>(Flags)) {
43     if (!SM.isMacroBodyExpansion(Flags->getBeginLoc()) &&
44         !SM.isMacroArgExpansion(Flags->getBeginLoc()))
45       return false;
46 
47     // Get the macro name.
48     auto MacroName = Lexer::getSourceText(
49         CharSourceRange::getTokenRange(Flags->getSourceRange()), SM, LangOpts);
50 
51     return MacroName == FlagName;
52   }
53   // If it's a binary OR operation.
54   if (const auto *BO = dyn_cast<BinaryOperator>(Flags))
55     if (BO->getOpcode() == BinaryOperatorKind::BO_Or)
56       return exprHasBitFlagWithSpelling(BO->getLHS()->IgnoreParenCasts(), SM,
57                                         LangOpts, FlagName) ||
58              exprHasBitFlagWithSpelling(BO->getRHS()->IgnoreParenCasts(), SM,
59                                         LangOpts, FlagName);
60 
61   // Otherwise, assume it has the flag.
62   return true;
63 }
64 
65 bool rangeIsEntirelyWithinMacroArgument(SourceRange Range,
66                                         const SourceManager *SM) {
67   // Check if the range is entirely contained within a macro argument.
68   SourceLocation MacroArgExpansionStartForRangeBegin;
69   SourceLocation MacroArgExpansionStartForRangeEnd;
70   bool RangeIsEntirelyWithinMacroArgument =
71       SM &&
72       SM->isMacroArgExpansion(Range.getBegin(),
73                               &MacroArgExpansionStartForRangeBegin) &&
74       SM->isMacroArgExpansion(Range.getEnd(),
75                               &MacroArgExpansionStartForRangeEnd) &&
76       MacroArgExpansionStartForRangeBegin == MacroArgExpansionStartForRangeEnd;
77 
78   return RangeIsEntirelyWithinMacroArgument;
79 }
80 
81 bool rangeContainsMacroExpansion(SourceRange Range, const SourceManager *SM) {
82   return rangeIsEntirelyWithinMacroArgument(Range, SM) ||
83          Range.getBegin().isMacroID() || Range.getEnd().isMacroID();
84 }
85 
86 bool rangeCanBeFixed(SourceRange Range, const SourceManager *SM) {
87   return utils::rangeIsEntirelyWithinMacroArgument(Range, SM) ||
88          !utils::rangeContainsMacroExpansion(Range, SM);
89 }
90 
91 bool areStatementsIdentical(const Stmt *FirstStmt, const Stmt *SecondStmt,
92                             const ASTContext &Context, bool Canonical) {
93   if (!FirstStmt || !SecondStmt)
94     return false;
95 
96   if (FirstStmt == SecondStmt)
97     return true;
98 
99   if (FirstStmt->getStmtClass() != SecondStmt->getStmtClass())
100     return false;
101 
102   if (isa<Expr>(FirstStmt) && isa<Expr>(SecondStmt)) {
103     // If we have errors in expressions, we will be unable
104     // to accurately profile and compute hashes for each statements.
105     if (llvm::cast<Expr>(FirstStmt)->containsErrors() ||
106         llvm::cast<Expr>(SecondStmt)->containsErrors())
107       return false;
108   }
109 
110   llvm::FoldingSetNodeID DataFirst, DataSecond;
111   FirstStmt->Profile(DataFirst, Context, Canonical);
112   SecondStmt->Profile(DataSecond, Context, Canonical);
113   return DataFirst == DataSecond;
114 }
115 
116 const IndirectFieldDecl *
117 findOutermostIndirectFieldDeclForField(const FieldDecl *FD) {
118   const RecordDecl *Record = FD->getParent();
119   assert(Record->isAnonymousStructOrUnion() &&
120          "FD must be a field in an anonymous record");
121 
122   const DeclContext *Context = Record;
123   while (isa<RecordDecl>(Context) &&
124          cast<RecordDecl>(Context)->isAnonymousStructOrUnion()) {
125     Context = Context->getParent();
126   }
127 
128   // Search for the target IndirectFieldDecl within the located context.
129   for (const auto *D : Context->decls()) {
130     const auto *IFD = dyn_cast<IndirectFieldDecl>(D);
131     if (!IFD)
132       continue;
133     if (IFD->getAnonField() == FD)
134       return IFD;
135   }
136 
137   return nullptr;
138 }
139 
140 } // namespace clang::tidy::utils
141