[clang-tools-extra] [clang-tidy] Add new check `modernize-use-structured-binding` (PR #158462)

via cfe-commits cfe-commits at lists.llvm.org
Sat Jan 31 06:04:23 PST 2026


https://github.com/flovent updated https://github.com/llvm/llvm-project/pull/158462

>From f13fbc9c42ed26e266d32b6f53390961624c87b1 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/68] [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 488c359661018..67487c12f8a90 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -50,6 +50,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 7224b2f32fd73..7ba3dbe18e6c8 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -51,6 +51,7 @@
 #include "UseStdFormatCheck.h"
 #include "UseStdNumbersCheck.h"
 #include "UseStdPrintCheck.h"
+#include "UseStructuredBindingCheck.h"
 #include "UseTrailingReturnTypeCheck.h"
 #include "UseTransparentFunctorsCheck.h"
 #include "UseUncaughtExceptionsCheck.h"
@@ -131,6 +132,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 8229810cbb429..cac5580de873d 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -261,6 +261,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 :doc:`readability-redundant-parentheses
   <clang-tidy/checks/readability/redundant-parentheses>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index e5e77b5cc418b..6e0f73f6e07cc 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -330,6 +330,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 b82ea00a3da027baedd5929b2c8b63228da58d27 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/68] 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 d64c67e695dbb4c3a943b1b16b055161309e3855 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/68] 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 622fd9ee61b4be696a96c4f0d56ac354e7f11226 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/68] 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 e36b90e6d26d1e77561870aee493625b5166e41f 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/68] 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 1e2b1cc1b223c33545e4889c796ca76052e1af79 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/68] [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 4e60bd54a62db5e2aa0cd779897d152f3bc828a4 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/68] [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 5275afa80b55cdacfd4c940645839008d0789bf1 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/68] [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 64c958add7b0905a9edddbf8f889d019380e6a82 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/68] [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 f482ce7a59ecaea995fff8d67d6dd851bf1193bf 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/68] [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 2606c5668bd0f2da48543036c086fd1c5f094b82 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/68] [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 2778fcbc97060a5f7f390e145578a9e150782f8f 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/68] [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 c3888621af55196be90250d2db9f4eaaefc0adb9 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/68] [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 93f575b6256aa4bb9f2566e437af7e411fd86c73 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/68] [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 62e53c03c202a6dfa84067e5573adffb2ebe4601 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/68] 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 21c0b43f10ef1d098a281fd7123345f52602505b 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/68] 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 8b8c5ad73e37e660812615d3415f0143f2eb04a1 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/68] 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 e9f1f52167b0f31ded8b1357721e35e05af853e6 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/68] 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 23116fb558f14469c6e05e24a2e61bba59f554f6 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/68] 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 d44c88373679e0057baf58e8bf362272332e60a5 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/68] 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 e6514a3ac5b3d68695619ea7d53e5d7647018263 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/68] [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 ccf0aee8428def95575c0fb6d79aa75d5038e766 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/68] 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 d09111d31fbea385cbca76c357c037d992991d30 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/68] [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 2a20d395591f6264a5b731bd304fc7f052d4c002 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/68] 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 7fb84f7ceece84e6aea4a562ae5dd01a1441e30e 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/68] [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 24aea715ad82bb147c68a586fb32c3e71cf090b0 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/68] [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 696aed445e94d954c0b26b81da1a3a8ee8b76149 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/68] [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 c70f9c7951f3ce33302737541b23ed29d97fa200 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/68] [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 f148d4a858307df7e96b4ce7d10f718c470db42c 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/68] 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 4dd059425e7d098e9a6de3936b73b46e65fbda36 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/68] 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 e5eeb1b5e98376b5605684fcf0a3e4a716a4d66e 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/68] [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 52ea5e436b45249963491edfb7539c1536aeeb1d 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/68] 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 803aa1d3e36e0fbea56f4ee68f742fbbd1e6b2c0 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/68] [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 0933058be240c22ed49fc12327d2982a34f94056 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/68] [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 c120178a4668a1a5c59a2e816e5b6a5c9bb743a2 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/68] [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 93dede1b316822b3f8a2ca9b5f0937cae587ea9f 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/68] [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 29a22c42dc57493de7f5272529b2eb38cc36c0dd 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/68] [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 fd37f13e67ca2691dfc35c14ca1cd1ade1a7bfb1 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/68] 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 647c0de4d87f16094e29c4439253acd280f0b7de 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/68] [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 bfabf0dae3bc029bfdcf2b9e127cf1bb6b98a6de 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/68] [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 9f04b0e189c041d905ef3df1f239930c0771c838 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/68] [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 53c3dba17da2e0da83c43518f5249e5b1f0f37eb 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/68] [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 6032ebe67a3fac070719f0144713eb98a502301f 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/68] [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 3f6f1b372c330bba8999059708f2890b2f2f0ff4 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/68] [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 98465b086277ea42a63732e64b3e5613caeb8748 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/68] [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 71db57d1f46d737de4992ac9bf60a4d2f1acdd03 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/68] [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 2594e878c824e4b286502d632899e262f75353ab 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/68] 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 60b0253d6e438cd7f5ef489c7386da813d88c653 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/68] [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() + ", " +

>From 2e7f2317493536f30044b816264b84fd3aebe20c Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Mon, 29 Sep 2025 22:23:03 +0800
Subject: [PATCH 49/68] Ignore direct init VarDecl and add some tests

---
 .../modernize/UseStructuredBindingCheck.cpp   |  6 +++++-
 .../fake_std_pair_tuple.h                     |  3 +++
 .../modernize/use-structured-binding.cpp      | 20 +++++++++++++++++++
 3 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 3919fddd01061..78a6595e1bf5a 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -184,6 +184,10 @@ AST_MATCHER(CXXRecordDecl, isPairType) {
   });
 }
 
+AST_MATCHER(VarDecl, isDirectInitialization) {
+  return Node.getInitStyle() != VarDecl::InitializationStyle::CInit;
+}
+
 } // namespace
 
 static auto getVarInitWithMemberMatcher(
@@ -267,7 +271,7 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
       declStmt(
           unless(isInMarco()),
           hasSingleDecl(
-              varDecl(UnlessShouldBeIgnored,
+              varDecl(UnlessShouldBeIgnored, unless(isDirectInitialization()),
                       hasType(qualType(anyOf(PairType, lValueReferenceType(
                                                            pointee(PairType))))
                                   .bind(PairVarTypeName)),
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
index b77341c852edb..b98696f14e8d8 100644
--- 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
@@ -3,6 +3,9 @@ namespace std {
   struct pair {
     T1 first;
     T2 second;
+
+    pair() = default;
+    pair(T1 first, T2 second) : first(first), second(second) {}
   };
 
   template<typename... Args>
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 86c99b36f5c79..7f2b9f94f1b1e 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
@@ -600,3 +600,23 @@ void StaticFieldPairTests() {
     int y = P.second;
   }
 }
+
+void IgnoreDirectInit() {
+  {
+    std::pair<int, int> P{1, 1};
+    int x = P.first;
+    int y = P.second; 
+  }
+
+  {
+    std::pair<int, int> P(1, 1);
+    int x = P.first;
+    int y = P.second;
+  }
+
+  {
+    std::pair<int, int> P;
+    int x = P.first;
+    int y = P.second;
+  }
+}

>From 0a2010fec0965c6b86807d7e5ca4a961ba963f25 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Fri, 3 Oct 2025 20:26:43 +0800
Subject: [PATCH 50/68] [NFC] simplify testcase: `All` is not needed

---
 .../modernize/use-structured-binding.cpp      | 176 +++++++++---------
 1 file changed, 88 insertions(+), 88 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 7f2b9f94f1b1e..64de51c287913 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 -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/
+// RUN: %check_clang_tidy -check-suffix=,CPP20ORLATER -std=c++20-or-later %s modernize-use-structured-binding %t -- -- -I %S/Inputs/use-structured-binding/
+// 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"
 
 template<typename T>
@@ -16,149 +16,149 @@ struct TestClass {
 void DecomposeByAssignWarnCases() {
   {
     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>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a 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; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   {
     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>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: auto [x, y] = getPair<int, int>();
     int x = P.first, y = P.second; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   {
     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>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: auto [x, y] = getPair<int, int>();
     int x = P.first, y = P.second; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
     int z;
   }
 
   {
     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>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a 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; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   {
     const 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: const auto [x, y] = getPair<int, int>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a 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; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   {
     std::pair<int, int> 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;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: auto& [x, y] = otherP;
     int& x = P.first;
     auto& y = P.second; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // 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;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a 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; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   {
     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>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a 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; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
     
     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>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: 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
+    // CHECK-FIXES: // REMOVE
   }
 }
 
 void forRangeWarnCases() {
   std::pair<int, int> Pairs[10];
   for (auto P : Pairs) {
-    // 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) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a 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; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   for (auto P : Pairs) {
-    // 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) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: for (auto [x, y] : Pairs) {
     int x = P.first, y = P.second; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   for (auto P : Pairs) {
-    // 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) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: for (auto [x, y] : Pairs) {
     int x = P.first, y = P.second; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
     int z;
   }
 
   for (const auto P : Pairs) {
-    // 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) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a 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; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   for (auto& P : Pairs) {
-    // 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) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a 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; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   for (const auto& P : Pairs) {
-    // 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) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a 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; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   std::pair<TestClass, TestClass> ClassPairs[10];
   for (auto P : ClassPairs) {
-    // 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) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a 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; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   for (const auto P : ClassPairs) {
-    // 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) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a 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; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 }
 
@@ -200,30 +200,30 @@ void forRangeNotWarnCases() {
 void stdTieWarnCases() {
   int a = 0;
   int b = 0; // REMOVE
-  // CHECK-FIXES-ALL: // REMOVE
+  // CHECK-FIXES: // REMOVE
   std::tie(a, b) = getPair<int, int>();
-  // 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>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-FIXES: auto [a, b] = getPair<int, int>();
 
   int x = 0, y = 0; // REMOVE
-  // CHECK-FIXES-ALL: // REMOVE
+  // CHECK-FIXES: // REMOVE
   std::tie(x, y) = getPair<int, int>();
-  // 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>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-FIXES: auto [x, y] = getPair<int, int>();
 
   int* pa = nullptr;
   int* pb = nullptr; // REMOVE
-  // CHECK-FIXES-ALL: // REMOVE
+  // CHECK-FIXES: // REMOVE
   std::tie(pa, pb) = getPair<int*, int*>();
-  // 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*>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a 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}; // REMOVE
-  // CHECK-FIXES-ALL: // REMOVE
+  // CHECK-FIXES: // REMOVE
   std::tie(c1, c2) = getPair<TestClass, TestClass>();
-  // 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>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-FIXES: auto [c1, c2] = getPair<TestClass, TestClass>();
 }
 
 void stdTieNotWarnCases() {
@@ -413,39 +413,39 @@ struct otherPair {
 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();
+    // 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;
   }
 
   {
     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();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: const auto [x, y] = otherPair();
     const int x = P.first;
     const auto y = P.second; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // 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;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: auto& [x, y] = otherP;
     int& x = P.first;
     auto& y = P.second; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // 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;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a 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; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 }
 
@@ -514,20 +514,20 @@ struct ConstFieldPair {
 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>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: const auto [x, y] = getCertainPair<ConstFieldPair>();
     const int x = P.first;
     const int y = P.second; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // 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>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: const auto& [x, y] = getCertainPair<ConstFieldPair>();
     const int& x = P.first;
     const int& y = P.second; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   {
@@ -545,11 +545,11 @@ struct PointerFieldPair {
 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>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: auto [x, y] = getCertainPair<PointerFieldPair>();
     int* x = P.first;
     int y = P.second; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   {
@@ -568,11 +568,11 @@ struct ConstRefFieldPair {
 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>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: auto [x, y] = getCertainPair<ConstRefFieldPair>();
     const int& x = P.first;
     int y = P.second; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   {

>From d2da3212c9581811fb445c78777bec0705d8096e Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Fri, 3 Oct 2025 20:35:01 +0800
Subject: [PATCH 51/68] [NFC][TEST] check for remove of first declstmt

---
 .../modernize/use-structured-binding.cpp      | 33 ++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

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 64de51c287913..9365260aa534c 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,6 +18,7 @@ void DecomposeByAssignWarnCases() {
     auto P = getPair<int, int>();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: auto [x, y] = getPair<int, int>();
+    // CHECK-NEXT: // REMOVE
     int x = P.first;
     int y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -44,6 +45,7 @@ void DecomposeByAssignWarnCases() {
     auto P = getPair<int, int>();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: auto [x, y] = getPair<int, int>();
+    // CHECK-NEXT: // REMOVE
     int x = P.first;
     auto y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -53,6 +55,7 @@ void DecomposeByAssignWarnCases() {
     const auto P = getPair<int, int>();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: const auto [x, y] = getPair<int, int>();
+    // CHECK-NEXT: // REMOVE
     const int x = P.first;
     const auto y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -63,6 +66,7 @@ void DecomposeByAssignWarnCases() {
     auto& P = otherP;
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: auto& [x, y] = otherP;
+    // CHECK-NEXT: // REMOVE
     int& x = P.first;
     auto& y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -73,6 +77,7 @@ void DecomposeByAssignWarnCases() {
     const auto& P = otherP;
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: const auto& [x, y] = otherP;
+    // CHECK-NEXT: // REMOVE
     const int& x = P.first;
     const auto& y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -82,6 +87,7 @@ void DecomposeByAssignWarnCases() {
     auto P = getPair<int, int>();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: auto [x, y] = getPair<int, int>();
+    // CHECK-NEXT: // REMOVE
     int x = P.first;
     int y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -89,6 +95,7 @@ void DecomposeByAssignWarnCases() {
     auto another_p = getPair<int, int>();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: auto [another_x, another_y] = getPair<int, int>();
+    // CHECK-NEXT: // REMOVE
     int another_x = another_p.first;
     int another_y = another_p.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -100,6 +107,7 @@ void forRangeWarnCases() {
   for (auto P : Pairs) {
     // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: for (auto [x, y] : Pairs) {
+    // CHECK-NEXT: // REMOVE
     int x = P.first;
     int y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -123,6 +131,7 @@ void forRangeWarnCases() {
   for (const auto P : Pairs) {
     // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: for (const auto [x, y] : Pairs) {
+    // CHECK-NEXT: // REMOVE
     const int x = P.first;
     const int y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -131,6 +140,7 @@ void forRangeWarnCases() {
   for (auto& P : Pairs) {
     // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: for (auto& [x, y] : Pairs) {
+    // CHECK-NEXT: // REMOVE
     int& x = P.first;
     int& y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -139,6 +149,7 @@ void forRangeWarnCases() {
   for (const auto& P : Pairs) {
     // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: for (const auto& [x, y] : Pairs) {
+    // CHECK-NEXT: // REMOVE
     const int& x = P.first;
     const int& y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -148,6 +159,7 @@ void forRangeWarnCases() {
   for (auto P : ClassPairs) {
     // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: for (auto [c1, c2] : ClassPairs) {
+    // CHECK-NEXT: // REMOVE
     TestClass c1 = P.first;
     TestClass c2 = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -156,6 +168,7 @@ void forRangeWarnCases() {
   for (const auto P : ClassPairs) {
     // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: for (const auto [c1, c2] : ClassPairs) {
+    // CHECK-NEXT: // REMOVE
     const TestClass c1 = P.first;
     const TestClass c2 = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -198,6 +211,7 @@ void forRangeNotWarnCases() {
 }
 
 void stdTieWarnCases() {
+  // CHECK-NEXT: // REMOVE
   int a = 0;
   int b = 0; // REMOVE
   // CHECK-FIXES: // REMOVE
@@ -211,6 +225,7 @@ void stdTieWarnCases() {
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES: auto [x, y] = getPair<int, int>();
 
+  // CHECK-NEXT: // REMOVE
   int* pa = nullptr;
   int* pb = nullptr; // REMOVE
   // CHECK-FIXES: // REMOVE
@@ -218,6 +233,7 @@ void stdTieWarnCases() {
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES: auto [pa, pb] = getPair<int*, int*>();
 
+  // CHECK-NEXT: // REMOVE
   TestClass c1 (1, 2);
   TestClass c2 = TestClass {3, 4}; // REMOVE
   // CHECK-FIXES: // REMOVE
@@ -327,6 +343,7 @@ void captureByVal() {
   auto P = getPair<int, int>();
   // 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>();
+  // CHECK-NEXT-CPP20ORLATER: // REMOVE
   int x = P.first;
   int y = P.second; // REMOVE
   // CHECK-FIXES-CPP20ORLATER: // REMOVE
@@ -340,6 +357,7 @@ void captureByRef() {
   auto P = getPair<int, int>();
   // 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>();
+  // CHECK-NEXT-CPP20ORLATER: // REMOVE
   int x = P.first;
   int y = P.second; // REMOVE
   // CHECK-FIXES-CPP20ORLATER: // REMOVE
@@ -353,6 +371,7 @@ void captureByAllRef() {
   auto P = getPair<int, int>();
   // 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>();
+  // CHECK-NEXT-CPP20ORLATER: // REMOVE
   int x = P.first;
   int y = P.second; // REMOVE
   // CHECK-FIXES-CPP20ORLATER: // REMOVE
@@ -366,6 +385,7 @@ void deepLambda() {
   auto P = getPair<int, int>();
   // 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>();
+  // CHECK-NEXT-CPP20ORLATER: // REMOVE
   int x = P.first;
   int y = P.second; // REMOVE
   // CHECK-FIXES-CPP20ORLATER: // REMOVE
@@ -382,6 +402,7 @@ void forRangeNotWarn() {
   for (auto P : Pairs) {
   // 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) {
+  // CHECK-NEXT-CPP20ORLATER: // REMOVE
     int x = P.first;
     int y = P.second; // REMOVE
     // CHECK-FIXES-CPP20ORLATER: // REMOVE
@@ -393,6 +414,7 @@ void forRangeNotWarn() {
 }
 
 void stdTieNotWarn() {
+  // CHECK-NEXT-CPP20ORLATER: // REMOVE
   int x = 0;
   int y = 0; // REMOVE
   // CHECK-FIXES-CPP20ORLATER: // REMOVE
@@ -415,14 +437,17 @@ void OtherPairTest() {
     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();
+    // CHECK-NEXT: // REMOVE
     int x = P.first;
-    int y = P.second;
+    int y = P.second; // REMOVE
+    // CHECK-FIXES: // REMOVE
   }
 
   {
     const auto P = otherPair();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: const auto [x, y] = otherPair();
+    // CHECK-NEXT: // REMOVE
     const int x = P.first;
     const auto y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -433,6 +458,7 @@ void OtherPairTest() {
     auto& P = otherP;
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: auto& [x, y] = otherP;
+    // CHECK-NEXT: // REMOVE
     int& x = P.first;
     auto& y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -443,6 +469,7 @@ void OtherPairTest() {
     const auto& P = otherP;
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: const auto& [x, y] = otherP;
+    // CHECK-NEXT: // REMOVE
     const int& x = P.first;
     const auto& y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -516,6 +543,7 @@ void ConstFieldPairTests() {
     const ConstFieldPair P = getCertainPair<ConstFieldPair>();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: const auto [x, y] = getCertainPair<ConstFieldPair>();
+    // CHECK-NEXT: // REMOVE
     const int x = P.first;
     const int y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -525,6 +553,7 @@ void ConstFieldPairTests() {
     const ConstFieldPair& P = getCertainPair<ConstFieldPair>();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: const auto& [x, y] = getCertainPair<ConstFieldPair>();
+    // CHECK-NEXT: // REMOVE
     const int& x = P.first;
     const int& y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -547,6 +576,7 @@ void PointerFieldPairTests() {
     PointerFieldPair P = getCertainPair<PointerFieldPair>();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: auto [x, y] = getCertainPair<PointerFieldPair>();
+    // CHECK-NEXT: // REMOVE
     int* x = P.first;
     int y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE
@@ -570,6 +600,7 @@ void ConstRefFieldPairTests() {
     ConstRefFieldPair P = getCertainPair<ConstRefFieldPair>();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: auto [x, y] = getCertainPair<ConstRefFieldPair>();
+    // CHECK-NEXT: // REMOVE
     const int& x = P.first;
     int y = P.second; // REMOVE
     // CHECK-FIXES: // REMOVE

>From 2b4acf1692b94c2dd27a27c90076602b1f5cc4f3 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Fri, 3 Oct 2025 22:16:39 +0800
Subject: [PATCH 52/68] [NFC][TEST] Add testcase for std::unordered_map

---
 .../fake_std_pair_tuple.h                      | 18 ++++++++++++++++++
 .../modernize/use-structured-binding.cpp       | 12 ++++++++++++
 2 files changed, 30 insertions(+)

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
index b98696f14e8d8..f49cfd840e128 100644
--- 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
@@ -20,6 +20,24 @@ namespace std {
   tuple<Args...> tie(Args&... args) {
     return tuple<Args...>(args...);
   }
+
+  template <typename Key, typename Value>
+  class unordered_map {
+  public:
+    using value_type = pair<Key, Value>;
+
+    class iterator {
+    public:
+      iterator& operator++();
+      bool operator!=(const iterator &other);
+      const value_type &operator*() const;
+      value_type operator*();
+      const value_type* operator->() const;
+    };
+
+    iterator begin() const;
+    iterator end() const;
+  };
 }
 
 template<typename T1, typename T2>
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 9365260aa534c..52098fbbce253 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
@@ -651,3 +651,15 @@ void IgnoreDirectInit() {
     int y = P.second;
   }
 }
+
+void StdMapTestCases() {
+  for (auto p : std::unordered_map<int, int>()) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: for (auto [x, y] : std::unordered_map<int, int>()) {
+    // CHECK-NEXT: // REMOVE
+    int x = p.first;
+    int y = p.second; // REMOVE
+    // CHECK-FIXES: // REMOVE
+  }
+}
+

>From b49b66c2103e844baf130359693a461294d86fea Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Fri, 3 Oct 2025 22:29:01 +0800
Subject: [PATCH 53/68] [NFC] Remove useless outside matcher

---
 .../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 78a6595e1bf5a..681656a2b84e1 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -275,8 +275,8 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
                       hasType(qualType(anyOf(PairType, lValueReferenceType(
                                                            pointee(PairType))))
                                   .bind(PairVarTypeName)),
-                      hasInitializer(expr(ignoringCopyCtorAndImplicitCast(
-                          expr().bind(InitExprName)))))
+                      hasInitializer(ignoringCopyCtorAndImplicitCast(
+                          expr().bind(InitExprName))))
                   .bind(PairDeclName)),
           hasNextTwoVarDecl(
               llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
@@ -296,8 +296,8 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
               varDecl(hasType(qualType(anyOf(PairType, lValueReferenceType(
                                                            pointee(PairType))))
                                   .bind(PairVarTypeName)),
-                      hasInitializer(expr(ignoringCopyCtorAndImplicitCast(
-                          expr().bind(InitExprName)))))
+                      hasInitializer(ignoringCopyCtorAndImplicitCast(
+                          expr().bind(InitExprName))))
                   .bind(PairDeclName)),
           hasBody(
               compoundStmt(

>From 6cc88e1722ba5689be58829aa480930f81c55c84 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sat, 4 Oct 2025 22:54:54 +0800
Subject: [PATCH 54/68] [NFC] Remove unnecessary ignoreImplictCast

---
 .../clang-tidy/modernize/UseStructuredBindingCheck.cpp    | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 681656a2b84e1..2be721671b445 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -194,14 +194,12 @@ static auto getVarInitWithMemberMatcher(
     StringRef PairName, StringRef MemberName, StringRef TypeName,
     StringRef BindingName,
     ast_matchers::internal::Matcher<VarDecl> ExtraMatcher) {
-  return varDecl(
-             ExtraMatcher,
-             hasInitializer(
-                 ignoringImpCasts(ignoringCopyCtorAndImplicitCast(memberExpr(
+  return varDecl(ExtraMatcher,
+                 hasInitializer(ignoringCopyCtorAndImplicitCast(memberExpr(
                      hasObjectExpression(ignoringImpCasts(declRefExpr(
                          to(equalsBoundNode(std::string(PairName)))))),
                      member(fieldDecl(hasName(MemberName),
-                                      hasType(qualType().bind(TypeName)))))))))
+                                      hasType(qualType().bind(TypeName))))))))
       .bind(BindingName);
 }
 

>From df88e007d930c23cba600af1a45c974251f327e4 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 23 Dec 2025 20:52:08 +0800
Subject: [PATCH 55/68] [NFC] use StringRef

---
 .../modernize/UseStructuredBindingCheck.cpp   | 28 +++++++++----------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 2be721671b445..fd058a04fe666 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -14,19 +14,19 @@ using namespace clang::ast_matchers;
 
 namespace clang::tidy::modernize {
 
-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 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";
-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";
+static constexpr StringRef PairDeclName = "PairVarD";
+static constexpr StringRef PairVarTypeName = "PairVarType";
+static constexpr StringRef FirstVarDeclName = "FirstVarDecl";
+static constexpr StringRef SecondVarDeclName = "SecondVarDecl";
+static constexpr StringRef BeginDeclStmtName = "BeginDeclStmt";
+static constexpr StringRef EndDeclStmtName = "EndDeclStmt";
+static constexpr StringRef FirstTypeName = "FirstType";
+static constexpr StringRef SecondTypeName = "SecondType";
+static constexpr StringRef ScopeBlockName = "ScopeBlock";
+static constexpr StringRef StdTieAssignStmtName = "StdTieAssign";
+static constexpr StringRef StdTieExprName = "StdTieExpr";
+static constexpr StringRef ForRangeStmtName = "ForRangeStmt";
+static constexpr StringRef 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
@@ -218,7 +218,7 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
       getVarInitWithMemberMatcher(PairDeclName, "second", SecondTypeName,
                                   SecondVarDeclName, UnlessShouldBeIgnored);
 
-  auto RefToBindName = [&UnlessShouldBeIgnored](const llvm::StringLiteral &Name)
+  auto RefToBindName = [&UnlessShouldBeIgnored](const StringRef &Name)
       -> ast_matchers::internal::BindableMatcher<Stmt> {
     return declRefExpr(to(varDecl(UnlessShouldBeIgnored).bind(Name)));
   };

>From b766548efceccdc31fcc16e0575fda0ec12333f5 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 23 Dec 2025 21:07:16 +0800
Subject: [PATCH 56/68] [NFC] use `llvm::reverse_conditionally`

---
 .../modernize/UseStructuredBindingCheck.cpp        | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index fd058a04fe666..cf5a0d950d295 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -68,16 +68,10 @@ static bool matchNVarDeclStartingWith(
     if (!BeginDS)
       BeginDS = EndDS;
 
-    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;
-      }
+    for (const auto *VD :
+         llvm::reverse_conditionally(EndDS->decls(), Backwards)) {
+      if (!Matches(VD))
+        return false;
     }
 
     // All the matchers is satisfied in those DeclStmts.

>From 030de1a2d9fe3bfa7c9a15b60f687af059acfa2d Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 23 Dec 2025 21:09:04 +0800
Subject: [PATCH 57/68] [NFC] Fix clang-tidy warning

---
 .../clang-tidy/modernize/UseStructuredBindingCheck.cpp      | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index cf5a0d950d295..557f11917b6db 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -35,7 +35,7 @@ static constexpr StringRef InitExprName = "init_expr";
 /// \p Backwards indicates whether to match the VarDecls in reverse order.
 template <typename Iterator>
 static bool matchNVarDeclStartingWith(
-    Iterator Iter, Iterator EndIter,
+    Iterator Iter, const Iterator &EndIter,
     ArrayRef<ast_matchers::internal::Matcher<VarDecl>> InnerMatchers,
     ast_matchers::internal::ASTMatchFinder *Finder,
     ast_matchers::internal::BoundNodesTreeBuilder *Builder,
@@ -187,7 +187,7 @@ AST_MATCHER(VarDecl, isDirectInitialization) {
 static auto getVarInitWithMemberMatcher(
     StringRef PairName, StringRef MemberName, StringRef TypeName,
     StringRef BindingName,
-    ast_matchers::internal::Matcher<VarDecl> ExtraMatcher) {
+    const ast_matchers::internal::Matcher<VarDecl> &ExtraMatcher) {
   return varDecl(ExtraMatcher,
                  hasInitializer(ignoringCopyCtorAndImplicitCast(memberExpr(
                      hasObjectExpression(ignoringImpCasts(declRefExpr(
@@ -218,7 +218,7 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
   };
 
   auto HasAnyLambdaCaptureThisVar =
-      [](ast_matchers::internal::Matcher<VarDecl> VDMatcher)
+      [](const ast_matchers::internal::Matcher<VarDecl> &VDMatcher)
       -> ast_matchers::internal::BindableMatcher<Stmt> {
     return compoundStmt(hasDescendant(
         lambdaExpr(hasAnyCapture(capturesVar(varDecl(VDMatcher))))));

>From 4d564be1f5400a3f46f2873cfc0ef98d354e5298 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 23 Dec 2025 22:08:24 +0800
Subject: [PATCH 58/68] [NFC] Fix clang-tidy warning

---
 .../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 557f11917b6db..eae7cc0ed61a3 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -42,7 +42,7 @@ static bool matchNVarDeclStartingWith(
     bool Backwards = false) {
   const DeclStmt *BeginDS = nullptr;
   const DeclStmt *EndDS = nullptr;
-  size_t N = InnerMatchers.size();
+  const size_t N = InnerMatchers.size();
   size_t Count = 0;
 
   auto Matches = [&](const Decl *VD) {
@@ -348,7 +348,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
       }
     }();
 
-    std::string ReplacementText =
+    const std::string ReplacementText =
         (Twine(Prefix) + " [" + FirstVar->getNameAsString() + ", " +
          SecondVar->getNameAsString() + "]" + (CFRS ? " :" : ""))
             .str();

>From dbee9574c614fb620a396295fcf159a73650a953 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 23 Dec 2025 22:09:10 +0800
Subject: [PATCH 59/68] [NFC] Fix doc8 warning

---
 .../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 f68041e266231..2feb32a74963c 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
@@ -68,7 +68,7 @@ to structured bindings, for example:
 
 .. code-block:: c++
 
-  const auto& results = mapping.try_emplace("hello!"); 
+  const auto& results = mapping.try_emplace("hello!");
   const iterator& it = results.first;
   bool succeed = results.second;
   // succeed is not changed in the following code

>From 34f9ce09eb093a8e9a9fdf11e3dc1e81d0cbd523 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 23 Dec 2025 23:01:40 +0800
Subject: [PATCH 60/68] [NFC] Simplify code by swap

---
 .../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 eae7cc0ed61a3..e81102aa0e112 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -76,11 +76,10 @@ static bool matchNVarDeclStartingWith(
 
     // 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));
+      if (Backwards)
+        std::swap(BeginDS, EndDS);
+      Builder->setBinding(BeginDeclStmtName, DynTypedNode::create(*BeginDS));
+      Builder->setBinding(EndDeclStmtName, DynTypedNode::create(*EndDS));
       return true;
     }
   }

>From 943624df1b129d8d949ed5d183976ae71ee33346 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 23 Dec 2025 23:02:11 +0800
Subject: [PATCH 61/68] [NFC] Use implict return type for lambdas

---
 .../modernize/UseStructuredBindingCheck.cpp          | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index e81102aa0e112..48e301b12c1a3 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -211,17 +211,15 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
       getVarInitWithMemberMatcher(PairDeclName, "second", SecondTypeName,
                                   SecondVarDeclName, UnlessShouldBeIgnored);
 
-  auto RefToBindName = [&UnlessShouldBeIgnored](const StringRef &Name)
-      -> ast_matchers::internal::BindableMatcher<Stmt> {
+  auto RefToBindName = [&UnlessShouldBeIgnored](const StringRef &Name) {
     return declRefExpr(to(varDecl(UnlessShouldBeIgnored).bind(Name)));
   };
 
   auto HasAnyLambdaCaptureThisVar =
-      [](const ast_matchers::internal::Matcher<VarDecl> &VDMatcher)
-      -> ast_matchers::internal::BindableMatcher<Stmt> {
-    return compoundStmt(hasDescendant(
-        lambdaExpr(hasAnyCapture(capturesVar(varDecl(VDMatcher))))));
-  };
+      [](const ast_matchers::internal::Matcher<VarDecl> &VDMatcher) {
+        return compoundStmt(hasDescendant(
+            lambdaExpr(hasAnyCapture(capturesVar(varDecl(VDMatcher))))));
+      };
 
   // Captured structured bindings are a C++20 extension
   auto UnlessFirstVarOrSecondVarIsCapturedByLambda =

>From 82dbd2de91397d7fac375c1b2639d422c2a0bb72 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Thu, 15 Jan 2026 21:39:27 +0800
Subject: [PATCH 62/68] ignore thread_local

---
 .../clang-tidy/modernize/UseStructuredBindingCheck.cpp      | 2 +-
 .../checkers/modernize/use-structured-binding.cpp           | 6 ++++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 48e301b12c1a3..6e3b329cc2f2f 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -148,7 +148,7 @@ AST_MATCHER_P(CompoundStmt, hasFirstTwoVarDecl,
 /// pair, so we ignore these cases.
 AST_MATCHER(VarDecl, hasAnySpecifiersShouldBeIgnored) {
   return Node.isStaticLocal() || Node.isConstexpr() || Node.hasAttrs() ||
-         Node.isInlineSpecified();
+         Node.isInlineSpecified() || Node.getTSCSpec() != TSCS_unspecified;
 }
 
 // Ignore nodes inside macros.
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 52098fbbce253..1cbbe30050e7b 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
@@ -287,6 +287,12 @@ void NotWarnForVarHasSpecifiers() {
     int x = P.first;
     int y = P.second;
   }
+
+  {
+    thread_local auto P = getPair<int, int>();
+    int x = P.first;
+    int y = P.second;
+  }
 }
 
 void NotWarnForMultiUsedPairVar() {

>From 4f4b77f8590014b17e76b64fbaff5ff750d79937 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Thu, 15 Jan 2026 22:23:01 +0800
Subject: [PATCH 63/68] typeOrLValueReferenceTo

---
 .../modernize/UseStructuredBindingCheck.cpp   | 26 +++++++++++--------
 1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 6e3b329cc2f2f..de42d85d6f766 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -196,6 +196,12 @@ static auto getVarInitWithMemberMatcher(
       .bind(BindingName);
 }
 
+static auto typeOrLValueReferenceTo(
+    const ast_matchers::internal::Matcher<QualType> &TypeMatcher) {
+  return qualType(
+      anyOf(TypeMatcher, lValueReferenceType(pointee(TypeMatcher))));
+}
+
 void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
   auto PairType = qualType(unless(isVolatileQualified()),
                            hasUnqualifiedDesugaredType(recordType(
@@ -259,14 +265,13 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
   Finder->addMatcher(
       declStmt(
           unless(isInMarco()),
-          hasSingleDecl(
-              varDecl(UnlessShouldBeIgnored, unless(isDirectInitialization()),
-                      hasType(qualType(anyOf(PairType, lValueReferenceType(
-                                                           pointee(PairType))))
-                                  .bind(PairVarTypeName)),
-                      hasInitializer(ignoringCopyCtorAndImplicitCast(
-                          expr().bind(InitExprName))))
-                  .bind(PairDeclName)),
+          hasSingleDecl(varDecl(UnlessShouldBeIgnored,
+                                unless(isDirectInitialization()),
+                                hasType(typeOrLValueReferenceTo(PairType).bind(
+                                    PairVarTypeName)),
+                                hasInitializer(ignoringCopyCtorAndImplicitCast(
+                                    expr().bind(InitExprName))))
+                            .bind(PairDeclName)),
           hasNextTwoVarDecl(
               llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
                   VarInitWithFirstMember, VarInitWithSecondMember}),
@@ -282,9 +287,8 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
       cxxForRangeStmt(
           unless(isInMarco()),
           hasLoopVariable(
-              varDecl(hasType(qualType(anyOf(PairType, lValueReferenceType(
-                                                           pointee(PairType))))
-                                  .bind(PairVarTypeName)),
+              varDecl(hasType(typeOrLValueReferenceTo(PairType).bind(
+                          PairVarTypeName)),
                       hasInitializer(ignoringCopyCtorAndImplicitCast(
                           expr().bind(InitExprName))))
                   .bind(PairDeclName)),

>From 3477973b086d60249912df207bdd9975bf414ff2 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Thu, 15 Jan 2026 22:44:34 +0800
Subject: [PATCH 64/68] ignore extern variable too

---
 .../clang-tidy/modernize/UseStructuredBindingCheck.cpp      | 3 ++-
 .../checkers/modernize/use-structured-binding.cpp           | 6 ++++++
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index de42d85d6f766..19b65a97e5965 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -148,7 +148,8 @@ AST_MATCHER_P(CompoundStmt, hasFirstTwoVarDecl,
 /// pair, so we ignore these cases.
 AST_MATCHER(VarDecl, hasAnySpecifiersShouldBeIgnored) {
   return Node.isStaticLocal() || Node.isConstexpr() || Node.hasAttrs() ||
-         Node.isInlineSpecified() || Node.getTSCSpec() != TSCS_unspecified;
+         Node.isInlineSpecified() || Node.getStorageClass() != SC_None ||
+         Node.getTSCSpec() != TSCS_unspecified;
 }
 
 // Ignore nodes inside macros.
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 1cbbe30050e7b..83d9a6bff47e1 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
@@ -293,6 +293,12 @@ void NotWarnForVarHasSpecifiers() {
     int x = P.first;
     int y = P.second;
   }
+
+  {
+    extern int a;
+    int b = 0;
+    std::tie(a, b) = getPair<int, int>();
+  }
 }
 
 void NotWarnForMultiUsedPairVar() {

>From a314e72760b3bfa9dacca69f38060b42e531d1b7 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 18 Jan 2026 20:01:07 +0800
Subject: [PATCH 65/68] fix typo `marco`

---
 .../clang-tidy/modernize/UseStructuredBindingCheck.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 19b65a97e5965..b56fb0579f1e3 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -153,7 +153,7 @@ AST_MATCHER(VarDecl, hasAnySpecifiersShouldBeIgnored) {
 }
 
 // Ignore nodes inside macros.
-AST_POLYMORPHIC_MATCHER(isInMarco,
+AST_POLYMORPHIC_MATCHER(isInMacro,
                         AST_POLYMORPHIC_SUPPORTED_TYPES(Stmt, Decl)) {
   return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID();
 }
@@ -209,7 +209,7 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
                                hasDeclaration(cxxRecordDecl(isPairType())))));
 
   auto UnlessShouldBeIgnored =
-      unless(anyOf(hasAnySpecifiersShouldBeIgnored(), isInMarco()));
+      unless(anyOf(hasAnySpecifiersShouldBeIgnored(), isInMacro()));
 
   auto VarInitWithFirstMember =
       getVarInitWithMemberMatcher(PairDeclName, "first", FirstTypeName,
@@ -241,7 +241,7 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
   // std::tie(x, y) = ...;
   Finder->addMatcher(
       exprWithCleanups(
-          unless(isInMarco()),
+          unless(isInMacro()),
           has(cxxOperatorCallExpr(
                   hasOverloadedOperatorName("="),
                   hasLHS(ignoringImplicit(
@@ -265,7 +265,7 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
   // Y y = p.second;
   Finder->addMatcher(
       declStmt(
-          unless(isInMarco()),
+          unless(isInMacro()),
           hasSingleDecl(varDecl(UnlessShouldBeIgnored,
                                 unless(isDirectInitialization()),
                                 hasType(typeOrLValueReferenceTo(PairType).bind(
@@ -286,7 +286,7 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
   // }
   Finder->addMatcher(
       cxxForRangeStmt(
-          unless(isInMarco()),
+          unless(isInMacro()),
           hasLoopVariable(
               varDecl(hasType(typeOrLValueReferenceTo(PairType).bind(
                           PairVarTypeName)),

>From db7406abc6135a2d4cc2a4d842d2caae631ec9cc Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 18 Jan 2026 21:25:15 +0800
Subject: [PATCH 66/68] add more testcase to address review

---
 .../fake_std_pair_tuple.h                     |  18 ++
 .../modernize/use-structured-binding.cpp      | 206 ++++++++++++++++++
 2 files changed, 224 insertions(+)

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
index f49cfd840e128..2f8ce2b157e3f 100644
--- 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
@@ -1,4 +1,14 @@
 namespace std {
+  struct _Swallow_assign
+  {
+    template<class _Tp>
+       const _Swallow_assign&
+      operator=(const _Tp&) const
+      { return *this; }
+  };
+
+  constexpr _Swallow_assign ignore{};
+
   template<typename T1, typename T2>
   struct pair {
     T1 first;
@@ -42,3 +52,11 @@ namespace std {
 
 template<typename T1, typename T2>
 std::pair<T1, T2> getPair();
+
+template<typename T1, typename T2>
+constexpr std::pair<T1, T2> getConstexprPair() {
+  return std::pair<T1, T2>();
+}
+
+template<typename T1, typename T2, typename T3>
+std::tuple<T1, T2, T3> getTuple();
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 83d9a6bff47e1..bbf5c3d582b6a 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
@@ -675,3 +675,209 @@ void StdMapTestCases() {
   }
 }
 
+void SpecialCases1() {
+  {
+    auto P = getPair<int, int>();                                                                                                                                                          
+    int y = P.second;  // second first                                                                                                                                                  
+    int x = P.first;
+  }
+
+  {
+    auto P = getPair<int, int>();                                                                                                                                                          
+    int x = P.first;                                                                                                                                                                       
+    // P.second never assigned
+  }
+
+  {
+    auto P = getPair<int, int>();                                                                                                                                                          
+    int x = P.first + 1;                                                                                                                                             
+    int y = P.second;  
+  }                                                                                                                                                                                                                                                                                                                                                                                                 
+  {
+    auto P = getPair<int, int>();                                                                                                                                                          
+    float x = P.first;  // int -> float                                                                                                                                                      
+    float y = P.second;   
+  }                                                                                                                                                                  
+                                                                                                                                                                                                                                                                                                                                           
+  {
+    auto P = getPair<int, int>();                                                                                                                                                          
+    int x = P.first;                                                                                                                                                                       
+    int dummy = 42;                                                                                                                                         
+    int y = P.second;     
+  }
+  
+  {
+    for (auto P : std::unordered_map<int, int>()) {
+      int x = P.first;                                                                                                                                                                     
+      int dummy = 42;                                                                                                                                              
+      int y = P.second;     
+    }
+  }
+                                                                                                                                                                                          
+  {
+    std::pair<int, int> somePair;
+    std::pair<int, int>* P = &somePair;                                                                                                                                                    
+    int x = P->first;                                                                                                                                                                      
+    int y = P->second;
+  }
+
+  {
+    std::pair<int, int> somePair;
+    std::pair<int, int>& P = somePair;                                        
+    int x = P.first;                                                                                                                                                                      
+    int y = P.second;
+  }
+
+  {
+    std::pair<int, int> somePair;
+    const std::pair<int, int>& P = somePair;                                        
+    int x = P.first;                                                                                                                                                                      
+    int y = P.second;
+  }
+}
+
+// Partial and full templates
+// Currently not supported because we can't get type from CXXDependentScopeMemberExpr, needs some extra work to do that.
+template<typename T>
+void templateFuncTest() {
+  auto P = getPair<T, int>();
+  T x = P.first;
+  int y = P.second;
+}
+
+template<typename T1, typename T2>
+void templateFuncTest() {
+  auto P = getPair<T1, T2>();
+  T1 x = P.first;
+  T2 y = P.second;
+}
+
+void SpecialCases2() {
+  // nested pairs
+  {
+    auto P = getPair<std::pair<int, int>, int>();
+    int val = P.second;
+    std::pair<int, int> inner = P.first;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: auto [a_in, b_in] = P.first;
+    // CHECK-NEXT: // REMOVE
+    int a_in = inner.first;
+    int b_in = inner.second; // REMOVE
+    // CHECK-FIXES: // REMOVE
+  }
+
+  // shadow
+  {
+    int x = 10;
+    {
+      auto P = getPair<int, int>();
+      // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+      // CHECK-FIXES: auto [x, y] = getPair<int, int>();
+      // CHECK-NEXT: // REMOVE
+      int x = P.first;
+      int y = P.second; // REMOVE
+      // CHECK-FIXES: // REMOVE
+    }
+  }
+
+  // only use first
+  {
+    auto P = getPair<int, int>();
+    int x = P.first;
+    int y = P.first;
+  }
+
+  // std::ignore in std::tie
+  {
+    int a = 0;
+    std::tie(a, std::ignore) = getPair<int, int>();
+  }
+
+  // tuple pair mixup
+  {
+    int a = 0, b = 0, c = 0;
+    std::tie(a, b, c) = getTuple<int, int, int>();
+    std::tie(a, a) = getPair<int, int>();
+  }
+
+  // volatile specifier
+  {
+    volatile std::pair<int, int> P = getPair<int, int>();
+    int x = P.first;
+    int y = P.second;
+  }
+
+  // decltype usage (there are invisible DeclRefExpr in decltype)
+  {
+    auto P = getPair<int, int>();
+    decltype(P.first) x = P.first;
+    decltype(P.second) y = P.second;
+  }
+
+  // constexpr pair
+  {
+    constexpr auto P = getConstexprPair<int, int>();
+    constexpr int x = P.first;
+    constexpr int y = P.second;
+  }
+
+  // initializer list
+  {
+    struct AggregatePair {
+      int first;
+      int second;
+    };
+    auto P = AggregatePair{.first = 1, .second = 2};
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: auto [x, y] = AggregatePair{.first = 1, .second = 2};
+    // CHECK-NEXT: // REMOVE
+    int x = P.first;
+    int y = P.second; // REMOVE
+    // CHECK-FIXES: // REMOVE
+  }
+
+  // reference to original pair
+  {
+    std::pair<int, int> original = getPair<int, int>();
+    const auto& P = original;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: const auto& [x, y] = original;
+    // CHECK-NEXT: // REMOVE
+    const int& x = P.first;
+    const int& y = P.second; // REMOVE
+    // CHECK-FIXES: // REMOVE
+  }
+
+  // typedef pair
+  {
+    using IntPair = std::pair<int, int>;
+    IntPair P = getPair<int, int>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: auto [x, y] = getPair<int, int>();
+    // CHECK-NEXT: // REMOVE
+    int x = P.first;
+    int y = P.second; // REMOVE
+    // CHECK-FIXES: // REMOVE
+  }
+
+  // assign to existing vars
+  {
+    int x, y;
+    auto P = getPair<int, int>();
+    x = P.first;  
+    y = P.second;
+  }
+
+  // conditional operator
+  {
+    bool cond;
+    std::pair<int, int> p1{1, 2}, p2{3, 4};
+    auto P = cond ? p1 : p2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES: auto [x, y] = cond ? p1 : p2;
+    // CHECK-NEXT: // REMOVE
+    int x = P.first;
+    int y = P.second; // REMOVE
+    // CHECK-FIXES: // REMOVE
+  }
+}

>From 3e11f97d6ec2bee059c7b11e4fa862f3e298d405 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 18 Jan 2026 23:32:10 +0800
Subject: [PATCH 67/68] 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 2feb32a74963c..08358df730f72 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,7 +55,7 @@ Limitations
 -----------
 
 The check currently ignores variables defined with attributes or qualifiers
-except const and & since it's not very common:
+except ``const`` and ``&`` since it's not very common:
 
 .. code-block:: c++
 

>From dde54cd9989d151eff845d6aa4a9c71f03bb8db1 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 18 Jan 2026 23:41:10 +0800
Subject: [PATCH 68/68] The check doesn't handle

---
 .../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 08358df730f72..9e8ce6b312bc0 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
@@ -63,7 +63,7 @@ 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 be transferred
+The check doesn't handle some situations which could possibly be transferred
 to structured bindings, for example:
 
 .. code-block:: c++



More information about the cfe-commits mailing list