https://github.com/and2049 updated 
https://github.com/llvm/llvm-project/pull/224715

>From 6ed8ce342fca741caaf2da7a6e345fd600bf0c21 Mon Sep 17 00:00:00 2001
From: Andre Sun <[email protected]>
Date: Fri, 18 Sep 2026 11:38:12 -0400
Subject: [PATCH] [clang-tidy] Fix readability-redundant-parentheses false
 positives on decltype

Preserve parentheses that affect type deduction in decltype operands and
decltype(auto) initializers and returns, including dependent operands in
templates and their instantiations, and operands that create temporaries.

Fixes #223522.
---
 .../readability/RedundantParenthesesCheck.cpp |  27 +++-
 clang-tools-extra/docs/ReleaseNotes.md        |   4 +
 .../redundant-parentheses-cxx14.cpp           | 143 ++++++++++++++++++
 .../redundant-parentheses-cxx20.cpp           |  27 ++++
 .../readability/redundant-parentheses.cpp     |   9 ++
 5 files changed, 209 insertions(+), 1 deletion(-)
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses-cxx14.cpp
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses-cxx20.cpp

diff --git 
a/clang-tools-extra/clang-tidy/readability/RedundantParenthesesCheck.cpp 
b/clang-tools-extra/clang-tidy/readability/RedundantParenthesesCheck.cpp
index 09dd53e83b547..0d75297ae7c36 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantParenthesesCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantParenthesesCheck.cpp
@@ -42,6 +42,8 @@ AST_MATCHER(TypeLoc, isTypeOfExprTypeLoc) {
   return !Node.getUnqualifiedLoc().getAs<TypeOfExprTypeLoc>().isNull();
 }
 
+AST_MATCHER(AutoType, isDecltypeAuto) { return Node.isDecltypeAuto(); }
+
 } // namespace
 
 static FixItHint createSpacedRemoval(SourceLocation Loc,
@@ -77,6 +79,27 @@ void RedundantParenthesesCheck::registerMatchers(MatchFinder 
*Finder) {
   const auto ConstantExpr =
       expr(anyOf(integerLiteral(), floatLiteral(), characterLiteral(),
                  cxxBoolLiteral(), stringLiteral(), cxxNullPtrLiteralExpr()));
+  // Include dependent and substituted operands so templates and their
+  // instantiations protect the same outer pair of parentheses.
+  const auto DecltypeOperand =
+      expr(anyOf(declRefExpr(), memberExpr(), dependentScopeDeclRefExpr(),
+                 cxxDependentScopeMemberExpr(), unresolvedLookupExpr(),
+                 substNonTypeTemplateParmExpr()));
+  const auto DecltypeAutoType = ignoringParens(autoType(isDecltypeAuto()));
+  const auto DecltypeAutoVar = varDecl(hasType(DecltypeAutoType));
+  // Operands that create temporaries are wrapped in ExprWithCleanups.
+  const auto InDecltypeAutoContext = anyOf(
+      hasParent(DecltypeAutoVar),
+      hasParent(
+          expr(anyOf(initListExpr(), parenListExpr()),
+               anyOf(hasParent(DecltypeAutoVar),
+                     
hasParent(exprWithCleanups(hasParent(DecltypeAutoVar)))))),
+      hasParent(
+          returnStmt(forCallable(functionDecl(returns(DecltypeAutoType))))));
+  const auto IsDecltypeOperand = allOf(
+      subExpr(ignoringParens(DecltypeOperand)),
+      anyOf(hasParent(typeLoc(loc(decltypeType()))), InDecltypeAutoContext,
+            hasParent(exprWithCleanups(InDecltypeAutoContext))));
   Finder->addMatcher(
       parenExpr(subExpr(anyOf(
                     parenExpr(), ConstantExpr,
@@ -91,7 +114,9 @@ void RedundantParenthesesCheck::registerMatchers(MatchFinder 
*Finder) {
                              // sizeof(...) is common used.
                              hasParent(unaryExprOrTypeTraitExpr()),
                              // typeof(...) parentheses are required syntax.
-                             hasParent(typeLoc(isTypeOfExprTypeLoc())))))
+                             hasParent(typeLoc(isTypeOfExprTypeLoc())),
+                             // decltype((x)) differs from decltype(x).
+                             IsDecltypeOperand)))
           .bind("dup"),
       this);
 }
diff --git a/clang-tools-extra/docs/ReleaseNotes.md 
b/clang-tools-extra/docs/ReleaseNotes.md
index 9e8a96ffe2da7..83fb6c8067360 100644
--- a/clang-tools-extra/docs/ReleaseNotes.md
+++ b/clang-tools-extra/docs/ReleaseNotes.md
@@ -354,6 +354,10 @@ infrastructure are described first, followed by 
tool-specific sections.
     in reference non-type template parameter uses, `__builtin_dump_struct` 
calls,
     and OpenMP `linear` clauses.
 
+  - Fixed false positives on parentheses that determine the deduced type in
+    `decltype((x))` and in the initializers and return statements of
+    `decltype(auto)` declarations.
+
 - Fixed {doc}`readability-simplify-boolean-expr
   <clang-tidy/checks/readability/simplify-boolean-expr>` producing invalid
   fixes when applying De Morgan's theorem to overloaded comparison operators.
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses-cxx14.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses-cxx14.cpp
new file mode 100644
index 0000000000000..56cecc32b7510
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses-cxx14.cpp
@@ -0,0 +1,143 @@
+// RUN: %check_clang_tidy -std=c++14-or-later %s 
readability-redundant-parentheses %t
+
+int g = 0;
+
+decltype(auto) returnDecltypeAuto() { return (g); }
+auto returnTrailingDecltypeAuto() -> decltype(auto) { return (g); }
+template <class T> decltype(auto) returnDecltypeAutoTemplate(T &t) { return 
(t); }
+int &instantiate() { return returnDecltypeAutoTemplate(g); }
+
+struct S {
+  decltype(auto) member() { return (g); }
+};
+
+auto returnAuto() { return (g); }
+// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+// CHECK-FIXES:    auto returnAuto() { return g; }
+int &returnReference() { return (g); }
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+// CHECK-FIXES:    int &returnReference() { return g; }
+decltype(auto) returnLiteral() { return (1); }
+// CHECK-MESSAGES: :[[@LINE-1]]:41: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+// CHECK-FIXES:    decltype(auto) returnLiteral() { return 1; }
+
+void decltypeAuto(int x) {
+  decltype(auto) a = (x);
+  decltype(auto) b((x));
+  decltype(auto) (c) = (x);
+  decltype(auto) d = ((x));
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    decltype(auto) d = (x);
+  decltype(auto) e = (x) + 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    decltype(auto) e = x + 1;
+  auto f = (x);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    auto f = x;
+  auto &h = (x);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    auto &h = x;
+  decltype(auto) i = (1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    decltype(auto) i = 1;
+  auto lambda = [&]() -> decltype(auto) { return (x); };
+  auto autoLambda = [&]() { return (x); };
+  // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    auto autoLambda = [&]() { return x; };
+}
+
+void listInit(int x) {
+  decltype(auto) a{(x)};
+  decltype(auto) b{((x))};
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    decltype(auto) b{(x)};
+  decltype(auto) c{(1)};
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    decltype(auto) c{1};
+}
+
+struct D {
+  int m;
+  static int sm;
+};
+template <class T> int vt = 0;
+
+// Dependent operands are matched both in the template and in its 
instantiation.
+template <class T> decltype(auto) dependentMember(T &t) { return ((t.m)); }
+// CHECK-MESSAGES: :[[@LINE-1]]:67: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+// CHECK-FIXES:    template <class T> decltype(auto) dependentMember(T &t) { 
return (t.m); }
+template <class T> decltype(auto) dependentStatic(T &) { return ((T::sm)); }
+// CHECK-MESSAGES: :[[@LINE-1]]:66: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+// CHECK-FIXES:    template <class T> decltype(auto) dependentStatic(T &) { 
return (T::sm); }
+template <class T> decltype(auto) dependentTemplate() { return ((vt<T>)); }
+// CHECK-MESSAGES: :[[@LINE-1]]:65: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+// CHECK-FIXES:    template <class T> decltype(auto) dependentTemplate() { 
return (vt<T>); }
+auto genericLambda = [](auto &t) -> decltype(auto) { return ((t.m)); };
+// CHECK-MESSAGES: :[[@LINE-1]]:62: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+// CHECK-FIXES:    auto genericLambda = [](auto &t) -> decltype(auto) { return 
(t.m); };
+
+template <int N> decltype(auto) substitutedParameter() { return ((N)); }
+// CHECK-MESSAGES: :[[@LINE-1]]:66: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+// CHECK-FIXES:    template <int N> decltype(auto) substitutedParameter() { 
return (N); }
+
+template <class T> void dependentInit(T &t) {
+  decltype(((t.m))) a = t.m;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    decltype((t.m)) a = t.m;
+  decltype(auto) b = ((t.m));
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    decltype(auto) b = (t.m);
+  decltype(auto) c((t));
+  decltype(auto) d{(t)};
+}
+
+void instantiate(D &d) {
+  dependentMember(d);
+  dependentStatic(d);
+  dependentTemplate<int>();
+  genericLambda(d);
+  substitutedParameter<1>();
+  dependentInit(d);
+}
+
+struct Temporary {
+  Temporary();
+  ~Temporary();
+  int m;
+  int value();
+};
+Temporary makeTemporary();
+
+void temporaryInit() {
+  decltype(auto) a = (Temporary().m);
+  decltype(auto) b{(makeTemporary().m)};
+  decltype(auto) c((makeTemporary().m));
+  decltype(auto) d = ((makeTemporary().m));
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    decltype(auto) d = (makeTemporary().m);
+  decltype(auto) e{((makeTemporary().m))};
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    decltype(auto) e{(makeTemporary().m)};
+  decltype(auto) value = (makeTemporary().value());
+  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    decltype(auto) value = makeTemporary().value();
+  static_assert(__is_same(decltype(a), int &&), "");
+  static_assert(__is_same(decltype(b), int &&), "");
+  static_assert(__is_same(decltype(c), int &&), "");
+  static_assert(__is_same(decltype(d), int &&), "");
+  static_assert(__is_same(decltype(e), int &&), "");
+  static_assert(__is_same(decltype((Temporary().m)), int &&), "");
+}
+
+// Create a cleanup without returning a reference to a temporary (ill-formed 
in C++26).
+Temporary &&getTemporary(Temporary);
+decltype(auto) temporaryReturn() { return (getTemporary(Temporary()).m); }
+decltype(auto) nestedTemporaryReturn() { return 
((getTemporary(Temporary()).m)); }
+// CHECK-MESSAGES: :[[@LINE-1]]:50: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+// CHECK-FIXES:    decltype(auto) nestedTemporaryReturn() { return 
(getTemporary(Temporary()).m); }
+template <class T> decltype(auto) dependentTemporaryReturn() { return 
((getTemporary(T()).m)); }
+// CHECK-MESSAGES: :[[@LINE-1]]:72: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+// CHECK-FIXES:    template <class T> decltype(auto) 
dependentTemporaryReturn() { return (getTemporary(T()).m); }
+static_assert(__is_same(decltype(temporaryReturn()), int &&), "");
+static_assert(__is_same(decltype(nestedTemporaryReturn()), int &&), "");
+static_assert(__is_same(decltype(dependentTemporaryReturn<Temporary>()), int 
&&), "");
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses-cxx20.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses-cxx20.cpp
new file mode 100644
index 0000000000000..2110fc9b5b89c
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses-cxx20.cpp
@@ -0,0 +1,27 @@
+// RUN: %check_clang_tidy -std=c++20-or-later %s 
readability-redundant-parentheses %t
+
+struct C {
+  int n;
+};
+
+// A class-type template parameter object is an lvalue, so the parentheses
+// select 'const C &' over 'C' in both the template and its instantiation.
+template <C N> decltype(auto) returnParameter() { return ((N)); }
+// CHECK-MESSAGES: :[[@LINE-1]]:59: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+// CHECK-FIXES:    template <C N> decltype(auto) returnParameter() { return 
(N); }
+template <C N> decltype(auto) initParameter() {
+  decltype(auto) v = ((N));
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    decltype(auto) v = (N);
+  return v;
+}
+template <C N> decltype(auto) decltypeParameter() {
+  decltype(((N))) v = N;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    decltype((N)) v = N;
+  return v;
+}
+
+static_assert(__is_same(decltype(returnParameter<C{1}>()), const C &));
+static_assert(__is_same(decltype(initParameter<C{1}>()), const C &));
+static_assert(__is_same(decltype(decltypeParameter<C{1}>()), const C &));
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses.cpp
index 12286cd513f85..6a55f7ffbceb0 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses.cpp
@@ -63,6 +63,15 @@ void exceptions() {
   // CHECK-FIXES:    alignof(3);
   __typeof__(1) t;
   const __typeof__(1) ct = 1;
+  int i = 0;
+  decltype((i)) ri = i;
+  const decltype((i)) cri = i;
+  decltype(((i))) rri = i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    decltype((i)) rri = i;
+  decltype((1)) k = 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant parentheses around 
expression [readability-redundant-parentheses]
+  // CHECK-FIXES:    decltype(1) k = 1;
 }
 
 namespace std {

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

Reply via email to