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