1 //===--- AvoidCArraysCheck.cpp - clang-tidy -------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "AvoidCArraysCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
14 using namespace clang::ast_matchers
;
16 namespace clang::tidy::modernize
{
20 AST_MATCHER(clang::TypeLoc
, hasValidBeginLoc
) {
21 return Node
.getBeginLoc().isValid();
24 AST_MATCHER_P(clang::TypeLoc
, hasType
,
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
);
32 AST_MATCHER(clang::RecordDecl
, isExternCContext
) {
33 return Node
.isExternCContext();
36 AST_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;
44 AvoidCArraysCheck::AvoidCArraysCheck(StringRef Name
, ClangTidyContext
*Context
)
45 : ClangTidyCheck(Name
, Context
),
46 AllowStringArrays(Options
.get("AllowStringArrays", false)) {}
48 void AvoidCArraysCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
49 Options
.store(Opts
, "AllowStringArrays", AllowStringArrays
);
52 void AvoidCArraysCheck::registerMatchers(MatchFinder
*Finder
) {
53 ast_matchers::internal::Matcher
<TypeLoc
> IgnoreStringArrayIfNeededMatcher
=
55 if (AllowStringArrays
)
56 IgnoreStringArrayIfNeededMatcher
=
57 unless(typeLoc(loc(hasCanonicalType(incompleteArrayType(
58 hasElementType(isAnyCharacter())))),
59 hasParent(varDecl(hasInitializer(stringLiteral()),
60 unless(parmVarDecl())))));
63 typeLoc(hasValidBeginLoc(), hasType(arrayType()),
64 optionally(hasParent(parmVarDecl().bind("param_decl"))),
65 unless(anyOf(hasParent(parmVarDecl(isArgvOfMain())),
66 hasParent(varDecl(isExternC())),
68 hasParent(recordDecl(isExternCContext())))),
69 hasAncestor(functionDecl(isExternC())))),
70 std::move(IgnoreStringArrayIfNeededMatcher
))
75 void AvoidCArraysCheck::check(const MatchFinder::MatchResult
&Result
) {
76 const auto *ArrayType
= Result
.Nodes
.getNodeAs
<TypeLoc
>("typeloc");
77 const bool IsInParam
=
78 Result
.Nodes
.getNodeAs
<ParmVarDecl
>("param_decl") != nullptr;
79 const bool IsVLA
= ArrayType
->getTypePtr()->isVariableArrayType();
80 enum class RecommendType
{ Array
, Vector
, Span
};
81 llvm::SmallVector
<const char *> RecommendTypes
{};
83 RecommendTypes
.push_back("'std::vector'");
84 } else if (ArrayType
->getTypePtr()->isIncompleteArrayType() && IsInParam
) {
85 // in function parameter, we also don't know the size of
86 // IncompleteArrayType.
87 if (Result
.Context
->getLangOpts().CPlusPlus20
)
88 RecommendTypes
.push_back("'std::span'");
90 RecommendTypes
.push_back("'std::array'");
91 RecommendTypes
.push_back("'std::vector'");
94 RecommendTypes
.push_back("'std::array'");
96 diag(ArrayType
->getBeginLoc(),
97 "do not declare %select{C-style|C VLA}0 arrays, use %1 instead")
98 << IsVLA
<< llvm::join(RecommendTypes
, " or ");
101 } // namespace clang::tidy::modernize