1 //===---------- ASTUtils.cpp - clang-tidy ---------------------------------===//
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
7 //===----------------------------------------------------------------------===//
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/Lex/Lexer.h"
15 namespace clang::tidy::utils
{
16 using namespace ast_matchers
;
18 const FunctionDecl
*getSurroundingFunction(ASTContext
&Context
,
19 const Stmt
&Statement
) {
20 return selectFirst
<const FunctionDecl
>(
21 "function", match(stmt(hasAncestor(functionDecl().bind("function"))),
25 bool isBinaryOrTernary(const Expr
*E
) {
26 const Expr
*EBase
= E
->IgnoreImpCasts();
27 if (isa
<BinaryOperator
>(EBase
) || isa
<ConditionalOperator
>(EBase
)) {
31 if (const auto *Operator
= dyn_cast
<CXXOperatorCallExpr
>(EBase
)) {
32 return Operator
->isInfixBinaryOp();
38 bool exprHasBitFlagWithSpelling(const Expr
*Flags
, const SourceManager
&SM
,
39 const LangOptions
&LangOpts
,
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()))
47 // Get the macro name.
48 auto MacroName
= Lexer::getSourceText(
49 CharSourceRange::getTokenRange(Flags
->getSourceRange()), SM
, LangOpts
);
51 return MacroName
== FlagName
;
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
,
61 // Otherwise, assume it has the flag.
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
=
72 SM
->isMacroArgExpansion(Range
.getBegin(),
73 &MacroArgExpansionStartForRangeBegin
) &&
74 SM
->isMacroArgExpansion(Range
.getEnd(),
75 &MacroArgExpansionStartForRangeEnd
) &&
76 MacroArgExpansionStartForRangeBegin
== MacroArgExpansionStartForRangeEnd
;
78 return RangeIsEntirelyWithinMacroArgument
;
81 bool rangeContainsMacroExpansion(SourceRange Range
, const SourceManager
*SM
) {
82 return rangeIsEntirelyWithinMacroArgument(Range
, SM
) ||
83 Range
.getBegin().isMacroID() || Range
.getEnd().isMacroID();
86 bool rangeCanBeFixed(SourceRange Range
, const SourceManager
*SM
) {
87 return utils::rangeIsEntirelyWithinMacroArgument(Range
, SM
) ||
88 !utils::rangeContainsMacroExpansion(Range
, SM
);
91 bool areStatementsIdentical(const Stmt
*FirstStmt
, const Stmt
*SecondStmt
,
92 const ASTContext
&Context
, bool Canonical
) {
93 if (!FirstStmt
|| !SecondStmt
)
96 if (FirstStmt
== SecondStmt
)
99 if (FirstStmt
->getStmtClass() != SecondStmt
->getStmtClass())
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())
110 llvm::FoldingSetNodeID DataFirst
, DataSecond
;
111 FirstStmt
->Profile(DataFirst
, Context
, Canonical
);
112 SecondStmt
->Profile(DataSecond
, Context
, Canonical
);
113 return DataFirst
== DataSecond
;
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");
122 const DeclContext
*Context
= Record
;
123 while (isa
<RecordDecl
>(Context
) &&
124 cast
<RecordDecl
>(Context
)->isAnonymousStructOrUnion()) {
125 Context
= Context
->getParent();
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
);
133 if (IFD
->getAnonField() == FD
)
140 } // namespace clang::tidy::utils