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