[clang-tools-extra] [clang-tidy] Add modernize-use-std-tie check (PR #191218)

2026-04-10 Thread via cfe-commits

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)

2026-04-10 Thread via cfe-commits

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)

2026-04-09 Thread via cfe-commits

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)

2026-04-09 Thread Daniil Dudkin via cfe-commits


@@ -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)

2026-04-09 Thread Daniil Dudkin via cfe-commits

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)

2026-04-09 Thread Daniil Dudkin via cfe-commits

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)

2026-04-09 Thread Daniil Dudkin via cfe-commits


@@ -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)

2026-04-09 Thread Daniil Dudkin via cfe-commits


@@ -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)

2026-04-09 Thread Daniil Dudkin via cfe-commits


@@ -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)

2026-04-09 Thread Daniil Dudkin via cfe-commits


@@ -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)

2026-04-09 Thread via cfe-commits

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)

2026-04-09 Thread via cfe-commits

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)

2026-04-09 Thread via cfe-commits


@@ -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)

2026-04-09 Thread via cfe-commits


@@ -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)

2026-04-09 Thread via cfe-commits


@@ -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)

2026-04-09 Thread via cfe-commits

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)

2026-04-09 Thread via cfe-commits


@@ -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)

2026-04-09 Thread via cfe-commits


@@ -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)

2026-04-09 Thread via cfe-commits


@@ -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)

2026-04-09 Thread via cfe-commits

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