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