11#include "clang/AST/Decl.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/ASTMatchers/ASTMatchers.h"
14#include "clang/ASTMatchers/ASTMatchersMacros.h"
15#include "clang/Basic/SourceLocation.h"
16#include "clang/Basic/Specifiers.h"
17#include "clang/Lex/Token.h"
18#include "llvm/ADT/DenseSet.h"
19#include "llvm/ADT/STLExtras.h"
20#include "llvm/ADT/SmallVector.h"
28 static llvm::ArrayRef<
29 std::pair<misc::UseInternalLinkageCheck::FixModeKind, StringRef>>
51 if (SM.isInMainFile(L))
54 L = SM.getIncludeLoc(SM.getFileID(L));
64AST_MATCHER(Decl, isFirstDecl) {
return Node.isFirstDecl(); }
66AST_MATCHER(FunctionDecl, hasBody) {
return Node.hasBody(); }
69 HeaderFileExtensions) {
70 return llvm::all_of(Node.redecls(), [&](
const Decl *D) {
71 return isInMainFile(D->getLocation(),
72 Finder->getASTContext().getSourceManager(),
73 HeaderFileExtensions);
78 AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
80 return Node.getStorageClass() == SC_Extern;
83AST_MATCHER(FunctionDecl, isAllocationOrDeallocationOverloadedFunction) {
90 static const llvm::DenseSet<OverloadedOperatorKind> OverloadedOperators{
91 OverloadedOperatorKind::OO_New,
92 OverloadedOperatorKind::OO_Array_New,
93 OverloadedOperatorKind::OO_Delete,
94 OverloadedOperatorKind::OO_Array_Delete,
96 return OverloadedOperators.contains(Node.getOverloadedOperator());
100 AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
102 return Finder->getASTContext().getLangOpts().CPlusPlus && Node.isExternC();
105AST_MATCHER(TagDecl, hasNameForLinkage) {
return Node.hasNameForLinkage(); }
107AST_MATCHER(CXXRecordDecl, isExplicitTemplateInstantiation) {
108 return Node.getTemplateSpecializationKind() ==
109 TSK_ExplicitInstantiationDefinition;
117 HeaderFileExtensions(Context->getHeaderFileExtensions()),
119 AnalyzeFunctions(Options.get(
"AnalyzeFunctions", true)),
120 AnalyzeVariables(Options.get(
"AnalyzeVariables", true)),
121 AnalyzeTypes(Options.get(
"AnalyzeTypes", true)) {
122 if (!AnalyzeFunctions && !AnalyzeVariables && !AnalyzeTypes)
124 "the 'misc-use-internal-linkage' check will not perform any "
125 "analysis because its 'AnalyzeFunctions', 'AnalyzeVariables', "
126 "and 'AnalyzeTypes' options have all been set to false");
130 Options.store(Opts,
"FixMode", FixMode);
131 Options.store(Opts,
"AnalyzeFunctions", AnalyzeFunctions);
132 Options.store(Opts,
"AnalyzeVariables", AnalyzeVariables);
133 Options.store(Opts,
"AnalyzeTypes", AnalyzeTypes);
138 allOf(isFirstDecl(), isAllRedeclsInMainFile(HeaderFileExtensions),
141 isInAnonymousNamespace(), hasAncestor(decl(anyOf(
146 if (AnalyzeFunctions)
151 isExplicitlyExternC(), isStaticStorageClass(),
152 isExternStorageClass(), isExplicitTemplateSpecialization(),
153 cxxMethodDecl(), isConsteval(),
154 isAllocationOrDeallocationOverloadedFunction(), isMain())))
157 if (AnalyzeVariables)
159 varDecl(Common, hasGlobalStorage(),
160 unless(anyOf(isExplicitlyExternC(), isStaticStorageClass(),
161 isExternStorageClass(),
162 isExplicitTemplateSpecialization(),
163 hasThreadStorageDuration())))
166 if (getLangOpts().CPlusPlus && AnalyzeTypes)
168 tagDecl(Common, isDefinition(), hasNameForLinkage(),
169 hasDeclContext(anyOf(translationUnitDecl(), namespaceDecl())),
171 classTemplatePartialSpecializationDecl(),
172 cxxRecordDecl(anyOf(isExplicitTemplateSpecialization(),
173 isExplicitTemplateInstantiation())))))
179 "%0 %1 can be made static %select{|or moved into an anonymous namespace }2"
180 "to enforce internal linkage";
183 if (
const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(
"fn")) {
184 const DiagnosticBuilder DB = diag(FD->getLocation(),
Message)
185 <<
"function" << FD << getLangOpts().CPlusPlus;
186 const SourceLocation FixLoc = FD->getInnerLocStart();
187 if (FixLoc.isInvalid() || FixLoc.isMacroID())
190 DB << FixItHint::CreateInsertion(FixLoc,
"static ");
193 if (
const auto *VD = Result.Nodes.getNodeAs<VarDecl>(
"var")) {
197 if (getLangOpts().CPlusPlus && VD->getType().isConstQualified())
200 const DiagnosticBuilder DB = diag(VD->getLocation(),
Message)
201 <<
"variable" << VD << getLangOpts().CPlusPlus;
202 const SourceLocation FixLoc = VD->getInnerLocStart();
203 if (FixLoc.isInvalid() || FixLoc.isMacroID())
206 DB << FixItHint::CreateInsertion(FixLoc,
"static ");
209 if (
const auto *TD = Result.Nodes.getNodeAs<TagDecl>(
"tag")) {
210 diag(TD->getLocation(),
"%0 %1 can be moved into an anonymous namespace "
211 "to enforce internal linkage")
212 << TD->getKindName() << TD;
215 llvm_unreachable(
"");
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
UseInternalLinkageCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
AST_POLYMORPHIC_MATCHER(isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc))
Matches AST nodes that were found within Abseil files.
AST_MATCHER(BinaryOperator, isRelationalOperator)
static constexpr StringRef Message
static bool isInMainFile(SourceLocation L, SourceManager &SM, const FileExtensionsSet &HeaderFileExtensions)
bool isExpansionLocInHeaderFile(SourceLocation Loc, const SourceManager &SM, const FileExtensionsSet &HeaderFileExtensions)
Checks whether expansion location of Loc is in header file.
llvm::SmallSet< llvm::StringRef, 5 > FileExtensionsSet
llvm::StringMap< ClangTidyValue > OptionMap
static llvm::ArrayRef< std::pair< misc::UseInternalLinkageCheck::FixModeKind, StringRef > > getEnumMapping()
This class should be specialized by any enum type that needs to be converted to and from an llvm::Str...