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