| //===--- SmartPtrArrayMismatchCheck.cpp - clang-tidy ----------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "SmartPtrArrayMismatchCheck.h" |
| #include "../utils/ASTUtils.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| #include "clang/Lex/Lexer.h" |
| |
| using namespace clang::ast_matchers; |
| |
| namespace clang::tidy::bugprone { |
| |
| namespace { |
| |
| constexpr char ConstructExprN[] = "found_construct_expr"; |
| constexpr char NewExprN[] = "found_new_expr"; |
| constexpr char ConstructorN[] = "found_constructor"; |
| |
| bool isInSingleDeclStmt(const DeclaratorDecl *D) { |
| const DynTypedNodeList Parents = |
| D->getASTContext().getParentMapContext().getParents(*D); |
| for (const DynTypedNode &PNode : Parents) |
| if (const auto *PDecl = PNode.get<DeclStmt>()) |
| return PDecl->isSingleDecl(); |
| return false; |
| } |
| |
| const DeclaratorDecl *getConstructedVarOrField(const Expr *FoundConstructExpr, |
| ASTContext &Ctx) { |
| const DynTypedNodeList ConstructParents = |
| Ctx.getParentMapContext().getParents(*FoundConstructExpr); |
| if (ConstructParents.size() != 1) |
| return nullptr; |
| const auto *ParentDecl = ConstructParents.begin()->get<DeclaratorDecl>(); |
| if (isa_and_nonnull<VarDecl, FieldDecl>(ParentDecl)) |
| return ParentDecl; |
| |
| return nullptr; |
| } |
| |
| } // namespace |
| |
| const char SmartPtrArrayMismatchCheck::PointerTypeN[] = "pointer_type"; |
| |
| SmartPtrArrayMismatchCheck::SmartPtrArrayMismatchCheck( |
| StringRef Name, ClangTidyContext *Context, StringRef SmartPointerName) |
| : ClangTidyCheck(Name, Context), SmartPointerName(SmartPointerName) {} |
| |
| void SmartPtrArrayMismatchCheck::storeOptions( |
| ClangTidyOptions::OptionMap &Opts) {} |
| |
| void SmartPtrArrayMismatchCheck::registerMatchers(MatchFinder *Finder) { |
| // For both shared and unique pointers, we need to find constructor with |
| // exactly one parameter that has the pointer type. Other constructors are |
| // not applicable for this check. |
| auto FindConstructor = |
| cxxConstructorDecl(ofClass(getSmartPointerClassMatcher()), |
| parameterCountIs(1), isExplicit()) |
| .bind(ConstructorN); |
| auto FindConstructExpr = |
| cxxConstructExpr( |
| hasDeclaration(FindConstructor), argumentCountIs(1), |
| hasArgument(0, |
| cxxNewExpr(isArray(), |
| hasType(hasCanonicalType(pointerType( |
| pointee(equalsBoundNode(PointerTypeN)))))) |
| .bind(NewExprN))) |
| .bind(ConstructExprN); |
| Finder->addMatcher(FindConstructExpr, this); |
| } |
| |
| void SmartPtrArrayMismatchCheck::check(const MatchFinder::MatchResult &Result) { |
| const auto *FoundNewExpr = Result.Nodes.getNodeAs<CXXNewExpr>(NewExprN); |
| const auto *FoundConstructExpr = |
| Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructExprN); |
| const auto *FoundConstructorDecl = |
| Result.Nodes.getNodeAs<CXXConstructorDecl>(ConstructorN); |
| |
| ASTContext &Ctx = FoundConstructorDecl->getASTContext(); |
| const DeclaratorDecl *VarOrField = |
| getConstructedVarOrField(FoundConstructExpr, Ctx); |
| |
| auto D = diag(FoundNewExpr->getBeginLoc(), |
| "%0 pointer to non-array is initialized with array") |
| << SmartPointerName; |
| D << FoundNewExpr->getSourceRange(); |
| |
| if (VarOrField) { |
| auto TSTypeLoc = VarOrField->getTypeSourceInfo() |
| ->getTypeLoc() |
| .getAsAdjusted<clang::TemplateSpecializationTypeLoc>(); |
| assert(TSTypeLoc.getNumArgs() >= 1 && |
| "Matched type should have at least 1 template argument."); |
| |
| SourceRange TemplateArgumentRange = TSTypeLoc.getArgLoc(0) |
| .getTypeSourceInfo() |
| ->getTypeLoc() |
| .getSourceRange(); |
| D << TemplateArgumentRange; |
| |
| if (isInSingleDeclStmt(VarOrField)) { |
| const SourceManager &SM = Ctx.getSourceManager(); |
| if (!utils::rangeCanBeFixed(TemplateArgumentRange, &SM)) |
| return; |
| |
| SourceLocation InsertLoc = Lexer::getLocForEndOfToken( |
| TemplateArgumentRange.getEnd(), 0, SM, Ctx.getLangOpts()); |
| D << FixItHint::CreateInsertion(InsertLoc, "[]"); |
| } |
| } |
| } |
| |
| } // namespace clang::tidy::bugprone |