StringviewNullptrCheck.cpp 14 KB

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