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
