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