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
