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
