Author: Andre Sun
Date: 2026-10-18T16:40:48+11:00
New Revision: 7bebaeed869a17710fb90ba1398aee0f20cc8cf2

URL: 
https://github.com/llvm/llvm-project/commit/7bebaeed869a17710fb90ba1398aee0f20cc8cf2
DIFF: 
https://github.com/llvm/llvm-project/commit/7bebaeed869a17710fb90ba1398aee0f20cc8cf2.diff

LOG: [clang-tidy] Fix readability-redundant-parentheses false positives on 
decltype (#224715)

Preserve parentheses that affect type deduction in `decltype` operands
and
`decltype(auto)` initializers and return statements. This also covers
dependent operands in templates and their instantiations, and operands
that create temporaries. Continue diagnosing redundant parentheses whose
removal does not affect type deduction, such as those in
`decltype((1))`.

Fixes #223522.

Added: 
    
clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses-cxx14.cpp
    
clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses-cxx20.cpp

Modified: 
    clang-tools-extra/clang-tidy/readability/RedundantParenthesesCheck.cpp
    clang-tools-extra/docs/ReleaseNotes.md
    
clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses.cpp

Removed: 
    


################################################################################
diff  --git 
a/clang-tools-extra/clang-tidy/readability/RedundantParenthesesCheck.cpp 
b/clang-tools-extra/clang-tidy/readability/RedundantParenthesesCheck.cpp
index 09dd53e83b5477..0d75297ae7c363 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)) 
diff ers from decltype(x).
+                             IsDecltypeOperand)))
           .bind("dup"),
       this);
 }

diff  --git a/clang-tools-extra/docs/ReleaseNotes.md 
b/clang-tools-extra/docs/ReleaseNotes.md
index 69f3ecee49926b..fdab8b19b63a8b 100644
--- a/clang-tools-extra/docs/ReleaseNotes.md
+++ b/clang-tools-extra/docs/ReleaseNotes.md
@@ -394,6 +394,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 00000000000000..56cecc32b7510f
--- /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 00000000000000..2110fc9b5b89cf
--- /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 12286cd513f853..6a55f7ffbceb05 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