10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/ASTMatchers/ASTMatchers.h"
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>();
23 return dyn_cast<TargetType>(Node);
28AST_MATCHER(TypeLoc, hasValidBeginLoc) {
return Node.getBeginLoc().isValid(); }
30AST_MATCHER_P(TypeLoc, hasType, ast_matchers::internal::Matcher<Type>,
32 const Type *TypeNode = Node.getTypePtr();
33 return TypeNode !=
nullptr &&
34 InnerMatcher.matches(*TypeNode, Finder, Builder);
37AST_MATCHER(RecordDecl, isExternCContext) {
return Node.isExternCContext(); }
40 const DeclContext *DC = Node.getDeclContext();
41 const auto *FD = dyn_cast<FunctionDecl>(DC);
42 return FD ? FD->isMain() :
false;
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);
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))
66 if ((ParentDecl = ParentNode.get<NamedDecl>()))
69 ParentNodes = Finder->getASTContext().getParents(ParentNode);
72 if (ParentDecl !=
nullptr) {
73 const DeclContext *DeclContext = ParentDecl->getDeclContext();
74 while (DeclContext !=
nullptr) {
75 if (IsImplicitTemplateInstantiation(DeclContext))
77 DeclContext = DeclContext->getParent();
88 AllowStringArrays(Options.get(
"AllowStringArrays", false)) {}
91 Options.store(Opts,
"AllowStringArrays", AllowStringArrays);
95 ast_matchers::internal::Matcher<TypeLoc> IgnoreStringArrayIfNeededMatcher =
97 if (AllowStringArrays)
98 IgnoreStringArrayIfNeededMatcher =
99 unless(typeLoc(loc(hasCanonicalType(incompleteArrayType(
100 hasElementType(isAnyCharacter())))),
101 hasParent(varDecl(hasInitializer(stringLiteral()),
102 unless(parmVarDecl())))));
105 typeLoc(hasValidBeginLoc(), hasType(arrayType()),
106 optionally(hasParent(parmVarDecl().bind(
"param_decl"))),
107 unless(anyOf(hasParent(parmVarDecl(isArgvOfMain())),
108 hasParent(varDecl(isExternC())),
110 hasParent(recordDecl(isExternCContext())))),
111 hasAncestor(functionDecl(isExternC())),
112 isWithinImplicitTemplateInstantiation())),
113 IgnoreStringArrayIfNeededMatcher)
118 templateArgumentLoc(hasTypeLoc(
119 typeLoc(hasType(arrayType())).bind(
"template_arg_array_typeloc"))),
124 TypeLoc ArrayTypeLoc{};
126 if (
const auto *MatchedTypeLoc = Result.Nodes.getNodeAs<TypeLoc>(
"typeloc"))
127 ArrayTypeLoc = *MatchedTypeLoc;
129 if (
const auto *TemplateArgArrayTypeLoc =
130 Result.Nodes.getNodeAs<TypeLoc>(
"template_arg_array_typeloc"))
131 ArrayTypeLoc = *TemplateArgArrayTypeLoc;
133 assert(!ArrayTypeLoc.isNull());
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 };
141 RecommendTypes.push_back(
"'std::vector'");
142 }
else if (ArrayTypeLoc.getTypePtr()->isIncompleteArrayType() && IsInParam) {
145 if (Result.Context->getLangOpts().CPlusPlus20) {
146 RecommendTypes.push_back(
"'std::span'");
148 RecommendTypes.push_back(
"'std::array'");
149 RecommendTypes.push_back(
"'std::vector'");
152 RecommendTypes.push_back(
"'std::array'");
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();
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