https://github.com/and2049 created https://github.com/llvm/llvm-project/pull/237781
Preserve parentheses in qualified member-address expressions like `&(S::value)`, which forms a pointer-to-member when removed. Fixes #237769. >From dd76cdc5e8e64babac0dfa4adcbc97101cf3ff19 Mon Sep 17 00:00:00 2001 From: Andre Sun <[email protected]> Date: Sun, 1 Nov 2026 16:54:49 -0500 Subject: [PATCH] [clang-tidy] Preserve required parentheses in member-address expressions --- .../readability/RedundantParenthesesCheck.cpp | 37 ++++++++++++- clang-tools-extra/docs/ReleaseNotes.md | 3 ++ .../readability/redundant-parentheses.cpp | 54 +++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/clang-tools-extra/clang-tidy/readability/RedundantParenthesesCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantParenthesesCheck.cpp index 0d75297ae7c363..3d861d4eae55a8 100644 --- a/clang-tools-extra/clang-tidy/readability/RedundantParenthesesCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/RedundantParenthesesCheck.cpp @@ -10,6 +10,7 @@ #include "../utils/Matchers.h" #include "../utils/OptionsUtils.h" #include "clang/AST/Expr.h" +#include "clang/AST/ParentMapContext.h" #include "clang/AST/TypeLoc.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" @@ -44,6 +45,28 @@ AST_MATCHER(TypeLoc, isTypeOfExprTypeLoc) { AST_MATCHER(AutoType, isDecltypeAuto) { return Node.isDecltypeAuto(); } +AST_MATCHER(MemberExpr, isQualifiedImplicitAccess) { + return Node.hasQualifier() && Node.isImplicitAccess(); +} + +// Skips the implicit casts, copies, temporaries, and conversion calls that +// wrap an operand; they add no tokens, so they share the operand's range. +AST_MATCHER_P(Expr, hasParentIgnoringImplicit, + ast_matchers::internal::Matcher<Expr>, InnerMatcher) { + const SourceRange Range = Node.getSourceRange(); + const Expr *E = &Node; + while (true) { + const DynTypedNodeList Parents = Finder->getASTContext().getParents(*E); + if (Parents.size() != 1) + return false; + E = Parents[0].get<Expr>(); + if (!E) + return false; + if (E->getSourceRange() != Range) + return InnerMatcher.matches(*E, Finder, Builder); + } +} + } // namespace static FixItHint createSpacedRemoval(SourceLocation Loc, @@ -100,6 +123,16 @@ void RedundantParenthesesCheck::registerMatchers(MatchFinder *Finder) { subExpr(ignoringParens(DecltypeOperand)), anyOf(hasParent(typeLoc(loc(decltypeType()))), InDecltypeAutoContext, hasParent(exprWithCleanups(InDecltypeAutoContext)))); + // &S::value is a pointer-to-member; T::value may instantiate to one. + const auto MemberAddressOperand = expr(anyOf( + memberExpr(isQualifiedImplicitAccess()), dependentScopeDeclRefExpr(), + declRefExpr(isInTemplateInstantiation()))); + const auto AddressOf = expr( + anyOf(unaryOperator(hasOperatorName("&")), + cxxOperatorCallExpr(hasOperatorName("&"), argumentCountIs(1)))); + const auto IsMemberAddressOperand = + allOf(subExpr(ignoringParens(MemberAddressOperand)), + hasParentIgnoringImplicit(AddressOf)); Finder->addMatcher( parenExpr(subExpr(anyOf( parenExpr(), ConstantExpr, @@ -116,7 +149,9 @@ void RedundantParenthesesCheck::registerMatchers(MatchFinder *Finder) { // typeof(...) parentheses are required syntax. hasParent(typeLoc(isTypeOfExprTypeLoc())), // decltype((x)) differs from decltype(x). - IsDecltypeOperand))) + IsDecltypeOperand, + // &(S::value) differs from &S::value. + IsMemberAddressOperand))) .bind("dup"), this); } diff --git a/clang-tools-extra/docs/ReleaseNotes.md b/clang-tools-extra/docs/ReleaseNotes.md index 679ca9ecbb4a39..92f7320206a303 100644 --- a/clang-tools-extra/docs/ReleaseNotes.md +++ b/clang-tools-extra/docs/ReleaseNotes.md @@ -424,6 +424,9 @@ infrastructure are described first, followed by tool-specific sections. `decltype((x))` and in the initializers and return statements of `decltype(auto)` declarations. + - Fixed an invalid fix-it that changed `&(S::value)` into the pointer-to-member + `&S::value`. + - 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.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-parentheses.cpp index 6a55f7ffbceb05..e37aedb5646f90 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 @@ -150,6 +150,60 @@ void memberExpr() { } } +struct Addr { bool *operator&(); }; +struct ConstAddr { bool *operator&() const; }; +struct ByValueAddr { ~ByValueAddr(); }; +bool *operator&(ByValueAddr); +struct Conv { operator ByValueAddr() const; }; +struct StaticX { static bool x; }; + +struct MemberAddress : Foo { + static bool s; + Addr a; + ConstAddr c; + ByValueAddr v; + Conv o; + void addressOf(MemberAddress &m) { + &(Foo::x); + &((Foo::x)); + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant parentheses around expression [readability-redundant-parentheses] + // CHECK-FIXES: &(Foo::x); + &(x); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant parentheses around expression [readability-redundant-parentheses] + // CHECK-FIXES: &x; + &(this->Foo::x); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant parentheses around expression [readability-redundant-parentheses] + // CHECK-FIXES: &this->Foo::x; + &(m.Foo::x); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant parentheses around expression [readability-redundant-parentheses] + // CHECK-FIXES: &m.Foo::x; + &(MemberAddress::s); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant parentheses around expression [readability-redundant-parentheses] + // CHECK-FIXES: &MemberAddress::s; + &(MemberAddress::a); + &(MemberAddress::c); + &(MemberAddress::v); + &(MemberAddress::o); + &(a); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant parentheses around expression [readability-redundant-parentheses] + // CHECK-FIXES: &a; + &ByValueAddr((MemberAddress::o)); + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: redundant parentheses around expression [readability-redundant-parentheses] + // CHECK-FIXES: &ByValueAddr(MemberAddress::o); + } +}; + +template <class T> struct DependentMemberAddress : T { + void addressOf() { + &(T::x); + &((T::x)); + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant parentheses around expression [readability-redundant-parentheses] + // CHECK-FIXES: &(T::x); + } +}; +template struct DependentMemberAddress<Foo>; +template struct DependentMemberAddress<StaticX>; + void call(bool (&func)(), Foo f) { if ((func())) {} // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant parentheses around expression [readability-redundant-parentheses] _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
