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

via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 23 06:23:33 PDT 2025


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

>From a1941312179171a8752c27f886158ce4fd90db33 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 14:33:59 +0800
Subject: [PATCH 01/48] [clang-tidy] Add new check
 `modernize-use-structured-binding`

---
 .../clang-tidy/modernize/CMakeLists.txt       |   1 +
 .../modernize/ModernizeTidyModule.cpp         |   3 +
 .../modernize/UseStructuredBindingCheck.cpp   | 419 ++++++++++++++++++
 .../modernize/UseStructuredBindingCheck.h     |  36 ++
 clang-tools-extra/docs/ReleaseNotes.rst       |   6 +
 .../docs/clang-tidy/checks/list.rst           |   1 +
 .../modernize/use-structured-binding.rst      |  58 +++
 .../fake_std_pair_tuple.h                     |  23 +
 .../use-structured-binding-custom.cpp         |  32 ++
 ...d-binding-skip-lambda-capture-in-cxx17.cpp |  67 +++
 .../modernize/use-structured-binding.cpp      | 216 +++++++++
 11 files changed, 862 insertions(+)
 create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
 create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
 create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-structured-binding/fake_std_pair_tuple.h
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp

diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index 619a27b2f9bb6..094f0a72b1570 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -47,6 +47,7 @@ add_clang_library(clangTidyModernizeModule STATIC
   UseStdFormatCheck.cpp
   UseStdNumbersCheck.cpp
   UseStdPrintCheck.cpp
+  UseStructuredBindingCheck.cpp
   UseTrailingReturnTypeCheck.cpp
   UseTransparentFunctorsCheck.cpp
   UseUncaughtExceptionsCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index fdf38bc4b6308..a79908500e904 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -48,6 +48,7 @@
 #include "UseStdFormatCheck.h"
 #include "UseStdNumbersCheck.h"
 #include "UseStdPrintCheck.h"
+#include "UseStructuredBindingCheck.h"
 #include "UseTrailingReturnTypeCheck.h"
 #include "UseTransparentFunctorsCheck.h"
 #include "UseUncaughtExceptionsCheck.h"
@@ -121,6 +122,8 @@ class ModernizeModule : public ClangTidyModule {
     CheckFactories.registerCheck<UseNoexceptCheck>("modernize-use-noexcept");
     CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
     CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
+    CheckFactories.registerCheck<UseStructuredBindingCheck>(
+        "modernize-use-structured-binding");
     CheckFactories.registerCheck<UseTrailingReturnTypeCheck>(
         "modernize-use-trailing-return-type");
     CheckFactories.registerCheck<UseTransparentFunctorsCheck>(
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
new file mode 100644
index 0000000000000..d6d6ae6cb83b3
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -0,0 +1,419 @@
+//===--- UseStructuredBindingCheck.cpp - clang-tidy -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseStructuredBindingCheck.h"
+#include "../utils/DeclRefExprUtils.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+namespace {
+constexpr const char *DefaultPairTypes = "std::pair";
+constexpr llvm::StringLiteral PairDeclName = "PairVarD";
+constexpr llvm::StringLiteral PairVarTypeName = "PairVarType";
+constexpr llvm::StringLiteral FirstVarDeclName = "FirstVarDecl";
+constexpr llvm::StringLiteral SecondVarDeclName = "SecondVarDecl";
+constexpr llvm::StringLiteral FirstDeclStmtName = "FirstDeclStmt";
+constexpr llvm::StringLiteral SecondDeclStmtName = "SecondDeclStmt";
+constexpr llvm::StringLiteral FirstTypeName = "FirstType";
+constexpr llvm::StringLiteral SecondTypeName = "SecondType";
+constexpr llvm::StringLiteral ScopeBlockName = "ScopeBlock";
+constexpr llvm::StringLiteral StdTieAssignStmtName = "StdTieAssign";
+constexpr llvm::StringLiteral StdTieExprName = "StdTieExpr";
+constexpr llvm::StringLiteral ForRangeStmtName = "ForRangeStmt";
+
+/// What qualifiers and specifiers are used to create structured binding
+/// declaration, it only supports the following four cases now.
+enum TransferType : uint8_t {
+  TT_ByVal,
+  TT_ByConstVal,
+  TT_ByRef,
+  TT_ByConstRef
+};
+
+/// Try to match exactly two VarDecl inside two DeclStmts, and set binding for
+/// the used DeclStmts.
+bool matchTwoVarDecl(const DeclStmt *DS1, const DeclStmt *DS2,
+                     ast_matchers::internal::Matcher<VarDecl> InnerMatcher1,
+                     ast_matchers::internal::Matcher<VarDecl> InnerMatcher2,
+                     internal::ASTMatchFinder *Finder,
+                     internal::BoundNodesTreeBuilder *Builder) {
+  SmallVector<std::pair<const VarDecl *, const DeclStmt *>, 2> Vars;
+  auto CollectVarsInDeclStmt = [&Vars](const DeclStmt *DS) -> bool {
+    if (!DS)
+      return true;
+
+    for (const auto *VD : DS->decls()) {
+      if (Vars.size() == 2)
+        return false;
+
+      if (const auto *Var = dyn_cast<VarDecl>(VD))
+        Vars.emplace_back(Var, DS);
+      else
+        return false;
+    }
+
+    return true;
+  };
+
+  if (!CollectVarsInDeclStmt(DS1) || !CollectVarsInDeclStmt(DS2))
+    return false;
+
+  if (Vars.size() != 2)
+    return false;
+
+  if (InnerMatcher1.matches(*Vars[0].first, Finder, Builder) &&
+      InnerMatcher2.matches(*Vars[1].first, Finder, Builder)) {
+    Builder->setBinding(FirstDeclStmtName,
+                        clang::DynTypedNode::create(*Vars[0].second));
+    if (Vars[0].second != Vars[1].second)
+      Builder->setBinding(SecondDeclStmtName,
+                          clang::DynTypedNode::create(*Vars[1].second));
+    return true;
+  }
+
+  return false;
+}
+
+/// Matches a Stmt whose parent is a CompoundStmt, and which is directly
+/// following two VarDecls matching the inner matcher, at the same time set
+/// binding for the CompoundStmt.
+AST_MATCHER_P2(Stmt, hasPreTwoVarDecl, ast_matchers::internal::Matcher<VarDecl>,
+               InnerMatcher1, ast_matchers::internal::Matcher<VarDecl>,
+               InnerMatcher2) {
+  DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
+  if (Parents.size() != 1)
+    return false;
+
+  auto *C = Parents[0].get<CompoundStmt>();
+  if (!C)
+    return false;
+
+  const auto I =
+      llvm::find(llvm::make_range(C->body_rbegin(), C->body_rend()), &Node);
+  assert(I != C->body_rend() && "C is parent of Node");
+  if ((I + 1) == C->body_rend())
+    return false;
+
+  const auto *DS2 = dyn_cast<DeclStmt>(*(I + 1));
+  if (!DS2)
+    return false;
+
+  const DeclStmt *DS1 = (!DS2->isSingleDecl() || ((I + 2) == C->body_rend())
+                             ? nullptr
+                             : dyn_cast<DeclStmt>(*(I + 2)));
+
+  if (matchTwoVarDecl(DS1, DS2, InnerMatcher1, InnerMatcher2, Finder,
+                      Builder)) {
+    Builder->setBinding(ScopeBlockName, clang::DynTypedNode::create(*C));
+    return true;
+  }
+
+  return false;
+}
+
+/// Matches a Stmt whose parent is a CompoundStmt, and which is directly
+/// followed by two VarDecls matching the inner matcher, at the same time set
+/// binding for the CompoundStmt.
+AST_MATCHER_P2(Stmt, hasNextTwoVarDecl,
+               ast_matchers::internal::Matcher<VarDecl>, InnerMatcher1,
+               ast_matchers::internal::Matcher<VarDecl>, InnerMatcher2) {
+  DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
+  if (Parents.size() != 1)
+    return false;
+
+  auto *C = Parents[0].get<CompoundStmt>();
+  if (!C)
+    return false;
+
+  const auto *I = llvm::find(C->body(), &Node);
+  assert(I != C->body_end() && "C is parent of Node");
+  if ((I + 1) == C->body_end())
+    return false;
+
+  if (matchTwoVarDecl(
+          dyn_cast<DeclStmt>(*(I + 1)),
+          ((I + 2) == C->body_end() ? nullptr : dyn_cast<DeclStmt>(*(I + 2))),
+          InnerMatcher1, InnerMatcher2, Finder, Builder)) {
+    Builder->setBinding(ScopeBlockName, clang::DynTypedNode::create(*C));
+    return true;
+  }
+
+  return false;
+}
+
+/// Matches a Stmt whose parent is a CompoundStmt, and there a two VarDecls
+/// matching the inner matcher in the beginning of CompoundStmt.
+AST_MATCHER_P2(CompoundStmt, hasFirstTwoVarDecl,
+               ast_matchers::internal::Matcher<VarDecl>, InnerMatcher1,
+               ast_matchers::internal::Matcher<VarDecl>, InnerMatcher2) {
+  const auto *I = Node.body_begin();
+  if ((I) == Node.body_end())
+    return false;
+
+  return matchTwoVarDecl(
+      dyn_cast<DeclStmt>(*(I)),
+      ((I + 1) == Node.body_end() ? nullptr : dyn_cast<DeclStmt>(*(I + 1))),
+      InnerMatcher1, InnerMatcher2, Finder, Builder);
+}
+
+/// It's not very common to have specifiers for variables used to decompose
+/// a pair, so we ignore these cases.
+AST_MATCHER(VarDecl, hasAnySpecifiersShouldBeIgnored) {
+  return Node.isStaticLocal() || Node.isConstexpr() || Node.hasAttrs() ||
+         Node.isInlineSpecified();
+}
+
+// Ignore nodes inside macros.
+AST_POLYMORPHIC_MATCHER(isInMarco,
+                        AST_POLYMORPHIC_SUPPORTED_TYPES(Stmt, Decl)) {
+  return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID();
+}
+
+AST_MATCHER_P(Expr, ignoringCopyCtorAndImplicitCast,
+              ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
+  if (const auto *CtorE = dyn_cast<CXXConstructExpr>(&Node)) {
+    if (const auto *CtorD = CtorE->getConstructor();
+        CtorD->isCopyConstructor() && CtorE->getNumArgs() == 1) {
+      return InnerMatcher.matches(*CtorE->getArg(0)->IgnoreImpCasts(), Finder,
+                                  Builder);
+    }
+  }
+
+  return InnerMatcher.matches(*Node.IgnoreImpCasts(), Finder, Builder);
+}
+
+} // namespace
+
+UseStructuredBindingCheck::UseStructuredBindingCheck(StringRef Name,
+                                                     ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      PairTypes(utils::options::parseStringList(
+          Options.get("PairTypes", DefaultPairTypes))) {
+  ;
+}
+
+static auto getVarInitWithMemberMatcher(StringRef PairName,
+                                        StringRef MemberName,
+                                        StringRef TypeName,
+                                        StringRef BindingName) {
+  return varDecl(
+             unless(hasAnySpecifiersShouldBeIgnored()), unless(isInMarco()),
+             hasInitializer(
+                 ignoringImpCasts(ignoringCopyCtorAndImplicitCast(memberExpr(
+                     hasObjectExpression(ignoringImpCasts(declRefExpr(
+                         to(equalsBoundNode(std::string(PairName)))))),
+                     member(fieldDecl(hasName(MemberName),
+                                      hasType(qualType().bind(TypeName)))))))))
+      .bind(BindingName);
+}
+
+void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
+  auto PairType =
+      qualType(unless(isVolatileQualified()),
+               hasUnqualifiedDesugaredType(recordType(
+                   hasDeclaration(cxxRecordDecl(hasAnyName(PairTypes))))));
+
+  auto VarInitWithFirstMember = getVarInitWithMemberMatcher(
+      PairDeclName, "first", FirstTypeName, FirstVarDeclName);
+  auto VarInitWithSecondMember = getVarInitWithMemberMatcher(
+      PairDeclName, "second", SecondTypeName, SecondVarDeclName);
+
+  // X x;
+  // Y y;
+  // std::tie(x, y) = ...;
+  Finder->addMatcher(
+      exprWithCleanups(
+          unless(isInMarco()),
+          has(cxxOperatorCallExpr(
+                  hasOverloadedOperatorName("="),
+                  hasLHS(ignoringImplicit(
+                      callExpr(
+                          callee(
+                              functionDecl(isInStdNamespace(), hasName("tie"))),
+                          hasArgument(
+                              0,
+                              declRefExpr(to(
+                                  varDecl(
+                                      unless(hasAnySpecifiersShouldBeIgnored()),
+                                      unless(isInMarco()))
+                                      .bind(FirstVarDeclName)))),
+                          hasArgument(
+                              1,
+                              declRefExpr(to(
+                                  varDecl(
+                                      unless(hasAnySpecifiersShouldBeIgnored()),
+                                      unless(isInMarco()))
+                                      .bind(SecondVarDeclName)))))
+                          .bind(StdTieExprName))),
+                  hasRHS(expr(hasType(PairType))))
+                  .bind(StdTieAssignStmtName)),
+          hasPreTwoVarDecl(
+              varDecl(equalsBoundNode(std::string(FirstVarDeclName))),
+              varDecl(equalsBoundNode(std::string(SecondVarDeclName))))),
+      this);
+
+  // pair<X, Y> p = ...;
+  // X x = p.first;
+  // Y y = p.second;
+  Finder->addMatcher(
+      declStmt(
+          unless(isInMarco()),
+          hasSingleDecl(
+              varDecl(unless(hasAnySpecifiersShouldBeIgnored()),
+                      hasType(qualType(anyOf(PairType, lValueReferenceType(
+                                                           pointee(PairType))))
+                                  .bind(PairVarTypeName)),
+                      hasInitializer(expr()))
+                  .bind(PairDeclName)),
+          hasNextTwoVarDecl(VarInitWithFirstMember, VarInitWithSecondMember)),
+      this);
+
+  // for (pair<X, Y> p : map) {
+  //    X x = p.first;
+  //    Y y = p.second;
+  // }
+  Finder->addMatcher(
+      cxxForRangeStmt(
+          unless(isInMarco()),
+          hasLoopVariable(
+              varDecl(hasType(qualType(anyOf(PairType, lValueReferenceType(
+                                                           pointee(PairType))))
+                                  .bind(PairVarTypeName)),
+                      hasInitializer(expr()))
+                  .bind(PairDeclName)),
+          hasBody(compoundStmt(hasFirstTwoVarDecl(VarInitWithFirstMember,
+                                                  VarInitWithSecondMember))
+                      .bind(ScopeBlockName)))
+          .bind(ForRangeStmtName),
+      this);
+}
+
+static std::optional<TransferType> getTransferType(const ASTContext &Ctx,
+                                                   QualType ResultType,
+                                                   QualType OriginType) {
+  ResultType = ResultType.getCanonicalType();
+  OriginType = OriginType.getCanonicalType();
+
+  if (ResultType == Ctx.getLValueReferenceType(OriginType.withConst()))
+    return TT_ByConstRef;
+
+  if (ResultType == Ctx.getLValueReferenceType(OriginType))
+    return TT_ByRef;
+
+  if (ResultType == OriginType.withConst())
+    return TT_ByConstVal;
+
+  if (ResultType == OriginType)
+    return TT_ByVal;
+
+  return std::nullopt;
+}
+
+void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *FirstVar = Result.Nodes.getNodeAs<VarDecl>(FirstVarDeclName);
+  const auto *SecondVar = Result.Nodes.getNodeAs<VarDecl>(SecondVarDeclName);
+
+  const auto *DS1 = Result.Nodes.getNodeAs<DeclStmt>(FirstDeclStmtName);
+  const auto *DS2 = Result.Nodes.getNodeAs<DeclStmt>(SecondDeclStmtName);
+  const auto *ScopeBlock = Result.Nodes.getNodeAs<CompoundStmt>(ScopeBlockName);
+
+  // Captured structured bindings are a C++20 extension
+  if (!Result.Context->getLangOpts().CPlusPlus20) {
+    if (auto Matchers = match(
+            compoundStmt(
+                hasDescendant(lambdaExpr(hasAnyCapture(capturesVar(varDecl(
+                    anyOf(equalsNode(FirstVar), equalsNode(SecondVar)))))))),
+            *ScopeBlock, *Result.Context);
+        !Matchers.empty())
+      return;
+  }
+
+  const auto *CFRS = Result.Nodes.getNodeAs<CXXForRangeStmt>(ForRangeStmtName);
+  auto DiagAndFix = [&](SourceLocation DiagLoc, SourceRange ReplaceRange,
+                        TransferType TT = TT_ByVal) {
+    StringRef Prefix;
+    switch (TT) {
+    case TT_ByVal:
+      Prefix = "auto";
+      break;
+    case TT_ByConstVal:
+      Prefix = "const auto";
+      break;
+    case TT_ByRef:
+      Prefix = "auto&";
+      break;
+    case TT_ByConstRef:
+      Prefix = "const auto&";
+      break;
+    }
+    std::vector<FixItHint> Hints;
+    if (DS1)
+      Hints.emplace_back(FixItHint::CreateRemoval(DS1->getSourceRange()));
+    if (DS2)
+      Hints.emplace_back(FixItHint::CreateRemoval(DS2->getSourceRange()));
+
+    std::string ReplacementText = Prefix.str() + " [" +
+                                  FirstVar->getNameAsString() + ", " +
+                                  SecondVar->getNameAsString() + "]";
+    if (CFRS)
+      ReplacementText += " :";
+    diag(DiagLoc, "Should use structured binding to decompose pair")
+        << FixItHint::CreateReplacement(ReplaceRange, ReplacementText) << Hints;
+  };
+
+  if (const auto *COCE =
+          Result.Nodes.getNodeAs<CXXOperatorCallExpr>(StdTieAssignStmtName)) {
+    DiagAndFix(COCE->getBeginLoc(),
+               Result.Nodes.getNodeAs<Expr>(StdTieExprName)->getSourceRange());
+    return;
+  }
+
+  // Check whether PairVar, FirstVar and SecondVar have the same transfer type,
+  // so they can be combined to structured binding.
+  const auto *PairVar = Result.Nodes.getNodeAs<VarDecl>(PairDeclName);
+  const Expr *InitE = PairVar->getInit();
+  if (auto Res =
+          match(expr(ignoringCopyCtorAndImplicitCast(expr().bind("init_expr"))),
+                *InitE, *Result.Context);
+      !Res.empty())
+    InitE = Res[0].getNodeAs<Expr>("init_expr");
+
+  std::optional<TransferType> PairCaptureType =
+      getTransferType(*Result.Context, PairVar->getType(), InitE->getType());
+  std::optional<TransferType> FirstVarCaptureType =
+      getTransferType(*Result.Context, FirstVar->getType(),
+                      *Result.Nodes.getNodeAs<QualType>(FirstTypeName));
+  std::optional<TransferType> SecondVarCaptureType =
+      getTransferType(*Result.Context, SecondVar->getType(),
+                      *Result.Nodes.getNodeAs<QualType>(SecondTypeName));
+  if (!PairCaptureType || !FirstVarCaptureType || !SecondVarCaptureType ||
+      *PairCaptureType != *FirstVarCaptureType ||
+      *FirstVarCaptureType != *SecondVarCaptureType)
+    return;
+
+  // Check PairVar is not used except for assignment members to firstVar and
+  // SecondVar.
+  if (auto AllRef = utils::decl_ref_expr::allDeclRefExprs(*PairVar, *ScopeBlock,
+                                                          *Result.Context);
+      AllRef.size() != 2)
+    return;
+
+  DiagAndFix(PairVar->getBeginLoc(),
+             CFRS ? PairVar->getSourceRange()
+                  : SourceRange(PairVar->getBeginLoc(),
+                                Lexer::getLocForEndOfToken(
+                                    PairVar->getLocation(), 0,
+                                    Result.Context->getSourceManager(),
+                                    Result.Context->getLangOpts())),
+             *PairCaptureType);
+}
+
+} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
new file mode 100644
index 0000000000000..63bc0a8c3da45
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
@@ -0,0 +1,36 @@
+//===--- UseStructuredBindingCheck.h - clang-tidy ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTRUCTUREDBINDINGCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTRUCTUREDBINDINGCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::modernize {
+
+/// Finds places where structured bindings could be used to decompose pairs and
+/// suggests replacing them.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-structured-binding.html
+class UseStructuredBindingCheck : public ClangTidyCheck {
+public:
+  UseStructuredBindingCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus17;
+  }
+
+private:
+  const std::vector<StringRef> PairTypes;
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTRUCTUREDBINDINGCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 34091906cbff2..8302cbf64f095 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -179,6 +179,12 @@ New checks
   Finds virtual function overrides with different visibility than the function
   in the base class.
 
+- New :doc:`modernize-use-structured-binding
+  <clang-tidy/checks/modernize/use-structured-binding>` check.
+
+  Finds places where structured bindings could be used to decompose pairs and
+  suggests replacing them.
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index c490d2ece2e0a..843a031dd2dd1 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -324,6 +324,7 @@ Clang-Tidy Checks
    :doc:`modernize-use-std-format <modernize/use-std-format>`, "Yes"
    :doc:`modernize-use-std-numbers <modernize/use-std-numbers>`, "Yes"
    :doc:`modernize-use-std-print <modernize/use-std-print>`, "Yes"
+   :doc:`modernize-use-structured-binding <modernize/use-structured-binding>`, "Yes"
    :doc:`modernize-use-trailing-return-type <modernize/use-trailing-return-type>`, "Yes"
    :doc:`modernize-use-transparent-functors <modernize/use-transparent-functors>`, "Yes"
    :doc:`modernize-use-uncaught-exceptions <modernize/use-uncaught-exceptions>`, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
new file mode 100644
index 0000000000000..66af859d4428f
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -0,0 +1,58 @@
+.. title:: clang-tidy - modernize-use-structured-binding
+
+modernize-use-structured-binding
+================================
+
+Suggests using C++17 structured bindings to decompose pairs.
+
+This check finds three code patterns and recommends using structured bindings for clearer, more idiomatic C++17 code.
+
+1. Decompose a pair variable by assigning its members to separate variables right after its definition:
+
+.. code-block:: c++
+
+  auto p = getPair<int, int>();
+  int x = p.first;
+  int y = p.second;
+
+  into:
+
+  auto [x, y] = getPair<int, int>();
+
+2. Use `std::tie` to decompose a pair into two predefined variables:
+
+.. code-block:: c++
+
+  int a;
+  int b;
+  std::tie(a, b) = getPair<int, int>();
+
+  into:
+
+  auto [a, b] = getPair<int, int>();
+
+3. Manually decompose a pair by assigning to its members to local variables in a range-based for loop:
+
+.. code-block:: c++
+
+  for (autop : vecOfPairs) {
+    int x = p.first;
+    int y = p.second;
+    // ...
+  }
+
+  into:
+
+  for (auto [x, y] : vecOfPairs) {
+    // use x and y
+  }
+
+The check also supports custom pair-like types via the `PairTypes` option.
+
+Options
+-------
+
+.. option:: PairTypes
+
+   A Semicolon-separated list of type names to be treated as pair-like for structured binding suggestions.  
+   Example: `PairTypes=MyPairType; OtherPairType`. Default is `std::pair`.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-structured-binding/fake_std_pair_tuple.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-structured-binding/fake_std_pair_tuple.h
new file mode 100644
index 0000000000000..b77341c852edb
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-structured-binding/fake_std_pair_tuple.h
@@ -0,0 +1,23 @@
+namespace std {
+  template<typename T1, typename T2>
+  struct pair {
+    T1 first;
+    T2 second;
+  };
+
+  template<typename... Args>
+  struct tuple {
+    tuple(Args&...) {}
+
+    template<typename T1, typename T2>
+    tuple<T1, T2> operator=(const std::pair<T1, T2>&);
+  };
+
+  template<typename... Args>
+  tuple<Args...> tie(Args&... args) {
+    return tuple<Args...>(args...);
+  }
+}
+
+template<typename T1, typename T2>
+std::pair<T1, T2> getPair();
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
new file mode 100644
index 0000000000000..d6d73430d6a3c
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
@@ -0,0 +1,32 @@
+// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-structured-binding %t \
+// RUN: -config="{CheckOptions: {modernize-use-structured-binding.PairTypes: 'custom::pair; otherPair'}}"
+
+namespace custom {
+  struct pair {
+    int first;
+    int second;
+  };
+}
+
+struct otherPair {
+  int first;
+  int second;
+};
+
+void OptionTest() {
+  {
+    auto P = custom::pair();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-FIXES: {{^}} auto [x, y] = custom::pair();
+    int x = P.first;
+    int y = P.second;
+  }
+
+  {
+    auto P = otherPair();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-FIXES: {{^}} auto [x, y] = otherPair();
+    int x = P.first;
+    int y = P.second;
+  }
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp
new file mode 100644
index 0000000000000..57f9f3488fa21
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp
@@ -0,0 +1,67 @@
+// RUN: %check_clang_tidy -std=c++17 %s modernize-use-structured-binding %t -- -- -I %S/Inputs/use-structured-binding/
+
+#include "fake_std_pair_tuple.h"
+
+void captureByVal() {
+  auto P = getPair<int, int>();
+  int x = P.first;
+  int y = P.second;
+
+  auto lambda = [x]() {
+    int y = x;
+  };
+}
+
+void captureByRef() {
+  auto P = getPair<int, int>();
+  int x = P.first;
+  int y = P.second;
+
+  auto lambda = [&x]() {
+    x = 1;
+  };
+}
+
+void captureByAllRef() {
+  auto P = getPair<int, int>();
+  int x = P.first;
+  int y = P.second;
+
+  auto lambda = [&]() {
+    x = 1;
+  };
+}
+
+void deepLambda() {
+  auto P = getPair<int, int>();
+  int x = P.first;
+  int y = P.second;
+
+  {
+    auto lambda = [x]() {
+      int y = x;
+    };
+  }
+}
+
+void forRangeNotWarn() {
+  std::pair<int, int> Pairs[10];
+  for (auto P : Pairs) {
+    int x = P.first;
+    int y = P.second;
+
+    auto lambda = [&]() {
+    x = 1;
+  };
+  }
+}
+
+void stdTieNotWarn() {
+  int x = 0;
+  int y = 0;
+  std::tie(x, y) = getPair<int, int>();
+
+  auto lambda = [&x]() {
+    x = 1;
+  };
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
new file mode 100644
index 0000000000000..97da76fea1d5f
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -0,0 +1,216 @@
+// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-structured-binding %t -- -- -I %S/Inputs/use-structured-binding/
+
+#include "fake_std_pair_tuple.h"
+
+template<typename T>
+void MarkUsed(T x);
+
+struct TestClass {
+  int a;
+  int b;
+  TestClass() : a(0), b(0) {}
+  TestClass(int x, int y) : a(x), b(y) {}
+};
+
+void DecomposeByAssignWarnCases() {
+  {
+    auto P = getPair<int, int>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-FIXES: {{^}} auto [x, y] = getPair<int, int>();
+    int x = P.first;
+    int y = P.second;
+  }
+
+  {
+    auto P = getPair<int, int>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-FIXES: {{^}} auto [x, y] = getPair<int, int>();
+    int x = P.first;
+    auto y = P.second;
+  }
+
+  {
+    const auto P = getPair<int, int>();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-FIXES: {{^}} const auto [x, y] = getPair<int, int>();
+    const int x = P.first;
+    const auto y = P.second;
+  }
+
+  {
+    std::pair<int, int> otherP;
+    auto& P = otherP;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-FIXES: {{^}} auto& [x, y] = otherP;
+    int& x = P.first;
+    auto& y = P.second;
+  }
+
+  {
+    std::pair<int, int> otherP;
+    const auto& P = otherP;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-FIXES: {{^}} const auto& [x, y] = otherP;
+    const int& x = P.first;
+    const auto& y = P.second;
+  }
+}
+
+void forRangeWarnCases() {
+  std::pair<int, int> Pairs[10];
+  for (auto P : Pairs) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-FIXES: {{^}} for (auto [x, y] : Pairs) {
+    int x = P.first;
+    int y = P.second;
+  }
+
+  for (const auto P : Pairs) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-FIXES: {{^}} for (const auto [x, y] : Pairs) {
+    const int x = P.first;
+    const int y = P.second;
+  }
+
+  for (auto& P : Pairs) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-FIXES: {{^}} for (auto& [x, y] : Pairs) {
+    int& x = P.first;
+    int& y = P.second;
+  }
+
+  for (const auto& P : Pairs) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-FIXES: {{^}} for (const auto& [x, y] : Pairs) {
+    const int& x = P.first;
+    const int& y = P.second;
+  }
+
+  std::pair<TestClass, TestClass> ClassPairs[10];
+  for (auto P : ClassPairs) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-FIXES: {{^}} for (auto [c1, c2] : ClassPairs) {
+    TestClass c1 = P.first;
+    TestClass c2 = P.second;
+  }
+
+  for (const auto P : ClassPairs) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-FIXES: {{^}} for (const auto [c1, c2] : ClassPairs) {
+    const TestClass c1 = P.first;
+    const TestClass c2 = P.second;
+  }
+}
+
+void stdTieWarnCases() {
+  int a = 0;
+  int b = 0;
+  std::tie(a, b) = getPair<int, int>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+  // CHECK-FIXES: {{^}} auto [a, b] = getPair<int, int>();
+
+  int* pa = nullptr;
+  int* pb = nullptr;
+  std::tie(pa, pb) = getPair<int*, int*>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+  // CHECK-FIXES: {{^}} auto [pa, pb] = getPair<int*, int*>();
+
+  TestClass c1 (1, 2);
+  TestClass c2 = TestClass {3, 4};
+  std::tie(c1, c2) = getPair<TestClass, TestClass>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+  // CHECK-FIXES: {{^}} auto [c1, c2] = getPair<TestClass, TestClass>();
+}
+
+void stdTieNotWarnCases() {
+  int a = 0;
+  int b = 0;
+  a = 4;
+  std::tie(a, b) = getPair<int, int>(); // no warning
+
+  int* pa = nullptr;
+  int* pb = nullptr;
+  MarkUsed(pa);
+  std::tie(pa, pb) = getPair<int*, int*>(); // no warning
+
+  TestClass c1 (1, 2);
+  TestClass c2 = TestClass {3, 4};
+  MarkUsed(c2);
+  std::tie(c1, c2) = getPair<TestClass, TestClass>();
+}
+
+void NotWarnForVarHasSpecifiers() {
+  {
+    auto P = getPair<int, int>();
+    const int x = P.first;
+    int y = P.second;
+  }
+
+  {
+    auto P = getPair<int, int>();
+    volatile int x = P.first;
+    int y = P.second;
+  }
+
+  {
+    auto P = getPair<int, int>();
+    int x = P.first;
+    [[maybe_unused]] int y = P.second;
+  }
+
+  {
+    static auto P = getPair<int, int>();
+    int x = P.first;
+    int y = P.second;
+  }
+}
+
+void NotWarnForMultiUsedPairVar() {
+  {
+    auto P = getPair<int, int>();
+    int x = P.first;
+    int y = P.second;
+    MarkUsed(P);
+  }
+
+  {
+    auto P = getPair<int, int>();
+    int x = P.first;
+    MarkUsed(P);
+    int y = P.second;
+  }
+
+  {
+    auto P = getPair<int, int>();
+    MarkUsed(P);
+    int x = P.first;
+    int y = P.second;
+  }
+
+  {
+    std::pair<int, int> Pairs[10];
+    for (auto P : Pairs) {
+      int x = P.first;
+      int y = P.second;
+
+      MarkUsed(P);
+    }
+  }
+}
+
+#define DECOMPOSE(P)                                                    \
+    int x = P.first;                                                    \
+    int y = P.second;                                                   \
+
+void NotWarnForMacro1() {
+  auto P = getPair<int, int>();
+  DECOMPOSE(P);
+}
+
+#define GETPAIR auto P = getPair<int, int>()
+
+void NotWarnForMacro2() {
+  GETPAIR;
+  int x = P.first;
+  int y = P.second;
+}

>From dd9bb2a4c980877f384d8159233c1fb75d3217fc Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 16:40:16 +0800
Subject: [PATCH 02/48] Update
 clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst

Co-authored-by: Baranov Victor <bar.victor.2002 at gmail.com>
---
 .../docs/clang-tidy/checks/modernize/use-structured-binding.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 66af859d4428f..e25dd8e2130f3 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -55,4 +55,4 @@ Options
 .. option:: PairTypes
 
    A Semicolon-separated list of type names to be treated as pair-like for structured binding suggestions.  
-   Example: `PairTypes=MyPairType; OtherPairType`. Default is `std::pair`.
+   Example: `MyPairType;OtherPairType`. Default is `std::pair`.

>From e37026ab52eb32b9b49aa50e1def060a0be0e51c Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 16:40:26 +0800
Subject: [PATCH 03/48] Update
 clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst

Co-authored-by: Baranov Victor <bar.victor.2002 at gmail.com>
---
 .../clang-tidy/checks/modernize/use-structured-binding.rst     | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index e25dd8e2130f3..377f48b822ded 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -3,7 +3,8 @@
 modernize-use-structured-binding
 ================================
 
-Suggests using C++17 structured bindings to decompose pairs.
+Finds places where structured bindings could be used to decompose pairs and
+suggests replacing them.
 
 This check finds three code patterns and recommends using structured bindings for clearer, more idiomatic C++17 code.
 

>From 1d01ffd04ccb87591d88e7f56391f81982cb8c8c Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 16:40:48 +0800
Subject: [PATCH 04/48] Update
 clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst

Co-authored-by: Baranov Victor <bar.victor.2002 at gmail.com>
---
 .../docs/clang-tidy/checks/modernize/use-structured-binding.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 377f48b822ded..38f4daaf97ec6 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -20,7 +20,7 @@ This check finds three code patterns and recommends using structured bindings fo
 
   auto [x, y] = getPair<int, int>();
 
-2. Use `std::tie` to decompose a pair into two predefined variables:
+2. Use ``std::tie`` to decompose a pair into two predefined variables:
 
 .. code-block:: c++
 

>From 4127b7b7c73720c0a170cf4b334d7de687bdccf7 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 16:41:17 +0800
Subject: [PATCH 05/48] Update
 clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst

Co-authored-by: Baranov Victor <bar.victor.2002 at gmail.com>
---
 .../docs/clang-tidy/checks/modernize/use-structured-binding.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 38f4daaf97ec6..0d227b75968a7 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -48,7 +48,7 @@ This check finds three code patterns and recommends using structured bindings fo
     // use x and y
   }
 
-The check also supports custom pair-like types via the `PairTypes` option.
+The check also supports custom pair-like types via the :option:`PairTypes` option.
 
 Options
 -------

>From f6c129b090262365fe0db9073a4a5a2ef9a73da0 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 16:44:32 +0800
Subject: [PATCH 06/48] [NFC] Change warning message to 'use structured binding
 to decompose a pair'

---
 .../modernize/UseStructuredBindingCheck.cpp   |  2 +-
 .../use-structured-binding-custom.cpp         |  4 +--
 .../modernize/use-structured-binding.cpp      | 28 +++++++++----------
 3 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index d6d6ae6cb83b3..fab4e8dc16cf1 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -365,7 +365,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
                                   SecondVar->getNameAsString() + "]";
     if (CFRS)
       ReplacementText += " :";
-    diag(DiagLoc, "Should use structured binding to decompose pair")
+    diag(DiagLoc, "use structured binding to decompose a pair")
         << FixItHint::CreateReplacement(ReplaceRange, ReplacementText) << Hints;
   };
 
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
index d6d73430d6a3c..a007447d172b7 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
@@ -16,7 +16,7 @@ struct otherPair {
 void OptionTest() {
   {
     auto P = custom::pair();
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: {{^}} auto [x, y] = custom::pair();
     int x = P.first;
     int y = P.second;
@@ -24,7 +24,7 @@ void OptionTest() {
 
   {
     auto P = otherPair();
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: {{^}} auto [x, y] = otherPair();
     int x = P.first;
     int y = P.second;
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 97da76fea1d5f..a4016397f52ef 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -15,7 +15,7 @@ struct TestClass {
 void DecomposeByAssignWarnCases() {
   {
     auto P = getPair<int, int>();
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: {{^}} auto [x, y] = getPair<int, int>();
     int x = P.first;
     int y = P.second;
@@ -23,7 +23,7 @@ void DecomposeByAssignWarnCases() {
 
   {
     auto P = getPair<int, int>();
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: {{^}} auto [x, y] = getPair<int, int>();
     int x = P.first;
     auto y = P.second;
@@ -31,7 +31,7 @@ void DecomposeByAssignWarnCases() {
 
   {
     const auto P = getPair<int, int>();
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: {{^}} const auto [x, y] = getPair<int, int>();
     const int x = P.first;
     const auto y = P.second;
@@ -40,7 +40,7 @@ void DecomposeByAssignWarnCases() {
   {
     std::pair<int, int> otherP;
     auto& P = otherP;
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: {{^}} auto& [x, y] = otherP;
     int& x = P.first;
     auto& y = P.second;
@@ -49,7 +49,7 @@ void DecomposeByAssignWarnCases() {
   {
     std::pair<int, int> otherP;
     const auto& P = otherP;
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: {{^}} const auto& [x, y] = otherP;
     const int& x = P.first;
     const auto& y = P.second;
@@ -59,28 +59,28 @@ void DecomposeByAssignWarnCases() {
 void forRangeWarnCases() {
   std::pair<int, int> Pairs[10];
   for (auto P : Pairs) {
-    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: {{^}} for (auto [x, y] : Pairs) {
     int x = P.first;
     int y = P.second;
   }
 
   for (const auto P : Pairs) {
-    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: {{^}} for (const auto [x, y] : Pairs) {
     const int x = P.first;
     const int y = P.second;
   }
 
   for (auto& P : Pairs) {
-    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: {{^}} for (auto& [x, y] : Pairs) {
     int& x = P.first;
     int& y = P.second;
   }
 
   for (const auto& P : Pairs) {
-    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: {{^}} for (const auto& [x, y] : Pairs) {
     const int& x = P.first;
     const int& y = P.second;
@@ -88,14 +88,14 @@ void forRangeWarnCases() {
 
   std::pair<TestClass, TestClass> ClassPairs[10];
   for (auto P : ClassPairs) {
-    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: {{^}} for (auto [c1, c2] : ClassPairs) {
     TestClass c1 = P.first;
     TestClass c2 = P.second;
   }
 
   for (const auto P : ClassPairs) {
-    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: {{^}} for (const auto [c1, c2] : ClassPairs) {
     const TestClass c1 = P.first;
     const TestClass c2 = P.second;
@@ -106,19 +106,19 @@ void stdTieWarnCases() {
   int a = 0;
   int b = 0;
   std::tie(a, b) = getPair<int, int>();
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES: {{^}} auto [a, b] = getPair<int, int>();
 
   int* pa = nullptr;
   int* pb = nullptr;
   std::tie(pa, pb) = getPair<int*, int*>();
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES: {{^}} auto [pa, pb] = getPair<int*, int*>();
 
   TestClass c1 (1, 2);
   TestClass c2 = TestClass {3, 4};
   std::tie(c1, c2) = getPair<TestClass, TestClass>();
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Should use structured binding to decompose pair [modernize-use-structured-binding]
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES: {{^}} auto [c1, c2] = getPair<TestClass, TestClass>();
 }
 

>From 24e9d4ea3ebfa20d804aafb420bf5a34cc0e86aa Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 16:53:53 +0800
Subject: [PATCH 07/48] [NFC] Add `forRangeNotWarnCases` in test file

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

diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index a4016397f52ef..033b380585258 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -102,6 +102,35 @@ void forRangeWarnCases() {
   }
 }
 
+void forRangeNotWarnCases() {
+  std::pair<int, int> Pairs[10];
+  for (auto P : Pairs) {
+    int x = P.first;
+    MarkUsed(x);
+    int y = P.second;
+  }
+
+  for (auto P : Pairs) {
+    MarkUsed(P);
+    int x = P.first;
+    int y = P.second;
+  }
+
+  for (auto P : Pairs) {
+    int x = P.first;
+    int y = P.second;
+    MarkUsed(P);
+  }
+
+  std::pair<TestClass, TestClass> ClassPairs[10];
+  int c;
+  for (auto P : ClassPairs) {
+    TestClass c1 = P.first;
+    c ++ ;
+    TestClass c2 = P.second;
+  }
+}
+
 void stdTieWarnCases() {
   int a = 0;
   int b = 0;

>From 08fd8db44d728ed0cdafcd5f88ac4bc8a0819e24 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 16:56:27 +0800
Subject: [PATCH 08/48] [NFC] Remove {{^}}

---
 .../use-structured-binding-custom.cpp         |  4 +--
 .../modernize/use-structured-binding.cpp      | 28 +++++++++----------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
index a007447d172b7..bd335e1edaf6e 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
@@ -17,7 +17,7 @@ void OptionTest() {
   {
     auto P = custom::pair();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: {{^}} auto [x, y] = custom::pair();
+    // CHECK-FIXES: auto [x, y] = custom::pair();
     int x = P.first;
     int y = P.second;
   }
@@ -25,7 +25,7 @@ void OptionTest() {
   {
     auto P = otherPair();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: {{^}} auto [x, y] = otherPair();
+    // CHECK-FIXES: auto [x, y] = otherPair();
     int x = P.first;
     int y = P.second;
   }
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 033b380585258..b2246e68a4d73 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -16,7 +16,7 @@ void DecomposeByAssignWarnCases() {
   {
     auto P = getPair<int, int>();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: {{^}} auto [x, y] = getPair<int, int>();
+    // CHECK-FIXES: auto [x, y] = getPair<int, int>();
     int x = P.first;
     int y = P.second;
   }
@@ -24,7 +24,7 @@ void DecomposeByAssignWarnCases() {
   {
     auto P = getPair<int, int>();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: {{^}} auto [x, y] = getPair<int, int>();
+    // CHECK-FIXES: auto [x, y] = getPair<int, int>();
     int x = P.first;
     auto y = P.second;
   }
@@ -32,7 +32,7 @@ void DecomposeByAssignWarnCases() {
   {
     const auto P = getPair<int, int>();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: {{^}} const auto [x, y] = getPair<int, int>();
+    // CHECK-FIXES: const auto [x, y] = getPair<int, int>();
     const int x = P.first;
     const auto y = P.second;
   }
@@ -41,7 +41,7 @@ void DecomposeByAssignWarnCases() {
     std::pair<int, int> otherP;
     auto& P = otherP;
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: {{^}} auto& [x, y] = otherP;
+    // CHECK-FIXES: auto& [x, y] = otherP;
     int& x = P.first;
     auto& y = P.second;
   }
@@ -50,7 +50,7 @@ void DecomposeByAssignWarnCases() {
     std::pair<int, int> otherP;
     const auto& P = otherP;
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: {{^}} const auto& [x, y] = otherP;
+    // CHECK-FIXES: const auto& [x, y] = otherP;
     const int& x = P.first;
     const auto& y = P.second;
   }
@@ -60,28 +60,28 @@ void forRangeWarnCases() {
   std::pair<int, int> Pairs[10];
   for (auto P : Pairs) {
     // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: {{^}} for (auto [x, y] : Pairs) {
+    // CHECK-FIXES: for (auto [x, y] : Pairs) {
     int x = P.first;
     int y = P.second;
   }
 
   for (const auto P : Pairs) {
     // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: {{^}} for (const auto [x, y] : Pairs) {
+    // CHECK-FIXES: for (const auto [x, y] : Pairs) {
     const int x = P.first;
     const int y = P.second;
   }
 
   for (auto& P : Pairs) {
     // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: {{^}} for (auto& [x, y] : Pairs) {
+    // CHECK-FIXES: for (auto& [x, y] : Pairs) {
     int& x = P.first;
     int& y = P.second;
   }
 
   for (const auto& P : Pairs) {
     // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: {{^}} for (const auto& [x, y] : Pairs) {
+    // CHECK-FIXES: for (const auto& [x, y] : Pairs) {
     const int& x = P.first;
     const int& y = P.second;
   }
@@ -89,14 +89,14 @@ void forRangeWarnCases() {
   std::pair<TestClass, TestClass> ClassPairs[10];
   for (auto P : ClassPairs) {
     // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: {{^}} for (auto [c1, c2] : ClassPairs) {
+    // CHECK-FIXES: for (auto [c1, c2] : ClassPairs) {
     TestClass c1 = P.first;
     TestClass c2 = P.second;
   }
 
   for (const auto P : ClassPairs) {
     // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: {{^}} for (const auto [c1, c2] : ClassPairs) {
+    // CHECK-FIXES: for (const auto [c1, c2] : ClassPairs) {
     const TestClass c1 = P.first;
     const TestClass c2 = P.second;
   }
@@ -136,19 +136,19 @@ void stdTieWarnCases() {
   int b = 0;
   std::tie(a, b) = getPair<int, int>();
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-  // CHECK-FIXES: {{^}} auto [a, b] = getPair<int, int>();
+  // CHECK-FIXES: auto [a, b] = getPair<int, int>();
 
   int* pa = nullptr;
   int* pb = nullptr;
   std::tie(pa, pb) = getPair<int*, int*>();
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-  // CHECK-FIXES: {{^}} auto [pa, pb] = getPair<int*, int*>();
+  // CHECK-FIXES: auto [pa, pb] = getPair<int*, int*>();
 
   TestClass c1 (1, 2);
   TestClass c2 = TestClass {3, 4};
   std::tie(c1, c2) = getPair<TestClass, TestClass>();
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-  // CHECK-FIXES: {{^}} auto [c1, c2] = getPair<TestClass, TestClass>();
+  // CHECK-FIXES: auto [c1, c2] = getPair<TestClass, TestClass>();
 }
 
 void stdTieNotWarnCases() {

>From 49bfcae8c804af3eed8369dd736f2283644fc7ce Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 17:04:53 +0800
Subject: [PATCH 09/48] [NFC] Add more testcase

---
 .../checkers/modernize/use-structured-binding.cpp          | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index b2246e68a4d73..1037944de26dd 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -9,6 +9,7 @@ struct TestClass {
   int a;
   int b;
   TestClass() : a(0), b(0) {}
+  TestClass& operator++();
   TestClass(int x, int y) : a(x), b(y) {}
 };
 
@@ -123,6 +124,12 @@ void forRangeNotWarnCases() {
   }
 
   std::pair<TestClass, TestClass> ClassPairs[10];
+  for (auto P : ClassPairs) {
+    TestClass c1 = P.first;
+    ++ c1 ;
+    TestClass c2 = P.second;
+  }
+
   int c;
   for (auto P : ClassPairs) {
     TestClass c1 = P.first;

>From aa3bf50840d07f276cd4a032d5eaf12c11cfa51b Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 17:53:53 +0800
Subject: [PATCH 10/48] [NFC] Merge lambda testcase to main test file for also
 test c++20-or-later

---
 ...d-binding-skip-lambda-capture-in-cxx17.cpp |  67 ---------
 .../modernize/use-structured-binding.cpp      | 136 ++++++++++++++----
 2 files changed, 106 insertions(+), 97 deletions(-)
 delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp

diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp
deleted file mode 100644
index 57f9f3488fa21..0000000000000
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-skip-lambda-capture-in-cxx17.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-// RUN: %check_clang_tidy -std=c++17 %s modernize-use-structured-binding %t -- -- -I %S/Inputs/use-structured-binding/
-
-#include "fake_std_pair_tuple.h"
-
-void captureByVal() {
-  auto P = getPair<int, int>();
-  int x = P.first;
-  int y = P.second;
-
-  auto lambda = [x]() {
-    int y = x;
-  };
-}
-
-void captureByRef() {
-  auto P = getPair<int, int>();
-  int x = P.first;
-  int y = P.second;
-
-  auto lambda = [&x]() {
-    x = 1;
-  };
-}
-
-void captureByAllRef() {
-  auto P = getPair<int, int>();
-  int x = P.first;
-  int y = P.second;
-
-  auto lambda = [&]() {
-    x = 1;
-  };
-}
-
-void deepLambda() {
-  auto P = getPair<int, int>();
-  int x = P.first;
-  int y = P.second;
-
-  {
-    auto lambda = [x]() {
-      int y = x;
-    };
-  }
-}
-
-void forRangeNotWarn() {
-  std::pair<int, int> Pairs[10];
-  for (auto P : Pairs) {
-    int x = P.first;
-    int y = P.second;
-
-    auto lambda = [&]() {
-    x = 1;
-  };
-  }
-}
-
-void stdTieNotWarn() {
-  int x = 0;
-  int y = 0;
-  std::tie(x, y) = getPair<int, int>();
-
-  auto lambda = [&x]() {
-    x = 1;
-  };
-}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 1037944de26dd..05b842e4d6614 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -1,5 +1,5 @@
-// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-structured-binding %t -- -- -I %S/Inputs/use-structured-binding/
-
+// RUN: %check_clang_tidy -check-suffix=ALL,CPP20ORLATER -std=c++20-or-later %s modernize-use-structured-binding %t -- -- -I %S/Inputs/use-structured-binding/
+// RUN: %check_clang_tidy -check-suffix=ALL -std=c++17 %s modernize-use-structured-binding %t -- -- -I %S/Inputs/use-structured-binding/
 #include "fake_std_pair_tuple.h"
 
 template<typename T>
@@ -16,24 +16,24 @@ struct TestClass {
 void DecomposeByAssignWarnCases() {
   {
     auto P = getPair<int, int>();
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: auto [x, y] = getPair<int, int>();
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
     int x = P.first;
     int y = P.second;
   }
 
   {
     auto P = getPair<int, int>();
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: auto [x, y] = getPair<int, int>();
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
     int x = P.first;
     auto y = P.second;
   }
 
   {
     const auto P = getPair<int, int>();
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: const auto [x, y] = getPair<int, int>();
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: const auto [x, y] = getPair<int, int>();
     const int x = P.first;
     const auto y = P.second;
   }
@@ -41,8 +41,8 @@ void DecomposeByAssignWarnCases() {
   {
     std::pair<int, int> otherP;
     auto& P = otherP;
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: auto& [x, y] = otherP;
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: auto& [x, y] = otherP;
     int& x = P.first;
     auto& y = P.second;
   }
@@ -50,8 +50,8 @@ void DecomposeByAssignWarnCases() {
   {
     std::pair<int, int> otherP;
     const auto& P = otherP;
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: const auto& [x, y] = otherP;
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: const auto& [x, y] = otherP;
     const int& x = P.first;
     const auto& y = P.second;
   }
@@ -60,44 +60,44 @@ void DecomposeByAssignWarnCases() {
 void forRangeWarnCases() {
   std::pair<int, int> Pairs[10];
   for (auto P : Pairs) {
-    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: for (auto [x, y] : Pairs) {
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
     int x = P.first;
     int y = P.second;
   }
 
   for (const auto P : Pairs) {
-    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: for (const auto [x, y] : Pairs) {
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: for (const auto [x, y] : Pairs) {
     const int x = P.first;
     const int y = P.second;
   }
 
   for (auto& P : Pairs) {
-    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: for (auto& [x, y] : Pairs) {
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: for (auto& [x, y] : Pairs) {
     int& x = P.first;
     int& y = P.second;
   }
 
   for (const auto& P : Pairs) {
-    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: for (const auto& [x, y] : Pairs) {
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: for (const auto& [x, y] : Pairs) {
     const int& x = P.first;
     const int& y = P.second;
   }
 
   std::pair<TestClass, TestClass> ClassPairs[10];
   for (auto P : ClassPairs) {
-    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: for (auto [c1, c2] : ClassPairs) {
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: for (auto [c1, c2] : ClassPairs) {
     TestClass c1 = P.first;
     TestClass c2 = P.second;
   }
 
   for (const auto P : ClassPairs) {
-    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: for (const auto [c1, c2] : ClassPairs) {
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: for (const auto [c1, c2] : ClassPairs) {
     const TestClass c1 = P.first;
     const TestClass c2 = P.second;
   }
@@ -142,20 +142,20 @@ void stdTieWarnCases() {
   int a = 0;
   int b = 0;
   std::tie(a, b) = getPair<int, int>();
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-  // CHECK-FIXES: auto [a, b] = getPair<int, int>();
+  // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-FIXES-ALL: auto [a, b] = getPair<int, int>();
 
   int* pa = nullptr;
   int* pb = nullptr;
   std::tie(pa, pb) = getPair<int*, int*>();
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-  // CHECK-FIXES: auto [pa, pb] = getPair<int*, int*>();
+  // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-FIXES-ALL: auto [pa, pb] = getPair<int*, int*>();
 
   TestClass c1 (1, 2);
   TestClass c2 = TestClass {3, 4};
   std::tie(c1, c2) = getPair<TestClass, TestClass>();
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
-  // CHECK-FIXES: auto [c1, c2] = getPair<TestClass, TestClass>();
+  // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-FIXES-ALL: auto [c1, c2] = getPair<TestClass, TestClass>();
 }
 
 void stdTieNotWarnCases() {
@@ -250,3 +250,79 @@ void NotWarnForMacro2() {
   int x = P.first;
   int y = P.second;
 }
+
+void captureByVal() {
+  auto P = getPair<int, int>();
+  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
+  int x = P.first;
+  int y = P.second;
+
+  auto lambda = [x]() {
+    int y = x;
+  };
+}
+
+void captureByRef() {
+  auto P = getPair<int, int>();
+  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
+  int x = P.first;
+  int y = P.second;
+
+  auto lambda = [&x]() {
+    x = 1;
+  };
+}
+
+void captureByAllRef() {
+  auto P = getPair<int, int>();
+  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
+  int x = P.first;
+  int y = P.second;
+
+  auto lambda = [&]() {
+    x = 1;
+  };
+}
+
+void deepLambda() {
+  auto P = getPair<int, int>();
+  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
+  int x = P.first;
+  int y = P.second;
+
+  {
+    auto lambda = [x]() {
+      int y = x;
+    };
+  }
+}
+
+void forRangeNotWarn() {
+  std::pair<int, int> Pairs[10];
+  for (auto P : Pairs) {
+  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-FIXES-CPP20ORLATER: for (auto [x, y] : Pairs) {
+    int x = P.first;
+    int y = P.second;
+
+    auto lambda = [&]() {
+    x = 1;
+  };
+  }
+}
+
+void stdTieNotWarn() {
+  int x = 0;
+  int y = 0;
+  std::tie(x, y) = getPair<int, int>();
+  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
+
+  auto lambda = [&x]() {
+    x = 1;
+  };
+}

>From c97ce9b2c775c9d0967483404ff82e2ff6ed68a8 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 18:10:43 +0800
Subject: [PATCH 11/48] [NFC] Test removed lines

---
 .../modernize/use-structured-binding.cpp      | 120 ++++++++++++------
 1 file changed, 80 insertions(+), 40 deletions(-)

diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 05b842e4d6614..55bdda5150d0f 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -18,24 +18,30 @@ void DecomposeByAssignWarnCases() {
     auto P = getPair<int, int>();
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
-    int x = P.first;
-    int y = P.second;
+    int x = P.first; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+    int y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
   }
 
   {
     auto P = getPair<int, int>();
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
-    int x = P.first;
-    auto y = P.second;
+    int x = P.first; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+    auto y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
   }
 
   {
     const auto P = getPair<int, int>();
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: const auto [x, y] = getPair<int, int>();
-    const int x = P.first;
-    const auto y = P.second;
+    const int x = P.first; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+    const auto y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
   }
 
   {
@@ -43,8 +49,10 @@ void DecomposeByAssignWarnCases() {
     auto& P = otherP;
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: auto& [x, y] = otherP;
-    int& x = P.first;
-    auto& y = P.second;
+    int& x = P.first; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+    auto& y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
   }
 
   {
@@ -52,8 +60,10 @@ void DecomposeByAssignWarnCases() {
     const auto& P = otherP;
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: const auto& [x, y] = otherP;
-    const int& x = P.first;
-    const auto& y = P.second;
+    const int& x = P.first; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+    const auto& y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
   }
 }
 
@@ -62,44 +72,56 @@ void forRangeWarnCases() {
   for (auto P : Pairs) {
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
-    int x = P.first;
-    int y = P.second;
+    int x = P.first; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+    int y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
   }
 
   for (const auto P : Pairs) {
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (const auto [x, y] : Pairs) {
-    const int x = P.first;
-    const int y = P.second;
+    const int x = P.first; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+    const int y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
   }
 
   for (auto& P : Pairs) {
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (auto& [x, y] : Pairs) {
-    int& x = P.first;
-    int& y = P.second;
+    int& x = P.first; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+    int& y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
   }
 
   for (const auto& P : Pairs) {
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (const auto& [x, y] : Pairs) {
-    const int& x = P.first;
-    const int& y = P.second;
+    const int& x = P.first; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+    const int& y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
   }
 
   std::pair<TestClass, TestClass> ClassPairs[10];
   for (auto P : ClassPairs) {
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (auto [c1, c2] : ClassPairs) {
-    TestClass c1 = P.first;
-    TestClass c2 = P.second;
+    TestClass c1 = P.first; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+    TestClass c2 = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
   }
 
   for (const auto P : ClassPairs) {
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (const auto [c1, c2] : ClassPairs) {
-    const TestClass c1 = P.first;
-    const TestClass c2 = P.second;
+    const TestClass c1 = P.first; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+    const TestClass c2 = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
   }
 }
 
@@ -139,20 +161,26 @@ void forRangeNotWarnCases() {
 }
 
 void stdTieWarnCases() {
-  int a = 0;
-  int b = 0;
+  int a = 0; // REMOVE
+  // CHECK-FIXES-ALL: // REMOVE
+  int b = 0; // REMOVE
+  // CHECK-FIXES-ALL: // REMOVE
   std::tie(a, b) = getPair<int, int>();
   // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-ALL: auto [a, b] = getPair<int, int>();
 
-  int* pa = nullptr;
-  int* pb = nullptr;
+  int* pa = nullptr; // REMOVE
+  // CHECK-FIXES-ALL: // REMOVE
+  int* pb = nullptr; // REMOVE
+  // CHECK-FIXES-ALL: // REMOVE
   std::tie(pa, pb) = getPair<int*, int*>();
   // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-ALL: auto [pa, pb] = getPair<int*, int*>();
 
-  TestClass c1 (1, 2);
-  TestClass c2 = TestClass {3, 4};
+  TestClass c1 (1, 2); // REMOVE
+  // CHECK-FIXES-ALL: // REMOVE
+  TestClass c2 = TestClass {3, 4}; // REMOVE
+  // CHECK-FIXES-ALL: // REMOVE
   std::tie(c1, c2) = getPair<TestClass, TestClass>();
   // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-ALL: auto [c1, c2] = getPair<TestClass, TestClass>();
@@ -255,8 +283,10 @@ void captureByVal() {
   auto P = getPair<int, int>();
   // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
-  int x = P.first;
-  int y = P.second;
+  int x = P.first; // REMOVE
+  // CHECK-FIXES-CPP20ORLATER: // REMOVE
+  int y = P.second; // REMOVE
+  // CHECK-FIXES-CPP20ORLATER: // REMOVE
 
   auto lambda = [x]() {
     int y = x;
@@ -267,8 +297,10 @@ void captureByRef() {
   auto P = getPair<int, int>();
   // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
-  int x = P.first;
-  int y = P.second;
+  int x = P.first; // REMOVE
+  // CHECK-FIXES-CPP20ORLATER: // REMOVE
+  int y = P.second; // REMOVE
+  // CHECK-FIXES-CPP20ORLATER: // REMOVE
 
   auto lambda = [&x]() {
     x = 1;
@@ -279,8 +311,10 @@ void captureByAllRef() {
   auto P = getPair<int, int>();
   // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
-  int x = P.first;
-  int y = P.second;
+  int x = P.first; // REMOVE
+  // CHECK-FIXES-CPP20ORLATER: // REMOVE
+  int y = P.second; // REMOVE
+  // CHECK-FIXES-CPP20ORLATER: // REMOVE
 
   auto lambda = [&]() {
     x = 1;
@@ -291,8 +325,10 @@ void deepLambda() {
   auto P = getPair<int, int>();
   // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
-  int x = P.first;
-  int y = P.second;
+  int x = P.first; // REMOVE
+  // CHECK-FIXES-CPP20ORLATER: // REMOVE
+  int y = P.second; // REMOVE
+  // CHECK-FIXES-CPP20ORLATER: // REMOVE
 
   {
     auto lambda = [x]() {
@@ -306,8 +342,10 @@ void forRangeNotWarn() {
   for (auto P : Pairs) {
   // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: for (auto [x, y] : Pairs) {
-    int x = P.first;
-    int y = P.second;
+    int x = P.first; // REMOVE
+    // CHECK-FIXES-CPP20ORLATER: // REMOVE
+    int y = P.second; // REMOVE
+    // CHECK-FIXES-CPP20ORLATER: // REMOVE
 
     auto lambda = [&]() {
     x = 1;
@@ -316,8 +354,10 @@ void forRangeNotWarn() {
 }
 
 void stdTieNotWarn() {
-  int x = 0;
-  int y = 0;
+  int x = 0; // REMOVE
+  // CHECK-FIXES-CPP20ORLATER: // REMOVE
+  int y = 0; // REMOVE
+  // CHECK-FIXES-CPP20ORLATER: // REMOVE
   std::tie(x, y) = getPair<int, int>();
   // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();

>From fbcb0999bc232c79a23a4370222560928af8f1e9 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 18:39:02 +0800
Subject: [PATCH 12/48] [NFC] 80-character for check doc

---
 .../checks/modernize/use-structured-binding.rst | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 0d227b75968a7..637f8bcee666f 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -6,9 +6,11 @@ modernize-use-structured-binding
 Finds places where structured bindings could be used to decompose pairs and
 suggests replacing them.
 
-This check finds three code patterns and recommends using structured bindings for clearer, more idiomatic C++17 code.
+This check finds three code patterns and recommends using structured bindings
+for clearer, more idiomatic C++17 code.
 
-1. Decompose a pair variable by assigning its members to separate variables right after its definition:
+1. Decompose a pair variable by assigning its members to separate variables
+right after its definition:
 
 .. code-block:: c++
 
@@ -32,7 +34,8 @@ This check finds three code patterns and recommends using structured bindings fo
 
   auto [a, b] = getPair<int, int>();
 
-3. Manually decompose a pair by assigning to its members to local variables in a range-based for loop:
+3. Manually decompose a pair by assigning to its members to local variables
+in a range-based for loop:
 
 .. code-block:: c++
 
@@ -48,12 +51,14 @@ This check finds three code patterns and recommends using structured bindings fo
     // use x and y
   }
 
-The check also supports custom pair-like types via the :option:`PairTypes` option.
+The check also supports custom pair-like types via the :option:`PairTypes`
+option.
 
 Options
 -------
 
 .. option:: PairTypes
 
-   A Semicolon-separated list of type names to be treated as pair-like for structured binding suggestions.  
-   Example: `MyPairType;OtherPairType`. Default is `std::pair`.
+   A Semicolon-separated list of type names to be treated as pair-like for
+   structured binding suggestions. Example: `MyPairType;OtherPairType`. 
+   Default is `std::pair`.

>From 3f95a697181f00e68971c26bccc18a9c33ba319c Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 18:45:25 +0800
Subject: [PATCH 13/48] [NFC] Add limitations section to doc

---
 .../modernize/use-structured-binding.rst      | 31 +++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 637f8bcee666f..a635158420e21 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -54,6 +54,37 @@ in a range-based for loop:
 The check also supports custom pair-like types via the :option:`PairTypes`
 option.
 
+Limitations
+-----------
+
+The check currently ignores variables defined with attributes or qualifiers
+except const and & since it's not very common:
+
+.. code-block:: c++
+
+  static auto pair = getPair();
+  static int b = pair.first;
+  static int c = pair.second;
+
+The check doesn't check for some situations which could possibly transfered
+to structured bnindings, for example:
+
+.. code-block:: c++
+
+  const auto& results = mapping.try_emplace("hello!"); 
+  const iterator& it = results.first;
+  bool succeed = results.second;
+  // succeed is not changed in the following code
+
+and:
+
+.. code-block:: c++
+
+  const auto results = mapping.try_emplace("hello!");
+  if (results.second) {
+      handle_inserted(results.first);
+  }
+
 Options
 -------
 

>From a5222a22546d1119896b22a58a1bc28914181e54 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 18:49:55 +0800
Subject: [PATCH 14/48] [NFC] Add some tests about two VarDecl in one DeclStmt.

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

diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 55bdda5150d0f..bef93027f72fb 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -24,6 +24,14 @@ void DecomposeByAssignWarnCases() {
     // CHECK-FIXES-ALL: // REMOVE
   }
 
+  {
+    auto P = getPair<int, int>();
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
+    int x = P.first, y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+  }
+
   {
     auto P = getPair<int, int>();
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
@@ -78,6 +86,13 @@ void forRangeWarnCases() {
     // CHECK-FIXES-ALL: // REMOVE
   }
 
+  for (auto P : Pairs) {
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
+    int x = P.first, y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+  }
+
   for (const auto P : Pairs) {
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (const auto [x, y] : Pairs) {
@@ -169,6 +184,12 @@ void stdTieWarnCases() {
   // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-ALL: auto [a, b] = getPair<int, int>();
 
+  int x = 0, y = 0; // REMOVE
+  // CHECK-FIXES-ALL: // REMOVE
+  std::tie(x, y) = getPair<int, int>();
+  // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
+
   int* pa = nullptr; // REMOVE
   // CHECK-FIXES-ALL: // REMOVE
   int* pb = nullptr; // REMOVE

>From 8c423b34ee9f6cd3103001b20e63d3ecee2875f4 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 21:35:53 +0800
Subject: [PATCH 15/48] Update
 clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp

Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
 .../clang-tidy/modernize/UseStructuredBindingCheck.cpp          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index fab4e8dc16cf1..f9d700f5e849c 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -1,4 +1,4 @@
-//===--- UseStructuredBindingCheck.cpp - clang-tidy -----------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.

>From 42a4e5b381fdb3a525159f2c8e558c9bf036b42f Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 21:36:13 +0800
Subject: [PATCH 16/48] Update
 clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h

Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
 .../clang-tidy/modernize/UseStructuredBindingCheck.h            | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
index 63bc0a8c3da45..83d262a5db3cd 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
@@ -1,4 +1,4 @@
-//===--- UseStructuredBindingCheck.h - clang-tidy ---------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.

>From 8919fdb385b2a69704c4348fecb5f99b22bea7d7 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 21:36:37 +0800
Subject: [PATCH 17/48] Update
 clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp

Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
 .../clang-tidy/modernize/UseStructuredBindingCheck.cpp          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index f9d700f5e849c..78582afe706e6 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -125,7 +125,7 @@ AST_MATCHER_P2(Stmt, hasPreTwoVarDecl, ast_matchers::internal::Matcher<VarDecl>,
 AST_MATCHER_P2(Stmt, hasNextTwoVarDecl,
                ast_matchers::internal::Matcher<VarDecl>, InnerMatcher1,
                ast_matchers::internal::Matcher<VarDecl>, InnerMatcher2) {
-  DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
+  const DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
   if (Parents.size() != 1)
     return false;
 

>From 88a243efa5243d7a846c820aeda1fcb79ba29eaf Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 21:36:47 +0800
Subject: [PATCH 18/48] Update
 clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp

Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
 .../clang-tidy/modernize/UseStructuredBindingCheck.cpp          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 78582afe706e6..99cef95aa0251 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -386,7 +386,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
       !Res.empty())
     InitE = Res[0].getNodeAs<Expr>("init_expr");
 
-  std::optional<TransferType> PairCaptureType =
+  const std::optional<TransferType> PairCaptureType =
       getTransferType(*Result.Context, PairVar->getType(), InitE->getType());
   std::optional<TransferType> FirstVarCaptureType =
       getTransferType(*Result.Context, FirstVar->getType(),

>From 8fa2edcce1f8ee83aeb7e3764ebf0ac19bcdc972 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 21:36:55 +0800
Subject: [PATCH 19/48] Update
 clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp

Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
 .../clang-tidy/modernize/UseStructuredBindingCheck.cpp          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 99cef95aa0251..b8b1540e69864 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -388,7 +388,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
 
   const std::optional<TransferType> PairCaptureType =
       getTransferType(*Result.Context, PairVar->getType(), InitE->getType());
-  std::optional<TransferType> FirstVarCaptureType =
+  const std::optional<TransferType> FirstVarCaptureType =
       getTransferType(*Result.Context, FirstVar->getType(),
                       *Result.Nodes.getNodeAs<QualType>(FirstTypeName));
   std::optional<TransferType> SecondVarCaptureType =

>From 2c980656ab7ea781fcb7c7dc6f140d56ab432384 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 14 Sep 2025 21:37:09 +0800
Subject: [PATCH 20/48] Update
 clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp

Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
 .../clang-tidy/modernize/UseStructuredBindingCheck.cpp          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index b8b1540e69864..f15ce7ca082ea 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -391,7 +391,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
   const std::optional<TransferType> FirstVarCaptureType =
       getTransferType(*Result.Context, FirstVar->getType(),
                       *Result.Nodes.getNodeAs<QualType>(FirstTypeName));
-  std::optional<TransferType> SecondVarCaptureType =
+  const std::optional<TransferType> SecondVarCaptureType =
       getTransferType(*Result.Context, SecondVar->getType(),
                       *Result.Nodes.getNodeAs<QualType>(SecondTypeName));
   if (!PairCaptureType || !FirstVarCaptureType || !SecondVarCaptureType ||

>From 48b747ec2fb81b69a8084f561b0dbf36f3201c7e Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 16 Sep 2025 20:01:01 +0800
Subject: [PATCH 21/48] [NFC] Correct comment words

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

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index f15ce7ca082ea..b8d9ac964961b 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -149,8 +149,8 @@ AST_MATCHER_P2(Stmt, hasNextTwoVarDecl,
   return false;
 }
 
-/// Matches a Stmt whose parent is a CompoundStmt, and there a two VarDecls
-/// matching the inner matcher in the beginning of CompoundStmt.
+/// Matches a CompoundStmt which has two VarDecls
+/// matching the inner matcher in the beginning.
 AST_MATCHER_P2(CompoundStmt, hasFirstTwoVarDecl,
                ast_matchers::internal::Matcher<VarDecl>, InnerMatcher1,
                ast_matchers::internal::Matcher<VarDecl>, InnerMatcher2) {

>From a46277ea8aac25188ee15c2a6841f258b1abad88 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 16 Sep 2025 23:14:36 +0800
Subject: [PATCH 22/48] Update
 clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst

Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
 .../docs/clang-tidy/checks/modernize/use-structured-binding.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index a635158420e21..a935516ed4307 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -90,6 +90,6 @@ Options
 
 .. option:: PairTypes
 
-   A Semicolon-separated list of type names to be treated as pair-like for
+   A semicolon-separated list of type names to be treated as pair-like for
    structured binding suggestions. Example: `MyPairType;OtherPairType`. 
    Default is `std::pair`.

>From 36011dd2fb1b1f225d15ed12e1f66e9f3c896c80 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 16 Sep 2025 23:15:49 +0800
Subject: [PATCH 23/48] [NFC] 80 chars limit  for comment

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

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index b8d9ac964961b..7ecfeefc6f8df 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -149,8 +149,8 @@ AST_MATCHER_P2(Stmt, hasNextTwoVarDecl,
   return false;
 }
 
-/// Matches a CompoundStmt which has two VarDecls
-/// matching the inner matcher in the beginning.
+/// Matches a CompoundStmt which has two VarDecls matching the inner matcher in
+/// the beginning.
 AST_MATCHER_P2(CompoundStmt, hasFirstTwoVarDecl,
                ast_matchers::internal::Matcher<VarDecl>, InnerMatcher1,
                ast_matchers::internal::Matcher<VarDecl>, InnerMatcher2) {
@@ -164,8 +164,8 @@ AST_MATCHER_P2(CompoundStmt, hasFirstTwoVarDecl,
       InnerMatcher1, InnerMatcher2, Finder, Builder);
 }
 
-/// It's not very common to have specifiers for variables used to decompose
-/// a pair, so we ignore these cases.
+/// It's not very common to have specifiers for variables used to decompose a
+/// pair, so we ignore these cases.
 AST_MATCHER(VarDecl, hasAnySpecifiersShouldBeIgnored) {
   return Node.isStaticLocal() || Node.isConstexpr() || Node.hasAttrs() ||
          Node.isInlineSpecified();

>From c5c673b602db51cb93cda6666fed4d22240a9dcc Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Wed, 17 Sep 2025 20:23:29 +0800
Subject: [PATCH 24/48] Update
 clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst

Co-authored-by: Congcong Cai <congcongcai0907 at 163.com>
---
 .../docs/clang-tidy/checks/modernize/use-structured-binding.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index a935516ed4307..0d02e6761cdd4 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -39,7 +39,7 @@ in a range-based for loop:
 
 .. code-block:: c++
 
-  for (autop : vecOfPairs) {
+  for (auto p : vecOfPairs) {
     int x = p.first;
     int y = p.second;
     // ...

>From d92f76849cad72881a87e0c36172ecaa670966dd Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Wed, 17 Sep 2025 21:25:00 +0800
Subject: [PATCH 25/48] [NFC] prefer static function

---
 .../modernize/UseStructuredBindingCheck.cpp   | 56 ++++++++++---------
 1 file changed, 29 insertions(+), 27 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 7ecfeefc6f8df..f04923f01a459 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -14,37 +14,29 @@
 using namespace clang::ast_matchers;
 
 namespace clang::tidy::modernize {
-namespace {
-constexpr const char *DefaultPairTypes = "std::pair";
-constexpr llvm::StringLiteral PairDeclName = "PairVarD";
-constexpr llvm::StringLiteral PairVarTypeName = "PairVarType";
-constexpr llvm::StringLiteral FirstVarDeclName = "FirstVarDecl";
-constexpr llvm::StringLiteral SecondVarDeclName = "SecondVarDecl";
-constexpr llvm::StringLiteral FirstDeclStmtName = "FirstDeclStmt";
-constexpr llvm::StringLiteral SecondDeclStmtName = "SecondDeclStmt";
-constexpr llvm::StringLiteral FirstTypeName = "FirstType";
-constexpr llvm::StringLiteral SecondTypeName = "SecondType";
-constexpr llvm::StringLiteral ScopeBlockName = "ScopeBlock";
-constexpr llvm::StringLiteral StdTieAssignStmtName = "StdTieAssign";
-constexpr llvm::StringLiteral StdTieExprName = "StdTieExpr";
-constexpr llvm::StringLiteral ForRangeStmtName = "ForRangeStmt";
 
-/// What qualifiers and specifiers are used to create structured binding
-/// declaration, it only supports the following four cases now.
-enum TransferType : uint8_t {
-  TT_ByVal,
-  TT_ByConstVal,
-  TT_ByRef,
-  TT_ByConstRef
-};
+static constexpr const char *DefaultPairTypes = "std::pair";
+static constexpr llvm::StringLiteral PairDeclName = "PairVarD";
+static constexpr llvm::StringLiteral PairVarTypeName = "PairVarType";
+static constexpr llvm::StringLiteral FirstVarDeclName = "FirstVarDecl";
+static constexpr llvm::StringLiteral SecondVarDeclName = "SecondVarDecl";
+static constexpr llvm::StringLiteral FirstDeclStmtName = "FirstDeclStmt";
+static constexpr llvm::StringLiteral SecondDeclStmtName = "SecondDeclStmt";
+static constexpr llvm::StringLiteral FirstTypeName = "FirstType";
+static constexpr llvm::StringLiteral SecondTypeName = "SecondType";
+static constexpr llvm::StringLiteral ScopeBlockName = "ScopeBlock";
+static constexpr llvm::StringLiteral StdTieAssignStmtName = "StdTieAssign";
+static constexpr llvm::StringLiteral StdTieExprName = "StdTieExpr";
+static constexpr llvm::StringLiteral ForRangeStmtName = "ForRangeStmt";
 
 /// Try to match exactly two VarDecl inside two DeclStmts, and set binding for
 /// the used DeclStmts.
-bool matchTwoVarDecl(const DeclStmt *DS1, const DeclStmt *DS2,
-                     ast_matchers::internal::Matcher<VarDecl> InnerMatcher1,
-                     ast_matchers::internal::Matcher<VarDecl> InnerMatcher2,
-                     internal::ASTMatchFinder *Finder,
-                     internal::BoundNodesTreeBuilder *Builder) {
+static bool
+matchTwoVarDecl(const DeclStmt *DS1, const DeclStmt *DS2,
+                ast_matchers::internal::Matcher<VarDecl> InnerMatcher1,
+                ast_matchers::internal::Matcher<VarDecl> InnerMatcher2,
+                internal::ASTMatchFinder *Finder,
+                internal::BoundNodesTreeBuilder *Builder) {
   SmallVector<std::pair<const VarDecl *, const DeclStmt *>, 2> Vars;
   auto CollectVarsInDeclStmt = [&Vars](const DeclStmt *DS) -> bool {
     if (!DS)
@@ -82,6 +74,16 @@ bool matchTwoVarDecl(const DeclStmt *DS1, const DeclStmt *DS2,
   return false;
 }
 
+namespace {
+/// What qualifiers and specifiers are used to create structured binding
+/// declaration, it only supports the following four cases now.
+enum TransferType : uint8_t {
+  TT_ByVal,
+  TT_ByConstVal,
+  TT_ByRef,
+  TT_ByConstRef
+};
+
 /// Matches a Stmt whose parent is a CompoundStmt, and which is directly
 /// following two VarDecls matching the inner matcher, at the same time set
 /// binding for the CompoundStmt.

>From e4ffb48e8c9cb84b9b3c7b14f2e1a32c00228905 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Wed, 17 Sep 2025 21:25:44 +0800
Subject: [PATCH 26/48] [NFC] Explicit capture in lambda

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

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index f04923f01a459..c7ae24fb0c840 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -339,8 +339,9 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
   }
 
   const auto *CFRS = Result.Nodes.getNodeAs<CXXForRangeStmt>(ForRangeStmtName);
-  auto DiagAndFix = [&](SourceLocation DiagLoc, SourceRange ReplaceRange,
-                        TransferType TT = TT_ByVal) {
+  auto DiagAndFix = [&DS1, &DS2, &FirstVar, &SecondVar, &CFRS,
+                     this](SourceLocation DiagLoc, SourceRange ReplaceRange,
+                           TransferType TT = TT_ByVal) {
     StringRef Prefix;
     switch (TT) {
     case TT_ByVal:

>From 73231c8de9e38686b03cbf4d7044172eece9fb93 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Wed, 17 Sep 2025 21:26:27 +0800
Subject: [PATCH 27/48] [NFC] Use llvm::twine to concat string

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

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index c7ae24fb0c840..8ca088ecf1405 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -363,11 +363,10 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
     if (DS2)
       Hints.emplace_back(FixItHint::CreateRemoval(DS2->getSourceRange()));
 
-    std::string ReplacementText = Prefix.str() + " [" +
-                                  FirstVar->getNameAsString() + ", " +
-                                  SecondVar->getNameAsString() + "]";
-    if (CFRS)
-      ReplacementText += " :";
+    std::string ReplacementText =
+        (Twine(Prefix) + " [" + FirstVar->getNameAsString() + ", " +
+         SecondVar->getNameAsString() + "]" + (CFRS ? " :" : ""))
+            .str();
     diag(DiagLoc, "use structured binding to decompose a pair")
         << FixItHint::CreateReplacement(ReplaceRange, ReplacementText) << Hints;
   };

>From 7ac2a3ed82e78e18c545a066929b53ac297560b6 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Wed, 17 Sep 2025 21:38:55 +0800
Subject: [PATCH 28/48] [NFC] Avoid some duplications

---
 .../modernize/UseStructuredBindingCheck.cpp   | 51 +++++++++----------
 1 file changed, 24 insertions(+), 27 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 8ca088ecf1405..24e59e3044d1d 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -202,12 +202,12 @@ UseStructuredBindingCheck::UseStructuredBindingCheck(StringRef Name,
   ;
 }
 
-static auto getVarInitWithMemberMatcher(StringRef PairName,
-                                        StringRef MemberName,
-                                        StringRef TypeName,
-                                        StringRef BindingName) {
+static auto getVarInitWithMemberMatcher(
+    StringRef PairName, StringRef MemberName, StringRef TypeName,
+    StringRef BindingName,
+    ast_matchers::internal::Matcher<VarDecl> ExtraMatcher) {
   return varDecl(
-             unless(hasAnySpecifiersShouldBeIgnored()), unless(isInMarco()),
+             ExtraMatcher,
              hasInitializer(
                  ignoringImpCasts(ignoringCopyCtorAndImplicitCast(memberExpr(
                      hasObjectExpression(ignoringImpCasts(declRefExpr(
@@ -223,10 +223,20 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
                hasUnqualifiedDesugaredType(recordType(
                    hasDeclaration(cxxRecordDecl(hasAnyName(PairTypes))))));
 
-  auto VarInitWithFirstMember = getVarInitWithMemberMatcher(
-      PairDeclName, "first", FirstTypeName, FirstVarDeclName);
-  auto VarInitWithSecondMember = getVarInitWithMemberMatcher(
-      PairDeclName, "second", SecondTypeName, SecondVarDeclName);
+  auto UnlessShouldBeIgnored =
+      unless(anyOf(hasAnySpecifiersShouldBeIgnored(), isInMarco()));
+
+  auto VarInitWithFirstMember =
+      getVarInitWithMemberMatcher(PairDeclName, "first", FirstTypeName,
+                                  FirstVarDeclName, UnlessShouldBeIgnored);
+  auto VarInitWithSecondMember =
+      getVarInitWithMemberMatcher(PairDeclName, "second", SecondTypeName,
+                                  SecondVarDeclName, UnlessShouldBeIgnored);
+
+  auto RefToBindName = [&UnlessShouldBeIgnored](const llvm::StringLiteral &Name)
+      -> ast_matchers::internal::BindableMatcher<Stmt> {
+    return declRefExpr(to(varDecl(UnlessShouldBeIgnored).bind(Name)));
+  };
 
   // X x;
   // Y y;
@@ -237,23 +247,10 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
           has(cxxOperatorCallExpr(
                   hasOverloadedOperatorName("="),
                   hasLHS(ignoringImplicit(
-                      callExpr(
-                          callee(
-                              functionDecl(isInStdNamespace(), hasName("tie"))),
-                          hasArgument(
-                              0,
-                              declRefExpr(to(
-                                  varDecl(
-                                      unless(hasAnySpecifiersShouldBeIgnored()),
-                                      unless(isInMarco()))
-                                      .bind(FirstVarDeclName)))),
-                          hasArgument(
-                              1,
-                              declRefExpr(to(
-                                  varDecl(
-                                      unless(hasAnySpecifiersShouldBeIgnored()),
-                                      unless(isInMarco()))
-                                      .bind(SecondVarDeclName)))))
+                      callExpr(callee(functionDecl(isInStdNamespace(),
+                                                   hasName("tie"))),
+                               hasArgument(0, RefToBindName(FirstVarDeclName)),
+                               hasArgument(1, RefToBindName(SecondVarDeclName)))
                           .bind(StdTieExprName))),
                   hasRHS(expr(hasType(PairType))))
                   .bind(StdTieAssignStmtName)),
@@ -269,7 +266,7 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
       declStmt(
           unless(isInMarco()),
           hasSingleDecl(
-              varDecl(unless(hasAnySpecifiersShouldBeIgnored()),
+              varDecl(UnlessShouldBeIgnored,
                       hasType(qualType(anyOf(PairType, lValueReferenceType(
                                                            pointee(PairType))))
                                   .bind(PairVarTypeName)),

>From 00285d9e49358ce8a83090b7f684183823973990 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Wed, 17 Sep 2025 21:47:01 +0800
Subject: [PATCH 29/48] Stop collecting when we got two VarDecls from DS1

---
 .../modernize/UseStructuredBindingCheck.cpp     |  7 +++----
 .../modernize/use-structured-binding.cpp        | 17 +++++++++++++++++
 2 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 24e59e3044d1d..43431721e439e 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -55,10 +55,9 @@ matchTwoVarDecl(const DeclStmt *DS1, const DeclStmt *DS2,
     return true;
   };
 
-  if (!CollectVarsInDeclStmt(DS1) || !CollectVarsInDeclStmt(DS2))
-    return false;
-
-  if (Vars.size() != 2)
+  // If we already get two VarDecls, then don't need to collect from DS2.
+  if (!CollectVarsInDeclStmt(DS1) ||
+      (Vars.size() != 2 && !CollectVarsInDeclStmt(DS2)) || Vars.size() != 2)
     return false;
 
   if (InnerMatcher1.matches(*Vars[0].first, Finder, Builder) &&
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index bef93027f72fb..33de71762ba87 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -32,6 +32,15 @@ void DecomposeByAssignWarnCases() {
     // CHECK-FIXES-ALL: // REMOVE
   }
 
+  {
+    auto P = getPair<int, int>();
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
+    int x = P.first, y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+    int z;
+  }
+
   {
     auto P = getPair<int, int>();
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
@@ -93,6 +102,14 @@ void forRangeWarnCases() {
     // CHECK-FIXES-ALL: // REMOVE
   }
 
+  for (auto P : Pairs) {
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
+    int x = P.first, y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+    int z;
+  }
+
   for (const auto P : Pairs) {
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (const auto [x, y] : Pairs) {

>From 242316ead2d67cd2ecbfab8c4344c63373f79f8c Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Wed, 17 Sep 2025 23:29:07 +0800
Subject: [PATCH 30/48] Exit for std::tie case when DS1 has multi child decl

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

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 43431721e439e..c471436c0c88a 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -110,6 +110,8 @@ AST_MATCHER_P2(Stmt, hasPreTwoVarDecl, ast_matchers::internal::Matcher<VarDecl>,
   const DeclStmt *DS1 = (!DS2->isSingleDecl() || ((I + 2) == C->body_rend())
                              ? nullptr
                              : dyn_cast<DeclStmt>(*(I + 2)));
+  if (DS1 && !DS1->isSingleDecl())
+    return false;
 
   if (matchTwoVarDecl(DS1, DS2, InnerMatcher1, InnerMatcher2, Finder,
                       Builder)) {
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 33de71762ba87..be3418a2ac224 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -230,6 +230,10 @@ void stdTieNotWarnCases() {
   a = 4;
   std::tie(a, b) = getPair<int, int>(); // no warning
 
+  int c = 0, d = 0;
+  int e = 0;
+  std::tie(a, b) = getPair<int, int>(); // no warning
+
   int* pa = nullptr;
   int* pb = nullptr;
   MarkUsed(pa);

>From d2eb66b5fd5309a6eea401b139d43787b71209b0 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Fri, 19 Sep 2025 11:59:12 +0800
Subject: [PATCH 31/48] [NFC] use llvm::reverse

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

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index c471436c0c88a..b0230d0819420 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -97,8 +97,7 @@ AST_MATCHER_P2(Stmt, hasPreTwoVarDecl, ast_matchers::internal::Matcher<VarDecl>,
   if (!C)
     return false;
 
-  const auto I =
-      llvm::find(llvm::make_range(C->body_rbegin(), C->body_rend()), &Node);
+  const auto I = llvm::find(llvm::reverse(C->body()), &Node);
   assert(I != C->body_rend() && "C is parent of Node");
   if ((I + 1) == C->body_rend())
     return false;

>From c6f68854b1f946953ad4357841858a9d8d5f72f6 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 13:45:50 +0800
Subject: [PATCH 32/48] Refactor the way how we match Two VarDecls

---
 .../modernize/UseStructuredBindingCheck.cpp   | 200 +++++++++---------
 .../modernize/use-structured-binding.cpp      |  60 ++----
 2 files changed, 115 insertions(+), 145 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index b0230d0819420..6530fce2f6484 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -20,8 +20,8 @@ static constexpr llvm::StringLiteral PairDeclName = "PairVarD";
 static constexpr llvm::StringLiteral PairVarTypeName = "PairVarType";
 static constexpr llvm::StringLiteral FirstVarDeclName = "FirstVarDecl";
 static constexpr llvm::StringLiteral SecondVarDeclName = "SecondVarDecl";
-static constexpr llvm::StringLiteral FirstDeclStmtName = "FirstDeclStmt";
-static constexpr llvm::StringLiteral SecondDeclStmtName = "SecondDeclStmt";
+static constexpr llvm::StringLiteral BeginDeclStmtName = "BeginDeclStmt";
+static constexpr llvm::StringLiteral EndDeclStmtName = "EndDeclStmt";
 static constexpr llvm::StringLiteral FirstTypeName = "FirstType";
 static constexpr llvm::StringLiteral SecondTypeName = "SecondType";
 static constexpr llvm::StringLiteral ScopeBlockName = "ScopeBlock";
@@ -29,45 +29,65 @@ static constexpr llvm::StringLiteral StdTieAssignStmtName = "StdTieAssign";
 static constexpr llvm::StringLiteral StdTieExprName = "StdTieExpr";
 static constexpr llvm::StringLiteral ForRangeStmtName = "ForRangeStmt";
 
-/// Try to match exactly two VarDecl inside two DeclStmts, and set binding for
-/// the used DeclStmts.
-static bool
-matchTwoVarDecl(const DeclStmt *DS1, const DeclStmt *DS2,
-                ast_matchers::internal::Matcher<VarDecl> InnerMatcher1,
-                ast_matchers::internal::Matcher<VarDecl> InnerMatcher2,
-                internal::ASTMatchFinder *Finder,
-                internal::BoundNodesTreeBuilder *Builder) {
-  SmallVector<std::pair<const VarDecl *, const DeclStmt *>, 2> Vars;
-  auto CollectVarsInDeclStmt = [&Vars](const DeclStmt *DS) -> bool {
-    if (!DS)
-      return true;
+/// Matches a sequence of VarDecls matching the inner matchers, starting from
+/// the \p Iter to \p EndIter and set bindings for the first DeclStmt and the
+/// last DeclStmt if matched.
+///
+/// \p Backwards indicates whether to match the VarDecls in reverse order.
+template <typename Iterator>
+static bool matchNVarDeclStartingWith(
+    Iterator Iter, Iterator EndIter,
+    ArrayRef<ast_matchers::internal::Matcher<VarDecl>> InnerMatchers,
+    ast_matchers::internal::ASTMatchFinder *Finder,
+    ast_matchers::internal::BoundNodesTreeBuilder *Builder,
+    bool Backwards = false) {
+  const DeclStmt *BeginDS = nullptr;
+  const DeclStmt *EndDS = nullptr;
+  size_t N = InnerMatchers.size();
+  size_t Count = 0;
+  for (; Iter != EndIter; ++Iter) {
+    EndDS = dyn_cast<DeclStmt>(*Iter);
+    if (!EndDS)
+      break;
 
-    for (const auto *VD : DS->decls()) {
-      if (Vars.size() == 2)
-        return false;
+    if (!BeginDS)
+      BeginDS = EndDS;
 
-      if (const auto *Var = dyn_cast<VarDecl>(VD))
-        Vars.emplace_back(Var, DS);
-      else
+    auto Matches = [&](const Decl *VD) {
+      if (Count == N)
         return false;
-    }
 
-    return true;
-  };
-
-  // If we already get two VarDecls, then don't need to collect from DS2.
-  if (!CollectVarsInDeclStmt(DS1) ||
-      (Vars.size() != 2 && !CollectVarsInDeclStmt(DS2)) || Vars.size() != 2)
-    return false;
+      if (const auto *Var = dyn_cast<VarDecl>(VD);
+          Var && InnerMatchers[Backwards ? N - Count - 1 : Count].matches(
+                     *Var, Finder, Builder)) {
+        ++Count;
+        return true;
+      }
+
+      return false;
+    };
+
+    if (Backwards) {
+      for (const auto *VD : llvm::reverse(EndDS->decls())) {
+        if (!Matches(VD))
+          return false;
+      }
+    } else {
+      for (const auto *VD : EndDS->decls()) {
+        if (!Matches(VD))
+          return false;
+      }
+    }
 
-  if (InnerMatcher1.matches(*Vars[0].first, Finder, Builder) &&
-      InnerMatcher2.matches(*Vars[1].first, Finder, Builder)) {
-    Builder->setBinding(FirstDeclStmtName,
-                        clang::DynTypedNode::create(*Vars[0].second));
-    if (Vars[0].second != Vars[1].second)
-      Builder->setBinding(SecondDeclStmtName,
-                          clang::DynTypedNode::create(*Vars[1].second));
-    return true;
+    // All the matchers is satisfied in those DeclStmts.
+    if (Count == N) {
+      Builder->setBinding(
+          BeginDeclStmtName,
+          clang::DynTypedNode::create(Backwards ? *EndDS : *BeginDS));
+      Builder->setBinding(EndDeclStmtName, clang::DynTypedNode::create(
+                                               Backwards ? *BeginDS : *EndDS));
+      return true;
+    }
   }
 
   return false;
@@ -84,12 +104,11 @@ enum TransferType : uint8_t {
 };
 
 /// Matches a Stmt whose parent is a CompoundStmt, and which is directly
-/// following two VarDecls matching the inner matcher, at the same time set
-/// binding for the CompoundStmt.
-AST_MATCHER_P2(Stmt, hasPreTwoVarDecl, ast_matchers::internal::Matcher<VarDecl>,
-               InnerMatcher1, ast_matchers::internal::Matcher<VarDecl>,
-               InnerMatcher2) {
-  DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
+/// following two VarDecls matching the inner matcher.
+AST_MATCHER_P(Stmt, hasPreTwoVarDecl,
+              llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>,
+              InnerMatchers) {
+  const DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
   if (Parents.size() != 1)
     return false;
 
@@ -99,34 +118,16 @@ AST_MATCHER_P2(Stmt, hasPreTwoVarDecl, ast_matchers::internal::Matcher<VarDecl>,
 
   const auto I = llvm::find(llvm::reverse(C->body()), &Node);
   assert(I != C->body_rend() && "C is parent of Node");
-  if ((I + 1) == C->body_rend())
-    return false;
-
-  const auto *DS2 = dyn_cast<DeclStmt>(*(I + 1));
-  if (!DS2)
-    return false;
-
-  const DeclStmt *DS1 = (!DS2->isSingleDecl() || ((I + 2) == C->body_rend())
-                             ? nullptr
-                             : dyn_cast<DeclStmt>(*(I + 2)));
-  if (DS1 && !DS1->isSingleDecl())
-    return false;
-
-  if (matchTwoVarDecl(DS1, DS2, InnerMatcher1, InnerMatcher2, Finder,
-                      Builder)) {
-    Builder->setBinding(ScopeBlockName, clang::DynTypedNode::create(*C));
-    return true;
-  }
-
-  return false;
+  return matchNVarDeclStartingWith(I + 1, C->body_rend(), InnerMatchers, Finder,
+                                   Builder, true);
 }
 
 /// Matches a Stmt whose parent is a CompoundStmt, and which is directly
-/// followed by two VarDecls matching the inner matcher, at the same time set
-/// binding for the CompoundStmt.
-AST_MATCHER_P2(Stmt, hasNextTwoVarDecl,
-               ast_matchers::internal::Matcher<VarDecl>, InnerMatcher1,
-               ast_matchers::internal::Matcher<VarDecl>, InnerMatcher2) {
+/// followed by two VarDecls matching the inner matcher.
+AST_MATCHER_P(Stmt, hasNextTwoVarDecl,
+              llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>,
+              InnerMatchers) {
+
   const DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
   if (Parents.size() != 1)
     return false;
@@ -137,33 +138,17 @@ AST_MATCHER_P2(Stmt, hasNextTwoVarDecl,
 
   const auto *I = llvm::find(C->body(), &Node);
   assert(I != C->body_end() && "C is parent of Node");
-  if ((I + 1) == C->body_end())
-    return false;
-
-  if (matchTwoVarDecl(
-          dyn_cast<DeclStmt>(*(I + 1)),
-          ((I + 2) == C->body_end() ? nullptr : dyn_cast<DeclStmt>(*(I + 2))),
-          InnerMatcher1, InnerMatcher2, Finder, Builder)) {
-    Builder->setBinding(ScopeBlockName, clang::DynTypedNode::create(*C));
-    return true;
-  }
-
-  return false;
+  return matchNVarDeclStartingWith(I + 1, C->body_end(), InnerMatchers, Finder,
+                                   Builder);
 }
 
 /// Matches a CompoundStmt which has two VarDecls matching the inner matcher in
 /// the beginning.
-AST_MATCHER_P2(CompoundStmt, hasFirstTwoVarDecl,
-               ast_matchers::internal::Matcher<VarDecl>, InnerMatcher1,
-               ast_matchers::internal::Matcher<VarDecl>, InnerMatcher2) {
-  const auto *I = Node.body_begin();
-  if ((I) == Node.body_end())
-    return false;
-
-  return matchTwoVarDecl(
-      dyn_cast<DeclStmt>(*(I)),
-      ((I + 1) == Node.body_end() ? nullptr : dyn_cast<DeclStmt>(*(I + 1))),
-      InnerMatcher1, InnerMatcher2, Finder, Builder);
+AST_MATCHER_P(CompoundStmt, hasFirstTwoVarDecl,
+              llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>,
+              InnerMatchers) {
+  return matchNVarDeclStartingWith(Node.body_begin(), Node.body_end(),
+                                   InnerMatchers, Finder, Builder);
 }
 
 /// It's not very common to have specifiers for variables used to decompose a
@@ -255,8 +240,10 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
                   hasRHS(expr(hasType(PairType))))
                   .bind(StdTieAssignStmtName)),
           hasPreTwoVarDecl(
-              varDecl(equalsBoundNode(std::string(FirstVarDeclName))),
-              varDecl(equalsBoundNode(std::string(SecondVarDeclName))))),
+              llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
+                  varDecl(equalsBoundNode(std::string(FirstVarDeclName))),
+                  varDecl(equalsBoundNode(std::string(SecondVarDeclName)))}),
+          hasParent(compoundStmt().bind(ScopeBlockName))),
       this);
 
   // pair<X, Y> p = ...;
@@ -272,7 +259,10 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
                                   .bind(PairVarTypeName)),
                       hasInitializer(expr()))
                   .bind(PairDeclName)),
-          hasNextTwoVarDecl(VarInitWithFirstMember, VarInitWithSecondMember)),
+          hasNextTwoVarDecl(
+              llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
+                  VarInitWithFirstMember, VarInitWithSecondMember}),
+          hasParent(compoundStmt().bind(ScopeBlockName))),
       this);
 
   // for (pair<X, Y> p : map) {
@@ -288,9 +278,12 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
                                   .bind(PairVarTypeName)),
                       hasInitializer(expr()))
                   .bind(PairDeclName)),
-          hasBody(compoundStmt(hasFirstTwoVarDecl(VarInitWithFirstMember,
-                                                  VarInitWithSecondMember))
-                      .bind(ScopeBlockName)))
+          hasBody(
+              compoundStmt(
+                  hasFirstTwoVarDecl(llvm::SmallVector<
+                                     ast_matchers::internal::Matcher<VarDecl>>{
+                      VarInitWithFirstMember, VarInitWithSecondMember}))
+                  .bind(ScopeBlockName)))
           .bind(ForRangeStmtName),
       this);
 }
@@ -320,8 +313,8 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
   const auto *FirstVar = Result.Nodes.getNodeAs<VarDecl>(FirstVarDeclName);
   const auto *SecondVar = Result.Nodes.getNodeAs<VarDecl>(SecondVarDeclName);
 
-  const auto *DS1 = Result.Nodes.getNodeAs<DeclStmt>(FirstDeclStmtName);
-  const auto *DS2 = Result.Nodes.getNodeAs<DeclStmt>(SecondDeclStmtName);
+  const auto *BeginDS = Result.Nodes.getNodeAs<DeclStmt>(BeginDeclStmtName);
+  const auto *EndDS = Result.Nodes.getNodeAs<DeclStmt>(EndDeclStmtName);
   const auto *ScopeBlock = Result.Nodes.getNodeAs<CompoundStmt>(ScopeBlockName);
 
   // Captured structured bindings are a C++20 extension
@@ -336,7 +329,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
   }
 
   const auto *CFRS = Result.Nodes.getNodeAs<CXXForRangeStmt>(ForRangeStmtName);
-  auto DiagAndFix = [&DS1, &DS2, &FirstVar, &SecondVar, &CFRS,
+  auto DiagAndFix = [&BeginDS, &EndDS, &FirstVar, &SecondVar, &CFRS,
                      this](SourceLocation DiagLoc, SourceRange ReplaceRange,
                            TransferType TT = TT_ByVal) {
     StringRef Prefix;
@@ -354,18 +347,15 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
       Prefix = "const auto&";
       break;
     }
-    std::vector<FixItHint> Hints;
-    if (DS1)
-      Hints.emplace_back(FixItHint::CreateRemoval(DS1->getSourceRange()));
-    if (DS2)
-      Hints.emplace_back(FixItHint::CreateRemoval(DS2->getSourceRange()));
 
     std::string ReplacementText =
         (Twine(Prefix) + " [" + FirstVar->getNameAsString() + ", " +
          SecondVar->getNameAsString() + "]" + (CFRS ? " :" : ""))
             .str();
     diag(DiagLoc, "use structured binding to decompose a pair")
-        << FixItHint::CreateReplacement(ReplaceRange, ReplacementText) << Hints;
+        << FixItHint::CreateReplacement(ReplaceRange, ReplacementText)
+        << FixItHint::CreateRemoval(
+               SourceRange{BeginDS->getBeginLoc(), EndDS->getEndLoc()});
   };
 
   if (const auto *COCE =
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index be3418a2ac224..48d48ec285575 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -18,8 +18,7 @@ void DecomposeByAssignWarnCases() {
     auto P = getPair<int, int>();
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
-    int x = P.first; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    int x = P.first;
     int y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
   }
@@ -45,8 +44,7 @@ void DecomposeByAssignWarnCases() {
     auto P = getPair<int, int>();
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
-    int x = P.first; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    int x = P.first;
     auto y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
   }
@@ -55,8 +53,7 @@ void DecomposeByAssignWarnCases() {
     const auto P = getPair<int, int>();
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: const auto [x, y] = getPair<int, int>();
-    const int x = P.first; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    const int x = P.first;
     const auto y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
   }
@@ -66,8 +63,7 @@ void DecomposeByAssignWarnCases() {
     auto& P = otherP;
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: auto& [x, y] = otherP;
-    int& x = P.first; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    int& x = P.first;
     auto& y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
   }
@@ -77,8 +73,7 @@ void DecomposeByAssignWarnCases() {
     const auto& P = otherP;
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: const auto& [x, y] = otherP;
-    const int& x = P.first; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    const int& x = P.first;
     const auto& y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
   }
@@ -89,8 +84,7 @@ void forRangeWarnCases() {
   for (auto P : Pairs) {
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
-    int x = P.first; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    int x = P.first;
     int y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
   }
@@ -113,8 +107,7 @@ void forRangeWarnCases() {
   for (const auto P : Pairs) {
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (const auto [x, y] : Pairs) {
-    const int x = P.first; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    const int x = P.first;
     const int y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
   }
@@ -122,8 +115,7 @@ void forRangeWarnCases() {
   for (auto& P : Pairs) {
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (auto& [x, y] : Pairs) {
-    int& x = P.first; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    int& x = P.first;
     int& y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
   }
@@ -131,8 +123,7 @@ void forRangeWarnCases() {
   for (const auto& P : Pairs) {
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (const auto& [x, y] : Pairs) {
-    const int& x = P.first; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    const int& x = P.first;
     const int& y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
   }
@@ -141,8 +132,7 @@ void forRangeWarnCases() {
   for (auto P : ClassPairs) {
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (auto [c1, c2] : ClassPairs) {
-    TestClass c1 = P.first; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    TestClass c1 = P.first;
     TestClass c2 = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
   }
@@ -150,8 +140,7 @@ void forRangeWarnCases() {
   for (const auto P : ClassPairs) {
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (const auto [c1, c2] : ClassPairs) {
-    const TestClass c1 = P.first; // REMOVE
-    // CHECK-FIXES-ALL: // REMOVE
+    const TestClass c1 = P.first;
     const TestClass c2 = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
   }
@@ -193,8 +182,7 @@ void forRangeNotWarnCases() {
 }
 
 void stdTieWarnCases() {
-  int a = 0; // REMOVE
-  // CHECK-FIXES-ALL: // REMOVE
+  int a = 0;
   int b = 0; // REMOVE
   // CHECK-FIXES-ALL: // REMOVE
   std::tie(a, b) = getPair<int, int>();
@@ -207,16 +195,14 @@ void stdTieWarnCases() {
   // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
 
-  int* pa = nullptr; // REMOVE
-  // CHECK-FIXES-ALL: // REMOVE
+  int* pa = nullptr;
   int* pb = nullptr; // REMOVE
   // CHECK-FIXES-ALL: // REMOVE
   std::tie(pa, pb) = getPair<int*, int*>();
   // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-ALL: auto [pa, pb] = getPair<int*, int*>();
 
-  TestClass c1 (1, 2); // REMOVE
-  // CHECK-FIXES-ALL: // REMOVE
+  TestClass c1 (1, 2);
   TestClass c2 = TestClass {3, 4}; // REMOVE
   // CHECK-FIXES-ALL: // REMOVE
   std::tie(c1, c2) = getPair<TestClass, TestClass>();
@@ -325,8 +311,7 @@ void captureByVal() {
   auto P = getPair<int, int>();
   // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
-  int x = P.first; // REMOVE
-  // CHECK-FIXES-CPP20ORLATER: // REMOVE
+  int x = P.first;
   int y = P.second; // REMOVE
   // CHECK-FIXES-CPP20ORLATER: // REMOVE
 
@@ -339,8 +324,7 @@ void captureByRef() {
   auto P = getPair<int, int>();
   // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
-  int x = P.first; // REMOVE
-  // CHECK-FIXES-CPP20ORLATER: // REMOVE
+  int x = P.first;
   int y = P.second; // REMOVE
   // CHECK-FIXES-CPP20ORLATER: // REMOVE
 
@@ -353,8 +337,7 @@ void captureByAllRef() {
   auto P = getPair<int, int>();
   // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
-  int x = P.first; // REMOVE
-  // CHECK-FIXES-CPP20ORLATER: // REMOVE
+  int x = P.first;
   int y = P.second; // REMOVE
   // CHECK-FIXES-CPP20ORLATER: // REMOVE
 
@@ -367,8 +350,7 @@ void deepLambda() {
   auto P = getPair<int, int>();
   // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
-  int x = P.first; // REMOVE
-  // CHECK-FIXES-CPP20ORLATER: // REMOVE
+  int x = P.first;
   int y = P.second; // REMOVE
   // CHECK-FIXES-CPP20ORLATER: // REMOVE
 
@@ -384,8 +366,7 @@ void forRangeNotWarn() {
   for (auto P : Pairs) {
   // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: for (auto [x, y] : Pairs) {
-    int x = P.first; // REMOVE
-    // CHECK-FIXES-CPP20ORLATER: // REMOVE
+    int x = P.first;
     int y = P.second; // REMOVE
     // CHECK-FIXES-CPP20ORLATER: // REMOVE
 
@@ -396,8 +377,7 @@ void forRangeNotWarn() {
 }
 
 void stdTieNotWarn() {
-  int x = 0; // REMOVE
-  // CHECK-FIXES-CPP20ORLATER: // REMOVE
+  int x = 0;
   int y = 0; // REMOVE
   // CHECK-FIXES-CPP20ORLATER: // REMOVE
   std::tie(x, y) = getPair<int, int>();

>From 2ebab5724bf2d1732a0785be692f82951f640c68 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 14:08:15 +0800
Subject: [PATCH 33/48] [NFC] Place lambda match operation to
 `registerMatchers`

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

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 6530fce2f6484..5796be4685110 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -223,6 +223,21 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
     return declRefExpr(to(varDecl(UnlessShouldBeIgnored).bind(Name)));
   };
 
+  auto HasAnyLambdaCaptureThisVar =
+      [](ast_matchers::internal::Matcher<VarDecl> VDMatcher)
+      -> ast_matchers::internal::BindableMatcher<Stmt> {
+    return compoundStmt(hasDescendant(
+        lambdaExpr(hasAnyCapture(capturesVar(varDecl(VDMatcher))))));
+  };
+
+  // Captured structured bindings are a C++20 extension
+  auto UnlessFirstVarOrSecondVarIsCapturedByLambda =
+      getLangOpts().CPlusPlus20
+          ? compoundStmt()
+          : compoundStmt(unless(HasAnyLambdaCaptureThisVar(
+                anyOf(equalsBoundNode(std::string(FirstVarDeclName)),
+                      equalsBoundNode(std::string(SecondVarDeclName))))));
+
   // X x;
   // Y y;
   // std::tie(x, y) = ...;
@@ -243,7 +258,8 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
               llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
                   varDecl(equalsBoundNode(std::string(FirstVarDeclName))),
                   varDecl(equalsBoundNode(std::string(SecondVarDeclName)))}),
-          hasParent(compoundStmt().bind(ScopeBlockName))),
+          hasParent(compoundStmt(UnlessFirstVarOrSecondVarIsCapturedByLambda)
+                        .bind(ScopeBlockName))),
       this);
 
   // pair<X, Y> p = ...;
@@ -262,7 +278,8 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
           hasNextTwoVarDecl(
               llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
                   VarInitWithFirstMember, VarInitWithSecondMember}),
-          hasParent(compoundStmt().bind(ScopeBlockName))),
+          hasParent(compoundStmt(UnlessFirstVarOrSecondVarIsCapturedByLambda)
+                        .bind(ScopeBlockName))),
       this);
 
   // for (pair<X, Y> p : map) {
@@ -282,7 +299,8 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
               compoundStmt(
                   hasFirstTwoVarDecl(llvm::SmallVector<
                                      ast_matchers::internal::Matcher<VarDecl>>{
-                      VarInitWithFirstMember, VarInitWithSecondMember}))
+                      VarInitWithFirstMember, VarInitWithSecondMember}),
+                  UnlessFirstVarOrSecondVarIsCapturedByLambda)
                   .bind(ScopeBlockName)))
           .bind(ForRangeStmtName),
       this);
@@ -317,17 +335,6 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
   const auto *EndDS = Result.Nodes.getNodeAs<DeclStmt>(EndDeclStmtName);
   const auto *ScopeBlock = Result.Nodes.getNodeAs<CompoundStmt>(ScopeBlockName);
 
-  // Captured structured bindings are a C++20 extension
-  if (!Result.Context->getLangOpts().CPlusPlus20) {
-    if (auto Matchers = match(
-            compoundStmt(
-                hasDescendant(lambdaExpr(hasAnyCapture(capturesVar(varDecl(
-                    anyOf(equalsNode(FirstVar), equalsNode(SecondVar)))))))),
-            *ScopeBlock, *Result.Context);
-        !Matchers.empty())
-      return;
-  }
-
   const auto *CFRS = Result.Nodes.getNodeAs<CXXForRangeStmt>(ForRangeStmtName);
   auto DiagAndFix = [&BeginDS, &EndDS, &FirstVar, &SecondVar, &CFRS,
                      this](SourceLocation DiagLoc, SourceRange ReplaceRange,

>From 5bdf20655f83f91d02cb67d586490e9d2a31b28a Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 14:10:24 +0800
Subject: [PATCH 34/48] [NFC] Doc typo fix

---
 .../clang-tidy/checks/modernize/use-structured-binding.rst    | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 0d02e6761cdd4..2526725c53ff9 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -66,8 +66,8 @@ except const and & since it's not very common:
   static int b = pair.first;
   static int c = pair.second;
 
-The check doesn't check for some situations which could possibly transfered
-to structured bnindings, for example:
+The check doesn't check for some situations which could possibly be transferred
+to structured bindings, for example:
 
 .. code-block:: c++
 

>From edac2d43b7a2ff0442d1ad58134ba3c8edc5cbca Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 15:05:58 +0800
Subject: [PATCH 35/48] [NFC] Move init expression logic to `registerMatchers`

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

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 5796be4685110..e92af08007db9 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -28,6 +28,7 @@ static constexpr llvm::StringLiteral ScopeBlockName = "ScopeBlock";
 static constexpr llvm::StringLiteral StdTieAssignStmtName = "StdTieAssign";
 static constexpr llvm::StringLiteral StdTieExprName = "StdTieExpr";
 static constexpr llvm::StringLiteral ForRangeStmtName = "ForRangeStmt";
+static constexpr llvm::StringLiteral InitExprName = "init_expr";
 
 /// Matches a sequence of VarDecls matching the inner matchers, starting from
 /// the \p Iter to \p EndIter and set bindings for the first DeclStmt and the
@@ -273,7 +274,7 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
                       hasType(qualType(anyOf(PairType, lValueReferenceType(
                                                            pointee(PairType))))
                                   .bind(PairVarTypeName)),
-                      hasInitializer(expr()))
+                      hasInitializer(expr(ignoringCopyCtorAndImplicitCast(expr().bind(InitExprName)))))
                   .bind(PairDeclName)),
           hasNextTwoVarDecl(
               llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
@@ -293,7 +294,7 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
               varDecl(hasType(qualType(anyOf(PairType, lValueReferenceType(
                                                            pointee(PairType))))
                                   .bind(PairVarTypeName)),
-                      hasInitializer(expr()))
+                      hasInitializer(expr(ignoringCopyCtorAndImplicitCast(expr().bind(InitExprName)))))
                   .bind(PairDeclName)),
           hasBody(
               compoundStmt(
@@ -375,12 +376,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
   // Check whether PairVar, FirstVar and SecondVar have the same transfer type,
   // so they can be combined to structured binding.
   const auto *PairVar = Result.Nodes.getNodeAs<VarDecl>(PairDeclName);
-  const Expr *InitE = PairVar->getInit();
-  if (auto Res =
-          match(expr(ignoringCopyCtorAndImplicitCast(expr().bind("init_expr"))),
-                *InitE, *Result.Context);
-      !Res.empty())
-    InitE = Res[0].getNodeAs<Expr>("init_expr");
+  const Expr *InitE = Result.Nodes.getNodeAs<Expr>(InitExprName);
 
   const std::optional<TransferType> PairCaptureType =
       getTransferType(*Result.Context, PairVar->getType(), InitE->getType());

>From 0b007e85679a06a4156f8597f64fe5bc0dd6834f Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 15:09:01 +0800
Subject: [PATCH 36/48] [NFC] Rename warning message to `use a structured
 binding...`

---
 .../modernize/UseStructuredBindingCheck.cpp   |  2 +-
 .../use-structured-binding-custom.cpp         |  4 +-
 .../modernize/use-structured-binding.cpp      | 50 +++++++++----------
 3 files changed, 28 insertions(+), 28 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index e92af08007db9..07a622d2938be 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -360,7 +360,7 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
         (Twine(Prefix) + " [" + FirstVar->getNameAsString() + ", " +
          SecondVar->getNameAsString() + "]" + (CFRS ? " :" : ""))
             .str();
-    diag(DiagLoc, "use structured binding to decompose a pair")
+    diag(DiagLoc, "use a structured binding to decompose a pair")
         << FixItHint::CreateReplacement(ReplaceRange, ReplacementText)
         << FixItHint::CreateRemoval(
                SourceRange{BeginDS->getBeginLoc(), EndDS->getEndLoc()});
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
index bd335e1edaf6e..d8f9bdc924d94 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
@@ -16,7 +16,7 @@ struct otherPair {
 void OptionTest() {
   {
     auto P = custom::pair();
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: auto [x, y] = custom::pair();
     int x = P.first;
     int y = P.second;
@@ -24,7 +24,7 @@ void OptionTest() {
 
   {
     auto P = otherPair();
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES: auto [x, y] = otherPair();
     int x = P.first;
     int y = P.second;
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 48d48ec285575..8a511f954e97e 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -16,7 +16,7 @@ struct TestClass {
 void DecomposeByAssignWarnCases() {
   {
     auto P = getPair<int, int>();
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
     int x = P.first;
     int y = P.second; // REMOVE
@@ -25,7 +25,7 @@ void DecomposeByAssignWarnCases() {
 
   {
     auto P = getPair<int, int>();
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
     int x = P.first, y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
@@ -33,7 +33,7 @@ void DecomposeByAssignWarnCases() {
 
   {
     auto P = getPair<int, int>();
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
     int x = P.first, y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
@@ -42,7 +42,7 @@ void DecomposeByAssignWarnCases() {
 
   {
     auto P = getPair<int, int>();
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
     int x = P.first;
     auto y = P.second; // REMOVE
@@ -51,7 +51,7 @@ void DecomposeByAssignWarnCases() {
 
   {
     const auto P = getPair<int, int>();
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: const auto [x, y] = getPair<int, int>();
     const int x = P.first;
     const auto y = P.second; // REMOVE
@@ -61,7 +61,7 @@ void DecomposeByAssignWarnCases() {
   {
     std::pair<int, int> otherP;
     auto& P = otherP;
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: auto& [x, y] = otherP;
     int& x = P.first;
     auto& y = P.second; // REMOVE
@@ -71,7 +71,7 @@ void DecomposeByAssignWarnCases() {
   {
     std::pair<int, int> otherP;
     const auto& P = otherP;
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: const auto& [x, y] = otherP;
     const int& x = P.first;
     const auto& y = P.second; // REMOVE
@@ -82,7 +82,7 @@ void DecomposeByAssignWarnCases() {
 void forRangeWarnCases() {
   std::pair<int, int> Pairs[10];
   for (auto P : Pairs) {
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
     int x = P.first;
     int y = P.second; // REMOVE
@@ -90,14 +90,14 @@ void forRangeWarnCases() {
   }
 
   for (auto P : Pairs) {
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
     int x = P.first, y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
   }
 
   for (auto P : Pairs) {
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (auto [x, y] : Pairs) {
     int x = P.first, y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
@@ -105,7 +105,7 @@ void forRangeWarnCases() {
   }
 
   for (const auto P : Pairs) {
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (const auto [x, y] : Pairs) {
     const int x = P.first;
     const int y = P.second; // REMOVE
@@ -113,7 +113,7 @@ void forRangeWarnCases() {
   }
 
   for (auto& P : Pairs) {
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (auto& [x, y] : Pairs) {
     int& x = P.first;
     int& y = P.second; // REMOVE
@@ -121,7 +121,7 @@ void forRangeWarnCases() {
   }
 
   for (const auto& P : Pairs) {
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (const auto& [x, y] : Pairs) {
     const int& x = P.first;
     const int& y = P.second; // REMOVE
@@ -130,7 +130,7 @@ void forRangeWarnCases() {
 
   std::pair<TestClass, TestClass> ClassPairs[10];
   for (auto P : ClassPairs) {
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (auto [c1, c2] : ClassPairs) {
     TestClass c1 = P.first;
     TestClass c2 = P.second; // REMOVE
@@ -138,7 +138,7 @@ void forRangeWarnCases() {
   }
 
   for (const auto P : ClassPairs) {
-    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: for (const auto [c1, c2] : ClassPairs) {
     const TestClass c1 = P.first;
     const TestClass c2 = P.second; // REMOVE
@@ -186,27 +186,27 @@ void stdTieWarnCases() {
   int b = 0; // REMOVE
   // CHECK-FIXES-ALL: // REMOVE
   std::tie(a, b) = getPair<int, int>();
-  // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-ALL: auto [a, b] = getPair<int, int>();
 
   int x = 0, y = 0; // REMOVE
   // CHECK-FIXES-ALL: // REMOVE
   std::tie(x, y) = getPair<int, int>();
-  // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
 
   int* pa = nullptr;
   int* pb = nullptr; // REMOVE
   // CHECK-FIXES-ALL: // REMOVE
   std::tie(pa, pb) = getPair<int*, int*>();
-  // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-ALL: auto [pa, pb] = getPair<int*, int*>();
 
   TestClass c1 (1, 2);
   TestClass c2 = TestClass {3, 4}; // REMOVE
   // CHECK-FIXES-ALL: // REMOVE
   std::tie(c1, c2) = getPair<TestClass, TestClass>();
-  // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-MESSAGES-ALL: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-ALL: auto [c1, c2] = getPair<TestClass, TestClass>();
 }
 
@@ -309,7 +309,7 @@ void NotWarnForMacro2() {
 
 void captureByVal() {
   auto P = getPair<int, int>();
-  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
   int x = P.first;
   int y = P.second; // REMOVE
@@ -322,7 +322,7 @@ void captureByVal() {
 
 void captureByRef() {
   auto P = getPair<int, int>();
-  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
   int x = P.first;
   int y = P.second; // REMOVE
@@ -335,7 +335,7 @@ void captureByRef() {
 
 void captureByAllRef() {
   auto P = getPair<int, int>();
-  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
   int x = P.first;
   int y = P.second; // REMOVE
@@ -348,7 +348,7 @@ void captureByAllRef() {
 
 void deepLambda() {
   auto P = getPair<int, int>();
-  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
   int x = P.first;
   int y = P.second; // REMOVE
@@ -364,7 +364,7 @@ void deepLambda() {
 void forRangeNotWarn() {
   std::pair<int, int> Pairs[10];
   for (auto P : Pairs) {
-  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:8: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:8: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: for (auto [x, y] : Pairs) {
     int x = P.first;
     int y = P.second; // REMOVE
@@ -381,7 +381,7 @@ void stdTieNotWarn() {
   int y = 0; // REMOVE
   // CHECK-FIXES-CPP20ORLATER: // REMOVE
   std::tie(x, y) = getPair<int, int>();
-  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use structured binding to decompose a pair [modernize-use-structured-binding]
+  // CHECK-MESSAGES-CPP20ORLATER: :[[@LINE-1]]:3: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
   // CHECK-FIXES-CPP20ORLATER: auto [x, y] = getPair<int, int>();
 
   auto lambda = [&x]() {

>From 5a3799e9bad64e9bdf5ad3f291d47a59384ea968 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 18:30:44 +0800
Subject: [PATCH 37/48] [NFC] cleanup

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

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 07a622d2938be..cbd0cf4e2c362 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -55,6 +55,7 @@ static bool matchNVarDeclStartingWith(
       BeginDS = EndDS;
 
     auto Matches = [&](const Decl *VD) {
+      // We don't want redundant decls in DeclStmt.
       if (Count == N)
         return false;
 
@@ -113,7 +114,7 @@ AST_MATCHER_P(Stmt, hasPreTwoVarDecl,
   if (Parents.size() != 1)
     return false;
 
-  auto *C = Parents[0].get<CompoundStmt>();
+  const auto *C = Parents[0].get<CompoundStmt>();
   if (!C)
     return false;
 
@@ -128,12 +129,11 @@ AST_MATCHER_P(Stmt, hasPreTwoVarDecl,
 AST_MATCHER_P(Stmt, hasNextTwoVarDecl,
               llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>,
               InnerMatchers) {
-
   const DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
   if (Parents.size() != 1)
     return false;
 
-  auto *C = Parents[0].get<CompoundStmt>();
+  const auto *C = Parents[0].get<CompoundStmt>();
   if (!C)
     return false;
 
@@ -274,7 +274,8 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
                       hasType(qualType(anyOf(PairType, lValueReferenceType(
                                                            pointee(PairType))))
                                   .bind(PairVarTypeName)),
-                      hasInitializer(expr(ignoringCopyCtorAndImplicitCast(expr().bind(InitExprName)))))
+                      hasInitializer(expr(ignoringCopyCtorAndImplicitCast(
+                          expr().bind(InitExprName)))))
                   .bind(PairDeclName)),
           hasNextTwoVarDecl(
               llvm::SmallVector<ast_matchers::internal::Matcher<VarDecl>>{
@@ -294,7 +295,8 @@ void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
               varDecl(hasType(qualType(anyOf(PairType, lValueReferenceType(
                                                            pointee(PairType))))
                                   .bind(PairVarTypeName)),
-                      hasInitializer(expr(ignoringCopyCtorAndImplicitCast(expr().bind(InitExprName)))))
+                      hasInitializer(expr(ignoringCopyCtorAndImplicitCast(
+                          expr().bind(InitExprName)))))
                   .bind(PairDeclName)),
           hasBody(
               compoundStmt(
@@ -376,10 +378,10 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
   // Check whether PairVar, FirstVar and SecondVar have the same transfer type,
   // so they can be combined to structured binding.
   const auto *PairVar = Result.Nodes.getNodeAs<VarDecl>(PairDeclName);
-  const Expr *InitE = Result.Nodes.getNodeAs<Expr>(InitExprName);
 
   const std::optional<TransferType> PairCaptureType =
-      getTransferType(*Result.Context, PairVar->getType(), InitE->getType());
+      getTransferType(*Result.Context, PairVar->getType(),
+                      Result.Nodes.getNodeAs<Expr>(InitExprName)->getType());
   const std::optional<TransferType> FirstVarCaptureType =
       getTransferType(*Result.Context, FirstVar->getType(),
                       *Result.Nodes.getNodeAs<QualType>(FirstTypeName));

>From c10eeeada8026e9363dc7b8504f1cad0dcb7cbd4 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 19:12:23 +0800
Subject: [PATCH 38/48] Use duck-typing to get pair type

---
 .../modernize/UseStructuredBindingCheck.cpp   | 24 +++++------
 .../modernize/UseStructuredBindingCheck.h     |  6 +--
 .../modernize/use-structured-binding.rst      |  9 ----
 .../use-structured-binding-custom.cpp         | 32 --------------
 .../modernize/use-structured-binding.cpp      | 43 +++++++++++++++++++
 5 files changed, 55 insertions(+), 59 deletions(-)
 delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index cbd0cf4e2c362..3a36ec7aa79fd 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -8,14 +8,12 @@
 
 #include "UseStructuredBindingCheck.h"
 #include "../utils/DeclRefExprUtils.h"
-#include "../utils/OptionsUtils.h"
 #include "clang/Lex/Lexer.h"
 
 using namespace clang::ast_matchers;
 
 namespace clang::tidy::modernize {
 
-static constexpr const char *DefaultPairTypes = "std::pair";
 static constexpr llvm::StringLiteral PairDeclName = "PairVarD";
 static constexpr llvm::StringLiteral PairVarTypeName = "PairVarType";
 static constexpr llvm::StringLiteral FirstVarDeclName = "FirstVarDecl";
@@ -178,16 +176,15 @@ AST_MATCHER_P(Expr, ignoringCopyCtorAndImplicitCast,
   return InnerMatcher.matches(*Node.IgnoreImpCasts(), Finder, Builder);
 }
 
-} // namespace
-
-UseStructuredBindingCheck::UseStructuredBindingCheck(StringRef Name,
-                                                     ClangTidyContext *Context)
-    : ClangTidyCheck(Name, Context),
-      PairTypes(utils::options::parseStringList(
-          Options.get("PairTypes", DefaultPairTypes))) {
-  ;
+AST_MATCHER(CXXRecordDecl, isPairType) {
+  return llvm::all_of(Node.fields(), [](const FieldDecl *FD) {
+    return FD->getAccess() == AS_public &&
+           (FD->getName() == "first" || FD->getName() == "second");
+  });
 }
 
+} // namespace
+
 static auto getVarInitWithMemberMatcher(
     StringRef PairName, StringRef MemberName, StringRef TypeName,
     StringRef BindingName,
@@ -204,10 +201,9 @@ static auto getVarInitWithMemberMatcher(
 }
 
 void UseStructuredBindingCheck::registerMatchers(MatchFinder *Finder) {
-  auto PairType =
-      qualType(unless(isVolatileQualified()),
-               hasUnqualifiedDesugaredType(recordType(
-                   hasDeclaration(cxxRecordDecl(hasAnyName(PairTypes))))));
+  auto PairType = qualType(unless(isVolatileQualified()),
+                           hasUnqualifiedDesugaredType(recordType(
+                               hasDeclaration(cxxRecordDecl(isPairType())))));
 
   auto UnlessShouldBeIgnored =
       unless(anyOf(hasAnySpecifiersShouldBeIgnored(), isInMarco()));
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
index 83d262a5db3cd..f8a4452890ecf 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
@@ -20,15 +20,13 @@ namespace clang::tidy::modernize {
 /// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-structured-binding.html
 class UseStructuredBindingCheck : public ClangTidyCheck {
 public:
-  UseStructuredBindingCheck(StringRef Name, ClangTidyContext *Context);
+  UseStructuredBindingCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
   void registerMatchers(ast_matchers::MatchFinder *Finder) override;
   void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
   bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
     return LangOpts.CPlusPlus17;
   }
-
-private:
-  const std::vector<StringRef> PairTypes;
 };
 
 } // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 2526725c53ff9..8222b67fbe65a 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -84,12 +84,3 @@ and:
   if (results.second) {
       handle_inserted(results.first);
   }
-
-Options
--------
-
-.. option:: PairTypes
-
-   A semicolon-separated list of type names to be treated as pair-like for
-   structured binding suggestions. Example: `MyPairType;OtherPairType`. 
-   Default is `std::pair`.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
deleted file mode 100644
index d8f9bdc924d94..0000000000000
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding-custom.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-structured-binding %t \
-// RUN: -config="{CheckOptions: {modernize-use-structured-binding.PairTypes: 'custom::pair; otherPair'}}"
-
-namespace custom {
-  struct pair {
-    int first;
-    int second;
-  };
-}
-
-struct otherPair {
-  int first;
-  int second;
-};
-
-void OptionTest() {
-  {
-    auto P = custom::pair();
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: auto [x, y] = custom::pair();
-    int x = P.first;
-    int y = P.second;
-  }
-
-  {
-    auto P = otherPair();
-    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES: auto [x, y] = otherPair();
-    int x = P.first;
-    int y = P.second;
-  }
-}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 8a511f954e97e..be9cae8980c75 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -388,3 +388,46 @@ void stdTieNotWarn() {
     x = 1;
   };
 }
+
+struct otherPair {
+  int first;
+  int second;
+};
+
+void OtherPairTest() {
+  {
+    auto P = otherPair();
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: auto [x, y] = otherPair();
+    int x = P.first;
+    int y = P.second;
+  }
+}
+
+struct otherNonPair1 {
+  int first;
+  int second;
+
+private:
+  int third;
+};
+
+struct otherNonPair2 {
+  int first;
+  int second;
+  int third;
+};
+
+void OtherNonPairTest() {
+  {
+    auto P = otherNonPair1();
+    int x = P.first;
+    int y = P.second;
+  }
+
+  {
+    auto P = otherNonPair2();
+    int x = P.first;
+    int y = P.second;
+  }
+}

>From 48b7134dd5ed28c8928d71bc5da7699045084d5a Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 19:18:00 +0800
Subject: [PATCH 39/48] [NFC] Fix doc

---
 .../clang-tidy/checks/modernize/use-structured-binding.rst     | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
index 8222b67fbe65a..f68041e266231 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst
@@ -51,9 +51,6 @@ in a range-based for loop:
     // use x and y
   }
 
-The check also supports custom pair-like types via the :option:`PairTypes`
-option.
-
 Limitations
 -----------
 

>From 06079985e59470c50335b0ccadbf8801901baf6b Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 20:44:30 +0800
Subject: [PATCH 40/48] [NFC] Should not use auto here

---
 .../clang-tidy/modernize/UseStructuredBindingCheck.cpp          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 3a36ec7aa79fd..11e198b60ffb5 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -166,7 +166,7 @@ AST_POLYMORPHIC_MATCHER(isInMarco,
 AST_MATCHER_P(Expr, ignoringCopyCtorAndImplicitCast,
               ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
   if (const auto *CtorE = dyn_cast<CXXConstructExpr>(&Node)) {
-    if (const auto *CtorD = CtorE->getConstructor();
+    if (const CXXConstructorDecl *CtorD = CtorE->getConstructor();
         CtorD->isCopyConstructor() && CtorE->getNumArgs() == 1) {
       return InnerMatcher.matches(*CtorE->getArg(0)->IgnoreImpCasts(), Finder,
                                   Builder);

>From 7b7ba725314ac7560f129e21ea61c29492290956 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 20:51:32 +0800
Subject: [PATCH 41/48] [NFC] Add more tests for custom pair type

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

diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index be9cae8980c75..b398e035e35c3 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -402,6 +402,61 @@ void OtherPairTest() {
     int x = P.first;
     int y = P.second;
   }
+
+  {
+    const auto P = otherPair();
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: const auto [x, y] = otherPair();
+    const int x = P.first;
+    const auto y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+  }
+
+  {
+    otherPair otherP;
+    auto& P = otherP;
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: auto& [x, y] = otherP;
+    int& x = P.first;
+    auto& y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+  }
+
+  {
+    std::pair<int, int> otherP;
+    const auto& P = otherP;
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: const auto& [x, y] = otherP;
+    const int& x = P.first;
+    const auto& y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+  }
+}
+
+void OtherPairNotWarnCases() {
+  {
+    auto P = otherPair();
+    const int x = P.first;
+    int y = P.second;
+  }
+
+  {
+    auto P = otherPair();
+    volatile int x = P.first;
+    int y = P.second;
+  }
+
+  {
+    auto P = otherPair();
+    int x = P.first;
+    [[maybe_unused]] int y = P.second;
+  }
+
+  {
+    static auto P = getPair<int, int>();
+    int x = P.first;
+    int y = P.second;
+  }
 }
 
 struct otherNonPair1 {

>From 52bc67c81e4847cdf68558f73d9169b4967e3d7a Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 20:59:33 +0800
Subject: [PATCH 42/48] [NFC] Place lambda out of loop

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

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 11e198b60ffb5..501939b4c5607 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -44,6 +44,22 @@ static bool matchNVarDeclStartingWith(
   const DeclStmt *EndDS = nullptr;
   size_t N = InnerMatchers.size();
   size_t Count = 0;
+
+  auto Matches = [&](const Decl *VD) {
+    // We don't want redundant decls in DeclStmt.
+    if (Count == N)
+      return false;
+
+    if (const auto *Var = dyn_cast<VarDecl>(VD);
+        Var && InnerMatchers[Backwards ? N - Count - 1 : Count].matches(
+                   *Var, Finder, Builder)) {
+      ++Count;
+      return true;
+    }
+
+    return false;
+  };
+
   for (; Iter != EndIter; ++Iter) {
     EndDS = dyn_cast<DeclStmt>(*Iter);
     if (!EndDS)
@@ -52,21 +68,6 @@ static bool matchNVarDeclStartingWith(
     if (!BeginDS)
       BeginDS = EndDS;
 
-    auto Matches = [&](const Decl *VD) {
-      // We don't want redundant decls in DeclStmt.
-      if (Count == N)
-        return false;
-
-      if (const auto *Var = dyn_cast<VarDecl>(VD);
-          Var && InnerMatchers[Backwards ? N - Count - 1 : Count].matches(
-                     *Var, Finder, Builder)) {
-        ++Count;
-        return true;
-      }
-
-      return false;
-    };
-
     if (Backwards) {
       for (const auto *VD : llvm::reverse(EndDS->decls())) {
         if (!Matches(VD))

>From d091ff4557f5625f1398fe94c665c2fd0ef4b71e Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 21:06:10 +0800
Subject: [PATCH 43/48] [NFC] Iterator's name from `I` to `It`

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

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 501939b4c5607..16e2922d25d50 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -117,10 +117,10 @@ AST_MATCHER_P(Stmt, hasPreTwoVarDecl,
   if (!C)
     return false;
 
-  const auto I = llvm::find(llvm::reverse(C->body()), &Node);
-  assert(I != C->body_rend() && "C is parent of Node");
-  return matchNVarDeclStartingWith(I + 1, C->body_rend(), InnerMatchers, Finder,
-                                   Builder, true);
+  const auto It = llvm::find(llvm::reverse(C->body()), &Node);
+  assert(It != C->body_rend() && "C is parent of Node");
+  return matchNVarDeclStartingWith(It + 1, C->body_rend(), InnerMatchers,
+                                   Finder, Builder, true);
 }
 
 /// Matches a Stmt whose parent is a CompoundStmt, and which is directly
@@ -136,9 +136,9 @@ AST_MATCHER_P(Stmt, hasNextTwoVarDecl,
   if (!C)
     return false;
 
-  const auto *I = llvm::find(C->body(), &Node);
-  assert(I != C->body_end() && "C is parent of Node");
-  return matchNVarDeclStartingWith(I + 1, C->body_end(), InnerMatchers, Finder,
+  const auto *It = llvm::find(C->body(), &Node);
+  assert(It != C->body_end() && "C is parent of Node");
+  return matchNVarDeclStartingWith(It + 1, C->body_end(), InnerMatchers, Finder,
                                    Builder);
 }
 

>From 6d44ea0706b9d69f1d09d9adb10d74deeaaf4108 Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Sun, 21 Sep 2025 22:35:37 +0800
Subject: [PATCH 44/48] [NFC] Add test for multi checked cases in one scope

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

diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index b398e035e35c3..1a359c23a016f 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -77,6 +77,23 @@ void DecomposeByAssignWarnCases() {
     const auto& y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
   }
+
+  {
+    auto P = getPair<int, int>(); // match
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>(); // match
+    int x = P.first;
+    int y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+    
+    // maybe match with `hasParent` but NOT with `has(declStmt())`
+    auto another_p = getPair<int, int>();
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: auto [another_x, another_y] = getPair<int, int>();
+    int another_x = another_p.first;
+    int another_y = another_p.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+  }
 }
 
 void forRangeWarnCases() {

>From 82dfe89a142aed019a57370a9096c45cb21b023c Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Mon, 22 Sep 2025 21:31:21 +0800
Subject: [PATCH 45/48] [NFC] Add testcases for pairs has qualifiers in field
 type.

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

diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 1a359c23a016f..7ccb51a16d9f7 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -503,3 +503,101 @@ void OtherNonPairTest() {
     int y = P.second;
   }
 }
+
+template<typename PairType>
+PairType getCertainPair();
+
+struct ConstFieldPair {
+  const int first;
+  int second;
+};
+
+void ConstFieldPairTests() {
+  {
+    const ConstFieldPair P = getCertainPair<ConstFieldPair>();
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: const auto [x, y] = getCertainPair<ConstFieldPair>();
+    const int x = P.first;
+    const int y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+  }
+
+  {
+    const ConstFieldPair& P = getCertainPair<ConstFieldPair>();
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: const auto& [x, y] = getCertainPair<ConstFieldPair>();
+    const int& x = P.first;
+    const int& y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+  }
+
+  {
+    ConstFieldPair P = getCertainPair<ConstFieldPair>(); // no warning
+    int x = P.first;
+    int y = P.second;
+  }
+}
+
+struct PointerFieldPair {
+  int* first;
+  int second;
+};
+
+void PointerFieldPairTests() {
+  {
+    PointerFieldPair P = getCertainPair<PointerFieldPair>();
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: auto [x, y] = getCertainPair<PointerFieldPair>();
+    int* x = P.first;
+    int y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+  }
+
+  {
+    PointerFieldPair P = getCertainPair<PointerFieldPair>(); // no warning
+    const int* x = P.first;
+    int y = P.second;
+  }
+}
+
+struct ConstRefFieldPair {
+  const int& first;
+  int second;
+  ConstRefFieldPair(int& f, int s) : first(f), second(s) {}
+};
+
+void ConstRefFieldPairTests() {
+  {
+    ConstRefFieldPair P = getCertainPair<ConstRefFieldPair>();
+    // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
+    // CHECK-FIXES-ALL: auto [x, y] = getCertainPair<ConstRefFieldPair>();
+    const int& x = P.first;
+    int y = P.second; // REMOVE
+    // CHECK-FIXES-ALL: // REMOVE
+  }
+
+  {
+    ConstRefFieldPair P = getCertainPair<ConstRefFieldPair>();; // no warning
+    int x = P.first;
+    int y = P.second;
+  }
+}
+
+struct StaticFieldPair {
+  static int first;
+  int second;
+};
+
+void StaticFieldPairTests() {
+  {
+    StaticFieldPair P; // Should not warn
+    int x = P.first;
+    int y = P.second;
+  }
+
+  {
+    StaticFieldPair P; // Should not warn
+    static int x = P.first;
+    int y = P.second;
+  }
+}

>From 653233fc4d39ada38618a2bd711b743ceec2412f Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Mon, 22 Sep 2025 21:44:52 +0800
Subject: [PATCH 46/48] [NFC] Remove unnecessary comments in testcase

---
 .../clang-tidy/checkers/modernize/use-structured-binding.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
index 7ccb51a16d9f7..86c99b36f5c79 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-structured-binding.cpp
@@ -79,14 +79,13 @@ void DecomposeByAssignWarnCases() {
   }
 
   {
-    auto P = getPair<int, int>(); // match
+    auto P = getPair<int, int>();
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
-    // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>(); // match
+    // CHECK-FIXES-ALL: auto [x, y] = getPair<int, int>();
     int x = P.first;
     int y = P.second; // REMOVE
     // CHECK-FIXES-ALL: // REMOVE
     
-    // maybe match with `hasParent` but NOT with `has(declStmt())`
     auto another_p = getPair<int, int>();
     // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: use a structured binding to decompose a pair [modernize-use-structured-binding]
     // CHECK-FIXES-ALL: auto [another_x, another_y] = getPair<int, int>();

>From 09f7bcdd89a6cb690650e074431acf07be15328b Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 23 Sep 2025 21:18:16 +0800
Subject: [PATCH 47/48] Update
 clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h

Co-authored-by: Victor Chernyakin <chernyakin.victor.j at outlook.com>
---
 .../clang-tidy/modernize/UseStructuredBindingCheck.h            | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
index f8a4452890ecf..0b11ce38c44db 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.h
@@ -17,7 +17,7 @@ namespace clang::tidy::modernize {
 /// suggests replacing them.
 ///
 /// For the user-facing documentation see:
-/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-structured-binding.html
+/// https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-structured-binding.html
 class UseStructuredBindingCheck : public ClangTidyCheck {
 public:
   UseStructuredBindingCheck(StringRef Name, ClangTidyContext *Context)

>From 230df4fddf210d9f3d68c5f0627e997fcee42cec Mon Sep 17 00:00:00 2001
From: flovent <flbven at protonmail.com>
Date: Tue, 23 Sep 2025 21:22:53 +0800
Subject: [PATCH 48/48] [NFC] IIFE

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

diff --git a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
index 16e2922d25d50..3919fddd01061 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStructuredBindingCheck.cpp
@@ -339,21 +339,18 @@ void UseStructuredBindingCheck::check(const MatchFinder::MatchResult &Result) {
   auto DiagAndFix = [&BeginDS, &EndDS, &FirstVar, &SecondVar, &CFRS,
                      this](SourceLocation DiagLoc, SourceRange ReplaceRange,
                            TransferType TT = TT_ByVal) {
-    StringRef Prefix;
-    switch (TT) {
-    case TT_ByVal:
-      Prefix = "auto";
-      break;
-    case TT_ByConstVal:
-      Prefix = "const auto";
-      break;
-    case TT_ByRef:
-      Prefix = "auto&";
-      break;
-    case TT_ByConstRef:
-      Prefix = "const auto&";
-      break;
-    }
+    const auto Prefix = [&TT]() -> StringRef {
+      switch (TT) {
+      case TT_ByVal:
+        return "auto";
+      case TT_ByConstVal:
+        return "const auto";
+      case TT_ByRef:
+        return "auto&";
+      case TT_ByConstRef:
+        return "const auto&";
+      }
+    }();
 
     std::string ReplacementText =
         (Twine(Prefix) + " [" + FirstVar->getNameAsString() + ", " +



More information about the cfe-commits mailing list