[clang-tools-extra] [clang-tidy] Fix false positive in misc-redundant-expression with type aliases (PR #198085)
via cfe-commits
cfe-commits at lists.llvm.org
Tue May 19 05:13:41 PDT 2026
https://github.com/voyager-jhk updated https://github.com/llvm/llvm-project/pull/198085
>From efe3fe07924778cedff93c3f29a5b1e5f9e01fe8 Mon Sep 17 00:00:00 2001
From: voyager-jhk <voyager.lpq at gmail.com>
Date: Sat, 16 May 2026 20:50:59 +0800
Subject: [PATCH] [clang-tidy] Fix false positive in misc-redundant-expression
with type aliases
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 (sugared types).
This patch uses `printPretty` to compare the source-level spellings of the
expressions, ensuring nested name specifiers and explicit template arguments
are correctly differentiated.
Fixes #145415
---
.../misc/RedundantExpressionCheck.cpp | 44 +++++++++++++++++--
clang-tools-extra/docs/ReleaseNotes.rst | 5 +++
.../redundant-expression-type-aliases.cpp | 26 +++++++++++
3 files changed, 72 insertions(+), 3 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/redundant-expression-type-aliases.cpp
diff --git a/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp b/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp
index 9db297990b274..f16d056ee8815 100644
--- a/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp
@@ -97,9 +97,47 @@ static bool areEquivalentExpr(const Expr *Left, const Expr *Right) {
return false;
return cast<DependentScopeDeclRefExpr>(Left)->getQualifier() ==
cast<DependentScopeDeclRefExpr>(Right)->getQualifier();
- case Stmt::DeclRefExprClass:
- return cast<DeclRefExpr>(Left)->getDecl() ==
- cast<DeclRefExpr>(Right)->getDecl();
+ case Stmt::DeclRefExprClass: {
+ const auto *L = cast<DeclRefExpr>(Left);
+ const auto *R = cast<DeclRefExpr>(Right);
+
+ if (L->getDecl() != R->getDecl() || L->getFoundDecl() != R->getFoundDecl())
+ return false;
+
+ ASTContext &Ctx = L->getDecl()->getASTContext();
+ const SourceManager &SM = Ctx.getSourceManager();
+ const LangOptions &LO = Ctx.getLangOpts();
+
+ auto GetRawText = [&](SourceRange Range) -> StringRef {
+ if (Range.isInvalid())
+ return "";
+ return Lexer::getSourceText(CharSourceRange::getTokenRange(Range), SM,
+ LO);
+ };
+
+ if (L->hasQualifier() != R->hasQualifier())
+ return false;
+ if (L->hasQualifier()) {
+ StringRef LQual = GetRawText(L->getQualifierLoc().getSourceRange());
+ StringRef RQual = GetRawText(R->getQualifierLoc().getSourceRange());
+ if (LQual != RQual)
+ return false;
+ }
+
+ if (L->hasExplicitTemplateArgs() != R->hasExplicitTemplateArgs())
+ return false;
+ if (L->hasExplicitTemplateArgs()) {
+ if (L->getNumTemplateArgs() != R->getNumTemplateArgs())
+ return false;
+ for (unsigned I = 0, E = L->getNumTemplateArgs(); I != E; ++I) {
+ StringRef LArg = GetRawText(L->getTemplateArgs()[I].getSourceRange());
+ StringRef RArg = GetRawText(R->getTemplateArgs()[I].getSourceRange());
+ if (LArg != RArg)
+ return false;
+ }
+ }
+ return true;
+ }
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 89fb1684bba7c..c4f9b86bdd87c 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -500,6 +500,11 @@ Changes in existing checks
<clang-tidy/checks/misc/multiple-inheritance>` by avoiding false positives when
virtual inheritance causes concrete bases to be counted more than once.
+- Improved :doc:`misc-redundant-expression
+ <clang-tidy/checks/misc/redundant-expression>` check to avoid false
+ positives when comparing expressions that are structurally identical but
+ use different 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-type-aliases.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/redundant-expression-type-aliases.cpp
new file mode 100644
index 0000000000000..30a16e64fbac6
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/redundant-expression-type-aliases.cpp
@@ -0,0 +1,26 @@
+// RUN: %check_clang_tidy -std=c++17-or-later %s misc-redundant-expression %t
+
+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>;
+
+void test() {
+ // False positive cases (Should NOT warn):
+ bool b1 = std::tuple_size<MonthArray>::value == std::tuple_size<ZodiacArray>::value;
+ bool b2 = std::tuple_size_v<MonthArray> == std::tuple_size_v<ZodiacArray>;
+
+ // True positive cases (Should warn):
+ bool b3 = std::tuple_size<MonthArray>::value == std::tuple_size<MonthArray>::value;
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: both sides of operator are equivalent [misc-redundant-expression]
+
+ bool b4 = std::tuple_size_v<MonthArray> == std::tuple_size_v<MonthArray>;
+ // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: both sides of operator are equivalent [misc-redundant-expression]
+}
More information about the cfe-commits
mailing list