diff --git a/clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp index e32687ba7b35eb..5ca2be2eb556d9 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp @@ -41,12 +41,28 @@ StatementMatcher makeCastSequenceMatcher() { unless(hasImplicitDestinationType(qualType(substTemplateTypeParmType()))), unless(hasSourceExpression(hasType(sugaredNullptrType())))); + auto IsOrHasDescendant = [](auto InnerMatcher) { + return anyOf(InnerMatcher, hasDescendant(InnerMatcher)); + }; + return traverse( TK_AsIs, - castExpr(anyOf(ImplicitCastToNull, - explicitCastExpr(hasDescendant(ImplicitCastToNull))), - unless(hasAncestor(explicitCastExpr()))) - .bind(CastSequence)); + anyOf(castExpr(anyOf(ImplicitCastToNull, + explicitCastExpr(hasDescendant(ImplicitCastToNull))), + unless(hasAncestor(explicitCastExpr())), + unless(hasAncestor(cxxRewrittenBinaryOperator()))) + .bind(CastSequence), + cxxRewrittenBinaryOperator( + // Match rewritten operators, but verify (in the check method) + // that if an implicit cast is found, it is not from another + // nested rewritten operator. + expr().bind("matchBinopOperands"), + hasEitherOperand(IsOrHasDescendant( + implicitCastExpr( + ImplicitCastToNull, + hasAncestor(cxxRewrittenBinaryOperator().bind( + "checkBinopOperands"))) + .bind(CastSequence)))))); } bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc, @@ -480,6 +496,11 @@ void UseNullptrCheck::check(const MatchFinder::MatchResult &Result) { const auto *NullCast = Result.Nodes.getNodeAs(CastSequence); assert(NullCast && "Bad Callback. No node provided"); + if (Result.Nodes.getNodeAs( + "matchBinopOperands") != + Result.Nodes.getNodeAs("checkBinopOperands")) + return; + // Given an implicit null-ptr cast or an explicit cast with an implicit // null-to-pointer cast within use CastSequenceVisitor to identify sequences // of explicit casts that can be converted into 'nullptr'. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize-use-nullptr-cxx20.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize-use-nullptr-cxx20.cpp new file mode 100644 index 00000000000000..b00cb5787ea079 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize-use-nullptr-cxx20.cpp @@ -0,0 +1,34 @@ +// RUN: %check_clang_tidy -std=c++20 %s modernize-use-nullptr %t + +namespace std { +struct strong_ordering { + int n; + constexpr operator int() const { return n; } + static const strong_ordering equal, greater, less; +}; +constexpr strong_ordering strong_ordering::equal = {0}; +constexpr strong_ordering strong_ordering::greater = {1}; +constexpr strong_ordering strong_ordering::less = {-1}; +} // namespace std + +class A { +public: + auto operator<=>(const A &other) const = default; +}; + +void test_cxx_rewritten_binary_ops() { + A a1, a2; + bool result; + // should not change next line to (a1 nullptr a2) + result = (a1 < a2); + // CHECK-FIXES: result = (a1 < a2); + // should not change next line to (a1 nullptr a2) + result = (a1 >= a2); + // CHECK-FIXES: result = (a1 >= a2); + int *ptr = 0; + // CHECK-FIXES: int *ptr = nullptr; + result = (a1 > (ptr == 0 ? a1 : a2)); + // CHECK-FIXES: result = (a1 > (ptr == nullptr ? a1 : a2)); + result = (a1 > ((a1 > (ptr == 0 ? a1 : a2)) ? a1 : a2)); + // CHECK-FIXES: result = (a1 > ((a1 > (ptr == nullptr ? a1 : a2)) ? a1 : a2)); +}