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