//===---------- ASTUtils.cpp - clang-tidy ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "ASTUtils.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Lex/Lexer.h" namespace clang::tidy::utils { using namespace ast_matchers; const FunctionDecl *getSurroundingFunction(ASTContext &Context, const Stmt &Statement) { return selectFirst( "function", match(stmt(hasAncestor(functionDecl().bind("function"))), Statement, Context)); } bool isBinaryOrTernary(const Expr *E) { const Expr *EBase = E->IgnoreImpCasts(); if (isa(EBase) || isa(EBase)) { return true; } if (const auto *Operator = dyn_cast(EBase)) { return Operator->isInfixBinaryOp(); } return false; } bool exprHasBitFlagWithSpelling(const Expr *Flags, const SourceManager &SM, const LangOptions &LangOpts, StringRef FlagName) { // If the Flag is an integer constant, check it. if (isa(Flags)) { if (!SM.isMacroBodyExpansion(Flags->getBeginLoc()) && !SM.isMacroArgExpansion(Flags->getBeginLoc())) return false; // Get the macro name. auto MacroName = Lexer::getSourceText( CharSourceRange::getTokenRange(Flags->getSourceRange()), SM, LangOpts); return MacroName == FlagName; } // If it's a binary OR operation. if (const auto *BO = dyn_cast(Flags)) if (BO->getOpcode() == BinaryOperatorKind::BO_Or) return exprHasBitFlagWithSpelling(BO->getLHS()->IgnoreParenCasts(), SM, LangOpts, FlagName) || exprHasBitFlagWithSpelling(BO->getRHS()->IgnoreParenCasts(), SM, LangOpts, FlagName); // Otherwise, assume it has the flag. return true; } bool rangeIsEntirelyWithinMacroArgument(SourceRange Range, const SourceManager *SM) { // Check if the range is entirely contained within a macro argument. SourceLocation MacroArgExpansionStartForRangeBegin; SourceLocation MacroArgExpansionStartForRangeEnd; bool RangeIsEntirelyWithinMacroArgument = SM && SM->isMacroArgExpansion(Range.getBegin(), &MacroArgExpansionStartForRangeBegin) && SM->isMacroArgExpansion(Range.getEnd(), &MacroArgExpansionStartForRangeEnd) && MacroArgExpansionStartForRangeBegin == MacroArgExpansionStartForRangeEnd; return RangeIsEntirelyWithinMacroArgument; } bool rangeContainsMacroExpansion(SourceRange Range, const SourceManager *SM) { return rangeIsEntirelyWithinMacroArgument(Range, SM) || Range.getBegin().isMacroID() || Range.getEnd().isMacroID(); } bool rangeCanBeFixed(SourceRange Range, const SourceManager *SM) { return utils::rangeIsEntirelyWithinMacroArgument(Range, SM) || !utils::rangeContainsMacroExpansion(Range, SM); } bool areStatementsIdentical(const Stmt *FirstStmt, const Stmt *SecondStmt, const ASTContext &Context, bool Canonical) { if (!FirstStmt || !SecondStmt) return false; if (FirstStmt == SecondStmt) return true; if (FirstStmt->getStmtClass() != FirstStmt->getStmtClass()) return false; if (isa(FirstStmt) && isa(SecondStmt)) { // If we have errors in expressions, we will be unable // to accurately profile and compute hashes for each statements. if (llvm::cast(FirstStmt)->containsErrors() || llvm::cast(SecondStmt)->containsErrors()) return false; } llvm::FoldingSetNodeID DataFirst, DataSecond; FirstStmt->Profile(DataFirst, Context, Canonical); SecondStmt->Profile(DataSecond, Context, Canonical); return DataFirst == DataSecond; } const IndirectFieldDecl * findOutermostIndirectFieldDeclForField(const FieldDecl *FD) { const RecordDecl *Record = FD->getParent(); assert(Record->isAnonymousStructOrUnion() && "FD must be a field in an anonymous record"); const DeclContext *Context = Record; while (isa(Context) && cast(Context)->isAnonymousStructOrUnion()) { Context = Context->getParent(); } // Search for the target IndirectFieldDecl within the located context. for (const auto *D : Context->decls()) { const auto *IFD = dyn_cast(D); if (!IFD) continue; if (IFD->getAnonField() == FD) return IFD; } return nullptr; } } // namespace clang::tidy::utils