brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.9 KiB · 71d89d3 Raw
167 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "AvoidCArraysCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/ASTMatchers/ASTMatchers.h"13 14using namespace clang::ast_matchers;15 16namespace clang::tidy::modernize {17 18template <typename TargetType, typename NodeType>19static const TargetType *getAs(const NodeType *Node) {20  if constexpr (std::is_same_v<NodeType, clang::DynTypedNode>)21    return Node->template get<TargetType>();22  else23    return llvm::dyn_cast<TargetType>(Node);24}25 26namespace {27 28AST_MATCHER(clang::TypeLoc, hasValidBeginLoc) {29  return Node.getBeginLoc().isValid();30}31 32AST_MATCHER_P(clang::TypeLoc, hasType,33              clang::ast_matchers::internal::Matcher<clang::Type>,34              InnerMatcher) {35  const clang::Type *TypeNode = Node.getTypePtr();36  return TypeNode != nullptr &&37         InnerMatcher.matches(*TypeNode, Finder, Builder);38}39 40AST_MATCHER(clang::RecordDecl, isExternCContext) {41  return Node.isExternCContext();42}43 44AST_MATCHER(clang::ParmVarDecl, isArgvOfMain) {45  const clang::DeclContext *DC = Node.getDeclContext();46  const auto *FD = llvm::dyn_cast<clang::FunctionDecl>(DC);47  return FD ? FD->isMain() : false;48}49 50AST_MATCHER(clang::TypeLoc, isWithinImplicitTemplateInstantiation) {51  const auto IsImplicitTemplateInstantiation = [](const auto *Node) {52    const auto IsImplicitInstantiation = [](const auto *Node) {53      return (Node != nullptr) && (Node->getTemplateSpecializationKind() ==54                                   TSK_ImplicitInstantiation);55    };56    return (IsImplicitInstantiation(getAs<clang::CXXRecordDecl>(Node)) ||57            IsImplicitInstantiation(getAs<clang::FunctionDecl>(Node)) ||58            IsImplicitInstantiation(getAs<clang::VarDecl>(Node)));59  };60 61  DynTypedNodeList ParentNodes = Finder->getASTContext().getParents(Node);62  const clang::NamedDecl *ParentDecl = nullptr;63  while (!ParentNodes.empty()) {64    const DynTypedNode &ParentNode = ParentNodes[0];65    if (IsImplicitTemplateInstantiation(&ParentNode))66      return true;67 68    // in case of a `NamedDecl` as parent node, it is more efficient to proceed69    // with the upward traversal via DeclContexts (see below) instead of via70    // parent nodes71    if ((ParentDecl = ParentNode.template get<clang::NamedDecl>()))72      break;73 74    ParentNodes = Finder->getASTContext().getParents(ParentNode);75  }76 77  if (ParentDecl != nullptr) {78    const clang::DeclContext *DeclContext = ParentDecl->getDeclContext();79    while (DeclContext != nullptr) {80      if (IsImplicitTemplateInstantiation(DeclContext))81        return true;82      DeclContext = DeclContext->getParent();83    }84  }85 86  return false;87}88 89} // namespace90 91AvoidCArraysCheck::AvoidCArraysCheck(StringRef Name, ClangTidyContext *Context)92    : ClangTidyCheck(Name, Context),93      AllowStringArrays(Options.get("AllowStringArrays", false)) {}94 95void AvoidCArraysCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {96  Options.store(Opts, "AllowStringArrays", AllowStringArrays);97}98 99void AvoidCArraysCheck::registerMatchers(MatchFinder *Finder) {100  ast_matchers::internal::Matcher<TypeLoc> IgnoreStringArrayIfNeededMatcher =101      anything();102  if (AllowStringArrays)103    IgnoreStringArrayIfNeededMatcher =104        unless(typeLoc(loc(hasCanonicalType(incompleteArrayType(105                           hasElementType(isAnyCharacter())))),106                       hasParent(varDecl(hasInitializer(stringLiteral()),107                                         unless(parmVarDecl())))));108 109  Finder->addMatcher(110      typeLoc(hasValidBeginLoc(), hasType(arrayType()),111              optionally(hasParent(parmVarDecl().bind("param_decl"))),112              unless(anyOf(hasParent(parmVarDecl(isArgvOfMain())),113                           hasParent(varDecl(isExternC())),114                           hasParent(fieldDecl(115                               hasParent(recordDecl(isExternCContext())))),116                           hasAncestor(functionDecl(isExternC())),117                           isWithinImplicitTemplateInstantiation())),118              IgnoreStringArrayIfNeededMatcher)119          .bind("typeloc"),120      this);121 122  Finder->addMatcher(123      templateArgumentLoc(hasTypeLoc(124          typeLoc(hasType(arrayType())).bind("template_arg_array_typeloc"))),125      this);126}127 128void AvoidCArraysCheck::check(const MatchFinder::MatchResult &Result) {129  TypeLoc ArrayTypeLoc{};130 131  if (const auto *MatchedTypeLoc = Result.Nodes.getNodeAs<TypeLoc>("typeloc"))132    ArrayTypeLoc = *MatchedTypeLoc;133 134  if (const auto *TemplateArgArrayTypeLoc =135          Result.Nodes.getNodeAs<TypeLoc>("template_arg_array_typeloc"))136    ArrayTypeLoc = *TemplateArgArrayTypeLoc;137 138  assert(!ArrayTypeLoc.isNull());139 140  const bool IsInParam =141      Result.Nodes.getNodeAs<ParmVarDecl>("param_decl") != nullptr;142  const bool IsVLA = ArrayTypeLoc.getTypePtr()->isVariableArrayType();143  enum class RecommendType { Array, Vector, Span };144  llvm::SmallVector<const char *> RecommendTypes{};145  if (IsVLA) {146    RecommendTypes.push_back("'std::vector'");147  } else if (ArrayTypeLoc.getTypePtr()->isIncompleteArrayType() && IsInParam) {148    // in function parameter, we also don't know the size of149    // IncompleteArrayType.150    if (Result.Context->getLangOpts().CPlusPlus20)151      RecommendTypes.push_back("'std::span'");152    else {153      RecommendTypes.push_back("'std::array'");154      RecommendTypes.push_back("'std::vector'");155    }156  } else {157    RecommendTypes.push_back("'std::array'");158  }159 160  diag(ArrayTypeLoc.getBeginLoc(),161       "do not declare %select{C-style|C VLA}0 arrays, use %1 instead")162      << IsVLA << llvm::join(RecommendTypes, " or ")163      << ArrayTypeLoc.getSourceRange();164}165 166} // namespace clang::tidy::modernize167