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 const 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 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 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 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 const auto PairType =
208 qualType(unless(isVolatileQualified()),
209 hasUnqualifiedDesugaredType(
210 recordType(hasDeclaration(cxxRecordDecl(isPairType())))));
212 auto UnlessShouldBeIgnored =
213 unless(anyOf(hasAnySpecifiersShouldBeIgnored(), isInMacro()));
215 const auto VarInitWithFirstMember =
218 const auto VarInitWithSecondMember =
222 const auto RefToBindName = [&UnlessShouldBeIgnored](
const StringRef &Name) {
223 return declRefExpr(to(varDecl(UnlessShouldBeIgnored).bind(Name)));
226 const auto HasAnyLambdaCaptureThisVar =
227 [](
const ast_matchers::internal::Matcher<VarDecl> &VDMatcher) {
228 return compoundStmt(hasDescendant(
229 lambdaExpr(hasAnyCapture(capturesVar(varDecl(VDMatcher))))));
233 const auto UnlessFirstVarOrSecondVarIsCapturedByLambda =
234 getLangOpts().CPlusPlus20
236 : compoundStmt(unless(HasAnyLambdaCaptureThisVar(
246 has(cxxOperatorCallExpr(
247 hasOverloadedOperatorName(
"="),
248 hasLHS(ignoringImplicit(
249 callExpr(callee(functionDecl(isInStdNamespace(),
254 hasRHS(expr(hasType(PairType))))
257 SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
260 hasParent(compoundStmt(UnlessFirstVarOrSecondVarIsCapturedByLambda)
270 hasSingleDecl(varDecl(UnlessShouldBeIgnored,
271 unless(isDirectInitialization()),
274 hasInitializer(ignoringCopyCtorAndImplicitCast(
278 SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
279 VarInitWithFirstMember, VarInitWithSecondMember}),
280 hasParent(compoundStmt(UnlessFirstVarOrSecondVarIsCapturedByLambda)
294 hasInitializer(ignoringCopyCtorAndImplicitCast(
297 hasBody(compoundStmt(
299 SmallVector<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 const auto DiagAndFix = [&BeginDS, &EndDS, &FirstVar, &SecondVar, &CFRS,
338 this](SourceLocation DiagLoc,
339 SourceRange ReplaceRange,
340 TransferType TT = TT_ByVal) {
341 const auto Prefix = [&TT]() -> StringRef {
350 return "const auto&";
352 llvm_unreachable(
"Unhandled TransferType enum");
355 const std::string ReplacementText =
356 (Twine(Prefix) +
" [" + FirstVar->getNameAsString() +
", " +
357 SecondVar->getNameAsString() +
"]" + (CFRS ?
" :" :
""))
359 diag(DiagLoc,
"use a structured binding to decompose a pair")
360 << FixItHint::CreateReplacement(ReplaceRange, ReplacementText)
361 << FixItHint::CreateRemoval(
362 SourceRange{BeginDS->getBeginLoc(), EndDS->getEndLoc()});
365 if (
const auto *COCE =
367 DiagAndFix(COCE->getBeginLoc(),
374 const auto *PairVar = Result.Nodes.getNodeAs<VarDecl>(
PairDeclName);
376 const std::optional<TransferType> PairCaptureType =
379 const std::optional<TransferType> FirstVarCaptureType =
382 const std::optional<TransferType> SecondVarCaptureType =
385 if (!PairCaptureType || !FirstVarCaptureType || !SecondVarCaptureType ||
386 *PairCaptureType != *FirstVarCaptureType ||
387 *FirstVarCaptureType != *SecondVarCaptureType)
393 *PairVar, *ScopeBlock, *Result.Context);
397 DiagAndFix(PairVar->getBeginLoc(),
398 CFRS ? PairVar->getSourceRange()
399 : SourceRange(PairVar->getBeginLoc(),
400 Lexer::getLocForEndOfToken(
401 PairVar->getLocation(), 0,
402 Result.Context->getSourceManager(),
403 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 ValueDecl &ValueDecl, const Stmt &Stmt, ASTContext &Context)
Returns set of all DeclRefExprs to ValueDecl within Stmt.