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