1 //===--- StringviewNullptrCheck.cpp - clang-tidy --------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "StringviewNullptrCheck.h" 10 #include "../utils/TransformerClangTidyCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/AST/Decl.h" 13 #include "clang/AST/OperationKinds.h" 14 #include "clang/ASTMatchers/ASTMatchFinder.h" 15 #include "clang/ASTMatchers/ASTMatchers.h" 16 #include "clang/Tooling/Transformer/RangeSelector.h" 17 #include "clang/Tooling/Transformer/RewriteRule.h" 18 #include "clang/Tooling/Transformer/Stencil.h" 19 #include "llvm/ADT/StringRef.h" 20 21 namespace clang { 22 namespace tidy { 23 namespace bugprone { 24 25 using namespace ::clang::ast_matchers; 26 using namespace ::clang::transformer; 27 28 namespace { 29 30 AST_MATCHER_P(InitListExpr, initCountIs, unsigned, N) { 31 return Node.getNumInits() == N; 32 } 33 34 AST_MATCHER(clang::VarDecl, isDirectInitialization) { 35 return Node.getInitStyle() != clang::VarDecl::InitializationStyle::CInit; 36 } 37 38 } // namespace 39 40 RewriteRule StringviewNullptrCheckImpl() { 41 auto construction_warning = 42 cat("constructing basic_string_view from null is undefined; replace with " 43 "the default constructor"); 44 auto static_cast_warning = 45 cat("casting to basic_string_view from null is undefined; replace with " 46 "the empty string"); 47 auto assignment_warning = 48 cat("assignment to basic_string_view from null is undefined; replace " 49 "with the default constructor"); 50 auto relative_comparison_warning = 51 cat("comparing basic_string_view to null is undefined; replace with the " 52 "empty string"); 53 auto equality_comparison_warning = 54 cat("comparing basic_string_view to null is undefined; replace with the " 55 "emptiness query"); 56 auto constructor_argument_warning = 57 cat("passing null as basic_string_view is undefined; replace with the " 58 "empty string"); 59 60 // Matches declarations and expressions of type `basic_string_view` 61 auto HasBasicStringViewType = hasType(hasUnqualifiedDesugaredType(recordType( 62 hasDeclaration(cxxRecordDecl(hasName("::std::basic_string_view")))))); 63 64 // Matches `nullptr` and `(nullptr)` binding to a pointer 65 auto NullLiteral = implicitCastExpr( 66 hasCastKind(clang::CK_NullToPointer), 67 hasSourceExpression(ignoringParens(cxxNullPtrLiteralExpr()))); 68 69 // Matches `{nullptr}` and `{(nullptr)}` binding to a pointer 70 auto NullInitList = initListExpr(initCountIs(1), hasInit(0, NullLiteral)); 71 72 // Matches `{}` 73 auto EmptyInitList = initListExpr(initCountIs(0)); 74 75 // Matches null construction without `basic_string_view` type spelling 76 auto BasicStringViewConstructingFromNullExpr = 77 cxxConstructExpr( 78 HasBasicStringViewType, argumentCountIs(1), 79 hasAnyArgument(/* `hasArgument` would skip over parens */ anyOf( 80 NullLiteral, NullInitList, EmptyInitList)), 81 unless(cxxTemporaryObjectExpr(/* filters out type spellings */)), 82 has(expr().bind("null_arg_expr"))) 83 .bind("construct_expr"); 84 85 // `std::string_view(null_arg_expr)` 86 auto HandleTemporaryCXXFunctionalCastExpr = 87 makeRule(cxxFunctionalCastExpr(hasSourceExpression( 88 BasicStringViewConstructingFromNullExpr)), 89 remove(node("null_arg_expr")), construction_warning); 90 91 // `std::string_view{null_arg_expr}` and `(std::string_view){null_arg_expr}` 92 auto HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr = makeRule( 93 cxxTemporaryObjectExpr(cxxConstructExpr( 94 HasBasicStringViewType, argumentCountIs(1), 95 hasAnyArgument(/* `hasArgument` would skip over parens */ anyOf( 96 NullLiteral, NullInitList, EmptyInitList)), 97 has(expr().bind("null_arg_expr")))), 98 remove(node("null_arg_expr")), construction_warning); 99 100 // `(std::string_view) null_arg_expr` 101 auto HandleTemporaryCStyleCastExpr = makeRule( 102 cStyleCastExpr( 103 hasSourceExpression(BasicStringViewConstructingFromNullExpr)), 104 changeTo(node("null_arg_expr"), cat("{}")), construction_warning); 105 106 // `static_cast<std::string_view>(null_arg_expr)` 107 auto HandleTemporaryCXXStaticCastExpr = makeRule( 108 cxxStaticCastExpr( 109 hasSourceExpression(BasicStringViewConstructingFromNullExpr)), 110 changeTo(node("null_arg_expr"), cat("\"\"")), static_cast_warning); 111 112 // `std::string_view sv = null_arg_expr;` 113 auto HandleStackCopyInitialization = makeRule( 114 varDecl(HasBasicStringViewType, 115 hasInitializer(ignoringImpCasts( 116 cxxConstructExpr(BasicStringViewConstructingFromNullExpr, 117 unless(isListInitialization())))), 118 unless(isDirectInitialization())), 119 changeTo(node("null_arg_expr"), cat("{}")), construction_warning); 120 121 // `std::string_view sv = {null_arg_expr};` 122 auto HandleStackCopyListInitialization = 123 makeRule(varDecl(HasBasicStringViewType, 124 hasInitializer(cxxConstructExpr( 125 BasicStringViewConstructingFromNullExpr, 126 isListInitialization())), 127 unless(isDirectInitialization())), 128 remove(node("null_arg_expr")), construction_warning); 129 130 // `std::string_view sv(null_arg_expr);` 131 auto HandleStackDirectInitialization = 132 makeRule(varDecl(HasBasicStringViewType, 133 hasInitializer(cxxConstructExpr( 134 BasicStringViewConstructingFromNullExpr, 135 unless(isListInitialization()))), 136 isDirectInitialization()) 137 .bind("var_decl"), 138 changeTo(node("construct_expr"), cat(name("var_decl"))), 139 construction_warning); 140 141 // `std::string_view sv{null_arg_expr};` 142 auto HandleStackDirectListInitialization = 143 makeRule(varDecl(HasBasicStringViewType, 144 hasInitializer(cxxConstructExpr( 145 BasicStringViewConstructingFromNullExpr, 146 isListInitialization())), 147 isDirectInitialization()), 148 remove(node("null_arg_expr")), construction_warning); 149 150 // `struct S { std::string_view sv = null_arg_expr; };` 151 auto HandleFieldInClassCopyInitialization = makeRule( 152 fieldDecl(HasBasicStringViewType, 153 hasInClassInitializer(ignoringImpCasts( 154 cxxConstructExpr(BasicStringViewConstructingFromNullExpr, 155 unless(isListInitialization()))))), 156 changeTo(node("null_arg_expr"), cat("{}")), construction_warning); 157 158 // `struct S { std::string_view sv = {null_arg_expr}; };` and 159 // `struct S { std::string_view sv{null_arg_expr}; };` 160 auto HandleFieldInClassCopyListAndDirectListInitialization = makeRule( 161 fieldDecl(HasBasicStringViewType, 162 hasInClassInitializer(ignoringImpCasts( 163 cxxConstructExpr(BasicStringViewConstructingFromNullExpr, 164 isListInitialization())))), 165 remove(node("null_arg_expr")), construction_warning); 166 167 // `class C { std::string_view sv; C() : sv(null_arg_expr) {} };` 168 auto HandleConstructorDirectInitialization = 169 makeRule(cxxCtorInitializer(forField(fieldDecl(HasBasicStringViewType)), 170 withInitializer(cxxConstructExpr( 171 BasicStringViewConstructingFromNullExpr, 172 unless(isListInitialization())))), 173 remove(node("null_arg_expr")), construction_warning); 174 175 // `class C { std::string_view sv; C() : sv{null_arg_expr} {} };` 176 auto HandleConstructorDirectListInitialization = 177 makeRule(cxxCtorInitializer(forField(fieldDecl(HasBasicStringViewType)), 178 withInitializer(cxxConstructExpr( 179 BasicStringViewConstructingFromNullExpr, 180 isListInitialization()))), 181 remove(node("null_arg_expr")), construction_warning); 182 183 // `void f(std::string_view sv = null_arg_expr);` 184 auto HandleDefaultArgumentCopyInitialization = makeRule( 185 parmVarDecl(HasBasicStringViewType, 186 hasInitializer(ignoringImpCasts( 187 cxxConstructExpr(BasicStringViewConstructingFromNullExpr, 188 unless(isListInitialization()))))), 189 changeTo(node("null_arg_expr"), cat("{}")), construction_warning); 190 191 // `void f(std::string_view sv = {null_arg_expr});` 192 auto HandleDefaultArgumentCopyListInitialization = 193 makeRule(parmVarDecl(HasBasicStringViewType, 194 hasInitializer(cxxConstructExpr( 195 BasicStringViewConstructingFromNullExpr, 196 isListInitialization()))), 197 remove(node("null_arg_expr")), construction_warning); 198 199 // `new std::string_view(null_arg_expr)` 200 auto HandleHeapDirectInitialization = makeRule( 201 cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr, 202 unless(isListInitialization()))), 203 unless(isArray()), unless(hasAnyPlacementArg(anything()))), 204 remove(node("null_arg_expr")), construction_warning); 205 206 // `new std::string_view{null_arg_expr}` 207 auto HandleHeapDirectListInitialization = makeRule( 208 cxxNewExpr(has(cxxConstructExpr(BasicStringViewConstructingFromNullExpr, 209 isListInitialization())), 210 unless(isArray()), unless(hasAnyPlacementArg(anything()))), 211 remove(node("null_arg_expr")), construction_warning); 212 213 // `function(null_arg_expr)` 214 auto HandleFunctionArgumentInitialization = makeRule( 215 callExpr(hasAnyArgument( 216 ignoringImpCasts(BasicStringViewConstructingFromNullExpr)), 217 unless(cxxOperatorCallExpr())), 218 changeTo(node("construct_expr"), cat("{}")), construction_warning); 219 220 // `sv = null_arg_expr` 221 auto HandleAssignment = makeRule( 222 cxxOperatorCallExpr(hasOverloadedOperatorName("="), 223 hasRHS(materializeTemporaryExpr( 224 has(BasicStringViewConstructingFromNullExpr)))), 225 changeTo(node("construct_expr"), cat("{}")), assignment_warning); 226 227 // `sv < null_arg_expr` 228 auto HandleRelativeComparison = makeRule( 229 cxxOperatorCallExpr(hasAnyOverloadedOperatorName("<", "<=", ">", ">="), 230 hasEitherOperand(ignoringImpCasts( 231 BasicStringViewConstructingFromNullExpr))), 232 changeTo(node("construct_expr"), cat("\"\"")), 233 relative_comparison_warning); 234 235 // `sv == null_arg_expr` 236 auto HandleEmptyEqualityComparison = makeRule( 237 cxxOperatorCallExpr( 238 hasOverloadedOperatorName("=="), 239 hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr), 240 traverse(clang::TK_IgnoreUnlessSpelledInSource, 241 expr().bind("instance")))) 242 .bind("root"), 243 changeTo(node("root"), cat(access("instance", cat("empty")), "()")), 244 equality_comparison_warning); 245 246 // `sv != null_arg_expr` 247 auto HandleNonEmptyEqualityComparison = makeRule( 248 cxxOperatorCallExpr( 249 hasOverloadedOperatorName("!="), 250 hasOperands(ignoringImpCasts(BasicStringViewConstructingFromNullExpr), 251 traverse(clang::TK_IgnoreUnlessSpelledInSource, 252 expr().bind("instance")))) 253 .bind("root"), 254 changeTo(node("root"), cat("!", access("instance", cat("empty")), "()")), 255 equality_comparison_warning); 256 257 // `return null_arg_expr;` 258 auto HandleReturnStatement = makeRule( 259 returnStmt(hasReturnValue( 260 ignoringImpCasts(BasicStringViewConstructingFromNullExpr))), 261 changeTo(node("construct_expr"), cat("{}")), construction_warning); 262 263 // `T(null_arg_expr)` 264 auto HandleConstructorInvocation = 265 makeRule(cxxConstructExpr( 266 hasAnyArgument(/* `hasArgument` would skip over parens */ 267 ignoringImpCasts( 268 BasicStringViewConstructingFromNullExpr)), 269 unless(HasBasicStringViewType)), 270 changeTo(node("construct_expr"), cat("\"\"")), 271 constructor_argument_warning); 272 273 return applyFirst( 274 {HandleTemporaryCXXFunctionalCastExpr, 275 HandleTemporaryCXXTemporaryObjectExprAndCompoundLiteralExpr, 276 HandleTemporaryCStyleCastExpr, 277 HandleTemporaryCXXStaticCastExpr, 278 HandleStackCopyInitialization, 279 HandleStackCopyListInitialization, 280 HandleStackDirectInitialization, 281 HandleStackDirectListInitialization, 282 HandleFieldInClassCopyInitialization, 283 HandleFieldInClassCopyListAndDirectListInitialization, 284 HandleConstructorDirectInitialization, 285 HandleConstructorDirectListInitialization, 286 HandleDefaultArgumentCopyInitialization, 287 HandleDefaultArgumentCopyListInitialization, 288 HandleHeapDirectInitialization, 289 HandleHeapDirectListInitialization, 290 HandleFunctionArgumentInitialization, 291 HandleAssignment, 292 HandleRelativeComparison, 293 HandleEmptyEqualityComparison, 294 HandleNonEmptyEqualityComparison, 295 HandleReturnStatement, 296 HandleConstructorInvocation}); 297 } 298 299 StringviewNullptrCheck::StringviewNullptrCheck(StringRef Name, 300 ClangTidyContext *Context) 301 : utils::TransformerClangTidyCheck(StringviewNullptrCheckImpl(), Name, 302 Context) {} 303 304 } // namespace bugprone 305 } // namespace tidy 306 } // namespace clang 307