[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
https://github.com/lucasvallejoo edited https://github.com/llvm/llvm-project/pull/191218 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
lucasvallejoo wrote: Hi @unterumarmung, Thank you so much for taking the time to write such a detailed and insightful review. You are completely right, my initial prototype relied too heavily on textual heuristics (token slicing) and is too fragile for the standard expected in clang-tidy. I really appreciate the explanation on how this should be properly designed. I am going to step back, study Clang's AST matchers more deeply, and rewrite this to be 100% AST-driven so it can safely handle edge cases, macros, side effects, and complex syntax variations. It will take me some time to learn the internals properly and expand both the positive and negative test coverage as you suggested, but I am on it. I'll update the PR once the new AST-based architecture is solid. Thanks again for pointing me in the right direction! https://github.com/llvm/llvm-project/pull/191218 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
github-actions[bot] wrote: :warning: RST documentation linter, doc8 found issues in your code. :warning: You can test this locally with the following command: ```bash doc8 -q clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-tie.rst clang-tools-extra/docs/clang-tidy/checks/list.rst --config clang-tools-extra/clang-tidy/doc8.ini ``` View the output from doc8 here. ``` clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-tie.rst:6: D001 Line too long clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-tie.rst:8: D001 Line too long clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-tie.rst:9: D001 Line too long clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-tie.rst:28: D005 No newline at end of file ``` Note: documentation lines should be less than 80 characters wide. https://github.com/llvm/llvm-project/pull/191218 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
@@ -0,0 +1,45 @@
+// RUN: %check_clang_tidy %s modernize-use-std-tie %t
+
+#include
+
+struct A {
+int n;
+double s;
+float w;
+float v;
+float d() const noexcept { return w / v; }
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:6: warning: use std::tie to implement
lexicographical comparison [modernize-use-std-tie]
+bool operator<(const A& lhs, const A& rhs) noexcept
+{
+if (lhs.n != rhs.n) {
+return lhs.n < rhs.n;
+} else if (lhs.s != rhs.s) {
+return lhs.s < rhs.s;
+}
+return lhs.d() < rhs.d();
+}
+// CHECK-FIXES: bool operator<(const A& lhs, const A& rhs) noexcept
+// CHECK-FIXES-NEXT: {
+// CHECK-FIXES-NEXT: const auto lhs_d = lhs.d();
+// CHECK-FIXES-NEXT: const auto rhs_d = rhs.d();
+// CHECK-FIXES-NEXT: return std::tie(lhs.n, lhs.s, lhs_d) <
std::tie(rhs.n, rhs.s, rhs_d);
unterumarmung wrote:
Why do you introduce new variables instead of just calling the method?
https://github.com/llvm/llvm-project/pull/191218
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
https://github.com/unterumarmung edited https://github.com/llvm/llvm-project/pull/191218 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
https://github.com/unterumarmung commented:
This is a useful prototype, but in its current form the implementation is too
fragile for a clang-tidy check.
The main concern is that the check matches every `operator<` / `operator>` and
then tries to recover intent by scraping raw source text from all `return`
statements in the body. That is far too permissive. The code never actually
proves that the function implements a lexicographical comparison, so it can
rewrite bodies with unrelated early returns, extra control flow, side effects,
mismatched fields, or mixed comparison directions. More generally, the
transformation is driven by textual heuristics (`find('<')`, `find('>')`,
`lhs.` prefix checks, special-casing `()`), not by AST structure, which makes
it brittle for anything beyond the exact spelling in the test: different
parameter names, member operators using `this`, nested expressions, casts,
dereferences, templates, macros, and other ordinary C++ syntax variations.
There are also fix-it correctness issues. The implementation assumes the RHS
can be reconstructed by mirroring the extracted LHS text, which is not
guaranteed. It does not guard against duplicate synthesized temporaries or name
collisions, and it only handles one very specific kind of computed expression
(`lhs.foo()`). That means even some cases it intends to support can easily
produce invalid or semantically different code.
I think this needs to be AST-driven instead. In practice, the matcher should
recognize a much narrower body shape up front, prove that each clause is of the
expected `if (lhs.k != rhs.k) return lhs.k < rhs.k;` / final-return form, and
then build the replacement from the matched expressions rather than token
slicing. Until that happens, I do not think this is safe to ship as an
automatic rewrite.
On the test side, the current coverage is much too narrow for a check like
this. Right now the tests only cover one happy-path shape: a free `operator<` /
`operator>` written as an `if` / `else if` chain, comparing direct members plus
one repeated nullary member call hoisted into temporaries. That is a fine smoke
test, but it leaves most of the interesting surface area untested.
We should have broader positive coverage for equivalent shapes, such as
sequential `if` statements, longer comparison chains, member operators, hidden
friends, out-of-line definitions, and variants with `constexpr`, attributes, or
trailing return types. It would also be useful to cover a wider range of key
expressions: nested member access, subscripts, dereferences, casts, getters,
and other cases where the fix may need to preserve evaluation semantics or
introduce temporaries.
Just as importantly, the suite needs substantially more negative coverage. For
a rewriting check, that matters as much as the positive path. There should be
tests showing that we do not transform near-miss cases with extra control flow,
side effects, mismatched compared expressions, mixed ordering directions,
non-strict operators, custom equality conditions, boolean-formula
implementations, macro-generated code, and dependent/template contexts that are
not provably safe. It would also be good to exercise interaction with modern
C++ features such as templates, concepts, macros, modules, and existing `<=>` /
defaulted comparisons.
As it stands, the tests show that one hand-written example works; they do not
yet demonstrate that the matcher is precise or that the rewrite is robust
across the range of C++ code this check will encounter.
https://github.com/llvm/llvm-project/pull/191218
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
@@ -0,0 +1,108 @@
+#include "UseStdTieCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+void UseStdTieCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(functionDecl(anyOf(hasOverloadedOperatorName("<"),
+hasOverloadedOperatorName(">")))
+ .bind("bad-operator"),
+ this);
+}
+
+void UseStdTieCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl =
+ Result.Nodes.getNodeAs("bad-operator");
+ if (!MatchedDecl || !MatchedDecl->hasBody())
+return;
+
+ // 1. SIMPLE SCANNER: Find all 'return' statements inside the function
+ auto ReturnMatches =
+ match(stmt(findAll(returnStmt(hasReturnValue(expr().bind("ret_val"),
+*MatchedDecl->getBody(), *Result.Context);
+
+ std::vector TieArgsLhs;
+ std::vector TieArgsRhs;
+ std::string Autos = "";
+
+ // 2. EXTRACT VARIABLES FROM TEXT
+ for (const auto &Node : ReturnMatches) {
+const auto *RetVal = Node.getNodeAs("ret_val");
+if (!RetVal)
+ continue;
+
+// Get the raw text, e.g., "lhs.n < rhs.n"
+std::string Text =
+Lexer::getSourceText(
+CharSourceRange::getTokenRange(RetVal->getSourceRange()),
+*Result.SourceManager, Result.Context->getLangOpts())
+.str();
+// Find the operator symbol to split the expression
+size_t OpPos = Text.find('<');
+if (OpPos == std::string::npos)
+ OpPos = Text.find('>');
+
+if (OpPos != std::string::npos) {
+ std::string LhsSide = Text.substr(0, OpPos);
+
+ // Cleanup: remove spaces and tabs to avoid compilation errors
+ std::string CleanLhs = "";
+ for (char c : LhsSide)
+if (c != ' ' && c != '\t' && c != '\n')
+ CleanLhs += c;
+
+ // If we successfully extracted the left-hand side pattern
+ if (CleanLhs.rfind("lhs.", 0) == 0) {
+std::string VarName = CleanLhs.substr(4);
+
+if (VarName.find("()") != std::string::npos) {
+ std::string CleanName = VarName.substr(0, VarName.length() - 2);
+ Autos +=
+ "const auto lhs_" + CleanName + " = lhs." + VarName + ";\n";
+ Autos +=
+ "const auto rhs_" + CleanName + " = rhs." + VarName + ";\n";
+ TieArgsLhs.push_back("lhs_" + CleanName);
+ TieArgsRhs.push_back("rhs_" + CleanName);
+} else {
+ TieArgsLhs.push_back("lhs." + VarName);
+ TieArgsRhs.push_back("rhs." + VarName);
unterumarmung wrote:
What if the parameter name is not `lhs` / `rhs`?..
https://github.com/llvm/llvm-project/pull/191218
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
@@ -0,0 +1,108 @@
+#include "UseStdTieCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+void UseStdTieCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(functionDecl(anyOf(hasOverloadedOperatorName("<"),
+hasOverloadedOperatorName(">")))
+ .bind("bad-operator"),
+ this);
+}
+
+void UseStdTieCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl =
+ Result.Nodes.getNodeAs("bad-operator");
+ if (!MatchedDecl || !MatchedDecl->hasBody())
+return;
+
+ // 1. SIMPLE SCANNER: Find all 'return' statements inside the function
unterumarmung wrote:
Please do not use CAPS.
https://github.com/llvm/llvm-project/pull/191218
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
@@ -0,0 +1,28 @@ +.. title:: clang-tidy - modernize-use-std-tie + +modernize-use-std-tie += + +Suggests replacing manual field-by-field lexicographical comparisons with ``std::tie``. + +Manual implementations of ``operator<`` and ``operator>`` can be error-prone and verbose. +Using ``std::tie`` makes the code more readable and optimizes branch prediction. unterumarmung wrote: > optimizes branch prediction Seems like it is too speculative to be in the docs. https://github.com/llvm/llvm-project/pull/191218 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
@@ -0,0 +1,108 @@
+#include "UseStdTieCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+void UseStdTieCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(functionDecl(anyOf(hasOverloadedOperatorName("<"),
+hasOverloadedOperatorName(">")))
unterumarmung wrote:
what about ==, != and others?
https://github.com/llvm/llvm-project/pull/191218
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
github-actions[bot] wrote:
:warning: C/C++ code linter, clang-tidy found issues in your code. :warning:
You can test this locally with the following command:
```bash
git diff -U0 origin/main...HEAD --
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.h
clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp |
python3 clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py -path build -p1
-quiet
```
View the output from clang-tidy here.
```
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp:30:15: warning:
redundant string initialization [readability-redundant-string-init]
30 | std::string Autos = "";
| ^~
| Autos
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp:39:5: warning:
variable 'Text' of type 'std::string' (aka 'basic_string') can be
declared 'const' [misc-const-correctness]
39 | std::string Text =
| ^
| const
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp:51:7: warning:
variable 'LhsSide' of type 'std::string' (aka 'basic_string') can be
declared 'const' [misc-const-correctness]
51 | std::string LhsSide = Text.substr(0, OpPos);
| ^
| const
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp:55:19: warning:
redundant string initialization [readability-redundant-string-init]
55 | std::string CleanLhs = "";
| ^
| CleanLhs
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp:56:12: warning:
variable 'c' of type 'char' can be declared 'const' [misc-const-correctness]
56 | for (char c : LhsSide)
|^
| const
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp:56:17: warning:
invalid case style for variable 'c' [readability-identifier-naming]
56 | for (char c : LhsSide)
| ^
| C
57 | if (c != ' ' && c != '\t' && c != '\n')
| ~ ~~
| C CC
58 | CleanLhs += c;
| ~
| C
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp:62:9: warning:
variable 'VarName' of type 'std::string' (aka 'basic_string') can be
declared 'const' [misc-const-correctness]
62 | std::string VarName = CleanLhs.substr(4); // Extrae "n", "s" o
"d()"
| ^
| const
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp:65:11: warning:
variable 'CleanName' of type 'std::string' (aka 'basic_string') can be
declared 'const' [misc-const-correctness]
65 | std::string CleanName = VarName.substr(0, VarName.length() -
2);
| ^
| const
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp:67:61: warning:
string concatenation results in allocation of unnecessary temporary strings;
consider using 'operator+=' or 'string::append()' instead
[performance-inefficient-string-concatenation]
67 | "const auto lhs_" + CleanName + " = lhs." + VarName +
";\n";
| ^
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp:69:61: warning:
string concatenation results in allocation of unnecessary temporary strings;
consider using 'operator+=' or 'string::append()' instead
[performance-inefficient-string-concatenation]
69 | "const auto rhs_" + CleanName + " = rhs." + VarName +
";\n";
| ^
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp:93:15: warning:
invalid case style for variable 'i' [readability-identifier-naming]
93 | for (size_t i = 1; i < TieArgsLhs.size(); ++i) {
| ^ ~~
| I II
94 | LhsTuple += ", " + TieArgsLhs[i];
| ~
| I
95 | RhsTuple += ", " + TieArgsRhs[i];
| ~
| I
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp:98:3: warning:
variable 'Symbol' of type 'std::string' (aka 'basic_string') can be
declared 'const' [misc-const-correctness]
98 | std::string Symbol =
| ^
| const
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp:102:3: warning:
variable 'Replacement' of type 'std::string' (aka 'basic_string') can be
declared 'const' [misc-const-correctness]
102 | std::string Replacement = "{\n" + Autos + "return std::tie(" +
LhsTuple +
| ^
| const
```
https://github.com/llvm/llvm-project/pu
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
https://github.com/lucasvallejoo updated
https://github.com/llvm/llvm-project/pull/191218
>From e3f970b6c3c7f7395ab7186d1a898f0c3548caa8 Mon Sep 17 00:00:00 2001
From: lucasvallejoo
Date: Thu, 9 Apr 2026 17:30:40 +0200
Subject: [PATCH 1/2] [clang-tidy] Add modernize-use-std-tie check
Implements a new check that suggests using std::tie to replace manual
field-by-field lexicographical comparisons.
It detects operator< and operator> implementations and dynamically extracts
the compared variables or method calls from the return statements to
generate a clean std::tie replacement. This optimizes branch prediction
and improves code readability.
Resolves #121367
---
.../clang-tidy/modernize/CMakeLists.txt | 1 +
.../modernize/ModernizeTidyModule.cpp | 3 +
.../clang-tidy/modernize/UseStdTieCheck.cpp | 111 ++
.../clang-tidy/modernize/UseStdTieCheck.h | 33 ++
clang-tools-extra/docs/ReleaseNotes.rst | 5 +
.../docs/clang-tidy/checks/list.rst | 3 +-
.../checks/modernize/use-std-tie.rst | 6 +
.../checkers/modernize/use-std-tie.cpp| 45 +++
8 files changed, 205 insertions(+), 2 deletions(-)
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-tie.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-tie.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index 2c5c44db587fe..4fb202dbb05cf 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -51,6 +51,7 @@ add_clang_library(clangTidyModernizeModule STATIC
UseStdFormatCheck.cpp
UseStdNumbersCheck.cpp
UseStdPrintCheck.cpp
+ UseStdTieCheck.cpp
UseStringViewCheck.cpp
UseStructuredBindingCheck.cpp
UseTrailingReturnTypeCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index cc13da7535bcb..572b17e845aed 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -51,6 +51,7 @@
#include "UseStdFormatCheck.h"
#include "UseStdNumbersCheck.h"
#include "UseStdPrintCheck.h"
+#include "UseStdTieCheck.h"
#include "UseStringViewCheck.h"
#include "UseStructuredBindingCheck.h"
#include "UseTrailingReturnTypeCheck.h"
@@ -134,6 +135,8 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck("modernize-use-noexcept");
CheckFactories.registerCheck("modernize-use-nullptr");
CheckFactories.registerCheck("modernize-use-override");
+CheckFactories.registerCheck(
+"modernize-use-std-tie");
CheckFactories.registerCheck(
"modernize-use-string-view");
CheckFactories.registerCheck(
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp
b/clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp
new file mode 100644
index 0..1d76ea619d948
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp
@@ -0,0 +1,111 @@
+#include "UseStdTieCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+void UseStdTieCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(functionDecl(anyOf(hasOverloadedOperatorName("<"),
+hasOverloadedOperatorName(">")))
+ .bind("bad-operator"),
+ this);
+}
+
+void UseStdTieCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl =
+ Result.Nodes.getNodeAs("bad-operator");
+ if (!MatchedDecl || !MatchedDecl->hasBody())
+return;
+
+ // 1. ESCÁNER SIMPLE: Buscamos todos los 'return' dentro de la función
+ auto ReturnMatches =
+ match(stmt(findAll(returnStmt(hasReturnValue(expr().bind("ret_val"),
+*MatchedDecl->getBody(), *Result.Context);
+
+ std::vector TieArgsLhs;
+ std::vector TieArgsRhs;
+ std::string Autos = "";
+
+ // 2. EXTRAER VARIABLES DEL TEXTO
+ for (const auto &Node : ReturnMatches) {
+const auto *RetVal = Node.getNodeAs("ret_val");
+if (!RetVal)
+ continue;
+
+// Obtenemos el texto en bruto, ej: "lhs.n < rhs.n"
+std::string Text =
+Lexer::getSourceText(
+CharSourceRange::getTokenRange(RetVal->getSourceRange()),
+*Result.SourceManager, Result.Context->getLangOpts())
+.str();
+
+// Buscamos dónde está el símbolo para partir la frase
+size_t OpPos = Text.find('<');
+if (OpPos == std::string::npos)
+ OpPos =
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
@@ -0,0 +1,6 @@ +.. title:: clang-tidy - modernize-use-std-tie + +modernize-use-std-tie += + +FIXME: Describe what patterns does the check detect and why. Give examples. EugeneZelenko wrote: Please add documentation. https://github.com/llvm/llvm-project/pull/191218 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
@@ -353,7 +351,6 @@ Clang-Tidy Checks :doc:`openmp-use-default-none `, :doc:`performance-avoid-endl `, "Yes" EugeneZelenko wrote: Ditto. https://github.com/llvm/llvm-project/pull/191218 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
@@ -242,7 +241,6 @@ Clang-Tidy Checks :doc:`google-runtime-int `, :doc:`google-runtime-operator `, :doc:`google-upgrade-googletest-case `, "Yes" EugeneZelenko wrote: Irrelevant change? https://github.com/llvm/llvm-project/pull/191218 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
github-actions[bot] wrote:
:warning: C/C++ code formatter, clang-format found issues in your code.
:warning:
You can test this locally with the following command:
``bash
git-clang-format --diff origin/main HEAD --extensions h,cpp --
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp
clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.h
clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-tie.cpp
clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
--diff_from_common_commit
``
:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:
View the diff from clang-format here.
``diff
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index 572b17e84..df08fa008 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -135,8 +135,7 @@ public:
CheckFactories.registerCheck("modernize-use-noexcept");
CheckFactories.registerCheck("modernize-use-nullptr");
CheckFactories.registerCheck("modernize-use-override");
-CheckFactories.registerCheck(
-"modernize-use-std-tie");
+CheckFactories.registerCheck("modernize-use-std-tie");
CheckFactories.registerCheck(
"modernize-use-string-view");
CheckFactories.registerCheck(
``
https://github.com/llvm/llvm-project/pull/191218
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
@@ -0,0 +1,45 @@
+// RUN: %check_clang_tidy %s modernize-use-std-tie %t
+
+#include
+
+struct A {
+int n;
+double s;
+float w;
+float v;
+float d() const noexcept { return w / v; }
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:6: warning: use std::tie to implement
lexicographical comparison [modernize-use-std-tie]
+bool operator<(const A& lhs, const A& rhs) noexcept
+{
+if (lhs.n != rhs.n) {
+return lhs.n < rhs.n;
+} else if (lhs.s != rhs.s) {
+return lhs.s < rhs.s;
+}
+return lhs.d() < rhs.d();
+}
+// CHECK-FIXES: bool operator<(const A& lhs, const A& rhs) noexcept
+// CHECK-FIXES-NEXT: {
+// CHECK-FIXES-NEXT: const auto lhs_d = lhs.d();
+// CHECK-FIXES-NEXT: const auto rhs_d = rhs.d();
+// CHECK-FIXES-NEXT: return std::tie(lhs.n, lhs.s, lhs_d) <
std::tie(rhs.n, rhs.s, rhs_d);
+// CHECK-FIXES-NEXT: }
+
+// CHECK-MESSAGES: :[[@LINE+1]]:6: warning: use std::tie to implement
lexicographical comparison [modernize-use-std-tie]
+bool operator>(const A& lhs, const A& rhs) noexcept
+{
+if (lhs.n != rhs.n) {
+return lhs.n > rhs.n;
+} else if (lhs.s != rhs.s) {
+return lhs.s > rhs.s;
+}
+return lhs.d() > rhs.d();
+}
+// CHECK-FIXES: bool operator>(const A& lhs, const A& rhs) noexcept
+// CHECK-FIXES-NEXT: {
+// CHECK-FIXES-NEXT: const auto lhs_d = lhs.d();
+// CHECK-FIXES-NEXT: const auto rhs_d = rhs.d();
+// CHECK-FIXES-NEXT: return std::tie(lhs.n, lhs.s, lhs_d) >
std::tie(rhs.n, rhs.s, rhs_d);
+// CHECK-FIXES-NEXT: }
EugeneZelenko wrote:
Please add newline.
https://github.com/llvm/llvm-project/pull/191218
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
@@ -151,6 +139,11 @@ New checks
Finds common idioms which can be replaced by standard functions from the
C++20 header.
+- New :doc:`modernize-use-std-tie
+ ` check.
+
+ FIXME: Write a short description.
EugeneZelenko wrote:
Missing description.
https://github.com/llvm/llvm-project/pull/191218
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
@@ -0,0 +1,111 @@
+#include "UseStdTieCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+void UseStdTieCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(functionDecl(anyOf(hasOverloadedOperatorName("<"),
+hasOverloadedOperatorName(">")))
+ .bind("bad-operator"),
+ this);
+}
+
+void UseStdTieCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl =
+ Result.Nodes.getNodeAs("bad-operator");
+ if (!MatchedDecl || !MatchedDecl->hasBody())
+return;
+
+ // 1. ESCÁNER SIMPLE: Buscamos todos los 'return' dentro de la función
EugeneZelenko wrote:
Please use English in comments. Same in other places.
https://github.com/llvm/llvm-project/pull/191218
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)
https://github.com/lucasvallejoo updated
https://github.com/llvm/llvm-project/pull/191218
>From e3f970b6c3c7f7395ab7186d1a898f0c3548caa8 Mon Sep 17 00:00:00 2001
From: lucasvallejoo
Date: Thu, 9 Apr 2026 17:30:40 +0200
Subject: [PATCH] [clang-tidy] Add modernize-use-std-tie check
Implements a new check that suggests using std::tie to replace manual
field-by-field lexicographical comparisons.
It detects operator< and operator> implementations and dynamically extracts
the compared variables or method calls from the return statements to
generate a clean std::tie replacement. This optimizes branch prediction
and improves code readability.
Resolves #121367
---
.../clang-tidy/modernize/CMakeLists.txt | 1 +
.../modernize/ModernizeTidyModule.cpp | 3 +
.../clang-tidy/modernize/UseStdTieCheck.cpp | 111 ++
.../clang-tidy/modernize/UseStdTieCheck.h | 33 ++
clang-tools-extra/docs/ReleaseNotes.rst | 5 +
.../docs/clang-tidy/checks/list.rst | 3 +-
.../checks/modernize/use-std-tie.rst | 6 +
.../checkers/modernize/use-std-tie.cpp| 45 +++
8 files changed, 205 insertions(+), 2 deletions(-)
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-tie.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-tie.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index 2c5c44db587fe..4fb202dbb05cf 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -51,6 +51,7 @@ add_clang_library(clangTidyModernizeModule STATIC
UseStdFormatCheck.cpp
UseStdNumbersCheck.cpp
UseStdPrintCheck.cpp
+ UseStdTieCheck.cpp
UseStringViewCheck.cpp
UseStructuredBindingCheck.cpp
UseTrailingReturnTypeCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index cc13da7535bcb..572b17e845aed 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -51,6 +51,7 @@
#include "UseStdFormatCheck.h"
#include "UseStdNumbersCheck.h"
#include "UseStdPrintCheck.h"
+#include "UseStdTieCheck.h"
#include "UseStringViewCheck.h"
#include "UseStructuredBindingCheck.h"
#include "UseTrailingReturnTypeCheck.h"
@@ -134,6 +135,8 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck("modernize-use-noexcept");
CheckFactories.registerCheck("modernize-use-nullptr");
CheckFactories.registerCheck("modernize-use-override");
+CheckFactories.registerCheck(
+"modernize-use-std-tie");
CheckFactories.registerCheck(
"modernize-use-string-view");
CheckFactories.registerCheck(
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp
b/clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp
new file mode 100644
index 0..1d76ea619d948
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdTieCheck.cpp
@@ -0,0 +1,111 @@
+#include "UseStdTieCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+void UseStdTieCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(functionDecl(anyOf(hasOverloadedOperatorName("<"),
+hasOverloadedOperatorName(">")))
+ .bind("bad-operator"),
+ this);
+}
+
+void UseStdTieCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl =
+ Result.Nodes.getNodeAs("bad-operator");
+ if (!MatchedDecl || !MatchedDecl->hasBody())
+return;
+
+ // 1. ESCÁNER SIMPLE: Buscamos todos los 'return' dentro de la función
+ auto ReturnMatches =
+ match(stmt(findAll(returnStmt(hasReturnValue(expr().bind("ret_val"),
+*MatchedDecl->getBody(), *Result.Context);
+
+ std::vector TieArgsLhs;
+ std::vector TieArgsRhs;
+ std::string Autos = "";
+
+ // 2. EXTRAER VARIABLES DEL TEXTO
+ for (const auto &Node : ReturnMatches) {
+const auto *RetVal = Node.getNodeAs("ret_val");
+if (!RetVal)
+ continue;
+
+// Obtenemos el texto en bruto, ej: "lhs.n < rhs.n"
+std::string Text =
+Lexer::getSourceText(
+CharSourceRange::getTokenRange(RetVal->getSourceRange()),
+*Result.SourceManager, Result.Context->getLangOpts())
+.str();
+
+// Buscamos dónde está el símbolo para partir la frase
+size_t OpPos = Text.find('<');
+if (OpPos == std::string::npos)
+ OpPos = Text
