11#include "clang/Lex/Lexer.h"
36template <
typename Iterator>
38 Iterator Iter,
const Iterator &EndIter,
39 ArrayRef<ast_matchers::internal::Matcher<VarDecl>> InnerMatchers,
40 ast_matchers::internal::ASTMatchFinder *Finder,
41 ast_matchers::internal::BoundNodesTreeBuilder *Builder,
42 bool Backwards =
false) {
43 const DeclStmt *BeginDS =
nullptr;
44 const DeclStmt *EndDS =
nullptr;
45 const size_t N = InnerMatchers.size();
48 auto Matches = [&](
const Decl *VD) {
53 if (
const auto *Var = dyn_cast<VarDecl>(VD);
54 Var && InnerMatchers[Backwards ? N - Count - 1 : Count].matches(
55 *Var, Finder, Builder)) {
63 for (; Iter != EndIter; ++Iter) {
64 EndDS = dyn_cast<DeclStmt>(*Iter);
72 llvm::reverse_conditionally(EndDS->decls(), Backwards)) {
80 std::swap(BeginDS, EndDS);
93enum TransferType : uint8_t {
102AST_MATCHER_P(Stmt, hasPreTwoVarDecl,
103 llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>,
105 const DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
106 if (Parents.size() != 1)
109 const auto *C = Parents[0].get<CompoundStmt>();
113 const auto It = llvm::find(llvm::reverse(C->body()), &Node);
114 assert(It != C->body_rend() &&
"C is parent of Node");
116 Finder, Builder,
true);
121AST_MATCHER_P(Stmt, hasNextTwoVarDecl,
122 llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>,
124 const DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
125 if (Parents.size() != 1)
128 const auto *C = Parents[0].get<CompoundStmt>();
132 const auto *It = llvm::find(C->body(), &Node);
133 assert(It != C->body_end() &&
"C is parent of Node");
141 llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>,
144 InnerMatchers, Finder, Builder);
149AST_MATCHER(VarDecl, hasAnySpecifiersShouldBeIgnored) {
150 return Node.isStaticLocal() || Node.isConstexpr() || Node.hasAttrs() ||
151 Node.isInlineSpecified() || Node.getStorageClass() != SC_None ||
152 Node.getTSCSpec() != TSCS_unspecified;
157 AST_POLYMORPHIC_SUPPORTED_TYPES(Stmt, Decl)) {
158 return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID();
162 ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
163 if (
const auto *CtorE = dyn_cast<CXXConstructExpr>(&Node)) {
164 if (
const CXXConstructorDecl *CtorD = CtorE->getConstructor();
165 CtorD->isCopyConstructor() && CtorE->getNumArgs() == 1) {
166 return InnerMatcher.matches(*CtorE->getArg(0)->IgnoreImpCasts(), Finder,
171 return InnerMatcher.matches(*Node.IgnoreImpCasts(), Finder, Builder);
175 return llvm::all_of(Node.fields(), [](
const FieldDecl *FD) {
176 return FD->getAccess() == AS_public &&
177 (FD->getName() ==
"first" || FD->getName() ==
"second");
182 return Node.getInitStyle() != VarDecl::InitializationStyle::CInit;
188 StringRef PairName, StringRef MemberName, StringRef TypeName,
189 StringRef BindingName,
190 const ast_matchers::internal::Matcher<VarDecl> &ExtraMatcher) {
191 return varDecl(ExtraMatcher,
192 hasInitializer(ignoringCopyCtorAndImplicitCast(memberExpr(
193 hasObjectExpression(ignoringImpCasts(declRefExpr(
194 to(equalsBoundNode(std::string(PairName)))))),
195 member(fieldDecl(hasName(MemberName),
196 hasType(qualType().bind(TypeName))))))))
201 const ast_matchers::internal::Matcher<QualType> &TypeMatcher) {
203 anyOf(TypeMatcher, lValueReferenceType(pointee(TypeMatcher))));
207 auto PairType = qualType(unless(isVolatileQualified()),
208 hasUnqualifiedDesugaredType(recordType(
209 hasDeclaration(cxxRecordDecl(isPairType())))));
211 auto UnlessShouldBeIgnored =
212 unless(anyOf(hasAnySpecifiersShouldBeIgnored(), isInMacro()));
214 auto VarInitWithFirstMember =
217 auto VarInitWithSecondMember =
221 auto RefToBindName = [&UnlessShouldBeIgnored](
const StringRef &Name) {
222 return declRefExpr(to(varDecl(UnlessShouldBeIgnored).bind(Name)));
225 auto HasAnyLambdaCaptureThisVar =
226 [](
const ast_matchers::internal::Matcher<VarDecl> &VDMatcher) {
227 return compoundStmt(hasDescendant(
228 lambdaExpr(hasAnyCapture(capturesVar(varDecl(VDMatcher))))));
232 auto UnlessFirstVarOrSecondVarIsCapturedByLambda =
233 getLangOpts().CPlusPlus20
235 : compoundStmt(unless(HasAnyLambdaCaptureThisVar(
245 has(cxxOperatorCallExpr(
246 hasOverloadedOperatorName(
"="),
247 hasLHS(ignoringImplicit(
248 callExpr(callee(functionDecl(isInStdNamespace(),
253 hasRHS(expr(hasType(PairType))))
256 llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
259 hasParent(compoundStmt(UnlessFirstVarOrSecondVarIsCapturedByLambda)
269 hasSingleDecl(varDecl(UnlessShouldBeIgnored,
270 unless(isDirectInitialization()),
273 hasInitializer(ignoringCopyCtorAndImplicitCast(
277 llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
278 VarInitWithFirstMember, VarInitWithSecondMember}),
279 hasParent(compoundStmt(UnlessFirstVarOrSecondVarIsCapturedByLambda)
293 hasInitializer(ignoringCopyCtorAndImplicitCast(
298 hasFirstTwoVarDecl(llvm::SmallVector<
299 ast_matchers::internal::Matcher<VarDecl>>{
300 VarInitWithFirstMember, VarInitWithSecondMember}),
301 UnlessFirstVarOrSecondVarIsCapturedByLambda)
309 QualType OriginType) {
310 ResultType = ResultType.getCanonicalType();
311 OriginType = OriginType.getCanonicalType();
313 if (ResultType == Ctx.getLValueReferenceType(OriginType.withConst()))
314 return TT_ByConstRef;
316 if (ResultType == Ctx.getLValueReferenceType(OriginType))
319 if (ResultType == OriginType.withConst())
320 return TT_ByConstVal;
322 if (ResultType == OriginType)
334 const auto *ScopeBlock = Result.Nodes.getNodeAs<CompoundStmt>(
ScopeBlockName);
336 const auto *CFRS = Result.Nodes.getNodeAs<CXXForRangeStmt>(
ForRangeStmtName);
337 auto DiagAndFix = [&BeginDS, &EndDS, &FirstVar, &SecondVar, &CFRS,
338 this](SourceLocation DiagLoc, SourceRange ReplaceRange,
339 TransferType TT = TT_ByVal) {
340 const auto Prefix = [&TT]() -> StringRef {
349 return "const auto&";
353 const std::string ReplacementText =
354 (Twine(Prefix) +
" [" + FirstVar->getNameAsString() +
", " +
355 SecondVar->getNameAsString() +
"]" + (CFRS ?
" :" :
""))
357 diag(DiagLoc,
"use a structured binding to decompose a pair")
358 << FixItHint::CreateReplacement(ReplaceRange, ReplacementText)
359 << FixItHint::CreateRemoval(
360 SourceRange{BeginDS->getBeginLoc(), EndDS->getEndLoc()});
363 if (
const auto *COCE =
365 DiagAndFix(COCE->getBeginLoc(),
372 const auto *PairVar = Result.Nodes.getNodeAs<VarDecl>(
PairDeclName);
374 const std::optional<TransferType> PairCaptureType =
377 const std::optional<TransferType> FirstVarCaptureType =
380 const std::optional<TransferType> SecondVarCaptureType =
383 if (!PairCaptureType || !FirstVarCaptureType || !SecondVarCaptureType ||
384 *PairCaptureType != *FirstVarCaptureType ||
385 *FirstVarCaptureType != *SecondVarCaptureType)
395 DiagAndFix(PairVar->getBeginLoc(),
396 CFRS ? PairVar->getSourceRange()
397 : SourceRange(PairVar->getBeginLoc(),
398 Lexer::getLocForEndOfToken(
399 PairVar->getLocation(), 0,
400 Result.Context->getSourceManager(),
401 Result.Context->getLangOpts())),
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
AST_POLYMORPHIC_MATCHER(isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc))
Matches AST nodes that were found within Abseil files.
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
AST_MATCHER(BinaryOperator, isRelationalOperator)
static bool matchNVarDeclStartingWith(Iterator Iter, const Iterator &EndIter, ArrayRef< ast_matchers::internal::Matcher< VarDecl > > InnerMatchers, ast_matchers::internal::ASTMatchFinder *Finder, ast_matchers::internal::BoundNodesTreeBuilder *Builder, bool Backwards=false)
Matches a sequence of VarDecls matching the inner matchers, starting from the Iter to EndIter and set...
static constexpr StringRef PairVarTypeName
static constexpr StringRef InitExprName
static constexpr StringRef StdTieAssignStmtName
static std::optional< TransferType > getTransferType(const ASTContext &Ctx, QualType ResultType, QualType OriginType)
static constexpr StringRef SecondTypeName
static constexpr StringRef BeginDeclStmtName
static constexpr StringRef ForRangeStmtName
static constexpr StringRef SecondVarDeclName
static constexpr StringRef StdTieExprName
static auto getVarInitWithMemberMatcher(StringRef PairName, StringRef MemberName, StringRef TypeName, StringRef BindingName, const ast_matchers::internal::Matcher< VarDecl > &ExtraMatcher)
static constexpr StringRef EndDeclStmtName
static constexpr StringRef FirstVarDeclName
static auto typeOrLValueReferenceTo(const ast_matchers::internal::Matcher< QualType > &TypeMatcher)
static constexpr StringRef PairDeclName
static constexpr StringRef ScopeBlockName
static constexpr StringRef FirstTypeName
SmallPtrSet< const DeclRefExpr *, 16 > allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context)
Returns set of all DeclRefExprs to VarDecl within Stmt.