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

Reply via email to