summaryrefslogtreecommitdiff
path: root/clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp')
-rw-r--r--clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp77
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