https://github.com/flovent updated https://github.com/llvm/llvm-project/pull/158462
>From a1941312179171a8752c27f886158ce4fd90db33 Mon Sep 17 00:00:00 2001 From: flovent <flb...@protonmail.com> Date: Sun, 14 Sep 2025 14:33:59 +0800 Subject: [PATCH 01/10] [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 <flb...@protonmail.com> Date: Sun, 14 Sep 2025 16:40:16 +0800 Subject: [PATCH 02/10] Update clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst Co-authored-by: Baranov Victor <bar.victor.2...@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 <flb...@protonmail.com> Date: Sun, 14 Sep 2025 16:40:26 +0800 Subject: [PATCH 03/10] Update clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst Co-authored-by: Baranov Victor <bar.victor.2...@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 <flb...@protonmail.com> Date: Sun, 14 Sep 2025 16:40:48 +0800 Subject: [PATCH 04/10] Update clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst Co-authored-by: Baranov Victor <bar.victor.2...@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 <flb...@protonmail.com> Date: Sun, 14 Sep 2025 16:41:17 +0800 Subject: [PATCH 05/10] Update clang-tools-extra/docs/clang-tidy/checks/modernize/use-structured-binding.rst Co-authored-by: Baranov Victor <bar.victor.2...@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 <flb...@protonmail.com> Date: Sun, 14 Sep 2025 16:44:32 +0800 Subject: [PATCH 06/10] [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 <flb...@protonmail.com> Date: Sun, 14 Sep 2025 16:53:53 +0800 Subject: [PATCH 07/10] [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 <flb...@protonmail.com> Date: Sun, 14 Sep 2025 16:56:27 +0800 Subject: [PATCH 08/10] [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 <flb...@protonmail.com> Date: Sun, 14 Sep 2025 17:04:53 +0800 Subject: [PATCH 09/10] [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 <flb...@protonmail.com> Date: Sun, 14 Sep 2025 17:53:53 +0800 Subject: [PATCH 10/10] [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; + }; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits