[clang-tools-extra] 98146c1 - [clang-tidy] fix modernize-use-nullptr false positive with spaceship operator comparisons

Nathan James via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 9 06:03:12 PST 2021


Author: poelmanc
Date: 2021-02-09T14:02:45Z
New Revision: 98146c1f5d0c772aec56149724119d49990f4d0c

URL: https://github.com/llvm/llvm-project/commit/98146c1f5d0c772aec56149724119d49990f4d0c
DIFF: https://github.com/llvm/llvm-project/commit/98146c1f5d0c772aec56149724119d49990f4d0c.diff

LOG: [clang-tidy] fix modernize-use-nullptr false positive with spaceship operator comparisons

`clang-tidy -std=c++20` with `modernize-use-nullptr` mistakenly inserts `nullptr` in place of the comparison operator if the comparison internally expands in the AST to a rewritten spaceship operator. This can be reproduced by running the new `modernize-use-nullptr-cxx20.cpp` test without applying the supplied patch to `UseNullptrCheck.cpp`; the current clang-tidy will mistakenly replace:
```result = (a1 < a2);```
with
```result = (a1 nullptr a2);```

Reviewed By: njames93

Differential Revision: https://reviews.llvm.org/D95714

Added: 
    clang-tools-extra/test/clang-tidy/checkers/modernize-use-nullptr-cxx20.cpp

Modified: 
    clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp
index e32687ba7b35..5ca2be2eb556 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<CastExpr>(CastSequence);
   assert(NullCast && "Bad Callback. No node provided");
 
+  if (Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
+          "matchBinopOperands") !=
+      Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>("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 000000000000..b00cb5787ea0
--- /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));
+}


        


More information about the cfe-commits mailing list