poelmanc created this revision.
poelmanc added reviewers: aaron.ballman, angelgarcia, hokein.
poelmanc added a project: clang-tools-extra.
Herald added a subscriber: yaxunl.
poelmanc requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

`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);

Oops!

The supplied patch fixes this by adding just one comment and one line of code 
to `UseNullptrCheck.cpp`:

  // Skip implicit casts to 0 generated by operator<=> rewrites.
  unless(hasAncestor(cxxRewrittenBinaryOperator())),

I looked for a `hasGrandparent` matcher which would be more precise, but none 
exists. This fix slightly overly-aggressively eliminates matches, so even with 
this patch `modernize-use-nullptr` will not convert

  result = (a1 > (ptr == 0 ? a1 : a2));

to

  result = (a1 > (ptr == nullptr ? a1 : a2));

because `ptr == 0` has an ancestor that is a rewritten spaceship operator. 
Changing the proposed one-line fix to:

  unless(hasGrandparent(cxxRewrittenBinaryOperator())),

resolves this, but requires another 57 lines of code across AstMatchers.h, 
AstMatchersInternal.h, and AstMatchFinder.cpp to create `hasGrandparent`. I'd 
be happy to supply that patch instead if folks think it worthwhile? Maybe 
others in the future can benefit from `hasGrandparent`?


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D95714

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


Index: 
clang-tools-extra/test/clang-tidy/checkers/modernize-use-nullptr-cxx20.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/modernize-use-nullptr-cxx20.cpp
@@ -0,0 +1,19 @@
+// RUN: %check_clang_tidy -std=c++20 %s modernize-use-nullptr %t
+
+#include <compare>
+
+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);
+}
Index: clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp
+++ clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp
@@ -45,6 +45,8 @@
       TK_AsIs,
       castExpr(anyOf(ImplicitCastToNull,
                      explicitCastExpr(hasDescendant(ImplicitCastToNull))),
+               // Skip implicit casts to 0 generated by operator<=> rewrites.
+               unless(hasAncestor(cxxRewrittenBinaryOperator())),
                unless(hasAncestor(explicitCastExpr())))
           .bind(CastSequence));
 }


Index: clang-tools-extra/test/clang-tidy/checkers/modernize-use-nullptr-cxx20.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/modernize-use-nullptr-cxx20.cpp
@@ -0,0 +1,19 @@
+// RUN: %check_clang_tidy -std=c++20 %s modernize-use-nullptr %t
+
+#include <compare>
+
+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);
+}
Index: clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp
+++ clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp
@@ -45,6 +45,8 @@
       TK_AsIs,
       castExpr(anyOf(ImplicitCastToNull,
                      explicitCastExpr(hasDescendant(ImplicitCastToNull))),
+               // Skip implicit casts to 0 generated by operator<=> rewrites.
+               unless(hasAncestor(cxxRewrittenBinaryOperator())),
                unless(hasAncestor(explicitCastExpr())))
           .bind(CastSequence));
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to