[clang-tools-extra] 7bebaee - [clang-tidy] Fix readability-redundant-parentheses false positives on decltype (#224715)

via cfe-commits cfe-commits at lists.llvm.org
Sat Oct 17 22:40:54 PDT 2026


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 {


        


More information about the cfe-commits mailing list