diff options
Diffstat (limited to 'clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp')
| -rw-r--r-- | clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp | 77 |
1 files changed, 71 insertions, 6 deletions
diff --git a/clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp b/clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp index ee70911dff1d..a5b535f7433b 100644 --- a/clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp @@ -39,6 +39,53 @@ AST_MATCHER(clang::ParmVarDecl, isArgvOfMain) { return FD ? FD->isMain() : false; } +template <typename TargetType, typename NodeType> +const TargetType *getAs(const NodeType *Node) { + if constexpr (std::is_same_v<NodeType, clang::DynTypedNode>) + return Node->template get<TargetType>(); + else + return llvm::dyn_cast<TargetType>(Node); +} + +AST_MATCHER(clang::TypeLoc, isWithinImplicitTemplateInstantiation) { + const auto IsImplicitTemplateInstantiation = [](const auto *Node) { + const auto IsImplicitInstantiation = [](const auto *Node) { + return (Node != nullptr) && (Node->getTemplateSpecializationKind() == + TSK_ImplicitInstantiation); + }; + return (IsImplicitInstantiation(getAs<clang::CXXRecordDecl>(Node)) || + IsImplicitInstantiation(getAs<clang::FunctionDecl>(Node)) || + IsImplicitInstantiation(getAs<clang::VarDecl>(Node))); + }; + + DynTypedNodeList ParentNodes = Finder->getASTContext().getParents(Node); + const clang::NamedDecl *ParentDecl = nullptr; + while (!ParentNodes.empty()) { + const DynTypedNode &ParentNode = ParentNodes[0]; + if (IsImplicitTemplateInstantiation(&ParentNode)) + return true; + + // in case of a `NamedDecl` as parent node, it is more efficient to proceed + // with the upward traversal via DeclContexts (see below) instead of via + // parent nodes + if ((ParentDecl = ParentNode.template get<clang::NamedDecl>())) + break; + + ParentNodes = Finder->getASTContext().getParents(ParentNode); + } + + if (ParentDecl != nullptr) { + const clang::DeclContext *DeclContext = ParentDecl->getDeclContext(); + while (DeclContext != nullptr) { + if (IsImplicitTemplateInstantiation(DeclContext)) + return true; + DeclContext = DeclContext->getParent(); + } + } + + return false; +} + } // namespace AvoidCArraysCheck::AvoidCArraysCheck(StringRef Name, ClangTidyContext *Context) @@ -66,22 +113,38 @@ void AvoidCArraysCheck::registerMatchers(MatchFinder *Finder) { hasParent(varDecl(isExternC())), hasParent(fieldDecl( hasParent(recordDecl(isExternCContext())))), - hasAncestor(functionDecl(isExternC())))), + hasAncestor(functionDecl(isExternC())), + isWithinImplicitTemplateInstantiation())), IgnoreStringArrayIfNeededMatcher) .bind("typeloc"), this); + + Finder->addMatcher( + templateArgumentLoc(hasTypeLoc( + typeLoc(hasType(arrayType())).bind("template_arg_array_typeloc"))), + this); } void AvoidCArraysCheck::check(const MatchFinder::MatchResult &Result) { - const auto *ArrayType = Result.Nodes.getNodeAs<TypeLoc>("typeloc"); + TypeLoc ArrayTypeLoc{}; + + if (const auto *MatchedTypeLoc = Result.Nodes.getNodeAs<TypeLoc>("typeloc")) + ArrayTypeLoc = *MatchedTypeLoc; + + if (const auto *TemplateArgArrayTypeLoc = + Result.Nodes.getNodeAs<TypeLoc>("template_arg_array_typeloc")) + ArrayTypeLoc = *TemplateArgArrayTypeLoc; + + assert(!ArrayTypeLoc.isNull()); + const bool IsInParam = Result.Nodes.getNodeAs<ParmVarDecl>("param_decl") != nullptr; - const bool IsVLA = ArrayType->getTypePtr()->isVariableArrayType(); + const bool IsVLA = ArrayTypeLoc.getTypePtr()->isVariableArrayType(); enum class RecommendType { Array, Vector, Span }; llvm::SmallVector<const char *> RecommendTypes{}; if (IsVLA) { RecommendTypes.push_back("'std::vector'"); - } else if (ArrayType->getTypePtr()->isIncompleteArrayType() && IsInParam) { + } else if (ArrayTypeLoc.getTypePtr()->isIncompleteArrayType() && IsInParam) { // in function parameter, we also don't know the size of // IncompleteArrayType. if (Result.Context->getLangOpts().CPlusPlus20) @@ -93,9 +156,11 @@ void AvoidCArraysCheck::check(const MatchFinder::MatchResult &Result) { } else { RecommendTypes.push_back("'std::array'"); } - diag(ArrayType->getBeginLoc(), + + diag(ArrayTypeLoc.getBeginLoc(), "do not declare %select{C-style|C VLA}0 arrays, use %1 instead") - << IsVLA << llvm::join(RecommendTypes, " or "); + << IsVLA << llvm::join(RecommendTypes, " or ") + << ArrayTypeLoc.getSourceRange(); } } // namespace clang::tidy::modernize |
