308 lines
14 KiB
C++
308 lines
14 KiB
C++
//===--- StringviewNullptrCheck.cpp - clang-tidy --------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "StringviewNullptrCheck.h"
|
|
#include "../utils/TransformerClangTidyCheck.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/OperationKinds.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.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 {
|
|
namespace tidy {
|
|
namespace bugprone {
|
|
|
|
using namespace ::clang::ast_matchers;
|
|
using namespace ::clang::transformer;
|
|
|
|
namespace {
|
|
|
|
AST_MATCHER_P(InitListExpr, initCountIs, unsigned, N) {
|
|
return Node.getNumInits() == N;
|
|
}
|
|
|
|
AST_MATCHER(clang::VarDecl, isDirectInitialization) {
|
|
return Node.getInitStyle() != clang::VarDecl::InitializationStyle::CInit;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
RewriteRuleWith<std::string> StringviewNullptrCheckImpl() {
|
|
auto construction_warning =
|
|
cat("constructing basic_string_view from null is undefined; replace with "
|
|
"the default constructor");
|
|
auto static_cast_warning =
|
|
cat("casting to basic_string_view from null is undefined; replace with "
|
|
"the empty string");
|
|
auto argument_construction_warning =
|
|
cat("passing null as basic_string_view is undefined; replace with the "
|
|
"empty string");
|
|
auto assignment_warning =
|
|
cat("assignment to basic_string_view from null is undefined; replace "
|
|
"with the default constructor");
|
|
auto relative_comparison_warning =
|
|
cat("comparing basic_string_view to null is undefined; replace with the "
|
|
"empty string");
|
|
auto equality_comparison_warning =
|
|
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(clang::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")), construction_warning);
|
|
|
|
// `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")), construction_warning);
|
|
|
|
// `(std::string_view) null_arg_expr`
|
|
auto HandleTemporaryCStyleCastExpr = makeRule(
|
|
cStyleCastExpr(
|
|
hasSourceExpression(BasicStringViewConstructingFromNullExpr)),
|
|
changeTo(node("null_arg_expr"), cat("{}")), construction_warning);
|
|
|
|
// `static_cast<std::string_view>(null_arg_expr)`
|
|
auto HandleTemporaryCXXStaticCastExpr = makeRule(
|
|
cxxStaticCastExpr(
|
|
hasSourceExpression(BasicStringViewConstructingFromNullExpr)),
|
|
changeTo(node("null_arg_expr"), cat("\"\"")), static_cast_warning);
|
|
|
|
// `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("{}")), construction_warning);
|
|
|
|
// `std::string_view sv = {null_arg_expr};`
|
|
auto HandleStackCopyListInitialization =
|
|
makeRule(varDecl(HasBasicStringViewType,
|
|
hasInitializer(cxxConstructExpr(
|
|
BasicStringViewConstructingFromNullExpr,
|
|
isListInitialization())),
|
|
unless(isDirectInitialization())),
|
|
remove(node("null_arg_expr")), construction_warning);
|
|
|
|
// `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"))),
|
|
construction_warning);
|
|
|
|
// `std::string_view sv{null_arg_expr};`
|
|
auto HandleStackDirectListInitialization =
|
|
makeRule(varDecl(HasBasicStringViewType,
|
|
hasInitializer(cxxConstructExpr(
|
|
BasicStringViewConstructingFromNullExpr,
|
|
isListInitialization())),
|
|
isDirectInitialization()),
|
|
remove(node("null_arg_expr")), construction_warning);
|
|
|
|
// `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("{}")), construction_warning);
|
|
|
|
// `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")), construction_warning);
|
|
|
|
// `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")), construction_warning);
|
|
|
|
// `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")), construction_warning);
|
|
|
|
// `void f(std::string_view sv = null_arg_expr);`
|
|
auto HandleDefaultArgumentCopyInitialization = makeRule(
|
|
parmVarDecl(HasBasicStringViewType,
|
|
hasInitializer(ignoringImpCasts(
|
|
cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
|
|
unless(isListInitialization()))))),
|
|
changeTo(node("null_arg_expr"), cat("{}")), construction_warning);
|
|
|
|
// `void f(std::string_view sv = {null_arg_expr});`
|
|
auto HandleDefaultArgumentCopyListInitialization =
|
|
makeRule(parmVarDecl(HasBasicStringViewType,
|
|
hasInitializer(cxxConstructExpr(
|
|
BasicStringViewConstructingFromNullExpr,
|
|
isListInitialization()))),
|
|
remove(node("null_arg_expr")), construction_warning);
|
|
|
|
// `new std::string_view(null_arg_expr)`
|
|
auto HandleHeapDirectInitialization = makeRule(
|
|
cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
|
|
unless(isListInitialization()))),
|
|
unless(isArray()), unless(hasAnyPlacementArg(anything()))),
|
|
remove(node("null_arg_expr")), construction_warning);
|
|
|
|
// `new std::string_view{null_arg_expr}`
|
|
auto HandleHeapDirectListInitialization = makeRule(
|
|
cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr,
|
|
isListInitialization())),
|
|
unless(isArray()), unless(hasAnyPlacementArg(anything()))),
|
|
remove(node("null_arg_expr")), construction_warning);
|
|
|
|
// `function(null_arg_expr)`
|
|
auto HandleFunctionArgumentInitialization =
|
|
makeRule(callExpr(hasAnyArgument(ignoringImpCasts(
|
|
BasicStringViewConstructingFromNullExpr)),
|
|
unless(cxxOperatorCallExpr())),
|
|
changeTo(node("construct_expr"), cat("\"\"")),
|
|
argument_construction_warning);
|
|
|
|
// `sv = null_arg_expr`
|
|
auto HandleAssignment = makeRule(
|
|
cxxOperatorCallExpr(hasOverloadedOperatorName("="),
|
|
hasRHS(materializeTemporaryExpr(
|
|
has(BasicStringViewConstructingFromNullExpr)))),
|
|
changeTo(node("construct_expr"), cat("{}")), assignment_warning);
|
|
|
|
// `sv < null_arg_expr`
|
|
auto HandleRelativeComparison = makeRule(
|
|
cxxOperatorCallExpr(hasAnyOverloadedOperatorName("<", "<=", ">", ">="),
|
|
hasEitherOperand(ignoringImpCasts(
|
|
BasicStringViewConstructingFromNullExpr))),
|
|
changeTo(node("construct_expr"), cat("\"\"")),
|
|
relative_comparison_warning);
|
|
|
|
// `sv == null_arg_expr`
|
|
auto HandleEmptyEqualityComparison = makeRule(
|
|
cxxOperatorCallExpr(
|
|
hasOverloadedOperatorName("=="),
|
|
hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr),
|
|
traverse(clang::TK_IgnoreUnlessSpelledInSource,
|
|
expr().bind("instance"))))
|
|
.bind("root"),
|
|
changeTo(node("root"), cat(access("instance", cat("empty")), "()")),
|
|
equality_comparison_warning);
|
|
|
|
// `sv != null_arg_expr`
|
|
auto HandleNonEmptyEqualityComparison = makeRule(
|
|
cxxOperatorCallExpr(
|
|
hasOverloadedOperatorName("!="),
|
|
hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr),
|
|
traverse(clang::TK_IgnoreUnlessSpelledInSource,
|
|
expr().bind("instance"))))
|
|
.bind("root"),
|
|
changeTo(node("root"), cat("!", access("instance", cat("empty")), "()")),
|
|
equality_comparison_warning);
|
|
|
|
// `return null_arg_expr;`
|
|
auto HandleReturnStatement = makeRule(
|
|
returnStmt(hasReturnValue(
|
|
ignoringImpCasts(BasicStringViewConstructingFromNullExpr))),
|
|
changeTo(node("construct_expr"), cat("{}")), construction_warning);
|
|
|
|
// `T(null_arg_expr)`
|
|
auto HandleConstructorInvocation =
|
|
makeRule(cxxConstructExpr(
|
|
hasAnyArgument(/* `hasArgument` would skip over parens */
|
|
ignoringImpCasts(
|
|
BasicStringViewConstructingFromNullExpr)),
|
|
unless(HasBasicStringViewType)),
|
|
changeTo(node("construct_expr"), cat("\"\"")),
|
|
argument_construction_warning);
|
|
|
|
return applyFirst(
|
|
{HandleTemporaryCXXFunctionalCastExpr,
|
|
HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr,
|
|
HandleTemporaryCStyleCastExpr,
|
|
HandleTemporaryCXXStaticCastExpr,
|
|
HandleStackCopyInitialization,
|
|
HandleStackCopyListInitialization,
|
|
HandleStackDirectInitialization,
|
|
HandleStackDirectListInitialization,
|
|
HandleFieldInClassCopyInitialization,
|
|
HandleFieldInClassCopyListAndDirectListInitialization,
|
|
HandleConstructorDirectInitialization,
|
|
HandleConstructorDirectListInitialization,
|
|
HandleDefaultArgumentCopyInitialization,
|
|
HandleDefaultArgumentCopyListInitialization,
|
|
HandleHeapDirectInitialization,
|
|
HandleHeapDirectListInitialization,
|
|
HandleFunctionArgumentInitialization,
|
|
HandleAssignment,
|
|
HandleRelativeComparison,
|
|
HandleEmptyEqualityComparison,
|
|
HandleNonEmptyEqualityComparison,
|
|
HandleReturnStatement,
|
|
HandleConstructorInvocation});
|
|
}
|
|
|
|
StringviewNullptrCheck::StringviewNullptrCheck(StringRef Name,
|
|
ClangTidyContext *Context)
|
|
: utils::TransformerClangTidyCheck(StringviewNullptrCheckImpl(), Name,
|
|
Context) {}
|
|
|
|
} // namespace bugprone
|
|
} // namespace tidy
|
|
} // namespace clang
|