SmartPtrArrayMismatchCheck.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. //===--- SmartPtrArrayMismatchCheck.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 "SmartPtrArrayMismatchCheck.h"
  9. #include "../utils/ASTUtils.h"
  10. #include "clang/ASTMatchers/ASTMatchFinder.h"
  11. #include "clang/Lex/Lexer.h"
  12. using namespace clang::ast_matchers;
  13. namespace clang::tidy::bugprone {
  14. namespace {
  15. constexpr char ConstructExprN[] = "found_construct_expr";
  16. constexpr char NewExprN[] = "found_new_expr";
  17. constexpr char ConstructorN[] = "found_constructor";
  18. bool isInSingleDeclStmt(const DeclaratorDecl *D) {
  19. const DynTypedNodeList Parents =
  20. D->getASTContext().getParentMapContext().getParents(*D);
  21. for (const DynTypedNode &PNode : Parents)
  22. if (const auto *PDecl = PNode.get<DeclStmt>())
  23. return PDecl->isSingleDecl();
  24. return false;
  25. }
  26. const DeclaratorDecl *getConstructedVarOrField(const Expr *FoundConstructExpr,
  27. ASTContext &Ctx) {
  28. const DynTypedNodeList ConstructParents =
  29. Ctx.getParentMapContext().getParents(*FoundConstructExpr);
  30. if (ConstructParents.size() != 1)
  31. return nullptr;
  32. const auto *ParentDecl = ConstructParents.begin()->get<DeclaratorDecl>();
  33. if (isa_and_nonnull<VarDecl, FieldDecl>(ParentDecl))
  34. return ParentDecl;
  35. return nullptr;
  36. }
  37. } // namespace
  38. const char SmartPtrArrayMismatchCheck::PointerTypeN[] = "pointer_type";
  39. SmartPtrArrayMismatchCheck::SmartPtrArrayMismatchCheck(
  40. StringRef Name, ClangTidyContext *Context, StringRef SmartPointerName)
  41. : ClangTidyCheck(Name, Context), SmartPointerName(SmartPointerName) {}
  42. void SmartPtrArrayMismatchCheck::storeOptions(
  43. ClangTidyOptions::OptionMap &Opts) {}
  44. void SmartPtrArrayMismatchCheck::registerMatchers(MatchFinder *Finder) {
  45. // For both shared and unique pointers, we need to find constructor with
  46. // exactly one parameter that has the pointer type. Other constructors are
  47. // not applicable for this check.
  48. auto FindConstructor =
  49. cxxConstructorDecl(ofClass(getSmartPointerClassMatcher()),
  50. parameterCountIs(1), isExplicit())
  51. .bind(ConstructorN);
  52. auto FindConstructExpr =
  53. cxxConstructExpr(
  54. hasDeclaration(FindConstructor), argumentCountIs(1),
  55. hasArgument(0,
  56. cxxNewExpr(isArray(),
  57. hasType(hasCanonicalType(pointerType(
  58. pointee(equalsBoundNode(PointerTypeN))))))
  59. .bind(NewExprN)))
  60. .bind(ConstructExprN);
  61. Finder->addMatcher(FindConstructExpr, this);
  62. }
  63. void SmartPtrArrayMismatchCheck::check(const MatchFinder::MatchResult &Result) {
  64. const auto *FoundNewExpr = Result.Nodes.getNodeAs<CXXNewExpr>(NewExprN);
  65. const auto *FoundConstructExpr =
  66. Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructExprN);
  67. const auto *FoundConstructorDecl =
  68. Result.Nodes.getNodeAs<CXXConstructorDecl>(ConstructorN);
  69. ASTContext &Ctx = FoundConstructorDecl->getASTContext();
  70. const DeclaratorDecl *VarOrField =
  71. getConstructedVarOrField(FoundConstructExpr, Ctx);
  72. auto D = diag(FoundNewExpr->getBeginLoc(),
  73. "%0 pointer to non-array is initialized with array")
  74. << SmartPointerName;
  75. D << FoundNewExpr->getSourceRange();
  76. if (VarOrField) {
  77. auto TSTypeLoc = VarOrField->getTypeSourceInfo()
  78. ->getTypeLoc()
  79. .getAsAdjusted<clang::TemplateSpecializationTypeLoc>();
  80. assert(TSTypeLoc.getNumArgs() >= 1 &&
  81. "Matched type should have at least 1 template argument.");
  82. SourceRange TemplateArgumentRange = TSTypeLoc.getArgLoc(0)
  83. .getTypeSourceInfo()
  84. ->getTypeLoc()
  85. .getSourceRange();
  86. D << TemplateArgumentRange;
  87. if (isInSingleDeclStmt(VarOrField)) {
  88. const SourceManager &SM = Ctx.getSourceManager();
  89. if (!utils::rangeCanBeFixed(TemplateArgumentRange, &SM))
  90. return;
  91. SourceLocation InsertLoc = Lexer::getLocForEndOfToken(
  92. TemplateArgumentRange.getEnd(), 0, SM, Ctx.getLangOpts());
  93. D << FixItHint::CreateInsertion(InsertLoc, "[]");
  94. }
  95. }
  96. }
  97. } // namespace clang::tidy::bugprone