clang-tools 22.0.0git
AvoidCArraysCheck.cpp
Go to the documentation of this file.
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-exception
6//
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
18namespace {
19
20AST_MATCHER(clang::TypeLoc, hasValidBeginLoc) {
21 return Node.getBeginLoc().isValid();
22}
23
24AST_MATCHER_P(clang::TypeLoc, hasType,
25 clang::ast_matchers::internal::Matcher<clang::Type>,
26 InnerMatcher) {
27 const clang::Type *TypeNode = Node.getTypePtr();
28 return TypeNode != nullptr &&
29 InnerMatcher.matches(*TypeNode, Finder, Builder);
30}
31
32AST_MATCHER(clang::RecordDecl, isExternCContext) {
33 return Node.isExternCContext();
34}
35
36AST_MATCHER(clang::ParmVarDecl, isArgvOfMain) {
37 const clang::DeclContext *DC = Node.getDeclContext();
38 const auto *FD = llvm::dyn_cast<clang::FunctionDecl>(DC);
39 return FD ? FD->isMain() : false;
40}
41
42template <typename TargetType, typename NodeType>
43const TargetType *getAs(const NodeType *Node) {
44 if constexpr (std::is_same_v<NodeType, clang::DynTypedNode>)
45 return Node->template get<TargetType>();
46 else
47 return llvm::dyn_cast<TargetType>(Node);
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 proceed
69 // with the upward traversal via DeclContexts (see below) instead of via
70 // parent nodes
71 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} // namespace
90
92 : ClangTidyCheck(Name, Context),
93 AllowStringArrays(Options.get("AllowStringArrays", false)) {}
94
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 of
149 // 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::modernize
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
AvoidCArraysCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
AST_MATCHER(BinaryOperator, isRelationalOperator)
llvm::StringMap< ClangTidyValue > OptionMap