Author: Peiqi Li
Date: 2026-07-14T12:53:06+08:00
New Revision: 44a42e631e5b4e01855750c371dea87fb3927550

URL: 
https://github.com/llvm/llvm-project/commit/44a42e631e5b4e01855750c371dea87fb3927550
DIFF: 
https://github.com/llvm/llvm-project/commit/44a42e631e5b4e01855750c371dea87fb3927550.diff

LOG: [clang-tidy] Fix false positive in misc-redundant-expression with type 
aliases (#198085)

The `misc-redundant-expression` check previously flagged expressions as
redundant if their underlying `DeclRefExpr` pointed to the same
declaration. This caused false positives when comparing identical values
accessed through distinct type aliases.

Following Clang's diagnostic `aka` logic, this patch uses the AST
Printer to stringify and compare the `NestedNameSpecifier` and
`TemplateArguments`. This safely preserves sugared types while natively
normalizing all whitespaces and newlines, ensuring robustness against
multiline formatting changes.

Fixes #145415

Added: 
    

Modified: 
    clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/test/clang-tidy/checkers/misc/redundant-expression.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp 
b/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp
index de489da96de3b..0475186862913 100644
--- a/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp
@@ -19,6 +19,7 @@
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/SmallBitVector.h"
 #include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <cassert>
 #include <cstdint>
@@ -44,6 +45,42 @@ static bool incrementWithoutOverflow(const APSInt &Value, 
APSInt &Result) {
   return Value < Result;
 }
 
+static bool areEquivalentDeclRefExpr(const DeclRefExpr *L,
+                                     const DeclRefExpr *R) {
+  if (L->getDecl() != R->getDecl())
+    return false;
+
+  const PrintingPolicy &Policy =
+      L->getDecl()->getASTContext().getPrintingPolicy();
+
+  if (L->hasQualifier() && R->hasQualifier()) {
+    std::string LQual, RQual;
+    llvm::raw_string_ostream LOS(LQual), ROS(RQual);
+    L->getQualifier().print(LOS, Policy);
+    R->getQualifier().print(ROS, Policy);
+    if (LQual != RQual)
+      return false;
+  }
+
+  if (L->hasExplicitTemplateArgs() != R->hasExplicitTemplateArgs())
+    return false;
+  if (L->hasExplicitTemplateArgs()) {
+    if (L->getNumTemplateArgs() != R->getNumTemplateArgs())
+      return false;
+
+    return llvm::equal(L->template_arguments(), R->template_arguments(),
+                       [&Policy](const TemplateArgumentLoc &LArg,
+                                 const TemplateArgumentLoc &RArg) {
+                         std::string LStr, RStr;
+                         llvm::raw_string_ostream LOS(LStr), ROS(RStr);
+                         LArg.getArgument().print(Policy, LOS, true);
+                         RArg.getArgument().print(Policy, ROS, true);
+                         return LStr == RStr;
+                       });
+  }
+  return true;
+}
+
 static bool areEquivalentExpr(const Expr *Left, const Expr *Right) {
   if (!Left || !Right)
     return !Left && !Right;
@@ -98,8 +135,8 @@ static bool areEquivalentExpr(const Expr *Left, const Expr 
*Right) {
     return cast<DependentScopeDeclRefExpr>(Left)->getQualifier() ==
            cast<DependentScopeDeclRefExpr>(Right)->getQualifier();
   case Stmt::DeclRefExprClass:
-    return cast<DeclRefExpr>(Left)->getDecl() ==
-           cast<DeclRefExpr>(Right)->getDecl();
+    return areEquivalentDeclRefExpr(cast<DeclRefExpr>(Left),
+                                    cast<DeclRefExpr>(Right));
   case Stmt::MemberExprClass:
     return cast<MemberExpr>(Left)->getMemberDecl() ==
            cast<MemberExpr>(Right)->getMemberDecl();

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index ec1bf0e9a1e29..cd568efb274ff 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -600,8 +600,13 @@ Changes in existing checks
   virtual inheritance causes concrete bases to be counted more than once.
 
 - Improved :doc:`misc-redundant-expression
-  <clang-tidy/checks/misc/redundant-expression>` check by fixing a crash when
-  evaluating bitwise comparisons against integer constants wider than 64 bits.
+  <clang-tidy/checks/misc/redundant-expression>` check:
+
+  - Fixed a crash when evaluating bitwise comparisons against integer constants
+    wider than 64 bits.
+
+  - Avoided false positives when comparing expressions that are structurally
+    identical but use 
diff erent type aliases.
 
 - Improved :doc:`misc-throw-by-value-catch-by-reference
   <clang-tidy/checks/misc/throw-by-value-catch-by-reference>` check:

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/misc/redundant-expression.cpp 
b/clang-tools-extra/test/clang-tidy/checkers/misc/redundant-expression.cpp
index 7ad5ca6d0b996..327f7e81e1f3a 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/misc/redundant-expression.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/redundant-expression.cpp
@@ -1126,3 +1126,49 @@ namespace PR35857 {
     decltype(x + y - (x + y)) z = 10;
   }
 }
+
+namespace GH145415 {
+
+namespace std {
+template <class T, int N> struct array {};
+template <class T> struct tuple_size;
+template <class T, int N> struct tuple_size<array<T, N>> {
+  static constexpr int value = N;
+};
+template <class T> constexpr int tuple_size_v = tuple_size<T>::value;
+} // namespace std
+
+using MonthArray = std::array<int, 12>;
+using ZodiacArray = std::array<int, 12>;
+
+namespace N {
+  int Value = 0;
+}
+using N::Value;
+
+template <typename T> void myFunc(T) {}
+template <typename T, typename U = int> void myDefaultFunc(T) {}
+using FuncPtr = void (*)(int);
+
+void TestGH145415() {
+
+  bool b1 = std::tuple_size<MonthArray>::value == 
std::tuple_size<ZodiacArray>::value;
+  bool b2 = std::tuple_size_v<MonthArray> == std::tuple_size_v<ZodiacArray>;
+
+  bool b3 = std::tuple_size<MonthArray>::value == 
std::tuple_size<MonthArray>::value;
+  // CHECK-MESSAGES: :[[@LINE-1]]:48: warning: both sides of operator are 
equivalent
+
+  bool b4 = std::tuple_size_v<
+                MonthArray> ==
+            std::tuple_size_v<MonthArray>;
+  // CHECK-MESSAGES: :[[@LINE-2]]:29: warning: both sides of operator are 
equivalent
+
+  bool b5 = N::Value == Value;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: both sides of operator are 
equivalent
+
+  bool b6 = FuncPtr(myFunc<int>) == FuncPtr(myFunc);
+
+  bool b7 = FuncPtr(myDefaultFunc<int>) == FuncPtr(myDefaultFunc<int, int>);
+}
+
+} // namespace GH145415


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to