https://github.com/OmarAzizi created 
https://github.com/llvm/llvm-project/pull/190438

Adds a new clang-tidy check that removes redundant empty parameter lists from 
lambda expressions when the rewrite is valid for the active language standard.

Fixes #190396

>From b1de6aa2612f0897cc544444070130b0691003a1 Mon Sep 17 00:00:00 2001
From: OmarAzizi <[email protected]>
Date: Sat, 4 Apr 2026 03:45:48 +0300
Subject: [PATCH] [clang-tidy] Add readability-redundant-lambda-parentheses
 check

---
 .../clang-tidy/readability/CMakeLists.txt     |  1 +
 .../readability/ReadabilityTidyModule.cpp     |  3 +
 .../RedundantLambdaParenthesesCheck.cpp       | 90 +++++++++++++++++++
 .../RedundantLambdaParenthesesCheck.h         | 34 +++++++
 clang-tools-extra/docs/ReleaseNotes.rst       |  5 ++
 .../docs/clang-tidy/checks/list.rst           |  3 +-
 .../redundant-lambda-parentheses.rst          | 35 ++++++++
 .../redundant-lambda-parentheses-cxx20.cpp    | 19 ++++
 .../redundant-lambda-parentheses-cxx23.cpp    | 41 +++++++++
 .../redundant-lambda-parentheses.cpp          | 38 ++++++++
 10 files changed, 267 insertions(+), 2 deletions(-)
 create mode 100644 
clang-tools-extra/clang-tidy/readability/RedundantLambdaParenthesesCheck.cpp
 create mode 100644 
clang-tools-extra/clang-tidy/readability/RedundantLambdaParenthesesCheck.h
 create mode 100644 
clang-tools-extra/docs/clang-tidy/checks/readability/redundant-lambda-parentheses.rst
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/readability/redundant-lambda-parentheses-cxx20.cpp
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/readability/redundant-lambda-parentheses-cxx23.cpp
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/readability/redundant-lambda-parentheses.cpp

diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index 686e7c19d650b..3702600e0496c 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -44,6 +44,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
   RedundantControlFlowCheck.cpp
   RedundantDeclarationCheck.cpp
   RedundantFunctionPtrDereferenceCheck.cpp
+  RedundantLambdaParenthesesCheck.cpp
   RedundantMemberInitCheck.cpp
   RedundantParenthesesCheck.cpp
   RedundantPreprocessorCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp 
b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index 8e9e00b23c84a..6bfeb6c548f48 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -46,6 +46,7 @@
 #include "RedundantDeclarationCheck.h"
 #include "RedundantFunctionPtrDereferenceCheck.h"
 #include "RedundantInlineSpecifierCheck.h"
+#include "RedundantLambdaParenthesesCheck.h"
 #include "RedundantMemberInitCheck.h"
 #include "RedundantParenthesesCheck.h"
 #include "RedundantPreprocessorCheck.h"
@@ -143,6 +144,8 @@ class ReadabilityModule : public ClangTidyModule {
         "readability-redundant-casting");
     CheckFactories.registerCheck<RedundantFunctionPtrDereferenceCheck>(
         "readability-redundant-function-ptr-dereference");
+    CheckFactories.registerCheck<RedundantLambdaParenthesesCheck>(
+        "readability-redundant-lambda-parentheses");
     CheckFactories.registerCheck<RedundantMemberInitCheck>(
         "readability-redundant-member-init");
     CheckFactories.registerCheck<RedundantParenthesesCheck>(
diff --git 
a/clang-tools-extra/clang-tidy/readability/RedundantLambdaParenthesesCheck.cpp 
b/clang-tools-extra/clang-tidy/readability/RedundantLambdaParenthesesCheck.cpp
new file mode 100644
index 0000000000000..275eba3448d4c
--- /dev/null
+++ 
b/clang-tools-extra/clang-tidy/readability/RedundantLambdaParenthesesCheck.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "RedundantLambdaParenthesesCheck.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void RedundantLambdaParenthesesCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(lambdaExpr().bind("lambda"), this);
+}
+
+void RedundantLambdaParenthesesCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
+
+  if (Lambda->getBeginLoc().isMacroID())
+    return;
+
+  if (!Lambda->hasExplicitParameters() && !Lambda->isGenericLambda())
+    return;
+
+  if (Lambda->getCallOperator()->getNumParams() != 0)
+    return;
+
+  if (Lambda->isGenericLambda() && !getLangOpts().CPlusPlus20)
+    return;
+
+  const LangOptions &LangOpts = getLangOpts();
+
+  SourceLocation ScanFrom;
+  if (Lambda->isGenericLambda()) {
+    TemplateParameterList *TPL = Lambda->getTemplateParameterList();
+    ScanFrom = Lexer::getLocForEndOfToken(TPL->getRAngleLoc(), 0,
+                                          *Result.SourceManager, LangOpts);
+  } else {
+    ScanFrom = 
Lexer::getLocForEndOfToken(Lambda->getIntroducerRange().getEnd(),
+                                          0, *Result.SourceManager, LangOpts);
+  }
+
+  Token Tok;
+  if (Lexer::getRawToken(ScanFrom, Tok, *Result.SourceManager, LangOpts,
+                         /*IgnoreWhiteSpace=*/true))
+    return;
+
+  if (Tok.isNot(tok::l_paren))
+    return;
+
+  SourceLocation LParenLoc = Tok.getLocation();
+  SourceLocation RParenLoc = Lexer::findLocationAfterToken(
+      LParenLoc, tok::r_paren, *Result.SourceManager, LangOpts,
+      /*SkipTrailingWhitespaceAndNewLine=*/false);
+
+  if (LParenLoc.isInvalid() || RParenLoc.isInvalid())
+    return;
+
+  if (!LangOpts.CPlusPlus23) {
+    std::optional<Token> RParen =
+        Lexer::findNextToken(LParenLoc, *Result.SourceManager, LangOpts);
+    if (!RParen || RParen->isNot(tok::r_paren))
+      return;
+    std::optional<Token> NextTok = Lexer::findNextToken(
+        RParen->getLocation(), *Result.SourceManager, LangOpts);
+    if (NextTok && NextTok->is(tok::raw_identifier)) {
+      StringRef Id = NextTok->getRawIdentifier();
+      if (Id == "constexpr" || Id == "consteval" || Id == "mutable" ||
+          Id == "noexcept")
+        return;
+    }
+    if (NextTok && NextTok->is(tok::arrow))
+      return;
+  }
+
+  CharSourceRange ParenRange =
+      CharSourceRange::getCharRange(LParenLoc, RParenLoc);
+
+  diag(LParenLoc, "redundant empty parameter list in lambda expression")
+      << FixItHint::CreateRemoval(ParenRange);
+}
+
+} // namespace clang::tidy::readability
diff --git 
a/clang-tools-extra/clang-tidy/readability/RedundantLambdaParenthesesCheck.h 
b/clang-tools-extra/clang-tidy/readability/RedundantLambdaParenthesesCheck.h
new file mode 100644
index 0000000000000..3437d703da1f4
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/RedundantLambdaParenthesesCheck.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_READABILITY_REDUNDANTLAMBDAPARENTHESESCHECK_H
+#define 
LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTLAMBDAPARENTHESESCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Finds lambda expressions with a redundant empty parameter list and removes
+/// it.
+///
+/// For the user-facing documentation see:
+/// 
https://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-lambda-parentheses.html
+class RedundantLambdaParenthesesCheck : public ClangTidyCheck {
+public:
+  RedundantLambdaParenthesesCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus11;
+  }
+};
+
+} // namespace clang::tidy::readability
+
+#endif // 
LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTLAMBDAPARENTHESESCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 36e311341f336..9672664901f0a 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -163,6 +163,11 @@ New checks
   Suggests insertion of ``std::move(...)`` to turn copy assignment operator
   calls into move assignment ones, when deemed valid and profitable.
 
+- New :doc:`readability-redundant-lambda-parentheses
+  <clang-tidy/checks/readability/redundant-lambda-parentheses>` check.
+
+  FIXME: Write a short description.
+
 - New :doc:`readability-redundant-qualified-alias
   <clang-tidy/checks/readability/redundant-qualified-alias>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst 
b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 2b5be931271ec..3f69d9f51d73f 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -241,7 +241,6 @@ Clang-Tidy Checks
    :doc:`google-runtime-int <google/runtime-int>`,
    :doc:`google-runtime-operator <google/runtime-operator>`,
    :doc:`google-upgrade-googletest-case <google/upgrade-googletest-case>`, 
"Yes"
-   :doc:`hicpp-exception-baseclass <hicpp/exception-baseclass>`,
    :doc:`hicpp-multiway-paths-covered <hicpp/multiway-paths-covered>`,
    :doc:`hicpp-signed-bitwise <hicpp/signed-bitwise>`,
    :doc:`linuxkernel-must-check-errs <linuxkernel/must-check-errs>`,
@@ -351,7 +350,6 @@ Clang-Tidy Checks
    :doc:`openmp-use-default-none <openmp/use-default-none>`,
    :doc:`performance-avoid-endl <performance/avoid-endl>`, "Yes"
    :doc:`performance-enum-size <performance/enum-size>`,
-   :doc:`performance-faster-string-find <performance/faster-string-find>`, 
"Yes"
    :doc:`performance-for-range-copy <performance/for-range-copy>`, "Yes"
    :doc:`performance-implicit-conversion-in-loop 
<performance/implicit-conversion-in-loop>`,
    :doc:`performance-inefficient-algorithm 
<performance/inefficient-algorithm>`, "Yes"
@@ -415,6 +413,7 @@ Clang-Tidy Checks
    :doc:`readability-redundant-declaration 
<readability/redundant-declaration>`, "Yes"
    :doc:`readability-redundant-function-ptr-dereference 
<readability/redundant-function-ptr-dereference>`, "Yes"
    :doc:`readability-redundant-inline-specifier 
<readability/redundant-inline-specifier>`, "Yes"
+   :doc:`readability-redundant-lambda-parentheses 
<readability/redundant-lambda-parentheses>`, "Yes"
    :doc:`readability-redundant-member-init 
<readability/redundant-member-init>`, "Yes"
    :doc:`readability-redundant-parentheses 
<readability/redundant-parentheses>`, "Yes"
    :doc:`readability-redundant-preprocessor 
<readability/redundant-preprocessor>`,
diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/readability/redundant-lambda-parentheses.rst
 
b/clang-tools-extra/docs/clang-tidy/checks/readability/redundant-lambda-parentheses.rst
new file mode 100644
index 0000000000000..3167b841b38bc
--- /dev/null
+++ 
b/clang-tools-extra/docs/clang-tidy/checks/readability/redundant-lambda-parentheses.rst
@@ -0,0 +1,35 @@
+.. title:: clang-tidy - readability-redundant-lambda-parentheses
+
+readability-redundant-lambda-parentheses
+========================================
+
+Finds lambda expressions with a redundant empty parameter list and removes it.
+
+In C++11 and later, a lambda with no parameters does not require an explicit
+``()`` unless it has a specifier such as ``mutable``, ``noexcept``, or a
+trailing return type. In C++23 and later, ``()`` is redundant even when such
+specifiers are present.
+
+.. code-block:: c++
+
+  // C++11 and later - the following lambdas will be rewritten:
+  auto a = []() { return 42; };
+  // becomes:
+  auto a = [] { return 42; };
+
+  auto b = [x = 1]() { return x; };
+  // becomes:
+  auto b = [x = 1] { return x; };
+
+  // C++23 and later - the following lambdas will also be rewritten:
+  auto c = []() mutable {};
+  // becomes:
+  auto c = [] mutable {};
+
+  auto d = []() noexcept {};
+  // becomes:
+  auto d = [] noexcept {};
+
+  auto e = []() -> int { return 0; };
+  // becomes:
+  auto e = [] -> int { return 0; };
\ No newline at end of file
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-lambda-parentheses-cxx20.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-lambda-parentheses-cxx20.cpp
new file mode 100644
index 0000000000000..769ffad629d03
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-lambda-parentheses-cxx20.cpp
@@ -0,0 +1,19 @@
+// RUN: %check_clang_tidy -std=c++20 %s 
readability-redundant-lambda-parentheses %t
+
+int main() {
+  // Generic lambdas - should warn in C++20 and later
+  auto a = []<class T>() { return sizeof(T); };
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: redundant empty parameter list 
in lambda expression [readability-redundant-lambda-parentheses]
+  // CHECK-FIXES: {{^}}  auto a = []<class T> { return sizeof(T); };{{$}}
+
+  auto b = []<class T>() requires true { return sizeof(T); };
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: redundant empty parameter list 
in lambda expression [readability-redundant-lambda-parentheses]
+  // CHECK-FIXES: {{^}}  auto b = []<class T> requires true { return 
sizeof(T); };{{$}}
+
+  // Should NOT warn - has parameters
+  auto c = []<class T>(T x) { return x; };
+
+  // Should NOT warn - has specifiers
+  auto d = []<class T>() mutable { return sizeof(T); };
+  auto e = []<class T>() noexcept { return sizeof(T); };
+}
\ No newline at end of file
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-lambda-parentheses-cxx23.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-lambda-parentheses-cxx23.cpp
new file mode 100644
index 0000000000000..4a9378564c149
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-lambda-parentheses-cxx23.cpp
@@ -0,0 +1,41 @@
+// RUN: %check_clang_tidy -std=c++23 %s 
readability-redundant-lambda-parentheses %t
+
+int main() {
+  // Basic cases - should warn
+  auto a = []() { return 42; };
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant empty parameter list 
in lambda expression [readability-redundant-lambda-parentheses]
+  // CHECK-FIXES: {{^}}  auto a = [] { return 42; };{{$}}
+
+  // Specifier cases - should also warn in C++23
+  auto b = []() mutable {};
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant empty parameter list 
in lambda expression [readability-redundant-lambda-parentheses]
+  // CHECK-FIXES: {{^}}  auto b = [] mutable {};{{$}}
+
+  auto c = []() noexcept {};
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant empty parameter list 
in lambda expression [readability-redundant-lambda-parentheses]
+  // CHECK-FIXES: {{^}}  auto c = [] noexcept {};{{$}}
+
+  auto d = []() -> int { return 0; };
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant empty parameter list 
in lambda expression [readability-redundant-lambda-parentheses]
+  // CHECK-FIXES: {{^}}  auto d = [] -> int { return 0; };{{$}}
+
+  auto e = []() mutable noexcept {};
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant empty parameter list 
in lambda expression [readability-redundant-lambda-parentheses]
+  // CHECK-FIXES: {{^}}  auto e = [] mutable noexcept {};{{$}}
+
+  auto f = []() constexpr { return 42; };
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant empty parameter list 
in lambda expression [readability-redundant-lambda-parentheses]
+  // CHECK-FIXES: {{^}}  auto f = [] constexpr { return 42; };{{$}}
+
+  auto g = []() consteval { return 42; };
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant empty parameter list 
in lambda expression [readability-redundant-lambda-parentheses]
+  // CHECK-FIXES: {{^}}  auto g = [] consteval { return 42; };{{$}}
+
+  // Should NOT warn - has parameters
+  auto h = [](int x) { return x; };
+
+  // Should NOT warn - macro
+#define LAMBDA []() { return 42; }
+  auto i = LAMBDA;
+#undef LAMBDA
+}
\ No newline at end of file
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-lambda-parentheses.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-lambda-parentheses.cpp
new file mode 100644
index 0000000000000..e04dca093dba5
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-lambda-parentheses.cpp
@@ -0,0 +1,38 @@
+// RUN: %check_clang_tidy -std=c++17 %s 
readability-redundant-lambda-parentheses %t
+
+int main() {
+  // Basic cases - should warn
+  auto a = []() { return 42; };
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant empty parameter list 
in lambda expression [readability-redundant-lambda-parentheses]
+  // CHECK-FIXES: {{^}}  auto a = [] { return 42; };{{$}}
+
+  auto b = [x = 1]() { return x; };
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: redundant empty parameter list 
in lambda expression [readability-redundant-lambda-parentheses]
+  // CHECK-FIXES: {{^}}  auto b = [x = 1] { return x; };{{$}}
+
+  // Lambda with no captures
+  auto c = []() {};
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant empty parameter list 
in lambda expression [readability-redundant-lambda-parentheses]
+  // CHECK-FIXES: {{^}}  auto c = [] {};{{$}}
+
+  // Lambda inside a function call
+  auto v = 1;
+  auto call = [&v]() { return v; };
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: redundant empty parameter list 
in lambda expression [readability-redundant-lambda-parentheses]
+  // CHECK-FIXES: {{^}}  auto call = [&v] { return v; };{{$}}
+
+  // Should NOT warn - has parameters
+  auto d = [](int x) { return x; };
+  auto e = [](int x, int y) { return x + y; };
+
+  // Should NOT warn - has specifiers, needs C++23
+  auto f = []() mutable {};
+  auto g = []() noexcept {};
+  auto h = []() -> int { return 0; };
+  auto i = []() constexpr { return 42; };
+
+  // Should NOT warn - macro
+#define LAMBDA []() { return 42; }
+  auto k = LAMBDA;
+#undef LAMBDA
+}
\ No newline at end of file

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to