10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/ASTMatchers/ASTMatchers.h"
21 return Node.getBeginLoc().isValid();
25 clang::ast_matchers::internal::Matcher<clang::Type>,
27 const clang::Type *TypeNode = Node.getTypePtr();
28 return TypeNode !=
nullptr &&
29 InnerMatcher.matches(*TypeNode, Finder, Builder);
33 return Node.isExternCContext();
37 const clang::DeclContext *DC = Node.getDeclContext();
38 const auto *FD = llvm::dyn_cast<clang::FunctionDecl>(DC);
39 return FD ? FD->isMain() :
false;
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>();
47 return llvm::dyn_cast<TargetType>(Node);
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);
56 return (IsImplicitInstantiation(getAs<clang::CXXRecordDecl>(Node)) ||
57 IsImplicitInstantiation(getAs<clang::FunctionDecl>(Node)) ||
58 IsImplicitInstantiation(getAs<clang::VarDecl>(Node)));
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))
71 if ((ParentDecl = ParentNode.template get<clang::NamedDecl>()))
74 ParentNodes = Finder->getASTContext().getParents(ParentNode);
77 if (ParentDecl !=
nullptr) {
78 const clang::DeclContext *DeclContext = ParentDecl->getDeclContext();
79 while (DeclContext !=
nullptr) {
80 if (IsImplicitTemplateInstantiation(DeclContext))
82 DeclContext = DeclContext->getParent();
93 AllowStringArrays(Options.get(
"AllowStringArrays", false)) {}
96 Options.store(Opts,
"AllowStringArrays", AllowStringArrays);
100 ast_matchers::internal::Matcher<TypeLoc> IgnoreStringArrayIfNeededMatcher =
102 if (AllowStringArrays)
103 IgnoreStringArrayIfNeededMatcher =
104 unless(typeLoc(loc(hasCanonicalType(incompleteArrayType(
105 hasElementType(isAnyCharacter())))),
106 hasParent(varDecl(hasInitializer(stringLiteral()),
107 unless(parmVarDecl())))));
110 typeLoc(hasValidBeginLoc(), hasType(arrayType()),
111 optionally(hasParent(parmVarDecl().bind(
"param_decl"))),
112 unless(anyOf(hasParent(parmVarDecl(isArgvOfMain())),
113 hasParent(varDecl(isExternC())),
115 hasParent(recordDecl(isExternCContext())))),
116 hasAncestor(functionDecl(isExternC())),
117 isWithinImplicitTemplateInstantiation())),
118 IgnoreStringArrayIfNeededMatcher)
123 templateArgumentLoc(hasTypeLoc(
124 typeLoc(hasType(arrayType())).bind(
"template_arg_array_typeloc"))),
129 TypeLoc ArrayTypeLoc{};
131 if (
const auto *MatchedTypeLoc = Result.Nodes.getNodeAs<TypeLoc>(
"typeloc"))
132 ArrayTypeLoc = *MatchedTypeLoc;
134 if (
const auto *TemplateArgArrayTypeLoc =
135 Result.Nodes.getNodeAs<TypeLoc>(
"template_arg_array_typeloc"))
136 ArrayTypeLoc = *TemplateArgArrayTypeLoc;
138 assert(!ArrayTypeLoc.isNull());
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{};
146 RecommendTypes.push_back(
"'std::vector'");
147 }
else if (ArrayTypeLoc.getTypePtr()->isIncompleteArrayType() && IsInParam) {
150 if (Result.Context->getLangOpts().CPlusPlus20)
151 RecommendTypes.push_back(
"'std::span'");
153 RecommendTypes.push_back(
"'std::array'");
154 RecommendTypes.push_back(
"'std::vector'");
157 RecommendTypes.push_back(
"'std::array'");
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();
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