[clang-tools-extra] [clang-tidy] Add new check `modernize-use-structured-binding` (PR #158462)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 23 06:23:33 PDT 2025
https://github.com/flovent updated https://github.com/llvm/llvm-project/pull/158462
>From a1941312179171a8752c27f886158ce4fd90db33 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 14:33:59 +0800
Subject: [PATCH 01/48] [clang-tidy] Add new check
`modernize-use-structured-binding`
---
.../clang-tidy/modernize/CMakeLists.txt | 1 +
.../modernize/ModernizeTidyModule.cpp | 3 +
.../modernize/UseStructuredBindingCheck.cpp | 419 ++++++++++++++++++
.../modernize/UseStructuredBindingCheck.h | 36 ++
clang-tools-extra/docs/ReleaseNotes.rst | 6 +
.../docs/clang-tidy/checks/list.rst | 1 +
.../modernize/use-structured-binding.rst | 58 +++
.../fake_std_pair_tuple.h | 23 +
.../use-structured-binding-custom.cpp | 32 ++
...d-binding-skip-lambda-capture-in-cxx17.cpp | 67 +++
.../modernize/use-structured-binding.cpp | 216 +++++++++
11 files changed, 862 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-structured-binding/fake_std_pair_tuple.h
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index 619a27b2f9bb6..094f0a72b1570 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -47,6 +47,7 @@ add_clang_library(clangTidyModernizeModule STATIC
UseStdFormatCheck.cpp
UseStdNumbersCheck.cpp
UseStdPrintCheck.cpp
+ UseStructuredBindingCheck.cpp
UseTrailingReturnTypeCheck.cpp
UseTransparentFunctorsCheck.cpp
UseUncaughtExceptionsCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index fdf38bc4b6308..a79908500e904 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -48,6 +48,7 @@
#include "UseStdFormatCheck.h"
#include "UseStdNumbersCheck.h"
#include "UseStdPrintCheck.h"
+#include "UseStructuredBindingCheck.h"
#include "UseTrailingReturnTypeCheck.h"
#include "UseTransparentFunctorsCheck.h"
#include "UseUncaughtExceptionsCheck.h"
@@ -121,6 +122,8 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<UseNoexceptCheck>("modernize-use-noexcept");
CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
+ CheckFactories.registerCheck<UseStructuredBindingCheck>(
+ "modernize-use-structured-binding");
CheckFactories.registerCheck<UseTrailingReturnTypeCheck>(
"modernize-use-trailing-return-type");
CheckFactories.registerCheck<UseTransparentFunctorsCheck>(
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
new file mode 100644
index 0000000000000..d6d6ae6cb83b3
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -0,0 +1,419 @@
+//===--- UseStructuredBindingCheck.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 "UseStructuredBindingCheck.h"
+#include "../utils/DeclRefExprUtils.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+namespace {
+constexpr const char *DefaultPairTypes = "std::pair";
+constexpr llvm::StringLiteral PairDeclName = "PairVarD";
+constexpr llvm::StringLiteral PairVarTypeName = "PairVarType";
+constexpr llvm::StringLiteral FirstVarDeclName = "FirstVarDecl";
+constexpr llvm::StringLiteral SecondVarDeclName = "SecondVarDecl";
+constexpr llvm::StringLiteral FirstDeclStmtName = "FirstDeclStmt";
+constexpr llvm::StringLiteral SecondDeclStmtName = "SecondDeclStmt";
+constexpr llvm::StringLiteral FirstTypeName = "FirstType";
+constexpr llvm::StringLiteral SecondTypeName = "SecondType";
+constexpr llvm::StringLiteral ScopeBlockName = "ScopeBlock";
+constexpr llvm::StringLiteral StdTieAssignStmtName = "StdTieAssign";
+constexpr llvm::StringLiteral StdTieExprName = "StdTieExpr";
+constexpr llvm::StringLiteral ForRangeStmtName = "ForRangeStmt";
+
+/// What qualifiers and specifiers are used to create structured binding
+/// declaration, it only supports the following four cases now.
+enum TransferType : uint8_t {
+ TT_ByVal,
+ TT_ByConstVal,
+ TT_ByRef,
+ TT_ByConstRef
+};
+
+/// Try to match exactly two VarDecl inside two DeclStmts, and set binding for
+/// the used DeclStmts.
+bool matchTwoVarDecl(const DeclStmt *DS1, const DeclStmt *DS2,
+ ast_matchers::internal::Matcher<VarDecl> InnerMatcher1,
+ ast_matchers::internal::Matcher<VarDecl> InnerMatcher2,
+ internal::ASTMatchFinder *Finder,
+ internal::BoundNodesTreeBuilder *Builder) {
+ SmallVector<std::pair<const VarDecl *, const DeclStmt *>, 2> Vars;
+ auto CollectVarsInDeclStmt = [&Vars](const DeclStmt *DS) -> bool {
+ if (!DS)
+ return true;
+
+ for (const auto *VD : DS->decls()) {
+ if (Vars.size() == 2)
+ return false;
+
+ if (const auto *Var = dyn_cast<VarDecl>(VD))
+ Vars.emplace_back(Var, DS);
+ else
+ return false;
+ }
+
+ return true;
+ };
+
+ if (!CollectVarsInDeclStmt(DS1) || !CollectVarsInDeclStmt(DS2))
+ return false;
+
+ if (Vars.size() != 2)
+ return false;
+
+ if (InnerMatcher1.matches(*Vars[0].first, Finder, Builder) &&
+ InnerMatcher2.matches(*Vars[1].first, Finder, Builder)) {
+ Builder->setBinding(FirstDeclStmtName,
+ clang::DynTypedNode::create(*Vars[0].second));
+ if (Vars[0].second != Vars[1].second)
+ Builder->setBinding(SecondDeclStmtName,
+ clang::DynTypedNode::create(*Vars[1].second));
+ return true;
+ }
+
+ return false;
+}
+
+/// Matches a Stmt whose parent is a CompoundStmt, and which is directly
+/// following two VarDecls matching the inner matcher, at the same time set
+/// binding for the CompoundStmt.
+AST_MATCHER_P2(Stmt, hasPreTwoVarDecl, ast_matchers::internal::Matcher<VarDecl>,
+ InnerMatcher1, ast_matchers::internal::Matcher<VarDecl>,
+ InnerMatcher2) {
+ DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
+ if (Parents.size() != 1)
+ return false;
+
+ auto *C = Parents[0].get<CompoundStmt>();
+ if (!C)
+ return false;
+
+ const auto I =
+ llvm::find(llvm::make_range(C->body_rbegin(), C->body_rend()), &Node);
+ assert(I != C->body_rend() && "C is parent of Node");
+ if ((I + 1) == C->body_rend())
+ return false;
+
+ const auto *DS2 = dyn_cast<DeclStmt>(*(I + 1));
+ if (!DS2)
+ return false;
+
+ const DeclStmt *DS1 = (!DS2->isSingleDecl() || ((I + 2) == C->body_rend())
+ ? nullptr
+ : dyn_cast<DeclStmt>(*(I + 2)));
+
+ if (matchTwoVarDecl(DS1, DS2, InnerMatcher1, InnerMatcher2, Finder,
+ Builder)) {
+ Builder->setBinding(ScopeBlockName, clang::DynTypedNode::create(*C));
+ return true;
+ }
+
+ return false;
+}
+
+/// Matches a Stmt whose parent is a CompoundStmt, and which is directly
+/// followed by two VarDecls matching the inner matcher, at the same time set
+/// binding for the CompoundStmt.
+AST_MATCHER_P2(Stmt, hasNextTwoVarDecl,
+ ast_matchers::internal::Matcher<VarDecl>, InnerMatcher1,
+ ast_matchers::internal::Matcher<VarDecl>, InnerMatcher2) {
+ DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
+ if (Parents.size() != 1)
+ return false;
+
+ auto *C = Parents[0].get<CompoundStmt>();
+ if (!C)
+ return false;
+
+ const auto *I = llvm::find(C->body(), &Node);
+ assert(I != C->body_end() && "C is parent of Node");
+ if ((I + 1) == C->body_end())
+ return false;
+
+ if (matchTwoVarDecl(
+ dyn_cast<DeclStmt>(*(I + 1)),
+ ((I + 2) == C->body_end() ? nullptr : dyn_cast<DeclStmt>(*(I + 2))),
+ InnerMatcher1, InnerMatcher2, Finder, Builder)) {
+ Builder->setBinding(ScopeBlockName, clang::DynTypedNode::create(*C));
+ return true;
+ }
+
+ return false;
+}
+
+/// Matches a Stmt whose parent is a CompoundStmt, and there a two VarDecls
+/// matching the inner matcher in the beginning of CompoundStmt.
+AST_MATCHER_P2(CompoundStmt, hasFirstTwoVarDecl,
+ ast_matchers::internal::Matcher<VarDecl>, InnerMatcher1,
+ ast_matchers::internal::Matcher<VarDecl>, InnerMatcher2) {
+ const auto *I = Node.body_begin();
+ if ((I) == Node.body_end())
+ return false;
+
+ return matchTwoVarDecl(
+ dyn_cast<DeclStmt>(*(I)),
+ ((I + 1) == Node.body_end() ? nullptr : dyn_cast<DeclStmt>(*(I + 1))),
+ InnerMatcher1, InnerMatcher2, Finder, Builder);
+}
+
+/// It's not very common to have specifiers for variables used to decompose
+/// a pair, so we ignore these cases.
+AST_MATCHER(VarDecl, hasAnySpecifiersShouldBeIgnored) {
+ return Node.isStaticLocal() || Node.isConstexpr() || Node.hasAttrs() ||
+ Node.isInlineSpecified();
+}
+
+// Ignore nodes inside macros.
+AST_POLYMORPHIC_MATCHER(isInMarco,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(Stmt, Decl)) {
+ return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID();
+}
+
+AST_MATCHER_P(Expr, ignoringCopyCtorAndImplicitCast,
+ ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
+ if (const auto *CtorE = dyn_cast<CXXConstructExpr>(&Node)) {
+ if (const auto *CtorD = CtorE->getConstructor();
+ CtorD->isCopyConstructor() && CtorE->getNumArgs() == 1) {
+ return InnerMatcher.matches(*CtorE->getArg(0)->IgnoreImpCasts(), Finder,
+ Builder);
+ }
+ }
+
+ return InnerMatcher.matches(*Node.IgnoreImpCasts(), Finder, Builder);
+}
+
+} // namespace
+
+UseStructuredBindingCheck::UseStructuredBindingCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ PairTypes(utils::options::parseStringList(
+ Options.get("PairTypes", DefaultPairTypes))) {
+ ;
+}
+
+static auto getVarInitWithMemberMatcher(StringRef PairName,
+ StringRef MemberName,
+ StringRef TypeName,
+ StringRef BindingName) {
+ return varDecl(
+ unless(hasAnySpecifiersShouldBeIgnored()), unless(isInMarco()),
+ hasInitializer(
+ ignoringImpCasts(ignoringCopyCtorAndImplicitCast(memberExpr(
+ hasObjectExpression(ignoringImpCasts(declRefExpr(
+ to(equalsBoundNode(std::string(PairName)))))),
+ member(fieldDecl(hasName(MemberName),
+ hasType(qualType().bind(TypeName)))))))))
+ .bind(BindingName);
+}
+
+void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
+ auto PairType =
+ qualType(unless(isVolatileQualified()),
+ hasUnqualifiedDesugaredType(recordType(
+ hasDeclaration(cxxRecordDecl(hasAnyName(PairTypes))))));
+
+ auto VarInitWithFirstMember = getVarInitWithMemberMatcher(
+ PairDeclName, "first", FirstTypeName, FirstVarDeclName);
+ auto VarInitWithSecondMember = getVarInitWithMemberMatcher(
+ PairDeclName, "second", SecondTypeName, SecondVarDeclName);
+
+ // X x;
+ // Y y;
+ // std::tie(x, y) = ...;
+ Finder->addMatcher(
+ exprWithCleanups(
+ unless(isInMarco()),
+ has(cxxOperatorCallExpr(
+ hasOverloadedOperatorName("="),
+ hasLHS(ignoringImplicit(
+ callExpr(
+ callee(
+ functionDecl(isInStdNamespace(), hasName("tie"))),
+ hasArgument(
+ 0,
+ declRefExpr(to(
+ varDecl(
+ unless(hasAnySpecifiersShouldBeIgnored()),
+ unless(isInMarco()))
+ .bind(FirstVarDeclName)))),
+ hasArgument(
+ 1,
+ declRefExpr(to(
+ varDecl(
+ unless(hasAnySpecifiersShouldBeIgnored()),
+ unless(isInMarco()))
+ .bind(SecondVarDeclName)))))
+ .bind(StdTieExprName))),
+ hasRHS(expr(hasType(PairType))))
+ .bind(StdTieAssignStmtName)),
+ hasPreTwoVarDecl(
+ varDecl(equalsBoundNode(std::string(FirstVarDeclName))),
+ varDecl(equalsBoundNode(std::string(SecondVarDeclName))))),
+ this);
+
+ // pair<X, Y> p = ...;
+ // X x = p.first;
+ // Y y = p.second;
+ Finder->addMatcher(
+ declStmt(
+ unless(isInMarco()),
+ hasSingleDecl(
+ varDecl(unless(hasAnySpecifiersShouldBeIgnored()),
+ hasType(qualType(anyOf(PairType, lValueReferenceType(
+ pointee(PairType))))
+ .bind(PairVarTypeName)),
+ hasInitializer(expr()))
+ .bind(PairDeclName)),
+ hasNextTwoVarDecl(VarInitWithFirstMember, VarInitWithSecondMember)),
+ this);
+
+ // for (pair<X, Y> p : map) {
+ // X x = p.first;
+ // Y y = p.second;
+ // }
+ Finder->addMatcher(
+ cxxForRangeStmt(
+ unless(isInMarco()),
+ hasLoopVariable(
+ varDecl(hasType(qualType(anyOf(PairType, lValueReferenceType(
+ pointee(PairType))))
+ .bind(PairVarTypeName)),
+ hasInitializer(expr()))
+ .bind(PairDeclName)),
+ hasBody(compoundStmt(hasFirstTwoVarDecl(VarInitWithFirstMember,
+ VarInitWithSecondMember))
+ .bind(ScopeBlockName)))
+ .bind(ForRangeStmtName),
+ this);
+}
+
+static std::optional<TransferType> getTransferType(const ASTContext &Ctx,
+ QualType ResultType,
+ QualType OriginType) {
+ ResultType = ResultType.getCanonicalType();
+ OriginType = OriginType.getCanonicalType();
+
+ if (ResultType == Ctx.getLValueReferenceType(OriginType.withConst()))
+ return TT_ByConstRef;
+
+ if (ResultType == Ctx.getLValueReferenceType(OriginType))
+ return TT_ByRef;
+
+ if (ResultType == OriginType.withConst())
+ return TT_ByConstVal;
+
+ if (ResultType == OriginType)
+ return TT_ByVal;
+
+ return std::nullopt;
+}
+
+void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *FirstVar = Result.Nodes.getNodeAs<VarDecl>(FirstVarDeclName);
+ const auto *SecondVar = Result.Nodes.getNodeAs<VarDecl>(SecondVarDeclName);
+
+ const auto *DS1 = Result.Nodes.getNodeAs<DeclStmt>(FirstDeclStmtName);
+ const auto *DS2 = Result.Nodes.getNodeAs<DeclStmt>(SecondDeclStmtName);
+ const auto *ScopeBlock = Result.Nodes.getNodeAs<CompoundStmt>(ScopeBlockName);
+
+ // Captured structured bindings are a C++20 extension
+ if (!Result.Context->getLangOpts().CPlusPlus20) {
+ if (auto Matchers = match(
+ compoundStmt(
+ hasDescendant(lambdaExpr(hasAnyCapture(capturesVar(varDecl(
+ anyOf(equalsNode(FirstVar), equalsNode(SecondVar)))))))),
+ *ScopeBlock, *Result.Context);
+ !Matchers.empty())
+ return;
+ }
+
+ const auto *CFRS = Result.Nodes.getNodeAs<CXXForRangeStmt>(ForRangeStmtName);
+ auto DiagAndFix = [&](SourceLocation DiagLoc, SourceRange ReplaceRange,
+ TransferType TT = TT_ByVal) {
+ StringRef Prefix;
+ switch (TT) {
+ case TT_ByVal:
+ Prefix = "auto";
+ break;
+ case TT_ByConstVal:
+ Prefix = "const auto";
+ break;
+ case TT_ByRef:
+ Prefix = "auto&";
+ break;
+ case TT_ByConstRef:
+ Prefix = "const auto&";
+ break;
+ }
+ std::vector<FixItHint> Hints;
+ if (DS1)
+ Hints.emplace_back(FixItHint::CreateRemoval(DS1->getSourceRange()));
+ if (DS2)
+ Hints.emplace_back(FixItHint::CreateRemoval(DS2->getSourceRange()));
+
+ std::string ReplacementText = Prefix.str() + " [" +
+ FirstVar->getNameAsString() + ", " +
+ SecondVar->getNameAsString() + "]";
+ if (CFRS)
+ ReplacementText += " :";
+ diag(DiagLoc, "Should use structured binding to decompose pair")
+ << FixItHint::CreateReplacement(ReplaceRange, ReplacementText) << Hints;
+ };
+
+ if (const auto *COCE =
+ Result.Nodes.getNodeAs<CXXOperatorCallExpr>(StdTieAssignStmtName)) {
+ DiagAndFix(COCE->getBeginLoc(),
+ Result.Nodes.getNodeAs<Expr>(StdTieExprName)->getSourceRange());
+ return;
+ }
+
+ // Check whether PairVar, FirstVar and SecondVar have the same transfer type,
+ // so they can be combined to structured binding.
+ const auto *PairVar = Result.Nodes.getNodeAs<VarDecl>(PairDeclName);
+ const Expr *InitE = PairVar->getInit();
+ if (auto Res =
+ match(expr(ignoringCopyCtorAndImplicitCast(expr().bind("init_expr"))),
+ *InitE, *Result.Context);
+ !Res.empty())
+ InitE = Res[0].getNodeAs<Expr>("init_expr");
+
+ std::optional<TransferType> PairCaptureType =
+ getTransferType(*Result.Context, PairVar->getType(), InitE->getType());
+ std::optional<TransferType> FirstVarCaptureType =
+ getTransferType(*Result.Context, FirstVar->getType(),
+ *Result.Nodes.getNodeAs<QualType>(FirstTypeName));
+ std::optional<TransferType> SecondVarCaptureType =
+ getTransferType(*Result.Context, SecondVar->getType(),
+ *Result.Nodes.getNodeAs<QualType>(SecondTypeName));
+ if (!PairCaptureType || !FirstVarCaptureType || !SecondVarCaptureType ||
+ *PairCaptureType != *FirstVarCaptureType ||
+ *FirstVarCaptureType != *SecondVarCaptureType)
+ return;
+
+ // Check PairVar is not used except for assignment members to firstVar and
+ // SecondVar.
+ if (auto AllRef = utils::decl_ref_expr::allDeclRefExprs(*PairVar, *ScopeBlock,
+ *Result.Context);
+ AllRef.size() != 2)
+ return;
+
+ DiagAndFix(PairVar->getBeginLoc(),
+ CFRS ? PairVar->getSourceRange()
+ : SourceRange(PairVar->getBeginLoc(),
+ Lexer::getLocForEndOfToken(
+ PairVar->getLocation(), 0,
+ Result.Context->getSourceManager(),
+ Result.Context->getLangOpts())),
+ *PairCaptureType);
+}
+
+} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
new file mode 100644
index 0000000000000..63bc0a8c3da45
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
@@ -0,0 +1,36 @@
+//===--- UseStructuredBindingCheck.h - clang-tidy ---------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTRUCTUREDBINDINGCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTRUCTUREDBINDINGCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::modernize {
+
+/// Finds places where structured bindings could be used to decompose pairs and
+/// suggests replacing them.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-structured-binding.html
+class UseStructuredBindingCheck : public ClangTidyCheck {
+public:
+ UseStructuredBindingCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus17;
+ }
+
+private:
+ const std::vector<StringRef> PairTypes;
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTRUCTUREDBINDINGCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 34091906cbff2..8302cbf64f095 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -179,6 +179,12 @@ New checks
Finds virtual function overrides with different visibility than the function
in the base class.
+- New :doc:`modernize-use-structured-binding
+ <clang-tidy/checks/modernize/use-structured-binding>` check.
+
+ Finds places where structured bindings could be used to decompose pairs and
+ suggests replacing them.
+
New check aliases
^^^^^^^^^^^^^^^^^
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index c490d2ece2e0a..843a031dd2dd1 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -324,6 +324,7 @@ Clang-Tidy Checks
:doc:`modernize-use-std-format <modernize/use-std-format>`, "Yes"
:doc:`modernize-use-std-numbers <modernize/use-std-numbers>`, "Yes"
:doc:`modernize-use-std-print <modernize/use-std-print>`, "Yes"
+ :doc:`modernize-use-structured-binding <modernize/use-structured-binding>`, "Yes"
:doc:`modernize-use-trailing-return-type <modernize/use-trailing-return-type>`, "Yes"
:doc:`modernize-use-transparent-functors <modernize/use-transparent-functors>`, "Yes"
:doc:`modernize-use-uncaught-exceptions <modernize/use-uncaught-exceptions>`, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
new file mode 100644
index 0000000000000..66af859d4428f
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -0,0 +1,58 @@
+.. title:: clang-tidy - modernize-use-structured-binding
+
+modernize-use-structured-binding
+================================
+
+Suggests using C++17 structured bindings to decompose pairs.
+
+This check finds three code patterns and recommends using structured bindings for clearer, more idiomatic C++17 code.
+
+1. Decompose a pair variable by assigning its members to separate variables right after its definition:
+
+.. code-block:: c++
+
+ auto p = getPair<int, int>();
+ int x = p.first;
+ int y = p.second;
+
+ into:
+
+ auto [x, y] = getPair<int, int>();
+
+2. Use `std::tie` to decompose a pair into two predefined variables:
+
+.. code-block:: c++
+
+ int a;
+ int b;
+ std::tie(a, b) = getPair<int, int>();
+
+ into:
+
+ auto [a, b] = getPair<int, int>();
+
+3. Manually decompose a pair by assigning to its members to local variables in a range-based for loop:
+
+.. code-block:: c++
+
+ for (autop : vecOfPairs) {
+ int x = p.first;
+ int y = p.second;
+ // ...
+ }
+
+ into:
+
+ for (auto [x, y] : vecOfPairs) {
+ // use x and y
+ }
+
+The check also supports custom pair-like types via the `PairTypes` option.
+
+Options
+-------
+
+.. option:: PairTypes
+
+ A Semicolon-separated list of type names to be treated as pair-like for structured binding suggestions.
+ Example: `PairTypes=MyPairType; OtherPairType`. Default is `std::pair`.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-structured-binding/fake_std_pair_tuple.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-structured-binding/fake_std_pair_tuple.h
new file mode 100644
index 0000000000000..b77341c852edb
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-structured-binding/fake_std_pair_tuple.h
@@ -0,0 +1,23 @@
+namespace std {
+ template<typename T1, typename T2>
+ struct pair {
+ T1 first;
+ T2 second;
+ };
+
+ template<typename... Args>
+ struct tuple {
+ tuple(Args&...) {}
+
+ template<typename T1, typename T2>
+ tuple<T1, T2> operator=(const std::pair<T1, T2>&);
+ };
+
+ template<typename... Args>
+ tuple<Args...> tie(Args&... args) {
+ return tuple<Args...>(args...);
+ }
+}
+
+template<typename T1, typename T2>
+std::pair<T1, T2> getPair();
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
new file mode 100644
index 0000000000000..d6d73430d6a3c
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
@@ -0,0 +1,32 @@
+// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-structured-binding %t \
+// RUN: -config="{CheckOptions: {modernize-use-structured-binding.PairTypes: 'custom::pair; otherPair'}}"
+
+namespace custom {
+ struct pair {
+ int first;
+ int second;
+ };
+}
+
+struct otherPair {
+ int first;
+ int second;
+};
+
+void OptionTest() {
+ {
+ auto P = custom::pair();
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} auto [x, y] = custom::pair();
+ int x = P.first;
+ int y = P.second;
+ }
+
+ {
+ auto P = otherPair();
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} auto [x, y] = otherPair();
+ int x = P.first;
+ int y = P.second;
+ }
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp
new file mode 100644
index 0000000000000..57f9f3488fa21
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp
@@ -0,0 +1,67 @@
+// RUN: %check_clang_tidy -std=c++17 %s modernize-use-structured-binding %t -- -- -I %S/Inputs/use-structured-binding/
+
+#include "fake_std_pair_tuple.h"
+
+void captureByVal() {
+ auto P = getPair<int, int>();
+ int x = P.first;
+ int y = P.second;
+
+ auto lambda = [x]() {
+ int y = x;
+ };
+}
+
+void captureByRef() {
+ auto P = getPair<int, int>();
+ int x = P.first;
+ int y = P.second;
+
+ auto lambda = [&x]() {
+ x = 1;
+ };
+}
+
+void captureByAllRef() {
+ auto P = getPair<int, int>();
+ int x = P.first;
+ int y = P.second;
+
+ auto lambda = [&]() {
+ x = 1;
+ };
+}
+
+void deepLambda() {
+ auto P = getPair<int, int>();
+ int x = P.first;
+ int y = P.second;
+
+ {
+ auto lambda = [x]() {
+ int y = x;
+ };
+ }
+}
+
+void forRangeNotWarn() {
+ std::pair<int, int> Pairs[10];
+ for (auto P : Pairs) {
+ int x = P.first;
+ int y = P.second;
+
+ auto lambda = [&]() {
+ x = 1;
+ };
+ }
+}
+
+void stdTieNotWarn() {
+ int x = 0;
+ int y = 0;
+ std::tie(x, y) = getPair<int, int>();
+
+ auto lambda = [&x]() {
+ x = 1;
+ };
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
new file mode 100644
index 0000000000000..97da76fea1d5f
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -0,0 +1,216 @@
+// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-structured-binding %t -- -- -I %S/Inputs/use-structured-binding/
+
+#include "fake_std_pair_tuple.h"
+
+template<typename T>
+void MarkUsed(T x);
+
+struct TestClass {
+ int a;
+ int b;
+ TestClass() : a(0), b(0) {}
+ TestClass(int x, int y) : a(x), b(y) {}
+};
+
+void DecomposeByAssignWarnCases() {
+ {
+ auto P = getPair<int, int>();
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} auto [x, y] = getPair<int, int>();
+ int x = P.first;
+ int y = P.second;
+ }
+
+ {
+ auto P = getPair<int, int>();
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} auto [x, y] = getPair<int, int>();
+ int x = P.first;
+ auto y = P.second;
+ }
+
+ {
+ const auto P = getPair<int, int>();
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} const auto [x, y] = getPair<int, int>();
+ const int x = P.first;
+ const auto y = P.second;
+ }
+
+ {
+ std::pair<int, int> otherP;
+ auto& P = otherP;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} auto& [x, y] = otherP;
+ int& x = P.first;
+ auto& y = P.second;
+ }
+
+ {
+ std::pair<int, int> otherP;
+ const auto& P = otherP;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} const auto& [x, y] = otherP;
+ const int& x = P.first;
+ const auto& y = P.second;
+ }
+}
+
+void forRangeWarnCases() {
+ std::pair<int, int> Pairs[10];
+ for (auto P : Pairs) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} for (auto [x, y] : Pairs) {
+ int x = P.first;
+ int y = P.second;
+ }
+
+ for (const auto P : Pairs) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} for (const auto [x, y] : Pairs) {
+ const int x = P.first;
+ const int y = P.second;
+ }
+
+ for (auto& P : Pairs) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} for (auto& [x, y] : Pairs) {
+ int& x = P.first;
+ int& y = P.second;
+ }
+
+ for (const auto& P : Pairs) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} for (const auto& [x, y] : Pairs) {
+ const int& x = P.first;
+ const int& y = P.second;
+ }
+
+ std::pair<TestClass, TestClass> ClassPairs[10];
+ for (auto P : ClassPairs) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} for (auto [c1, c2] : ClassPairs) {
+ TestClass c1 = P.first;
+ TestClass c2 = P.second;
+ }
+
+ for (const auto P : ClassPairs) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} for (const auto [c1, c2] : ClassPairs) {
+ const TestClass c1 = P.first;
+ const TestClass c2 = P.second;
+ }
+}
+
+void stdTieWarnCases() {
+ int a = 0;
+ int b = 0;
+ std::tie(a, b) = getPair<int, int>();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} auto [a, b] = getPair<int, int>();
+
+ int* pa = nullptr;
+ int* pb = nullptr;
+ std::tie(pa, pb) = getPair<int*, int*>();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} auto [pa, pb] = getPair<int*, int*>();
+
+ TestClass c1 (1, 2);
+ TestClass c2 = TestClass {3, 4};
+ std::tie(c1, c2) = getPair<TestClass, TestClass>();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-FIXES: {{^}} auto [c1, c2] = getPair<TestClass, TestClass>();
+}
+
+void stdTieNotWarnCases() {
+ int a = 0;
+ int b = 0;
+ a = 4;
+ std::tie(a, b) = getPair<int, int>(); // no warning
+
+ int* pa = nullptr;
+ int* pb = nullptr;
+ MarkUsed(pa);
+ std::tie(pa, pb) = getPair<int*, int*>(); // no warning
+
+ TestClass c1 (1, 2);
+ TestClass c2 = TestClass {3, 4};
+ MarkUsed(c2);
+ std::tie(c1, c2) = getPair<TestClass, TestClass>();
+}
+
+void NotWarnForVarHasSpecifiers() {
+ {
+ auto P = getPair<int, int>();
+ const int x = P.first;
+ int y = P.second;
+ }
+
+ {
+ auto P = getPair<int, int>();
+ volatile int x = P.first;
+ int y = P.second;
+ }
+
+ {
+ auto P = getPair<int, int>();
+ int x = P.first;
+ [[maybe_unused]] int y = P.second;
+ }
+
+ {
+ static auto P = getPair<int, int>();
+ int x = P.first;
+ int y = P.second;
+ }
+}
+
+void NotWarnForMultiUsedPairVar() {
+ {
+ auto P = getPair<int, int>();
+ int x = P.first;
+ int y = P.second;
+ MarkUsed(P);
+ }
+
+ {
+ auto P = getPair<int, int>();
+ int x = P.first;
+ MarkUsed(P);
+ int y = P.second;
+ }
+
+ {
+ auto P = getPair<int, int>();
+ MarkUsed(P);
+ int x = P.first;
+ int y = P.second;
+ }
+
+ {
+ std::pair<int, int> Pairs[10];
+ for (auto P : Pairs) {
+ int x = P.first;
+ int y = P.second;
+
+ MarkUsed(P);
+ }
+ }
+}
+
+#define DECOMPOSE(P) \
+ int x = P.first; \
+ int y = P.second; \
+
+void NotWarnForMacro1() {
+ auto P = getPair<int, int>();
+ DECOMPOSE(P);
+}
+
+#define GETPAIR auto P = getPair<int, int>()
+
+void NotWarnForMacro2() {
+ GETPAIR;
+ int x = P.first;
+ int y = P.second;
+}
>From dd9bb2a4c980877f384d8159233c1fb75d3217fc Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 16:40:16 +0800
Subject: [PATCH 02/48] Update
clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
Co-authored-by: Baranov Victor <bar.victor.2002 at gmail.com>
---
.../docs/clang-tidy/checks/modernize/use-structured-binding.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 66af859d4428f..e25dd8e2130f3 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -55,4 +55,4 @@ Options
.. option:: PairTypes
A Semicolon-separated list of type names to be treated as pair-like for structured binding suggestions.
- Example: `PairTypes=MyPairType; OtherPairType`. Default is `std::pair`.
+ Example: `MyPairType;OtherPairType`. Default is `std::pair`.
>From e37026ab52eb32b9b49aa50e1def060a0be0e51c Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 16:40:26 +0800
Subject: [PATCH 03/48] Update
clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
Co-authored-by: Baranov Victor <bar.victor.2002 at gmail.com>
---
.../clang-tidy/checks/modernize/use-structured-binding.rst | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index e25dd8e2130f3..377f48b822ded 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -3,7 +3,8 @@
modernize-use-structured-binding
================================
-Suggests using C++17 structured bindings to decompose pairs.
+Finds places where structured bindings could be used to decompose pairs and
+suggests replacing them.
This check finds three code patterns and recommends using structured bindings for clearer, more idiomatic C++17 code.
>From 1d01ffd04ccb87591d88e7f56391f81982cb8c8c Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 16:40:48 +0800
Subject: [PATCH 04/48] Update
clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
Co-authored-by: Baranov Victor <bar.victor.2002 at gmail.com>
---
.../docs/clang-tidy/checks/modernize/use-structured-binding.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 377f48b822ded..38f4daaf97ec6 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -20,7 +20,7 @@ This check finds three code patterns and recommends using structured bindings fo
auto [x, y] = getPair<int, int>();
-2. Use `std::tie` to decompose a pair into two predefined variables:
+2. Use ``std::tie`` to decompose a pair into two predefined variables:
.. code-block:: c++
>From 4127b7b7c73720c0a170cf4b334d7de687bdccf7 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 16:41:17 +0800
Subject: [PATCH 05/48] Update
clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
Co-authored-by: Baranov Victor <bar.victor.2002 at gmail.com>
---
.../docs/clang-tidy/checks/modernize/use-structured-binding.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 38f4daaf97ec6..0d227b75968a7 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -48,7 +48,7 @@ This check finds three code patterns and recommends using structured bindings fo
// use x and y
}
-The check also supports custom pair-like types via the `PairTypes` option.
+The check also supports custom pair-like types via the :option:`PairTypes` option.
Options
-------
>From f6c129b090262365fe0db9073a4a5a2ef9a73da0 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 16:44:32 +0800
Subject: [PATCH 06/48] [NFC] Change warning message to 'use structured binding
to decompose a pair'
---
.../modernize/UseStructuredBindingCheck.cpp | 2 +-
.../use-structured-binding-custom.cpp | 4 +--
.../modernize/use-structured-binding.cpp | 28 +++++++++----------
3 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index d6d6ae6cb83b3..fab4e8dc16cf1 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -365,7 +365,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
SecondVar->getNameAsString() + "]";
if (CFRS)
ReplacementText += " :";
- diag(DiagLoc, "Should use structured binding to decompose pair")
+ diag(DiagLoc, "use structured binding to decompose a pair")
<< FixItHint::CreateReplacement(ReplaceRange, ReplacementText) << Hints;
};
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
index d6d73430d6a3c..a007447d172b7 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
@@ -16,7 +16,7 @@ struct otherPair {
void OptionTest() {
{
auto P = custom::pair();
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} auto [x, y] = custom::pair();
int x = P.first;
int y = P.second;
@@ -24,7 +24,7 @@ void OptionTest() {
{
auto P = otherPair();
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} auto [x, y] = otherPair();
int x = P.first;
int y = P.second;
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 97da76fea1d5f..a4016397f52ef 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -15,7 +15,7 @@ struct TestClass {
void DecomposeByAssignWarnCases() {
{
auto P = getPair<int, int>();
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} auto [x, y] = getPair<int, int>();
int x = P.first;
int y = P.second;
@@ -23,7 +23,7 @@ void DecomposeByAssignWarnCases() {
{
auto P = getPair<int, int>();
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} auto [x, y] = getPair<int, int>();
int x = P.first;
auto y = P.second;
@@ -31,7 +31,7 @@ void DecomposeByAssignWarnCases() {
{
const auto P = getPair<int, int>();
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} const auto [x, y] = getPair<int, int>();
const int x = P.first;
const auto y = P.second;
@@ -40,7 +40,7 @@ void DecomposeByAssignWarnCases() {
{
std::pair<int, int> otherP;
auto& P = otherP;
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} auto& [x, y] = otherP;
int& x = P.first;
auto& y = P.second;
@@ -49,7 +49,7 @@ void DecomposeByAssignWarnCases() {
{
std::pair<int, int> otherP;
const auto& P = otherP;
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} const auto& [x, y] = otherP;
const int& x = P.first;
const auto& y = P.second;
@@ -59,28 +59,28 @@ void DecomposeByAssignWarnCases() {
void forRangeWarnCases() {
std::pair<int, int> Pairs[10];
for (auto P : Pairs) {
- // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} for (auto [x, y] : Pairs) {
int x = P.first;
int y = P.second;
}
for (const auto P : Pairs) {
- // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} for (const auto [x, y] : Pairs) {
const int x = P.first;
const int y = P.second;
}
for (auto& P : Pairs) {
- // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} for (auto& [x, y] : Pairs) {
int& x = P.first;
int& y = P.second;
}
for (const auto& P : Pairs) {
- // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} for (const auto& [x, y] : Pairs) {
const int& x = P.first;
const int& y = P.second;
@@ -88,14 +88,14 @@ void forRangeWarnCases() {
std::pair<TestClass, TestClass> ClassPairs[10];
for (auto P : ClassPairs) {
- // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} for (auto [c1, c2] : ClassPairs) {
TestClass c1 = P.first;
TestClass c2 = P.second;
}
for (const auto P : ClassPairs) {
- // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} for (const auto [c1, c2] : ClassPairs) {
const TestClass c1 = P.first;
const TestClass c2 = P.second;
@@ -106,19 +106,19 @@ void stdTieWarnCases() {
int a = 0;
int b = 0;
std::tie(a, b) = getPair<int, int>();
- // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} auto [a, b] = getPair<int, int>();
int* pa = nullptr;
int* pb = nullptr;
std::tie(pa, pb) = getPair<int*, int*>();
- // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} auto [pa, pb] = getPair<int*, int*>();
TestClass c1 (1, 2);
TestClass c2 = TestClass {3, 4};
std::tie(c1, c2) = getPair<TestClass, TestClass>();
- // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: {{^}} auto [c1, c2] = getPair<TestClass, TestClass>();
}
>From 24e9d4ea3ebfa20d804aafb420bf5a34cc0e86aa Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 16:53:53 +0800
Subject: [PATCH 07/48] [NFC] Add `forRangeNotWarnCases` in test file
---
.../modernize/use-structured-binding.cpp | 29 +++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index a4016397f52ef..033b380585258 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -102,6 +102,35 @@ void forRangeWarnCases() {
}
}
+void forRangeNotWarnCases() {
+ std::pair<int, int> Pairs[10];
+ for (auto P : Pairs) {
+ int x = P.first;
+ MarkUsed(x);
+ int y = P.second;
+ }
+
+ for (auto P : Pairs) {
+ MarkUsed(P);
+ int x = P.first;
+ int y = P.second;
+ }
+
+ for (auto P : Pairs) {
+ int x = P.first;
+ int y = P.second;
+ MarkUsed(P);
+ }
+
+ std::pair<TestClass, TestClass> ClassPairs[10];
+ int c;
+ for (auto P : ClassPairs) {
+ TestClass c1 = P.first;
+ c ++ ;
+ TestClass c2 = P.second;
+ }
+}
+
void stdTieWarnCases() {
int a = 0;
int b = 0;
>From 08fd8db44d728ed0cdafcd5f88ac4bc8a0819e24 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 16:56:27 +0800
Subject: [PATCH 08/48] [NFC] Remove {{^}}
---
.../use-structured-binding-custom.cpp | 4 +--
.../modernize/use-structured-binding.cpp | 28 +++++++++----------
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
index a007447d172b7..bd335e1edaf6e 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
@@ -17,7 +17,7 @@ void OptionTest() {
{
auto P = custom::pair();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} auto [x, y] = custom::pair();
+ // CHECK-FIXES: auto [x, y] = custom::pair();
int x = P.first;
int y = P.second;
}
@@ -25,7 +25,7 @@ void OptionTest() {
{
auto P = otherPair();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} auto [x, y] = otherPair();
+ // CHECK-FIXES: auto [x, y] = otherPair();
int x = P.first;
int y = P.second;
}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 033b380585258..b2246e68a4d73 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -16,7 +16,7 @@ void DecomposeByAssignWarnCases() {
{
auto P = getPair<int, int>();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} auto [x, y] = getPair<int, int>();
+ // CHECK-FIXES: auto [x, y] = getPair<int, int>();
int x = P.first;
int y = P.second;
}
@@ -24,7 +24,7 @@ void DecomposeByAssignWarnCases() {
{
auto P = getPair<int, int>();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} auto [x, y] = getPair<int, int>();
+ // CHECK-FIXES: auto [x, y] = getPair<int, int>();
int x = P.first;
auto y = P.second;
}
@@ -32,7 +32,7 @@ void DecomposeByAssignWarnCases() {
{
const auto P = getPair<int, int>();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} const auto [x, y] = getPair<int, int>();
+ // CHECK-FIXES: const auto [x, y] = getPair<int, int>();
const int x = P.first;
const auto y = P.second;
}
@@ -41,7 +41,7 @@ void DecomposeByAssignWarnCases() {
std::pair<int, int> otherP;
auto& P = otherP;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} auto& [x, y] = otherP;
+ // CHECK-FIXES: auto& [x, y] = otherP;
int& x = P.first;
auto& y = P.second;
}
@@ -50,7 +50,7 @@ void DecomposeByAssignWarnCases() {
std::pair<int, int> otherP;
const auto& P = otherP;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} const auto& [x, y] = otherP;
+ // CHECK-FIXES: const auto& [x, y] = otherP;
const int& x = P.first;
const auto& y = P.second;
}
@@ -60,28 +60,28 @@ void forRangeWarnCases() {
std::pair<int, int> Pairs[10];
for (auto P : Pairs) {
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} for (auto [x, y] : Pairs) {
+ // CHECK-FIXES: for (auto [x, y] : Pairs) {
int x = P.first;
int y = P.second;
}
for (const auto P : Pairs) {
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} for (const auto [x, y] : Pairs) {
+ // CHECK-FIXES: for (const auto [x, y] : Pairs) {
const int x = P.first;
const int y = P.second;
}
for (auto& P : Pairs) {
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} for (auto& [x, y] : Pairs) {
+ // CHECK-FIXES: for (auto& [x, y] : Pairs) {
int& x = P.first;
int& y = P.second;
}
for (const auto& P : Pairs) {
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} for (const auto& [x, y] : Pairs) {
+ // CHECK-FIXES: for (const auto& [x, y] : Pairs) {
const int& x = P.first;
const int& y = P.second;
}
@@ -89,14 +89,14 @@ void forRangeWarnCases() {
std::pair<TestClass, TestClass> ClassPairs[10];
for (auto P : ClassPairs) {
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} for (auto [c1, c2] : ClassPairs) {
+ // CHECK-FIXES: for (auto [c1, c2] : ClassPairs) {
TestClass c1 = P.first;
TestClass c2 = P.second;
}
for (const auto P : ClassPairs) {
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} for (const auto [c1, c2] : ClassPairs) {
+ // CHECK-FIXES: for (const auto [c1, c2] : ClassPairs) {
const TestClass c1 = P.first;
const TestClass c2 = P.second;
}
@@ -136,19 +136,19 @@ void stdTieWarnCases() {
int b = 0;
std::tie(a, b) = getPair<int, int>();
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} auto [a, b] = getPair<int, int>();
+ // CHECK-FIXES: auto [a, b] = getPair<int, int>();
int* pa = nullptr;
int* pb = nullptr;
std::tie(pa, pb) = getPair<int*, int*>();
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} auto [pa, pb] = getPair<int*, int*>();
+ // CHECK-FIXES: auto [pa, pb] = getPair<int*, int*>();
TestClass c1 (1, 2);
TestClass c2 = TestClass {3, 4};
std::tie(c1, c2) = getPair<TestClass, TestClass>();
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: {{^}} auto [c1, c2] = getPair<TestClass, TestClass>();
+ // CHECK-FIXES: auto [c1, c2] = getPair<TestClass, TestClass>();
}
void stdTieNotWarnCases() {
>From 49bfcae8c804af3eed8369dd736f2283644fc7ce Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 17:04:53 +0800
Subject: [PATCH 09/48] [NFC] Add more testcase
---
.../checkers/modernize/use-structured-binding.cpp | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index b2246e68a4d73..1037944de26dd 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -9,6 +9,7 @@ struct TestClass {
int a;
int b;
TestClass() : a(0), b(0) {}
+ TestClass& operator++();
TestClass(int x, int y) : a(x), b(y) {}
};
@@ -123,6 +124,12 @@ void forRangeNotWarnCases() {
}
std::pair<TestClass, TestClass> ClassPairs[10];
+ for (auto P : ClassPairs) {
+ TestClass c1 = P.first;
+ ++ c1 ;
+ TestClass c2 = P.second;
+ }
+
int c;
for (auto P : ClassPairs) {
TestClass c1 = P.first;
>From aa3bf50840d07f276cd4a032d5eaf12c11cfa51b Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 17:53:53 +0800
Subject: [PATCH 10/48] [NFC] Merge lambda testcase to main test file for also
test c++20-or-later
---
...d-binding-skip-lambda-capture-in-cxx17.cpp | 67 ---------
.../modernize/use-structured-binding.cpp | 136 ++++++++++++++----
2 files changed, 106 insertions(+), 97 deletions(-)
delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp
deleted file mode 100644
index 57f9f3488fa21..0000000000000
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-// RUN: %check_clang_tidy -std=c++17 %s modernize-use-structured-binding %t -- -- -I %S/Inputs/use-structured-binding/
-
-#include "fake_std_pair_tuple.h"
-
-void captureByVal() {
- auto P = getPair<int, int>();
- int x = P.first;
- int y = P.second;
-
- auto lambda = [x]() {
- int y = x;
- };
-}
-
-void captureByRef() {
- auto P = getPair<int, int>();
- int x = P.first;
- int y = P.second;
-
- auto lambda = [&x]() {
- x = 1;
- };
-}
-
-void captureByAllRef() {
- auto P = getPair<int, int>();
- int x = P.first;
- int y = P.second;
-
- auto lambda = [&]() {
- x = 1;
- };
-}
-
-void deepLambda() {
- auto P = getPair<int, int>();
- int x = P.first;
- int y = P.second;
-
- {
- auto lambda = [x]() {
- int y = x;
- };
- }
-}
-
-void forRangeNotWarn() {
- std::pair<int, int> Pairs[10];
- for (auto P : Pairs) {
- int x = P.first;
- int y = P.second;
-
- auto lambda = [&]() {
- x = 1;
- };
- }
-}
-
-void stdTieNotWarn() {
- int x = 0;
- int y = 0;
- std::tie(x, y) = getPair<int, int>();
-
- auto lambda = [&x]() {
- x = 1;
- };
-}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 1037944de26dd..05b842e4d6614 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -1,5 +1,5 @@
-// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-structured-binding %t -- -- -I %S/Inputs/use-structured-binding/
-
+// RUN: %check_clang_tidy -check-suffix=ALL,CPP20ORLATER -std=c++20-or-later %s modernize-use-structured-binding %t -- -- -I %S/Inputs/use-structured-binding/
+// RUN: %check_clang_tidy -check-suffix=ALL -std=c++17 %s modernize-use-structured-binding %t -- -- -I %S/Inputs/use-structured-binding/
#include "fake_std_pair_tuple.h"
template<typename T>
@@ -16,24 +16,24 @@ struct TestClass {
void DecomposeByAssignWarnCases() {
{
auto P = getPair<int, int>();
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: auto [x, y] = getPair<int, int>();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
int x = P.first;
int y = P.second;
}
{
auto P = getPair<int, int>();
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: auto [x, y] = getPair<int, int>();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
int x = P.first;
auto y = P.second;
}
{
const auto P = getPair<int, int>();
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: const auto [x, y] = getPair<int, int>();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: const auto [x, y] = getPair<int, int>();
const int x = P.first;
const auto y = P.second;
}
@@ -41,8 +41,8 @@ void DecomposeByAssignWarnCases() {
{
std::pair<int, int> otherP;
auto& P = otherP;
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: auto& [x, y] = otherP;
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto& [x, y] = otherP;
int& x = P.first;
auto& y = P.second;
}
@@ -50,8 +50,8 @@ void DecomposeByAssignWarnCases() {
{
std::pair<int, int> otherP;
const auto& P = otherP;
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: const auto& [x, y] = otherP;
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: const auto& [x, y] = otherP;
const int& x = P.first;
const auto& y = P.second;
}
@@ -60,44 +60,44 @@ void DecomposeByAssignWarnCases() {
void forRangeWarnCases() {
std::pair<int, int> Pairs[10];
for (auto P : Pairs) {
- // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: for (auto [x, y] : Pairs) {
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
int x = P.first;
int y = P.second;
}
for (const auto P : Pairs) {
- // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: for (const auto [x, y] : Pairs) {
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: for (const auto [x, y] : Pairs) {
const int x = P.first;
const int y = P.second;
}
for (auto& P : Pairs) {
- // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: for (auto& [x, y] : Pairs) {
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: for (auto& [x, y] : Pairs) {
int& x = P.first;
int& y = P.second;
}
for (const auto& P : Pairs) {
- // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: for (const auto& [x, y] : Pairs) {
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: for (const auto& [x, y] : Pairs) {
const int& x = P.first;
const int& y = P.second;
}
std::pair<TestClass, TestClass> ClassPairs[10];
for (auto P : ClassPairs) {
- // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: for (auto [c1, c2] : ClassPairs) {
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: for (auto [c1, c2] : ClassPairs) {
TestClass c1 = P.first;
TestClass c2 = P.second;
}
for (const auto P : ClassPairs) {
- // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: for (const auto [c1, c2] : ClassPairs) {
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: for (const auto [c1, c2] : ClassPairs) {
const TestClass c1 = P.first;
const TestClass c2 = P.second;
}
@@ -142,20 +142,20 @@ void stdTieWarnCases() {
int a = 0;
int b = 0;
std::tie(a, b) = getPair<int, int>();
- // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: auto [a, b] = getPair<int, int>();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto [a, b] = getPair<int, int>();
int* pa = nullptr;
int* pb = nullptr;
std::tie(pa, pb) = getPair<int*, int*>();
- // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: auto [pa, pb] = getPair<int*, int*>();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto [pa, pb] = getPair<int*, int*>();
TestClass c1 (1, 2);
TestClass c2 = TestClass {3, 4};
std::tie(c1, c2) = getPair<TestClass, TestClass>();
- // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: auto [c1, c2] = getPair<TestClass, TestClass>();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto [c1, c2] = getPair<TestClass, TestClass>();
}
void stdTieNotWarnCases() {
@@ -250,3 +250,79 @@ void NotWarnForMacro2() {
int x = P.first;
int y = P.second;
}
+
+void captureByVal() {
+ auto P = getPair<int, int>();
+ // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
+ int x = P.first;
+ int y = P.second;
+
+ auto lambda = [x]() {
+ int y = x;
+ };
+}
+
+void captureByRef() {
+ auto P = getPair<int, int>();
+ // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
+ int x = P.first;
+ int y = P.second;
+
+ auto lambda = [&x]() {
+ x = 1;
+ };
+}
+
+void captureByAllRef() {
+ auto P = getPair<int, int>();
+ // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
+ int x = P.first;
+ int y = P.second;
+
+ auto lambda = [&]() {
+ x = 1;
+ };
+}
+
+void deepLambda() {
+ auto P = getPair<int, int>();
+ // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
+ int x = P.first;
+ int y = P.second;
+
+ {
+ auto lambda = [x]() {
+ int y = x;
+ };
+ }
+}
+
+void forRangeNotWarn() {
+ std::pair<int, int> Pairs[10];
+ for (auto P : Pairs) {
+ // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-CPP20ORLATER: for (auto [x, y] : Pairs) {
+ int x = P.first;
+ int y = P.second;
+
+ auto lambda = [&]() {
+ x = 1;
+ };
+ }
+}
+
+void stdTieNotWarn() {
+ int x = 0;
+ int y = 0;
+ std::tie(x, y) = getPair<int, int>();
+ // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
+
+ auto lambda = [&x]() {
+ x = 1;
+ };
+}
>From c97ce9b2c775c9d0967483404ff82e2ff6ed68a8 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 18:10:43 +0800
Subject: [PATCH 11/48] [NFC] Test removed lines
---
.../modernize/use-structured-binding.cpp | 120 ++++++++++++------
1 file changed, 80 insertions(+), 40 deletions(-)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 05b842e4d6614..55bdda5150d0f 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -18,24 +18,30 @@ void DecomposeByAssignWarnCases() {
auto P = getPair<int, int>();
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
- int x = P.first;
- int y = P.second;
+ int x = P.first; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ int y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
}
{
auto P = getPair<int, int>();
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
- int x = P.first;
- auto y = P.second;
+ int x = P.first; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ auto y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
}
{
const auto P = getPair<int, int>();
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: const auto [x, y] = getPair<int, int>();
- const int x = P.first;
- const auto y = P.second;
+ const int x = P.first; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ const auto y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
}
{
@@ -43,8 +49,10 @@ void DecomposeByAssignWarnCases() {
auto& P = otherP;
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto& [x, y] = otherP;
- int& x = P.first;
- auto& y = P.second;
+ int& x = P.first; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ auto& y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
}
{
@@ -52,8 +60,10 @@ void DecomposeByAssignWarnCases() {
const auto& P = otherP;
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: const auto& [x, y] = otherP;
- const int& x = P.first;
- const auto& y = P.second;
+ const int& x = P.first; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ const auto& y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
}
}
@@ -62,44 +72,56 @@ void forRangeWarnCases() {
for (auto P : Pairs) {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
- int x = P.first;
- int y = P.second;
+ int x = P.first; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ int y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
}
for (const auto P : Pairs) {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (const auto [x, y] : Pairs) {
- const int x = P.first;
- const int y = P.second;
+ const int x = P.first; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ const int y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
}
for (auto& P : Pairs) {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (auto& [x, y] : Pairs) {
- int& x = P.first;
- int& y = P.second;
+ int& x = P.first; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ int& y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
}
for (const auto& P : Pairs) {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (const auto& [x, y] : Pairs) {
- const int& x = P.first;
- const int& y = P.second;
+ const int& x = P.first; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ const int& y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
}
std::pair<TestClass, TestClass> ClassPairs[10];
for (auto P : ClassPairs) {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (auto [c1, c2] : ClassPairs) {
- TestClass c1 = P.first;
- TestClass c2 = P.second;
+ TestClass c1 = P.first; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ TestClass c2 = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
}
for (const auto P : ClassPairs) {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (const auto [c1, c2] : ClassPairs) {
- const TestClass c1 = P.first;
- const TestClass c2 = P.second;
+ const TestClass c1 = P.first; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ const TestClass c2 = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
}
}
@@ -139,20 +161,26 @@ void forRangeNotWarnCases() {
}
void stdTieWarnCases() {
- int a = 0;
- int b = 0;
+ int a = 0; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ int b = 0; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
std::tie(a, b) = getPair<int, int>();
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [a, b] = getPair<int, int>();
- int* pa = nullptr;
- int* pb = nullptr;
+ int* pa = nullptr; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ int* pb = nullptr; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
std::tie(pa, pb) = getPair<int*, int*>();
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [pa, pb] = getPair<int*, int*>();
- TestClass c1 (1, 2);
- TestClass c2 = TestClass {3, 4};
+ TestClass c1 (1, 2); // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ TestClass c2 = TestClass {3, 4}; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
std::tie(c1, c2) = getPair<TestClass, TestClass>();
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [c1, c2] = getPair<TestClass, TestClass>();
@@ -255,8 +283,10 @@ void captureByVal() {
auto P = getPair<int, int>();
// CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
- int x = P.first;
- int y = P.second;
+ int x = P.first; // REMOVE
+ // CHECK-FIXES-CPP20ORLATER: // REMOVE
+ int y = P.second; // REMOVE
+ // CHECK-FIXES-CPP20ORLATER: // REMOVE
auto lambda = [x]() {
int y = x;
@@ -267,8 +297,10 @@ void captureByRef() {
auto P = getPair<int, int>();
// CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
- int x = P.first;
- int y = P.second;
+ int x = P.first; // REMOVE
+ // CHECK-FIXES-CPP20ORLATER: // REMOVE
+ int y = P.second; // REMOVE
+ // CHECK-FIXES-CPP20ORLATER: // REMOVE
auto lambda = [&x]() {
x = 1;
@@ -279,8 +311,10 @@ void captureByAllRef() {
auto P = getPair<int, int>();
// CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
- int x = P.first;
- int y = P.second;
+ int x = P.first; // REMOVE
+ // CHECK-FIXES-CPP20ORLATER: // REMOVE
+ int y = P.second; // REMOVE
+ // CHECK-FIXES-CPP20ORLATER: // REMOVE
auto lambda = [&]() {
x = 1;
@@ -291,8 +325,10 @@ void deepLambda() {
auto P = getPair<int, int>();
// CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
- int x = P.first;
- int y = P.second;
+ int x = P.first; // REMOVE
+ // CHECK-FIXES-CPP20ORLATER: // REMOVE
+ int y = P.second; // REMOVE
+ // CHECK-FIXES-CPP20ORLATER: // REMOVE
{
auto lambda = [x]() {
@@ -306,8 +342,10 @@ void forRangeNotWarn() {
for (auto P : Pairs) {
// CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: for (auto [x, y] : Pairs) {
- int x = P.first;
- int y = P.second;
+ int x = P.first; // REMOVE
+ // CHECK-FIXES-CPP20ORLATER: // REMOVE
+ int y = P.second; // REMOVE
+ // CHECK-FIXES-CPP20ORLATER: // REMOVE
auto lambda = [&]() {
x = 1;
@@ -316,8 +354,10 @@ void forRangeNotWarn() {
}
void stdTieNotWarn() {
- int x = 0;
- int y = 0;
+ int x = 0; // REMOVE
+ // CHECK-FIXES-CPP20ORLATER: // REMOVE
+ int y = 0; // REMOVE
+ // CHECK-FIXES-CPP20ORLATER: // REMOVE
std::tie(x, y) = getPair<int, int>();
// CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
>From fbcb0999bc232c79a23a4370222560928af8f1e9 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 18:39:02 +0800
Subject: [PATCH 12/48] [NFC] 80-character for check doc
---
.../checks/modernize/use-structured-binding.rst | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 0d227b75968a7..637f8bcee666f 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -6,9 +6,11 @@ modernize-use-structured-binding
Finds places where structured bindings could be used to decompose pairs and
suggests replacing them.
-This check finds three code patterns and recommends using structured bindings for clearer, more idiomatic C++17 code.
+This check finds three code patterns and recommends using structured bindings
+for clearer, more idiomatic C++17 code.
-1. Decompose a pair variable by assigning its members to separate variables right after its definition:
+1. Decompose a pair variable by assigning its members to separate variables
+right after its definition:
.. code-block:: c++
@@ -32,7 +34,8 @@ This check finds three code patterns and recommends using structured bindings fo
auto [a, b] = getPair<int, int>();
-3. Manually decompose a pair by assigning to its members to local variables in a range-based for loop:
+3. Manually decompose a pair by assigning to its members to local variables
+in a range-based for loop:
.. code-block:: c++
@@ -48,12 +51,14 @@ This check finds three code patterns and recommends using structured bindings fo
// use x and y
}
-The check also supports custom pair-like types via the :option:`PairTypes` option.
+The check also supports custom pair-like types via the :option:`PairTypes`
+option.
Options
-------
.. option:: PairTypes
- A Semicolon-separated list of type names to be treated as pair-like for structured binding suggestions.
- Example: `MyPairType;OtherPairType`. Default is `std::pair`.
+ A Semicolon-separated list of type names to be treated as pair-like for
+ structured binding suggestions. Example: `MyPairType;OtherPairType`.
+ Default is `std::pair`.
>From 3f95a697181f00e68971c26bccc18a9c33ba319c Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 18:45:25 +0800
Subject: [PATCH 13/48] [NFC] Add limitations section to doc
---
.../modernize/use-structured-binding.rst | 31 +++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 637f8bcee666f..a635158420e21 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -54,6 +54,37 @@ in a range-based for loop:
The check also supports custom pair-like types via the :option:`PairTypes`
option.
+Limitations
+-----------
+
+The check currently ignores variables defined with attributes or qualifiers
+except const and & since it's not very common:
+
+.. code-block:: c++
+
+ static auto pair = getPair();
+ static int b = pair.first;
+ static int c = pair.second;
+
+The check doesn't check for some situations which could possibly transfered
+to structured bnindings, for example:
+
+.. code-block:: c++
+
+ const auto& results = mapping.try_emplace("hello!");
+ const iterator& it = results.first;
+ bool succeed = results.second;
+ // succeed is not changed in the following code
+
+and:
+
+.. code-block:: c++
+
+ const auto results = mapping.try_emplace("hello!");
+ if (results.second) {
+ handle_inserted(results.first);
+ }
+
Options
-------
>From a5222a22546d1119896b22a58a1bc28914181e54 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 18:49:55 +0800
Subject: [PATCH 14/48] [NFC] Add some tests about two VarDecl in one DeclStmt.
---
.../modernize/use-structured-binding.cpp | 21 +++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 55bdda5150d0f..bef93027f72fb 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -24,6 +24,14 @@ void DecomposeByAssignWarnCases() {
// CHECK-FIXES-ALL: // REMOVE
}
+ {
+ auto P = getPair<int, int>();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
+ int x = P.first, y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ }
+
{
auto P = getPair<int, int>();
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
@@ -78,6 +86,13 @@ void forRangeWarnCases() {
// CHECK-FIXES-ALL: // REMOVE
}
+ for (auto P : Pairs) {
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
+ int x = P.first, y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ }
+
for (const auto P : Pairs) {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (const auto [x, y] : Pairs) {
@@ -169,6 +184,12 @@ void stdTieWarnCases() {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [a, b] = getPair<int, int>();
+ int x = 0, y = 0; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ std::tie(x, y) = getPair<int, int>();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
+
int* pa = nullptr; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
int* pb = nullptr; // REMOVE
>From 8c423b34ee9f6cd3103001b20e63d3ecee2875f4 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 21:35:53 +0800
Subject: [PATCH 15/48] Update
clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
.../clang-tidy/modernize/UseStructuredBindingCheck.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index fab4e8dc16cf1..f9d700f5e849c 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -1,4 +1,4 @@
-//===--- UseStructuredBindingCheck.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.
>From 42a4e5b381fdb3a525159f2c8e558c9bf036b42f Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 21:36:13 +0800
Subject: [PATCH 16/48] Update
clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
.../clang-tidy/modernize/UseStructuredBindingCheck.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
index 63bc0a8c3da45..83d262a5db3cd 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
@@ -1,4 +1,4 @@
-//===--- UseStructuredBindingCheck.h - clang-tidy ---------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
>From 8919fdb385b2a69704c4348fecb5f99b22bea7d7 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 21:36:37 +0800
Subject: [PATCH 17/48] Update
clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
.../clang-tidy/modernize/UseStructuredBindingCheck.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index f9d700f5e849c..78582afe706e6 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -125,7 +125,7 @@ AST_MATCHER_P2(Stmt, hasPreTwoVarDecl, ast_matchers::internal::Matcher<VarDecl>,
AST_MATCHER_P2(Stmt, hasNextTwoVarDecl,
ast_matchers::internal::Matcher<VarDecl>, InnerMatcher1,
ast_matchers::internal::Matcher<VarDecl>, InnerMatcher2) {
- DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
+ const DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
if (Parents.size() != 1)
return false;
>From 88a243efa5243d7a846c820aeda1fcb79ba29eaf Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 21:36:47 +0800
Subject: [PATCH 18/48] Update
clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
.../clang-tidy/modernize/UseStructuredBindingCheck.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 78582afe706e6..99cef95aa0251 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -386,7 +386,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
!Res.empty())
InitE = Res[0].getNodeAs<Expr>("init_expr");
- std::optional<TransferType> PairCaptureType =
+ const std::optional<TransferType> PairCaptureType =
getTransferType(*Result.Context, PairVar->getType(), InitE->getType());
std::optional<TransferType> FirstVarCaptureType =
getTransferType(*Result.Context, FirstVar->getType(),
>From 8fa2edcce1f8ee83aeb7e3764ebf0ac19bcdc972 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 21:36:55 +0800
Subject: [PATCH 19/48] Update
clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
.../clang-tidy/modernize/UseStructuredBindingCheck.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 99cef95aa0251..b8b1540e69864 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -388,7 +388,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
const std::optional<TransferType> PairCaptureType =
getTransferType(*Result.Context, PairVar->getType(), InitE->getType());
- std::optional<TransferType> FirstVarCaptureType =
+ const std::optional<TransferType> FirstVarCaptureType =
getTransferType(*Result.Context, FirstVar->getType(),
*Result.Nodes.getNodeAs<QualType>(FirstTypeName));
std::optional<TransferType> SecondVarCaptureType =
>From 2c980656ab7ea781fcb7c7dc6f140d56ab432384 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 21:37:09 +0800
Subject: [PATCH 20/48] Update
clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
.../clang-tidy/modernize/UseStructuredBindingCheck.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index b8b1540e69864..f15ce7ca082ea 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -391,7 +391,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
const std::optional<TransferType> FirstVarCaptureType =
getTransferType(*Result.Context, FirstVar->getType(),
*Result.Nodes.getNodeAs<QualType>(FirstTypeName));
- std::optional<TransferType> SecondVarCaptureType =
+ const std::optional<TransferType> SecondVarCaptureType =
getTransferType(*Result.Context, SecondVar->getType(),
*Result.Nodes.getNodeAs<QualType>(SecondTypeName));
if (!PairCaptureType || !FirstVarCaptureType || !SecondVarCaptureType ||
>From 48b747ec2fb81b69a8084f561b0dbf36f3201c7e Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 16 Sep 2025 20:01:01 +0800
Subject: [PATCH 21/48] [NFC] Correct comment words
---
.../clang-tidy/modernize/UseStructuredBindingCheck.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index f15ce7ca082ea..b8d9ac964961b 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -149,8 +149,8 @@ AST_MATCHER_P2(Stmt, hasNextTwoVarDecl,
return false;
}
-/// Matches a Stmt whose parent is a CompoundStmt, and there a two VarDecls
-/// matching the inner matcher in the beginning of CompoundStmt.
+/// Matches a CompoundStmt which has two VarDecls
+/// matching the inner matcher in the beginning.
AST_MATCHER_P2(CompoundStmt, hasFirstTwoVarDecl,
ast_matchers::internal::Matcher<VarDecl>, InnerMatcher1,
ast_matchers::internal::Matcher<VarDecl>, InnerMatcher2) {
>From a46277ea8aac25188ee15c2a6841f258b1abad88 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 16 Sep 2025 23:14:36 +0800
Subject: [PATCH 22/48] Update
clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
.../docs/clang-tidy/checks/modernize/use-structured-binding.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index a635158420e21..a935516ed4307 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -90,6 +90,6 @@ Options
.. option:: PairTypes
- A Semicolon-separated list of type names to be treated as pair-like for
+ A semicolon-separated list of type names to be treated as pair-like for
structured binding suggestions. Example: `MyPairType;OtherPairType`.
Default is `std::pair`.
>From 36011dd2fb1b1f225d15ed12e1f66e9f3c896c80 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 16 Sep 2025 23:15:49 +0800
Subject: [PATCH 23/48] [NFC] 80 chars limit for comment
---
.../clang-tidy/modernize/UseStructuredBindingCheck.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index b8d9ac964961b..7ecfeefc6f8df 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -149,8 +149,8 @@ AST_MATCHER_P2(Stmt, hasNextTwoVarDecl,
return false;
}
-/// Matches a CompoundStmt which has two VarDecls
-/// matching the inner matcher in the beginning.
+/// Matches a CompoundStmt which has two VarDecls matching the inner matcher in
+/// the beginning.
AST_MATCHER_P2(CompoundStmt, hasFirstTwoVarDecl,
ast_matchers::internal::Matcher<VarDecl>, InnerMatcher1,
ast_matchers::internal::Matcher<VarDecl>, InnerMatcher2) {
@@ -164,8 +164,8 @@ AST_MATCHER_P2(CompoundStmt, hasFirstTwoVarDecl,
InnerMatcher1, InnerMatcher2, Finder, Builder);
}
-/// It's not very common to have specifiers for variables used to decompose
-/// a pair, so we ignore these cases.
+/// It's not very common to have specifiers for variables used to decompose a
+/// pair, so we ignore these cases.
AST_MATCHER(VarDecl, hasAnySpecifiersShouldBeIgnored) {
return Node.isStaticLocal() || Node.isConstexpr() || Node.hasAttrs() ||
Node.isInlineSpecified();
>From c5c673b602db51cb93cda6666fed4d22240a9dcc Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Wed, 17 Sep 2025 20:23:29 +0800
Subject: [PATCH 24/48] Update
clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
Co-authored-by: Congcong Cai <congcongcai0907 at 163.com>
---
.../docs/clang-tidy/checks/modernize/use-structured-binding.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index a935516ed4307..0d02e6761cdd4 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -39,7 +39,7 @@ in a range-based for loop:
.. code-block:: c++
- for (autop : vecOfPairs) {
+ for (auto p : vecOfPairs) {
int x = p.first;
int y = p.second;
// ...
>From d92f76849cad72881a87e0c36172ecaa670966dd Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Wed, 17 Sep 2025 21:25:00 +0800
Subject: [PATCH 25/48] [NFC] prefer static function
---
.../modernize/UseStructuredBindingCheck.cpp | 56 ++++++++++---------
1 file changed, 29 insertions(+), 27 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 7ecfeefc6f8df..f04923f01a459 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -14,37 +14,29 @@
using namespace clang::ast_matchers;
namespace clang::tidy::modernize {
-namespace {
-constexpr const char *DefaultPairTypes = "std::pair";
-constexpr llvm::StringLiteral PairDeclName = "PairVarD";
-constexpr llvm::StringLiteral PairVarTypeName = "PairVarType";
-constexpr llvm::StringLiteral FirstVarDeclName = "FirstVarDecl";
-constexpr llvm::StringLiteral SecondVarDeclName = "SecondVarDecl";
-constexpr llvm::StringLiteral FirstDeclStmtName = "FirstDeclStmt";
-constexpr llvm::StringLiteral SecondDeclStmtName = "SecondDeclStmt";
-constexpr llvm::StringLiteral FirstTypeName = "FirstType";
-constexpr llvm::StringLiteral SecondTypeName = "SecondType";
-constexpr llvm::StringLiteral ScopeBlockName = "ScopeBlock";
-constexpr llvm::StringLiteral StdTieAssignStmtName = "StdTieAssign";
-constexpr llvm::StringLiteral StdTieExprName = "StdTieExpr";
-constexpr llvm::StringLiteral ForRangeStmtName = "ForRangeStmt";
-/// What qualifiers and specifiers are used to create structured binding
-/// declaration, it only supports the following four cases now.
-enum TransferType : uint8_t {
- TT_ByVal,
- TT_ByConstVal,
- TT_ByRef,
- TT_ByConstRef
-};
+static constexpr const char *DefaultPairTypes = "std::pair";
+static constexpr llvm::StringLiteral PairDeclName = "PairVarD";
+static constexpr llvm::StringLiteral PairVarTypeName = "PairVarType";
+static constexpr llvm::StringLiteral FirstVarDeclName = "FirstVarDecl";
+static constexpr llvm::StringLiteral SecondVarDeclName = "SecondVarDecl";
+static constexpr llvm::StringLiteral FirstDeclStmtName = "FirstDeclStmt";
+static constexpr llvm::StringLiteral SecondDeclStmtName = "SecondDeclStmt";
+static constexpr llvm::StringLiteral FirstTypeName = "FirstType";
+static constexpr llvm::StringLiteral SecondTypeName = "SecondType";
+static constexpr llvm::StringLiteral ScopeBlockName = "ScopeBlock";
+static constexpr llvm::StringLiteral StdTieAssignStmtName = "StdTieAssign";
+static constexpr llvm::StringLiteral StdTieExprName = "StdTieExpr";
+static constexpr llvm::StringLiteral ForRangeStmtName = "ForRangeStmt";
/// Try to match exactly two VarDecl inside two DeclStmts, and set binding for
/// the used DeclStmts.
-bool matchTwoVarDecl(const DeclStmt *DS1, const DeclStmt *DS2,
- ast_matchers::internal::Matcher<VarDecl> InnerMatcher1,
- ast_matchers::internal::Matcher<VarDecl> InnerMatcher2,
- internal::ASTMatchFinder *Finder,
- internal::BoundNodesTreeBuilder *Builder) {
+static bool
+matchTwoVarDecl(const DeclStmt *DS1, const DeclStmt *DS2,
+ ast_matchers::internal::Matcher<VarDecl> InnerMatcher1,
+ ast_matchers::internal::Matcher<VarDecl> InnerMatcher2,
+ internal::ASTMatchFinder *Finder,
+ internal::BoundNodesTreeBuilder *Builder) {
SmallVector<std::pair<const VarDecl *, const DeclStmt *>, 2> Vars;
auto CollectVarsInDeclStmt = [&Vars](const DeclStmt *DS) -> bool {
if (!DS)
@@ -82,6 +74,16 @@ bool matchTwoVarDecl(const DeclStmt *DS1, const DeclStmt *DS2,
return false;
}
+namespace {
+/// What qualifiers and specifiers are used to create structured binding
+/// declaration, it only supports the following four cases now.
+enum TransferType : uint8_t {
+ TT_ByVal,
+ TT_ByConstVal,
+ TT_ByRef,
+ TT_ByConstRef
+};
+
/// Matches a Stmt whose parent is a CompoundStmt, and which is directly
/// following two VarDecls matching the inner matcher, at the same time set
/// binding for the CompoundStmt.
>From e4ffb48e8c9cb84b9b3c7b14f2e1a32c00228905 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Wed, 17 Sep 2025 21:25:44 +0800
Subject: [PATCH 26/48] [NFC] Explicit capture in lambda
---
.../clang-tidy/modernize/UseStructuredBindingCheck.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index f04923f01a459..c7ae24fb0c840 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -339,8 +339,9 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
}
const auto *CFRS = Result.Nodes.getNodeAs<CXXForRangeStmt>(ForRangeStmtName);
- auto DiagAndFix = [&](SourceLocation DiagLoc, SourceRange ReplaceRange,
- TransferType TT = TT_ByVal) {
+ auto DiagAndFix = [&DS1, &DS2, &FirstVar, &SecondVar, &CFRS,
+ this](SourceLocation DiagLoc, SourceRange ReplaceRange,
+ TransferType TT = TT_ByVal) {
StringRef Prefix;
switch (TT) {
case TT_ByVal:
>From 73231c8de9e38686b03cbf4d7044172eece9fb93 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Wed, 17 Sep 2025 21:26:27 +0800
Subject: [PATCH 27/48] [NFC] Use llvm::twine to concat string
---
.../clang-tidy/modernize/UseStructuredBindingCheck.cpp | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index c7ae24fb0c840..8ca088ecf1405 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -363,11 +363,10 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
if (DS2)
Hints.emplace_back(FixItHint::CreateRemoval(DS2->getSourceRange()));
- std::string ReplacementText = Prefix.str() + " [" +
- FirstVar->getNameAsString() + ", " +
- SecondVar->getNameAsString() + "]";
- if (CFRS)
- ReplacementText += " :";
+ std::string ReplacementText =
+ (Twine(Prefix) + " [" + FirstVar->getNameAsString() + ", " +
+ SecondVar->getNameAsString() + "]" + (CFRS ? " :" : ""))
+ .str();
diag(DiagLoc, "use structured binding to decompose a pair")
<< FixItHint::CreateReplacement(ReplaceRange, ReplacementText) << Hints;
};
>From 7ac2a3ed82e78e18c545a066929b53ac297560b6 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Wed, 17 Sep 2025 21:38:55 +0800
Subject: [PATCH 28/48] [NFC] Avoid some duplications
---
.../modernize/UseStructuredBindingCheck.cpp | 51 +++++++++----------
1 file changed, 24 insertions(+), 27 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 8ca088ecf1405..24e59e3044d1d 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -202,12 +202,12 @@ UseStructuredBindingCheck::UseStructuredBindingCheck(StringRef Name,
;
}
-static auto getVarInitWithMemberMatcher(StringRef PairName,
- StringRef MemberName,
- StringRef TypeName,
- StringRef BindingName) {
+static auto getVarInitWithMemberMatcher(
+ StringRef PairName, StringRef MemberName, StringRef TypeName,
+ StringRef BindingName,
+ ast_matchers::internal::Matcher<VarDecl> ExtraMatcher) {
return varDecl(
- unless(hasAnySpecifiersShouldBeIgnored()), unless(isInMarco()),
+ ExtraMatcher,
hasInitializer(
ignoringImpCasts(ignoringCopyCtorAndImplicitCast(memberExpr(
hasObjectExpression(ignoringImpCasts(declRefExpr(
@@ -223,10 +223,20 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
hasUnqualifiedDesugaredType(recordType(
hasDeclaration(cxxRecordDecl(hasAnyName(PairTypes))))));
- auto VarInitWithFirstMember = getVarInitWithMemberMatcher(
- PairDeclName, "first", FirstTypeName, FirstVarDeclName);
- auto VarInitWithSecondMember = getVarInitWithMemberMatcher(
- PairDeclName, "second", SecondTypeName, SecondVarDeclName);
+ auto UnlessShouldBeIgnored =
+ unless(anyOf(hasAnySpecifiersShouldBeIgnored(), isInMarco()));
+
+ auto VarInitWithFirstMember =
+ getVarInitWithMemberMatcher(PairDeclName, "first", FirstTypeName,
+ FirstVarDeclName, UnlessShouldBeIgnored);
+ auto VarInitWithSecondMember =
+ getVarInitWithMemberMatcher(PairDeclName, "second", SecondTypeName,
+ SecondVarDeclName, UnlessShouldBeIgnored);
+
+ auto RefToBindName = [&UnlessShouldBeIgnored](const llvm::StringLiteral &Name)
+ -> ast_matchers::internal::BindableMatcher<Stmt> {
+ return declRefExpr(to(varDecl(UnlessShouldBeIgnored).bind(Name)));
+ };
// X x;
// Y y;
@@ -237,23 +247,10 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
has(cxxOperatorCallExpr(
hasOverloadedOperatorName("="),
hasLHS(ignoringImplicit(
- callExpr(
- callee(
- functionDecl(isInStdNamespace(), hasName("tie"))),
- hasArgument(
- 0,
- declRefExpr(to(
- varDecl(
- unless(hasAnySpecifiersShouldBeIgnored()),
- unless(isInMarco()))
- .bind(FirstVarDeclName)))),
- hasArgument(
- 1,
- declRefExpr(to(
- varDecl(
- unless(hasAnySpecifiersShouldBeIgnored()),
- unless(isInMarco()))
- .bind(SecondVarDeclName)))))
+ callExpr(callee(functionDecl(isInStdNamespace(),
+ hasName("tie"))),
+ hasArgument(0, RefToBindName(FirstVarDeclName)),
+ hasArgument(1, RefToBindName(SecondVarDeclName)))
.bind(StdTieExprName))),
hasRHS(expr(hasType(PairType))))
.bind(StdTieAssignStmtName)),
@@ -269,7 +266,7 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
declStmt(
unless(isInMarco()),
hasSingleDecl(
- varDecl(unless(hasAnySpecifiersShouldBeIgnored()),
+ varDecl(UnlessShouldBeIgnored,
hasType(qualType(anyOf(PairType, lValueReferenceType(
pointee(PairType))))
.bind(PairVarTypeName)),
>From 00285d9e49358ce8a83090b7f684183823973990 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Wed, 17 Sep 2025 21:47:01 +0800
Subject: [PATCH 29/48] Stop collecting when we got two VarDecls from DS1
---
.../modernize/UseStructuredBindingCheck.cpp | 7 +++----
.../modernize/use-structured-binding.cpp | 17 +++++++++++++++++
2 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 24e59e3044d1d..43431721e439e 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -55,10 +55,9 @@ matchTwoVarDecl(const DeclStmt *DS1, const DeclStmt *DS2,
return true;
};
- if (!CollectVarsInDeclStmt(DS1) || !CollectVarsInDeclStmt(DS2))
- return false;
-
- if (Vars.size() != 2)
+ // If we already get two VarDecls, then don't need to collect from DS2.
+ if (!CollectVarsInDeclStmt(DS1) ||
+ (Vars.size() != 2 && !CollectVarsInDeclStmt(DS2)) || Vars.size() != 2)
return false;
if (InnerMatcher1.matches(*Vars[0].first, Finder, Builder) &&
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index bef93027f72fb..33de71762ba87 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -32,6 +32,15 @@ void DecomposeByAssignWarnCases() {
// CHECK-FIXES-ALL: // REMOVE
}
+ {
+ auto P = getPair<int, int>();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
+ int x = P.first, y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ int z;
+ }
+
{
auto P = getPair<int, int>();
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
@@ -93,6 +102,14 @@ void forRangeWarnCases() {
// CHECK-FIXES-ALL: // REMOVE
}
+ for (auto P : Pairs) {
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
+ int x = P.first, y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ int z;
+ }
+
for (const auto P : Pairs) {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (const auto [x, y] : Pairs) {
>From 242316ead2d67cd2ecbfab8c4344c63373f79f8c Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Wed, 17 Sep 2025 23:29:07 +0800
Subject: [PATCH 30/48] Exit for std::tie case when DS1 has multi child decl
---
.../clang-tidy/modernize/UseStructuredBindingCheck.cpp | 2 ++
.../clang-tidy/checkers/modernize/use-structured-binding.cpp | 4 ++++
2 files changed, 6 insertions(+)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 43431721e439e..c471436c0c88a 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -110,6 +110,8 @@ AST_MATCHER_P2(Stmt, hasPreTwoVarDecl, ast_matchers::internal::Matcher<VarDecl>,
const DeclStmt *DS1 = (!DS2->isSingleDecl() || ((I + 2) == C->body_rend())
? nullptr
: dyn_cast<DeclStmt>(*(I + 2)));
+ if (DS1 && !DS1->isSingleDecl())
+ return false;
if (matchTwoVarDecl(DS1, DS2, InnerMatcher1, InnerMatcher2, Finder,
Builder)) {
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 33de71762ba87..be3418a2ac224 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -230,6 +230,10 @@ void stdTieNotWarnCases() {
a = 4;
std::tie(a, b) = getPair<int, int>(); // no warning
+ int c = 0, d = 0;
+ int e = 0;
+ std::tie(a, b) = getPair<int, int>(); // no warning
+
int* pa = nullptr;
int* pb = nullptr;
MarkUsed(pa);
>From d2eb66b5fd5309a6eea401b139d43787b71209b0 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Fri, 19 Sep 2025 11:59:12 +0800
Subject: [PATCH 31/48] [NFC] use llvm::reverse
---
.../clang-tidy/modernize/UseStructuredBindingCheck.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index c471436c0c88a..b0230d0819420 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -97,8 +97,7 @@ AST_MATCHER_P2(Stmt, hasPreTwoVarDecl, ast_matchers::internal::Matcher<VarDecl>,
if (!C)
return false;
- const auto I =
- llvm::find(llvm::make_range(C->body_rbegin(), C->body_rend()), &Node);
+ const auto I = llvm::find(llvm::reverse(C->body()), &Node);
assert(I != C->body_rend() && "C is parent of Node");
if ((I + 1) == C->body_rend())
return false;
>From c6f68854b1f946953ad4357841858a9d8d5f72f6 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 13:45:50 +0800
Subject: [PATCH 32/48] Refactor the way how we match Two VarDecls
---
.../modernize/UseStructuredBindingCheck.cpp | 200 +++++++++---------
.../modernize/use-structured-binding.cpp | 60 ++----
2 files changed, 115 insertions(+), 145 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index b0230d0819420..6530fce2f6484 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -20,8 +20,8 @@ static constexpr llvm::StringLiteral PairDeclName = "PairVarD";
static constexpr llvm::StringLiteral PairVarTypeName = "PairVarType";
static constexpr llvm::StringLiteral FirstVarDeclName = "FirstVarDecl";
static constexpr llvm::StringLiteral SecondVarDeclName = "SecondVarDecl";
-static constexpr llvm::StringLiteral FirstDeclStmtName = "FirstDeclStmt";
-static constexpr llvm::StringLiteral SecondDeclStmtName = "SecondDeclStmt";
+static constexpr llvm::StringLiteral BeginDeclStmtName = "BeginDeclStmt";
+static constexpr llvm::StringLiteral EndDeclStmtName = "EndDeclStmt";
static constexpr llvm::StringLiteral FirstTypeName = "FirstType";
static constexpr llvm::StringLiteral SecondTypeName = "SecondType";
static constexpr llvm::StringLiteral ScopeBlockName = "ScopeBlock";
@@ -29,45 +29,65 @@ static constexpr llvm::StringLiteral StdTieAssignStmtName = "StdTieAssign";
static constexpr llvm::StringLiteral StdTieExprName = "StdTieExpr";
static constexpr llvm::StringLiteral ForRangeStmtName = "ForRangeStmt";
-/// Try to match exactly two VarDecl inside two DeclStmts, and set binding for
-/// the used DeclStmts.
-static bool
-matchTwoVarDecl(const DeclStmt *DS1, const DeclStmt *DS2,
- ast_matchers::internal::Matcher<VarDecl> InnerMatcher1,
- ast_matchers::internal::Matcher<VarDecl> InnerMatcher2,
- internal::ASTMatchFinder *Finder,
- internal::BoundNodesTreeBuilder *Builder) {
- SmallVector<std::pair<const VarDecl *, const DeclStmt *>, 2> Vars;
- auto CollectVarsInDeclStmt = [&Vars](const DeclStmt *DS) -> bool {
- if (!DS)
- return true;
+/// Matches a sequence of VarDecls matching the inner matchers, starting from
+/// the \p Iter to \p EndIter and set bindings for the first DeclStmt and the
+/// last DeclStmt if matched.
+///
+/// \p Backwards indicates whether to match the VarDecls in reverse order.
+template <typename Iterator>
+static bool matchNVarDeclStartingWith(
+ Iterator Iter, Iterator EndIter,
+ ArrayRef<ast_matchers::internal::Matcher<VarDecl>> InnerMatchers,
+ ast_matchers::internal::ASTMatchFinder *Finder,
+ ast_matchers::internal::BoundNodesTreeBuilder *Builder,
+ bool Backwards = false) {
+ const DeclStmt *BeginDS = nullptr;
+ const DeclStmt *EndDS = nullptr;
+ size_t N = InnerMatchers.size();
+ size_t Count = 0;
+ for (; Iter != EndIter; ++Iter) {
+ EndDS = dyn_cast<DeclStmt>(*Iter);
+ if (!EndDS)
+ break;
- for (const auto *VD : DS->decls()) {
- if (Vars.size() == 2)
- return false;
+ if (!BeginDS)
+ BeginDS = EndDS;
- if (const auto *Var = dyn_cast<VarDecl>(VD))
- Vars.emplace_back(Var, DS);
- else
+ auto Matches = [&](const Decl *VD) {
+ if (Count == N)
return false;
- }
- return true;
- };
-
- // If we already get two VarDecls, then don't need to collect from DS2.
- if (!CollectVarsInDeclStmt(DS1) ||
- (Vars.size() != 2 && !CollectVarsInDeclStmt(DS2)) || Vars.size() != 2)
- return false;
+ if (const auto *Var = dyn_cast<VarDecl>(VD);
+ Var && InnerMatchers[Backwards ? N - Count - 1 : Count].matches(
+ *Var, Finder, Builder)) {
+ ++Count;
+ return true;
+ }
+
+ return false;
+ };
+
+ if (Backwards) {
+ for (const auto *VD : llvm::reverse(EndDS->decls())) {
+ if (!Matches(VD))
+ return false;
+ }
+ } else {
+ for (const auto *VD : EndDS->decls()) {
+ if (!Matches(VD))
+ return false;
+ }
+ }
- if (InnerMatcher1.matches(*Vars[0].first, Finder, Builder) &&
- InnerMatcher2.matches(*Vars[1].first, Finder, Builder)) {
- Builder->setBinding(FirstDeclStmtName,
- clang::DynTypedNode::create(*Vars[0].second));
- if (Vars[0].second != Vars[1].second)
- Builder->setBinding(SecondDeclStmtName,
- clang::DynTypedNode::create(*Vars[1].second));
- return true;
+ // All the matchers is satisfied in those DeclStmts.
+ if (Count == N) {
+ Builder->setBinding(
+ BeginDeclStmtName,
+ clang::DynTypedNode::create(Backwards ? *EndDS : *BeginDS));
+ Builder->setBinding(EndDeclStmtName, clang::DynTypedNode::create(
+ Backwards ? *BeginDS : *EndDS));
+ return true;
+ }
}
return false;
@@ -84,12 +104,11 @@ enum TransferType : uint8_t {
};
/// Matches a Stmt whose parent is a CompoundStmt, and which is directly
-/// following two VarDecls matching the inner matcher, at the same time set
-/// binding for the CompoundStmt.
-AST_MATCHER_P2(Stmt, hasPreTwoVarDecl, ast_matchers::internal::Matcher<VarDecl>,
- InnerMatcher1, ast_matchers::internal::Matcher<VarDecl>,
- InnerMatcher2) {
- DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
+/// following two VarDecls matching the inner matcher.
+AST_MATCHER_P(Stmt, hasPreTwoVarDecl,
+ llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>,
+ InnerMatchers) {
+ const DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
if (Parents.size() != 1)
return false;
@@ -99,34 +118,16 @@ AST_MATCHER_P2(Stmt, hasPreTwoVarDecl, ast_matchers::internal::Matcher<VarDecl>,
const auto I = llvm::find(llvm::reverse(C->body()), &Node);
assert(I != C->body_rend() && "C is parent of Node");
- if ((I + 1) == C->body_rend())
- return false;
-
- const auto *DS2 = dyn_cast<DeclStmt>(*(I + 1));
- if (!DS2)
- return false;
-
- const DeclStmt *DS1 = (!DS2->isSingleDecl() || ((I + 2) == C->body_rend())
- ? nullptr
- : dyn_cast<DeclStmt>(*(I + 2)));
- if (DS1 && !DS1->isSingleDecl())
- return false;
-
- if (matchTwoVarDecl(DS1, DS2, InnerMatcher1, InnerMatcher2, Finder,
- Builder)) {
- Builder->setBinding(ScopeBlockName, clang::DynTypedNode::create(*C));
- return true;
- }
-
- return false;
+ return matchNVarDeclStartingWith(I + 1, C->body_rend(), InnerMatchers, Finder,
+ Builder, true);
}
/// Matches a Stmt whose parent is a CompoundStmt, and which is directly
-/// followed by two VarDecls matching the inner matcher, at the same time set
-/// binding for the CompoundStmt.
-AST_MATCHER_P2(Stmt, hasNextTwoVarDecl,
- ast_matchers::internal::Matcher<VarDecl>, InnerMatcher1,
- ast_matchers::internal::Matcher<VarDecl>, InnerMatcher2) {
+/// followed by two VarDecls matching the inner matcher.
+AST_MATCHER_P(Stmt, hasNextTwoVarDecl,
+ llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>,
+ InnerMatchers) {
+
const DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
if (Parents.size() != 1)
return false;
@@ -137,33 +138,17 @@ AST_MATCHER_P2(Stmt, hasNextTwoVarDecl,
const auto *I = llvm::find(C->body(), &Node);
assert(I != C->body_end() && "C is parent of Node");
- if ((I + 1) == C->body_end())
- return false;
-
- if (matchTwoVarDecl(
- dyn_cast<DeclStmt>(*(I + 1)),
- ((I + 2) == C->body_end() ? nullptr : dyn_cast<DeclStmt>(*(I + 2))),
- InnerMatcher1, InnerMatcher2, Finder, Builder)) {
- Builder->setBinding(ScopeBlockName, clang::DynTypedNode::create(*C));
- return true;
- }
-
- return false;
+ return matchNVarDeclStartingWith(I + 1, C->body_end(), InnerMatchers, Finder,
+ Builder);
}
/// Matches a CompoundStmt which has two VarDecls matching the inner matcher in
/// the beginning.
-AST_MATCHER_P2(CompoundStmt, hasFirstTwoVarDecl,
- ast_matchers::internal::Matcher<VarDecl>, InnerMatcher1,
- ast_matchers::internal::Matcher<VarDecl>, InnerMatcher2) {
- const auto *I = Node.body_begin();
- if ((I) == Node.body_end())
- return false;
-
- return matchTwoVarDecl(
- dyn_cast<DeclStmt>(*(I)),
- ((I + 1) == Node.body_end() ? nullptr : dyn_cast<DeclStmt>(*(I + 1))),
- InnerMatcher1, InnerMatcher2, Finder, Builder);
+AST_MATCHER_P(CompoundStmt, hasFirstTwoVarDecl,
+ llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>,
+ InnerMatchers) {
+ return matchNVarDeclStartingWith(Node.body_begin(), Node.body_end(),
+ InnerMatchers, Finder, Builder);
}
/// It's not very common to have specifiers for variables used to decompose a
@@ -255,8 +240,10 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
hasRHS(expr(hasType(PairType))))
.bind(StdTieAssignStmtName)),
hasPreTwoVarDecl(
- varDecl(equalsBoundNode(std::string(FirstVarDeclName))),
- varDecl(equalsBoundNode(std::string(SecondVarDeclName))))),
+ llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
+ varDecl(equalsBoundNode(std::string(FirstVarDeclName))),
+ varDecl(equalsBoundNode(std::string(SecondVarDeclName)))}),
+ hasParent(compoundStmt().bind(ScopeBlockName))),
this);
// pair<X, Y> p = ...;
@@ -272,7 +259,10 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
.bind(PairVarTypeName)),
hasInitializer(expr()))
.bind(PairDeclName)),
- hasNextTwoVarDecl(VarInitWithFirstMember, VarInitWithSecondMember)),
+ hasNextTwoVarDecl(
+ llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
+ VarInitWithFirstMember, VarInitWithSecondMember}),
+ hasParent(compoundStmt().bind(ScopeBlockName))),
this);
// for (pair<X, Y> p : map) {
@@ -288,9 +278,12 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
.bind(PairVarTypeName)),
hasInitializer(expr()))
.bind(PairDeclName)),
- hasBody(compoundStmt(hasFirstTwoVarDecl(VarInitWithFirstMember,
- VarInitWithSecondMember))
- .bind(ScopeBlockName)))
+ hasBody(
+ compoundStmt(
+ hasFirstTwoVarDecl(llvm::SmallVector<
+ ast_matchers::internal::Matcher<VarDecl>>{
+ VarInitWithFirstMember, VarInitWithSecondMember}))
+ .bind(ScopeBlockName)))
.bind(ForRangeStmtName),
this);
}
@@ -320,8 +313,8 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
const auto *FirstVar = Result.Nodes.getNodeAs<VarDecl>(FirstVarDeclName);
const auto *SecondVar = Result.Nodes.getNodeAs<VarDecl>(SecondVarDeclName);
- const auto *DS1 = Result.Nodes.getNodeAs<DeclStmt>(FirstDeclStmtName);
- const auto *DS2 = Result.Nodes.getNodeAs<DeclStmt>(SecondDeclStmtName);
+ const auto *BeginDS = Result.Nodes.getNodeAs<DeclStmt>(BeginDeclStmtName);
+ const auto *EndDS = Result.Nodes.getNodeAs<DeclStmt>(EndDeclStmtName);
const auto *ScopeBlock = Result.Nodes.getNodeAs<CompoundStmt>(ScopeBlockName);
// Captured structured bindings are a C++20 extension
@@ -336,7 +329,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
}
const auto *CFRS = Result.Nodes.getNodeAs<CXXForRangeStmt>(ForRangeStmtName);
- auto DiagAndFix = [&DS1, &DS2, &FirstVar, &SecondVar, &CFRS,
+ auto DiagAndFix = [&BeginDS, &EndDS, &FirstVar, &SecondVar, &CFRS,
this](SourceLocation DiagLoc, SourceRange ReplaceRange,
TransferType TT = TT_ByVal) {
StringRef Prefix;
@@ -354,18 +347,15 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
Prefix = "const auto&";
break;
}
- std::vector<FixItHint> Hints;
- if (DS1)
- Hints.emplace_back(FixItHint::CreateRemoval(DS1->getSourceRange()));
- if (DS2)
- Hints.emplace_back(FixItHint::CreateRemoval(DS2->getSourceRange()));
std::string ReplacementText =
(Twine(Prefix) + " [" + FirstVar->getNameAsString() + ", " +
SecondVar->getNameAsString() + "]" + (CFRS ? " :" : ""))
.str();
diag(DiagLoc, "use structured binding to decompose a pair")
- << FixItHint::CreateReplacement(ReplaceRange, ReplacementText) << Hints;
+ << FixItHint::CreateReplacement(ReplaceRange, ReplacementText)
+ << FixItHint::CreateRemoval(
+ SourceRange{BeginDS->getBeginLoc(), EndDS->getEndLoc()});
};
if (const auto *COCE =
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index be3418a2ac224..48d48ec285575 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -18,8 +18,7 @@ void DecomposeByAssignWarnCases() {
auto P = getPair<int, int>();
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
- int x = P.first; // REMOVE
- // CHECK-FIXES-ALL: // REMOVE
+ int x = P.first;
int y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
}
@@ -45,8 +44,7 @@ void DecomposeByAssignWarnCases() {
auto P = getPair<int, int>();
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
- int x = P.first; // REMOVE
- // CHECK-FIXES-ALL: // REMOVE
+ int x = P.first;
auto y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
}
@@ -55,8 +53,7 @@ void DecomposeByAssignWarnCases() {
const auto P = getPair<int, int>();
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: const auto [x, y] = getPair<int, int>();
- const int x = P.first; // REMOVE
- // CHECK-FIXES-ALL: // REMOVE
+ const int x = P.first;
const auto y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
}
@@ -66,8 +63,7 @@ void DecomposeByAssignWarnCases() {
auto& P = otherP;
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto& [x, y] = otherP;
- int& x = P.first; // REMOVE
- // CHECK-FIXES-ALL: // REMOVE
+ int& x = P.first;
auto& y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
}
@@ -77,8 +73,7 @@ void DecomposeByAssignWarnCases() {
const auto& P = otherP;
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: const auto& [x, y] = otherP;
- const int& x = P.first; // REMOVE
- // CHECK-FIXES-ALL: // REMOVE
+ const int& x = P.first;
const auto& y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
}
@@ -89,8 +84,7 @@ void forRangeWarnCases() {
for (auto P : Pairs) {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
- int x = P.first; // REMOVE
- // CHECK-FIXES-ALL: // REMOVE
+ int x = P.first;
int y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
}
@@ -113,8 +107,7 @@ void forRangeWarnCases() {
for (const auto P : Pairs) {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (const auto [x, y] : Pairs) {
- const int x = P.first; // REMOVE
- // CHECK-FIXES-ALL: // REMOVE
+ const int x = P.first;
const int y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
}
@@ -122,8 +115,7 @@ void forRangeWarnCases() {
for (auto& P : Pairs) {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (auto& [x, y] : Pairs) {
- int& x = P.first; // REMOVE
- // CHECK-FIXES-ALL: // REMOVE
+ int& x = P.first;
int& y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
}
@@ -131,8 +123,7 @@ void forRangeWarnCases() {
for (const auto& P : Pairs) {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (const auto& [x, y] : Pairs) {
- const int& x = P.first; // REMOVE
- // CHECK-FIXES-ALL: // REMOVE
+ const int& x = P.first;
const int& y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
}
@@ -141,8 +132,7 @@ void forRangeWarnCases() {
for (auto P : ClassPairs) {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (auto [c1, c2] : ClassPairs) {
- TestClass c1 = P.first; // REMOVE
- // CHECK-FIXES-ALL: // REMOVE
+ TestClass c1 = P.first;
TestClass c2 = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
}
@@ -150,8 +140,7 @@ void forRangeWarnCases() {
for (const auto P : ClassPairs) {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (const auto [c1, c2] : ClassPairs) {
- const TestClass c1 = P.first; // REMOVE
- // CHECK-FIXES-ALL: // REMOVE
+ const TestClass c1 = P.first;
const TestClass c2 = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
}
@@ -193,8 +182,7 @@ void forRangeNotWarnCases() {
}
void stdTieWarnCases() {
- int a = 0; // REMOVE
- // CHECK-FIXES-ALL: // REMOVE
+ int a = 0;
int b = 0; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
std::tie(a, b) = getPair<int, int>();
@@ -207,16 +195,14 @@ void stdTieWarnCases() {
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
- int* pa = nullptr; // REMOVE
- // CHECK-FIXES-ALL: // REMOVE
+ int* pa = nullptr;
int* pb = nullptr; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
std::tie(pa, pb) = getPair<int*, int*>();
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [pa, pb] = getPair<int*, int*>();
- TestClass c1 (1, 2); // REMOVE
- // CHECK-FIXES-ALL: // REMOVE
+ TestClass c1 (1, 2);
TestClass c2 = TestClass {3, 4}; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
std::tie(c1, c2) = getPair<TestClass, TestClass>();
@@ -325,8 +311,7 @@ void captureByVal() {
auto P = getPair<int, int>();
// CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
- int x = P.first; // REMOVE
- // CHECK-FIXES-CPP20ORLATER: // REMOVE
+ int x = P.first;
int y = P.second; // REMOVE
// CHECK-FIXES-CPP20ORLATER: // REMOVE
@@ -339,8 +324,7 @@ void captureByRef() {
auto P = getPair<int, int>();
// CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
- int x = P.first; // REMOVE
- // CHECK-FIXES-CPP20ORLATER: // REMOVE
+ int x = P.first;
int y = P.second; // REMOVE
// CHECK-FIXES-CPP20ORLATER: // REMOVE
@@ -353,8 +337,7 @@ void captureByAllRef() {
auto P = getPair<int, int>();
// CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
- int x = P.first; // REMOVE
- // CHECK-FIXES-CPP20ORLATER: // REMOVE
+ int x = P.first;
int y = P.second; // REMOVE
// CHECK-FIXES-CPP20ORLATER: // REMOVE
@@ -367,8 +350,7 @@ void deepLambda() {
auto P = getPair<int, int>();
// CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
- int x = P.first; // REMOVE
- // CHECK-FIXES-CPP20ORLATER: // REMOVE
+ int x = P.first;
int y = P.second; // REMOVE
// CHECK-FIXES-CPP20ORLATER: // REMOVE
@@ -384,8 +366,7 @@ void forRangeNotWarn() {
for (auto P : Pairs) {
// CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: for (auto [x, y] : Pairs) {
- int x = P.first; // REMOVE
- // CHECK-FIXES-CPP20ORLATER: // REMOVE
+ int x = P.first;
int y = P.second; // REMOVE
// CHECK-FIXES-CPP20ORLATER: // REMOVE
@@ -396,8 +377,7 @@ void forRangeNotWarn() {
}
void stdTieNotWarn() {
- int x = 0; // REMOVE
- // CHECK-FIXES-CPP20ORLATER: // REMOVE
+ int x = 0;
int y = 0; // REMOVE
// CHECK-FIXES-CPP20ORLATER: // REMOVE
std::tie(x, y) = getPair<int, int>();
>From 2ebab5724bf2d1732a0785be692f82951f640c68 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 14:08:15 +0800
Subject: [PATCH 33/48] [NFC] Place lambda match operation to
`registerMatchers`
---
.../modernize/UseStructuredBindingCheck.cpp | 35 +++++++++++--------
1 file changed, 21 insertions(+), 14 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 6530fce2f6484..5796be4685110 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -223,6 +223,21 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
return declRefExpr(to(varDecl(UnlessShouldBeIgnored).bind(Name)));
};
+ auto HasAnyLambdaCaptureThisVar =
+ [](ast_matchers::internal::Matcher<VarDecl> VDMatcher)
+ -> ast_matchers::internal::BindableMatcher<Stmt> {
+ return compoundStmt(hasDescendant(
+ lambdaExpr(hasAnyCapture(capturesVar(varDecl(VDMatcher))))));
+ };
+
+ // Captured structured bindings are a C++20 extension
+ auto UnlessFirstVarOrSecondVarIsCapturedByLambda =
+ getLangOpts().CPlusPlus20
+ ? compoundStmt()
+ : compoundStmt(unless(HasAnyLambdaCaptureThisVar(
+ anyOf(equalsBoundNode(std::string(FirstVarDeclName)),
+ equalsBoundNode(std::string(SecondVarDeclName))))));
+
// X x;
// Y y;
// std::tie(x, y) = ...;
@@ -243,7 +258,8 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
varDecl(equalsBoundNode(std::string(FirstVarDeclName))),
varDecl(equalsBoundNode(std::string(SecondVarDeclName)))}),
- hasParent(compoundStmt().bind(ScopeBlockName))),
+ hasParent(compoundStmt(UnlessFirstVarOrSecondVarIsCapturedByLambda)
+ .bind(ScopeBlockName))),
this);
// pair<X, Y> p = ...;
@@ -262,7 +278,8 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
hasNextTwoVarDecl(
llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
VarInitWithFirstMember, VarInitWithSecondMember}),
- hasParent(compoundStmt().bind(ScopeBlockName))),
+ hasParent(compoundStmt(UnlessFirstVarOrSecondVarIsCapturedByLambda)
+ .bind(ScopeBlockName))),
this);
// for (pair<X, Y> p : map) {
@@ -282,7 +299,8 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
compoundStmt(
hasFirstTwoVarDecl(llvm::SmallVector<
ast_matchers::internal::Matcher<VarDecl>>{
- VarInitWithFirstMember, VarInitWithSecondMember}))
+ VarInitWithFirstMember, VarInitWithSecondMember}),
+ UnlessFirstVarOrSecondVarIsCapturedByLambda)
.bind(ScopeBlockName)))
.bind(ForRangeStmtName),
this);
@@ -317,17 +335,6 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
const auto *EndDS = Result.Nodes.getNodeAs<DeclStmt>(EndDeclStmtName);
const auto *ScopeBlock = Result.Nodes.getNodeAs<CompoundStmt>(ScopeBlockName);
- // Captured structured bindings are a C++20 extension
- if (!Result.Context->getLangOpts().CPlusPlus20) {
- if (auto Matchers = match(
- compoundStmt(
- hasDescendant(lambdaExpr(hasAnyCapture(capturesVar(varDecl(
- anyOf(equalsNode(FirstVar), equalsNode(SecondVar)))))))),
- *ScopeBlock, *Result.Context);
- !Matchers.empty())
- return;
- }
-
const auto *CFRS = Result.Nodes.getNodeAs<CXXForRangeStmt>(ForRangeStmtName);
auto DiagAndFix = [&BeginDS, &EndDS, &FirstVar, &SecondVar, &CFRS,
this](SourceLocation DiagLoc, SourceRange ReplaceRange,
>From 5bdf20655f83f91d02cb67d586490e9d2a31b28a Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 14:10:24 +0800
Subject: [PATCH 34/48] [NFC] Doc typo fix
---
.../clang-tidy/checks/modernize/use-structured-binding.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 0d02e6761cdd4..2526725c53ff9 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -66,8 +66,8 @@ except const and & since it's not very common:
static int b = pair.first;
static int c = pair.second;
-The check doesn't check for some situations which could possibly transfered
-to structured bnindings, for example:
+The check doesn't check for some situations which could possibly be transferred
+to structured bindings, for example:
.. code-block:: c++
>From edac2d43b7a2ff0442d1ad58134ba3c8edc5cbca Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 15:05:58 +0800
Subject: [PATCH 35/48] [NFC] Move init expression logic to `registerMatchers`
---
.../modernize/UseStructuredBindingCheck.cpp | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 5796be4685110..e92af08007db9 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -28,6 +28,7 @@ static constexpr llvm::StringLiteral ScopeBlockName = "ScopeBlock";
static constexpr llvm::StringLiteral StdTieAssignStmtName = "StdTieAssign";
static constexpr llvm::StringLiteral StdTieExprName = "StdTieExpr";
static constexpr llvm::StringLiteral ForRangeStmtName = "ForRangeStmt";
+static constexpr llvm::StringLiteral InitExprName = "init_expr";
/// Matches a sequence of VarDecls matching the inner matchers, starting from
/// the \p Iter to \p EndIter and set bindings for the first DeclStmt and the
@@ -273,7 +274,7 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
hasType(qualType(anyOf(PairType, lValueReferenceType(
pointee(PairType))))
.bind(PairVarTypeName)),
- hasInitializer(expr()))
+ hasInitializer(expr(ignoringCopyCtorAndImplicitCast(expr().bind(InitExprName)))))
.bind(PairDeclName)),
hasNextTwoVarDecl(
llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
@@ -293,7 +294,7 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
varDecl(hasType(qualType(anyOf(PairType, lValueReferenceType(
pointee(PairType))))
.bind(PairVarTypeName)),
- hasInitializer(expr()))
+ hasInitializer(expr(ignoringCopyCtorAndImplicitCast(expr().bind(InitExprName)))))
.bind(PairDeclName)),
hasBody(
compoundStmt(
@@ -375,12 +376,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
// Check whether PairVar, FirstVar and SecondVar have the same transfer type,
// so they can be combined to structured binding.
const auto *PairVar = Result.Nodes.getNodeAs<VarDecl>(PairDeclName);
- const Expr *InitE = PairVar->getInit();
- if (auto Res =
- match(expr(ignoringCopyCtorAndImplicitCast(expr().bind("init_expr"))),
- *InitE, *Result.Context);
- !Res.empty())
- InitE = Res[0].getNodeAs<Expr>("init_expr");
+ const Expr *InitE = Result.Nodes.getNodeAs<Expr>(InitExprName);
const std::optional<TransferType> PairCaptureType =
getTransferType(*Result.Context, PairVar->getType(), InitE->getType());
>From 0b007e85679a06a4156f8597f64fe5bc0dd6834f Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 15:09:01 +0800
Subject: [PATCH 36/48] [NFC] Rename warning message to `use a structured
binding...`
---
.../modernize/UseStructuredBindingCheck.cpp | 2 +-
.../use-structured-binding-custom.cpp | 4 +-
.../modernize/use-structured-binding.cpp | 50 +++++++++----------
3 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index e92af08007db9..07a622d2938be 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -360,7 +360,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
(Twine(Prefix) + " [" + FirstVar->getNameAsString() + ", " +
SecondVar->getNameAsString() + "]" + (CFRS ? " :" : ""))
.str();
- diag(DiagLoc, "use structured binding to decompose a pair")
+ diag(DiagLoc, "use a structured binding to decompose a pair")
<< FixItHint::CreateReplacement(ReplaceRange, ReplacementText)
<< FixItHint::CreateRemoval(
SourceRange{BeginDS->getBeginLoc(), EndDS->getEndLoc()});
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
index bd335e1edaf6e..d8f9bdc924d94 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
@@ -16,7 +16,7 @@ struct otherPair {
void OptionTest() {
{
auto P = custom::pair();
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: auto [x, y] = custom::pair();
int x = P.first;
int y = P.second;
@@ -24,7 +24,7 @@ void OptionTest() {
{
auto P = otherPair();
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES: auto [x, y] = otherPair();
int x = P.first;
int y = P.second;
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 48d48ec285575..8a511f954e97e 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -16,7 +16,7 @@ struct TestClass {
void DecomposeByAssignWarnCases() {
{
auto P = getPair<int, int>();
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
int x = P.first;
int y = P.second; // REMOVE
@@ -25,7 +25,7 @@ void DecomposeByAssignWarnCases() {
{
auto P = getPair<int, int>();
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
int x = P.first, y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
@@ -33,7 +33,7 @@ void DecomposeByAssignWarnCases() {
{
auto P = getPair<int, int>();
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
int x = P.first, y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
@@ -42,7 +42,7 @@ void DecomposeByAssignWarnCases() {
{
auto P = getPair<int, int>();
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
int x = P.first;
auto y = P.second; // REMOVE
@@ -51,7 +51,7 @@ void DecomposeByAssignWarnCases() {
{
const auto P = getPair<int, int>();
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: const auto [x, y] = getPair<int, int>();
const int x = P.first;
const auto y = P.second; // REMOVE
@@ -61,7 +61,7 @@ void DecomposeByAssignWarnCases() {
{
std::pair<int, int> otherP;
auto& P = otherP;
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto& [x, y] = otherP;
int& x = P.first;
auto& y = P.second; // REMOVE
@@ -71,7 +71,7 @@ void DecomposeByAssignWarnCases() {
{
std::pair<int, int> otherP;
const auto& P = otherP;
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: const auto& [x, y] = otherP;
const int& x = P.first;
const auto& y = P.second; // REMOVE
@@ -82,7 +82,7 @@ void DecomposeByAssignWarnCases() {
void forRangeWarnCases() {
std::pair<int, int> Pairs[10];
for (auto P : Pairs) {
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
int x = P.first;
int y = P.second; // REMOVE
@@ -90,14 +90,14 @@ void forRangeWarnCases() {
}
for (auto P : Pairs) {
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
int x = P.first, y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
}
for (auto P : Pairs) {
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
int x = P.first, y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
@@ -105,7 +105,7 @@ void forRangeWarnCases() {
}
for (const auto P : Pairs) {
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (const auto [x, y] : Pairs) {
const int x = P.first;
const int y = P.second; // REMOVE
@@ -113,7 +113,7 @@ void forRangeWarnCases() {
}
for (auto& P : Pairs) {
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (auto& [x, y] : Pairs) {
int& x = P.first;
int& y = P.second; // REMOVE
@@ -121,7 +121,7 @@ void forRangeWarnCases() {
}
for (const auto& P : Pairs) {
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (const auto& [x, y] : Pairs) {
const int& x = P.first;
const int& y = P.second; // REMOVE
@@ -130,7 +130,7 @@ void forRangeWarnCases() {
std::pair<TestClass, TestClass> ClassPairs[10];
for (auto P : ClassPairs) {
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (auto [c1, c2] : ClassPairs) {
TestClass c1 = P.first;
TestClass c2 = P.second; // REMOVE
@@ -138,7 +138,7 @@ void forRangeWarnCases() {
}
for (const auto P : ClassPairs) {
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: for (const auto [c1, c2] : ClassPairs) {
const TestClass c1 = P.first;
const TestClass c2 = P.second; // REMOVE
@@ -186,27 +186,27 @@ void stdTieWarnCases() {
int b = 0; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
std::tie(a, b) = getPair<int, int>();
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [a, b] = getPair<int, int>();
int x = 0, y = 0; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
std::tie(x, y) = getPair<int, int>();
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
int* pa = nullptr;
int* pb = nullptr; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
std::tie(pa, pb) = getPair<int*, int*>();
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [pa, pb] = getPair<int*, int*>();
TestClass c1 (1, 2);
TestClass c2 = TestClass {3, 4}; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
std::tie(c1, c2) = getPair<TestClass, TestClass>();
- // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [c1, c2] = getPair<TestClass, TestClass>();
}
@@ -309,7 +309,7 @@ void NotWarnForMacro2() {
void captureByVal() {
auto P = getPair<int, int>();
- // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
int x = P.first;
int y = P.second; // REMOVE
@@ -322,7 +322,7 @@ void captureByVal() {
void captureByRef() {
auto P = getPair<int, int>();
- // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
int x = P.first;
int y = P.second; // REMOVE
@@ -335,7 +335,7 @@ void captureByRef() {
void captureByAllRef() {
auto P = getPair<int, int>();
- // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
int x = P.first;
int y = P.second; // REMOVE
@@ -348,7 +348,7 @@ void captureByAllRef() {
void deepLambda() {
auto P = getPair<int, int>();
- // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
int x = P.first;
int y = P.second; // REMOVE
@@ -364,7 +364,7 @@ void deepLambda() {
void forRangeNotWarn() {
std::pair<int, int> Pairs[10];
for (auto P : Pairs) {
- // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: for (auto [x, y] : Pairs) {
int x = P.first;
int y = P.second; // REMOVE
@@ -381,7 +381,7 @@ void stdTieNotWarn() {
int y = 0; // REMOVE
// CHECK-FIXES-CPP20ORLATER: // REMOVE
std::tie(x, y) = getPair<int, int>();
- // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
auto lambda = [&x]() {
>From 5a3799e9bad64e9bdf5ad3f291d47a59384ea968 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 18:30:44 +0800
Subject: [PATCH 37/48] [NFC] cleanup
---
.../modernize/UseStructuredBindingCheck.cpp | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 07a622d2938be..cbd0cf4e2c362 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -55,6 +55,7 @@ static bool matchNVarDeclStartingWith(
BeginDS = EndDS;
auto Matches = [&](const Decl *VD) {
+ // We don't want redundant decls in DeclStmt.
if (Count == N)
return false;
@@ -113,7 +114,7 @@ AST_MATCHER_P(Stmt, hasPreTwoVarDecl,
if (Parents.size() != 1)
return false;
- auto *C = Parents[0].get<CompoundStmt>();
+ const auto *C = Parents[0].get<CompoundStmt>();
if (!C)
return false;
@@ -128,12 +129,11 @@ AST_MATCHER_P(Stmt, hasPreTwoVarDecl,
AST_MATCHER_P(Stmt, hasNextTwoVarDecl,
llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>,
InnerMatchers) {
-
const DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
if (Parents.size() != 1)
return false;
- auto *C = Parents[0].get<CompoundStmt>();
+ const auto *C = Parents[0].get<CompoundStmt>();
if (!C)
return false;
@@ -274,7 +274,8 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
hasType(qualType(anyOf(PairType, lValueReferenceType(
pointee(PairType))))
.bind(PairVarTypeName)),
- hasInitializer(expr(ignoringCopyCtorAndImplicitCast(expr().bind(InitExprName)))))
+ hasInitializer(expr(ignoringCopyCtorAndImplicitCast(
+ expr().bind(InitExprName)))))
.bind(PairDeclName)),
hasNextTwoVarDecl(
llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
@@ -294,7 +295,8 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
varDecl(hasType(qualType(anyOf(PairType, lValueReferenceType(
pointee(PairType))))
.bind(PairVarTypeName)),
- hasInitializer(expr(ignoringCopyCtorAndImplicitCast(expr().bind(InitExprName)))))
+ hasInitializer(expr(ignoringCopyCtorAndImplicitCast(
+ expr().bind(InitExprName)))))
.bind(PairDeclName)),
hasBody(
compoundStmt(
@@ -376,10 +378,10 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
// Check whether PairVar, FirstVar and SecondVar have the same transfer type,
// so they can be combined to structured binding.
const auto *PairVar = Result.Nodes.getNodeAs<VarDecl>(PairDeclName);
- const Expr *InitE = Result.Nodes.getNodeAs<Expr>(InitExprName);
const std::optional<TransferType> PairCaptureType =
- getTransferType(*Result.Context, PairVar->getType(), InitE->getType());
+ getTransferType(*Result.Context, PairVar->getType(),
+ Result.Nodes.getNodeAs<Expr>(InitExprName)->getType());
const std::optional<TransferType> FirstVarCaptureType =
getTransferType(*Result.Context, FirstVar->getType(),
*Result.Nodes.getNodeAs<QualType>(FirstTypeName));
>From c10eeeada8026e9363dc7b8504f1cad0dcb7cbd4 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 19:12:23 +0800
Subject: [PATCH 38/48] Use duck-typing to get pair type
---
.../modernize/UseStructuredBindingCheck.cpp | 24 +++++------
.../modernize/UseStructuredBindingCheck.h | 6 +--
.../modernize/use-structured-binding.rst | 9 ----
.../use-structured-binding-custom.cpp | 32 --------------
.../modernize/use-structured-binding.cpp | 43 +++++++++++++++++++
5 files changed, 55 insertions(+), 59 deletions(-)
delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index cbd0cf4e2c362..3a36ec7aa79fd 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -8,14 +8,12 @@
#include "UseStructuredBindingCheck.h"
#include "../utils/DeclRefExprUtils.h"
-#include "../utils/OptionsUtils.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang::tidy::modernize {
-static constexpr const char *DefaultPairTypes = "std::pair";
static constexpr llvm::StringLiteral PairDeclName = "PairVarD";
static constexpr llvm::StringLiteral PairVarTypeName = "PairVarType";
static constexpr llvm::StringLiteral FirstVarDeclName = "FirstVarDecl";
@@ -178,16 +176,15 @@ AST_MATCHER_P(Expr, ignoringCopyCtorAndImplicitCast,
return InnerMatcher.matches(*Node.IgnoreImpCasts(), Finder, Builder);
}
-} // namespace
-
-UseStructuredBindingCheck::UseStructuredBindingCheck(StringRef Name,
- ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context),
- PairTypes(utils::options::parseStringList(
- Options.get("PairTypes", DefaultPairTypes))) {
- ;
+AST_MATCHER(CXXRecordDecl, isPairType) {
+ return llvm::all_of(Node.fields(), [](const FieldDecl *FD) {
+ return FD->getAccess() == AS_public &&
+ (FD->getName() == "first" || FD->getName() == "second");
+ });
}
+} // namespace
+
static auto getVarInitWithMemberMatcher(
StringRef PairName, StringRef MemberName, StringRef TypeName,
StringRef BindingName,
@@ -204,10 +201,9 @@ static auto getVarInitWithMemberMatcher(
}
void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
- auto PairType =
- qualType(unless(isVolatileQualified()),
- hasUnqualifiedDesugaredType(recordType(
- hasDeclaration(cxxRecordDecl(hasAnyName(PairTypes))))));
+ auto PairType = qualType(unless(isVolatileQualified()),
+ hasUnqualifiedDesugaredType(recordType(
+ hasDeclaration(cxxRecordDecl(isPairType())))));
auto UnlessShouldBeIgnored =
unless(anyOf(hasAnySpecifiersShouldBeIgnored(), isInMarco()));
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
index 83d262a5db3cd..f8a4452890ecf 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
@@ -20,15 +20,13 @@ namespace clang::tidy::modernize {
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-structured-binding.html
class UseStructuredBindingCheck : public ClangTidyCheck {
public:
- UseStructuredBindingCheck(StringRef Name, ClangTidyContext *Context);
+ UseStructuredBindingCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus17;
}
-
-private:
- const std::vector<StringRef> PairTypes;
};
} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 2526725c53ff9..8222b67fbe65a 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -84,12 +84,3 @@ and:
if (results.second) {
handle_inserted(results.first);
}
-
-Options
--------
-
-.. option:: PairTypes
-
- A semicolon-separated list of type names to be treated as pair-like for
- structured binding suggestions. Example: `MyPairType;OtherPairType`.
- Default is `std::pair`.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
deleted file mode 100644
index d8f9bdc924d94..0000000000000
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-structured-binding %t \
-// RUN: -config="{CheckOptions: {modernize-use-structured-binding.PairTypes: 'custom::pair; otherPair'}}"
-
-namespace custom {
- struct pair {
- int first;
- int second;
- };
-}
-
-struct otherPair {
- int first;
- int second;
-};
-
-void OptionTest() {
- {
- auto P = custom::pair();
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: auto [x, y] = custom::pair();
- int x = P.first;
- int y = P.second;
- }
-
- {
- auto P = otherPair();
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES: auto [x, y] = otherPair();
- int x = P.first;
- int y = P.second;
- }
-}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 8a511f954e97e..be9cae8980c75 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -388,3 +388,46 @@ void stdTieNotWarn() {
x = 1;
};
}
+
+struct otherPair {
+ int first;
+ int second;
+};
+
+void OtherPairTest() {
+ {
+ auto P = otherPair();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto [x, y] = otherPair();
+ int x = P.first;
+ int y = P.second;
+ }
+}
+
+struct otherNonPair1 {
+ int first;
+ int second;
+
+private:
+ int third;
+};
+
+struct otherNonPair2 {
+ int first;
+ int second;
+ int third;
+};
+
+void OtherNonPairTest() {
+ {
+ auto P = otherNonPair1();
+ int x = P.first;
+ int y = P.second;
+ }
+
+ {
+ auto P = otherNonPair2();
+ int x = P.first;
+ int y = P.second;
+ }
+}
>From 48b7134dd5ed28c8928d71bc5da7699045084d5a Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 19:18:00 +0800
Subject: [PATCH 39/48] [NFC] Fix doc
---
.../clang-tidy/checks/modernize/use-structured-binding.rst | 3 ---
1 file changed, 3 deletions(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 8222b67fbe65a..f68041e266231 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -51,9 +51,6 @@ in a range-based for loop:
// use x and y
}
-The check also supports custom pair-like types via the :option:`PairTypes`
-option.
-
Limitations
-----------
>From 06079985e59470c50335b0ccadbf8801901baf6b Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 20:44:30 +0800
Subject: [PATCH 40/48] [NFC] Should not use auto here
---
.../clang-tidy/modernize/UseStructuredBindingCheck.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 3a36ec7aa79fd..11e198b60ffb5 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -166,7 +166,7 @@ AST_POLYMORPHIC_MATCHER(isInMarco,
AST_MATCHER_P(Expr, ignoringCopyCtorAndImplicitCast,
ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
if (const auto *CtorE = dyn_cast<CXXConstructExpr>(&Node)) {
- if (const auto *CtorD = CtorE->getConstructor();
+ if (const CXXConstructorDecl *CtorD = CtorE->getConstructor();
CtorD->isCopyConstructor() && CtorE->getNumArgs() == 1) {
return InnerMatcher.matches(*CtorE->getArg(0)->IgnoreImpCasts(), Finder,
Builder);
>From 7b7ba725314ac7560f129e21ea61c29492290956 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 20:51:32 +0800
Subject: [PATCH 41/48] [NFC] Add more tests for custom pair type
---
.../modernize/use-structured-binding.cpp | 55 +++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index be9cae8980c75..b398e035e35c3 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -402,6 +402,61 @@ void OtherPairTest() {
int x = P.first;
int y = P.second;
}
+
+ {
+ const auto P = otherPair();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: const auto [x, y] = otherPair();
+ const int x = P.first;
+ const auto y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ }
+
+ {
+ otherPair otherP;
+ auto& P = otherP;
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto& [x, y] = otherP;
+ int& x = P.first;
+ auto& y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ }
+
+ {
+ std::pair<int, int> otherP;
+ const auto& P = otherP;
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: const auto& [x, y] = otherP;
+ const int& x = P.first;
+ const auto& y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ }
+}
+
+void OtherPairNotWarnCases() {
+ {
+ auto P = otherPair();
+ const int x = P.first;
+ int y = P.second;
+ }
+
+ {
+ auto P = otherPair();
+ volatile int x = P.first;
+ int y = P.second;
+ }
+
+ {
+ auto P = otherPair();
+ int x = P.first;
+ [[maybe_unused]] int y = P.second;
+ }
+
+ {
+ static auto P = getPair<int, int>();
+ int x = P.first;
+ int y = P.second;
+ }
}
struct otherNonPair1 {
>From 52bc67c81e4847cdf68558f73d9169b4967e3d7a Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 20:59:33 +0800
Subject: [PATCH 42/48] [NFC] Place lambda out of loop
---
.../modernize/UseStructuredBindingCheck.cpp | 31 ++++++++++---------
1 file changed, 16 insertions(+), 15 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 11e198b60ffb5..501939b4c5607 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -44,6 +44,22 @@ static bool matchNVarDeclStartingWith(
const DeclStmt *EndDS = nullptr;
size_t N = InnerMatchers.size();
size_t Count = 0;
+
+ auto Matches = [&](const Decl *VD) {
+ // We don't want redundant decls in DeclStmt.
+ if (Count == N)
+ return false;
+
+ if (const auto *Var = dyn_cast<VarDecl>(VD);
+ Var && InnerMatchers[Backwards ? N - Count - 1 : Count].matches(
+ *Var, Finder, Builder)) {
+ ++Count;
+ return true;
+ }
+
+ return false;
+ };
+
for (; Iter != EndIter; ++Iter) {
EndDS = dyn_cast<DeclStmt>(*Iter);
if (!EndDS)
@@ -52,21 +68,6 @@ static bool matchNVarDeclStartingWith(
if (!BeginDS)
BeginDS = EndDS;
- auto Matches = [&](const Decl *VD) {
- // We don't want redundant decls in DeclStmt.
- if (Count == N)
- return false;
-
- if (const auto *Var = dyn_cast<VarDecl>(VD);
- Var && InnerMatchers[Backwards ? N - Count - 1 : Count].matches(
- *Var, Finder, Builder)) {
- ++Count;
- return true;
- }
-
- return false;
- };
-
if (Backwards) {
for (const auto *VD : llvm::reverse(EndDS->decls())) {
if (!Matches(VD))
>From d091ff4557f5625f1398fe94c665c2fd0ef4b71e Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 21:06:10 +0800
Subject: [PATCH 43/48] [NFC] Iterator's name from `I` to `It`
---
.../modernize/UseStructuredBindingCheck.cpp | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 501939b4c5607..16e2922d25d50 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -117,10 +117,10 @@ AST_MATCHER_P(Stmt, hasPreTwoVarDecl,
if (!C)
return false;
- const auto I = llvm::find(llvm::reverse(C->body()), &Node);
- assert(I != C->body_rend() && "C is parent of Node");
- return matchNVarDeclStartingWith(I + 1, C->body_rend(), InnerMatchers, Finder,
- Builder, true);
+ const auto It = llvm::find(llvm::reverse(C->body()), &Node);
+ assert(It != C->body_rend() && "C is parent of Node");
+ return matchNVarDeclStartingWith(It + 1, C->body_rend(), InnerMatchers,
+ Finder, Builder, true);
}
/// Matches a Stmt whose parent is a CompoundStmt, and which is directly
@@ -136,9 +136,9 @@ AST_MATCHER_P(Stmt, hasNextTwoVarDecl,
if (!C)
return false;
- const auto *I = llvm::find(C->body(), &Node);
- assert(I != C->body_end() && "C is parent of Node");
- return matchNVarDeclStartingWith(I + 1, C->body_end(), InnerMatchers, Finder,
+ const auto *It = llvm::find(C->body(), &Node);
+ assert(It != C->body_end() && "C is parent of Node");
+ return matchNVarDeclStartingWith(It + 1, C->body_end(), InnerMatchers, Finder,
Builder);
}
>From 6d44ea0706b9d69f1d09d9adb10d74deeaaf4108 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 22:35:37 +0800
Subject: [PATCH 44/48] [NFC] Add test for multi checked cases in one scope
---
.../modernize/use-structured-binding.cpp | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index b398e035e35c3..1a359c23a016f 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -77,6 +77,23 @@ void DecomposeByAssignWarnCases() {
const auto& y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
}
+
+ {
+ auto P = getPair<int, int>(); // match
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>(); // match
+ int x = P.first;
+ int y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+
+ // maybe match with `hasParent` but NOT with `has(declStmt())`
+ auto another_p = getPair<int, int>();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto [another_x, another_y] = getPair<int, int>();
+ int another_x = another_p.first;
+ int another_y = another_p.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ }
}
void forRangeWarnCases() {
>From 82dfe89a142aed019a57370a9096c45cb21b023c Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Mon, 22 Sep 2025 21:31:21 +0800
Subject: [PATCH 45/48] [NFC] Add testcases for pairs has qualifiers in field
type.
---
.../modernize/use-structured-binding.cpp | 98 +++++++++++++++++++
1 file changed, 98 insertions(+)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 1a359c23a016f..7ccb51a16d9f7 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -503,3 +503,101 @@ void OtherNonPairTest() {
int y = P.second;
}
}
+
+template<typename PairType>
+PairType getCertainPair();
+
+struct ConstFieldPair {
+ const int first;
+ int second;
+};
+
+void ConstFieldPairTests() {
+ {
+ const ConstFieldPair P = getCertainPair<ConstFieldPair>();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: const auto [x, y] = getCertainPair<ConstFieldPair>();
+ const int x = P.first;
+ const int y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ }
+
+ {
+ const ConstFieldPair& P = getCertainPair<ConstFieldPair>();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: const auto& [x, y] = getCertainPair<ConstFieldPair>();
+ const int& x = P.first;
+ const int& y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ }
+
+ {
+ ConstFieldPair P = getCertainPair<ConstFieldPair>(); // no warning
+ int x = P.first;
+ int y = P.second;
+ }
+}
+
+struct PointerFieldPair {
+ int* first;
+ int second;
+};
+
+void PointerFieldPairTests() {
+ {
+ PointerFieldPair P = getCertainPair<PointerFieldPair>();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto [x, y] = getCertainPair<PointerFieldPair>();
+ int* x = P.first;
+ int y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ }
+
+ {
+ PointerFieldPair P = getCertainPair<PointerFieldPair>(); // no warning
+ const int* x = P.first;
+ int y = P.second;
+ }
+}
+
+struct ConstRefFieldPair {
+ const int& first;
+ int second;
+ ConstRefFieldPair(int& f, int s) : first(f), second(s) {}
+};
+
+void ConstRefFieldPairTests() {
+ {
+ ConstRefFieldPair P = getCertainPair<ConstRefFieldPair>();
+ // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+ // CHECK-FIXES-ALL: auto [x, y] = getCertainPair<ConstRefFieldPair>();
+ const int& x = P.first;
+ int y = P.second; // REMOVE
+ // CHECK-FIXES-ALL: // REMOVE
+ }
+
+ {
+ ConstRefFieldPair P = getCertainPair<ConstRefFieldPair>();; // no warning
+ int x = P.first;
+ int y = P.second;
+ }
+}
+
+struct StaticFieldPair {
+ static int first;
+ int second;
+};
+
+void StaticFieldPairTests() {
+ {
+ StaticFieldPair P; // Should not warn
+ int x = P.first;
+ int y = P.second;
+ }
+
+ {
+ StaticFieldPair P; // Should not warn
+ static int x = P.first;
+ int y = P.second;
+ }
+}
>From 653233fc4d39ada38618a2bd711b743ceec2412f Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Mon, 22 Sep 2025 21:44:52 +0800
Subject: [PATCH 46/48] [NFC] Remove unnecessary comments in testcase
---
.../clang-tidy/checkers/modernize/use-structured-binding.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 7ccb51a16d9f7..86c99b36f5c79 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -79,14 +79,13 @@ void DecomposeByAssignWarnCases() {
}
{
- auto P = getPair<int, int>(); // match
+ auto P = getPair<int, int>();
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
- // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>(); // match
+ // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
int x = P.first;
int y = P.second; // REMOVE
// CHECK-FIXES-ALL: // REMOVE
- // maybe match with `hasParent` but NOT with `has(declStmt())`
auto another_p = getPair<int, int>();
// CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
// CHECK-FIXES-ALL: auto [another_x, another_y] = getPair<int, int>();
>From 09f7bcdd89a6cb690650e074431acf07be15328b Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 23 Sep 2025 21:18:16 +0800
Subject: [PATCH 47/48] Update
clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
Co-authored-by: Victor Chernyakin <chernyakin.victor.j at outlook.com>
---
.../clang-tidy/modernize/UseStructuredBindingCheck.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
index f8a4452890ecf..0b11ce38c44db 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
@@ -17,7 +17,7 @@ namespace clang::tidy::modernize {
/// suggests replacing them.
///
/// For the user-facing documentation see:
-/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-structured-binding.html
+/// https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-structured-binding.html
class UseStructuredBindingCheck : public ClangTidyCheck {
public:
UseStructuredBindingCheck(StringRef Name, ClangTidyContext *Context)
>From 230df4fddf210d9f3d68c5f0627e997fcee42cec Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 23 Sep 2025 21:22:53 +0800
Subject: [PATCH 48/48] [NFC] IIFE
---
.../modernize/UseStructuredBindingCheck.cpp | 27 +++++++++----------
1 file changed, 12 insertions(+), 15 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 16e2922d25d50..3919fddd01061 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -339,21 +339,18 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
auto DiagAndFix = [&BeginDS, &EndDS, &FirstVar, &SecondVar, &CFRS,
this](SourceLocation DiagLoc, SourceRange ReplaceRange,
TransferType TT = TT_ByVal) {
- StringRef Prefix;
- switch (TT) {
- case TT_ByVal:
- Prefix = "auto";
- break;
- case TT_ByConstVal:
- Prefix = "const auto";
- break;
- case TT_ByRef:
- Prefix = "auto&";
- break;
- case TT_ByConstRef:
- Prefix = "const auto&";
- break;
- }
+ const auto Prefix = [&TT]() -> StringRef {
+ switch (TT) {
+ case TT_ByVal:
+ return "auto";
+ case TT_ByConstVal:
+ return "const auto";
+ case TT_ByRef:
+ return "auto&";
+ case TT_ByConstRef:
+ return "const auto&";
+ }
+ }();
std::string ReplacementText =
(Twine(Prefix) + " [" + FirstVar->getNameAsString() + ", " +
More information about the cfe-commits
mailing list