[clang-tools-extra] [clang-tidy] Speed up/rewrite `bugprone-stringview-nullptr` (PR #192889)

Victor Chernyakin via cfe-commits cfe-commits at lists.llvm.org
Sun May 3 15:35:21 PDT 2026


================
@@ -7,294 +7,93 @@
 //===----------------------------------------------------------------------===//
 
 #include "StringviewNullptrCheck.h"
-#include "../utils/TransformerClangTidyCheck.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/OperationKinds.h"
+#include "../utils/LexerUtils.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
-#include "clang/Tooling/Transformer/RangeSelector.h"
-#include "clang/Tooling/Transformer/RewriteRule.h"
-#include "clang/Tooling/Transformer/Stencil.h"
-#include "llvm/ADT/StringRef.h"
-
-namespace clang::tidy::bugprone {
 
 using namespace ::clang::ast_matchers;
-using namespace ::clang::transformer;
+
+namespace clang::tidy::bugprone {
 
 namespace {
 
-AST_MATCHER_P(InitListExpr, initCountIs, unsigned, N) {
-  return Node.getNumInits() == N;
-}
+AST_MATCHER(CXXConstructExpr, constructedFromNullptr) {
+  if (Node.getNumArgs() != 1)
+    return false;
 
-AST_MATCHER(VarDecl, isDirectInitialization) {
-  return Node.getInitStyle() != VarDecl::InitializationStyle::CInit;
+  const Expr *Arg = Node.getArg(0);
+  bool ArgValue; // NOLINT(cppcoreguidelines-init-variables)
+  return !Arg->isValueDependent() &&
+         Arg->EvaluateAsBooleanCondition(ArgValue, Finder->getASTContext()) &&
+         !ArgValue;
 }
 
 } // namespace
 
-static RewriteRuleWith<std::string> stringviewNullptrCheckImpl() {
-  auto ConstructionWarning =
-      cat("constructing basic_string_view from null is undefined; replace with "
-          "the default constructor");
-  auto StaticCastWarning =
-      cat("casting to basic_string_view from null is undefined; replace with "
-          "the empty string");
-  auto ArgumentConstructionWarning =
-      cat("passing null as basic_string_view is undefined; replace with the "
-          "empty string");
-  auto AssignmentWarning =
-      cat("assignment to basic_string_view from null is undefined; replace "
-          "with the default constructor");
-  auto RelativeComparisonWarning =
-      cat("comparing basic_string_view to null is undefined; replace with the "
-          "empty string");
-  auto EqualityComparisonWarning =
-      cat("comparing basic_string_view to null is undefined; replace with the "
-          "emptiness query");
-
-  // Matches declarations and expressions of type `basic_string_view`
-  auto HasBasicStringViewType = hasType(hasUnqualifiedDesugaredType(recordType(
-      hasDeclaration(cxxRecordDecl(hasName("::std::basic_string_view"))))));
-
-  // Matches `nullptr` and `(nullptr)` binding to a pointer
-  auto NullLiteral = implicitCastExpr(
-      hasCastKind(CK_NullToPointer),
-      hasSourceExpression(ignoringParens(cxxNullPtrLiteralExpr())));
-
-  // Matches `{nullptr}` and `{(nullptr)}` binding to a pointer
-  auto NullInitList = initListExpr(initCountIs(1), hasInit(0, NullLiteral));
-
-  // Matches `{}`
-  auto EmptyInitList = initListExpr(initCountIs(0));
-
-  // Matches null construction without `basic_string_view` type spelling
-  auto BasicStringViewConstructingFromNullExpr =
-      cxxConstructExpr(
-          HasBasicStringViewType, argumentCountIs(1),
-          hasAnyArgument(/* `hasArgument` would skip over parens */ anyOf(
-              NullLiteral, NullInitList, EmptyInitList)),
-          unless(cxxTemporaryObjectExpr(/* filters out type spellings */)),
-          has(expr().bind("null_arg_expr")))
-          .bind("construct_expr");
-
-  // `std::string_view(null_arg_expr)`
-  auto HandleTemporaryCXXFunctionalCastExpr =
-      makeRule(cxxFunctionalCastExpr(hasSourceExpression(
-                   BasicStringViewConstructingFromNullExpr)),
-               remove(node("null_arg_expr")), ConstructionWarning);
+void StringviewNullptrCheck::registerMatchers(MatchFinder *Finder) {
+  const auto HasBasicStringViewType =
+      hasType(hasUnqualifiedDesugaredType(recordType(
+          hasDeclaration(cxxRecordDecl(hasName("::std::basic_string_view"))))));
 
-  // `std::string_view{null_arg_expr}` and `(std::string_view){null_arg_expr}`
-  auto HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr = makeRule(
-      cxxTemporaryObjectExpr(cxxConstructExpr(
-          HasBasicStringViewType, argumentCountIs(1),
-          hasAnyArgument(/* `hasArgument` would skip over parens */ anyOf(
-              NullLiteral, NullInitList, EmptyInitList)),
-          has(expr().bind("null_arg_expr")))),
-      remove(node("null_arg_expr")), ConstructionWarning);
-
-  // `(std::string_view) null_arg_expr`
-  auto HandleTemporaryCStyleCastExpr =
-      makeRule(cStyleCastExpr(hasSourceExpression(
-                   BasicStringViewConstructingFromNullExpr)),
-               changeTo(node("null_arg_expr"), cat("{}")), ConstructionWarning);
-
-  // `static_cast<std::string_view>(null_arg_expr)`
-  auto HandleTemporaryCXXStaticCastExpr =
-      makeRule(cxxStaticCastExpr(hasSourceExpression(
-                   BasicStringViewConstructingFromNullExpr)),
-               changeTo(node("null_arg_expr"), cat("\"\"")), StaticCastWarning);
-
-  // `std::string_view sv = null_arg_expr;`
-  auto HandleStackCopyInitialization =
-      makeRule(varDecl(HasBasicStringViewType,
-                       hasInitializer(ignoringImpCasts(cxxConstructExpr(
-                           BasicStringViewConstructingFromNullExpr,
-                           unless(isListInitialization())))),
-                       unless(isDirectInitialization())),
-               changeTo(node("null_arg_expr"), cat("{}")), ConstructionWarning);
-
-  // `std::string_view sv = {null_arg_expr};`
-  auto HandleStackCopyListInitialization =
-      makeRule(varDecl(HasBasicStringViewType,
-                       hasInitializer(cxxConstructExpr(
-                           BasicStringViewConstructingFromNullExpr,
-                           isListInitialization())),
-                       unless(isDirectInitialization())),
-               remove(node("null_arg_expr")), ConstructionWarning);
-
-  // `std::string_view sv(null_arg_expr);`
-  auto HandleStackDirectInitialization =
-      makeRule(varDecl(HasBasicStringViewType,
-                       hasInitializer(cxxConstructExpr(
-                           BasicStringViewConstructingFromNullExpr,
-                           unless(isListInitialization()))),
-                       isDirectInitialization())
-                   .bind("var_decl"),
-               changeTo(node("construct_expr"), cat(name("var_decl"))),
-               ConstructionWarning);
-
-  // `std::string_view sv{null_arg_expr};`
-  auto HandleStackDirectListInitialization =
-      makeRule(varDecl(HasBasicStringViewType,
-                       hasInitializer(cxxConstructExpr(
-                           BasicStringViewConstructingFromNullExpr,
-                           isListInitialization())),
-                       isDirectInitialization()),
-               remove(node("null_arg_expr")), ConstructionWarning);
-
-  // `struct S { std::string_view sv = null_arg_expr; };`
-  auto HandleFieldInClassCopyInitialization = makeRule(
-      fieldDecl(HasBasicStringViewType,
-                hasInClassInitializer(ignoringImpCasts(
-                    cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
-                                     unless(isListInitialization()))))),
-      changeTo(node("null_arg_expr"), cat("{}")), ConstructionWarning);
-
-  // `struct S { std::string_view sv = {null_arg_expr}; };` and
-  // `struct S { std::string_view sv{null_arg_expr}; };`
-  auto HandleFieldInClassCopyListAndDirectListInitialization = makeRule(
-      fieldDecl(HasBasicStringViewType,
-                hasInClassInitializer(ignoringImpCasts(
-                    cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
-                                     isListInitialization())))),
-      remove(node("null_arg_expr")), ConstructionWarning);
+  Finder->addMatcher(
+      cxxConstructExpr(HasBasicStringViewType, constructedFromNullptr())
+          .bind("construct_expr"),
+      this);
+}
 
-  // `class C { std::string_view sv; C() : sv(null_arg_expr) {} };`
-  auto HandleConstructorDirectInitialization =
-      makeRule(cxxCtorInitializer(forField(fieldDecl(HasBasicStringViewType)),
-                                  withInitializer(cxxConstructExpr(
-                                      BasicStringViewConstructingFromNullExpr,
-                                      unless(isListInitialization())))),
-               remove(node("null_arg_expr")), ConstructionWarning);
+void StringviewNullptrCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *ConstructExpr =
+      Result.Nodes.getNodeAs<CXXConstructExpr>("construct_expr");
+  const Expr *NullArg = ConstructExpr->getArg(0);
 
-  // `class C { std::string_view sv; C() : sv{null_arg_expr} {} };`
-  auto HandleConstructorDirectListInitialization =
-      makeRule(cxxCtorInitializer(forField(fieldDecl(HasBasicStringViewType)),
-                                  withInitializer(cxxConstructExpr(
-                                      BasicStringViewConstructingFromNullExpr,
-                                      isListInitialization()))),
-               remove(node("null_arg_expr")), ConstructionWarning);
+  auto Diag = diag(NullArg->getBeginLoc(),
+                   "constructing basic_string_view from null is undefined");
----------------
localspook wrote:

I agree that "constructing" is a bit weird for `sv != nullptr`, but I would argue that "using a null pointer as a basic_string_view is undefined" is a bit weird for *explicit* constructions:
```cpp
std::string_view sv {nullptr}; // warning: using a null pointer as a basic_string_view is undefined
```
We could have separate diagnostic messages for the two cases, but I think that would be better as a separate PR.

https://github.com/llvm/llvm-project/pull/192889


More information about the cfe-commits mailing list