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/Module.h"
16#include "clang/Basic/SourceLocation.h"
17#include "clang/Basic/Specifiers.h"
18#include "clang/Lex/Token.h"
19#include "llvm/ADT/DenseSet.h"
20#include "llvm/ADT/STLExtras.h"
21#include "llvm/ADT/SmallVector.h"
29 static llvm::ArrayRef<
30 std::pair<misc::UseInternalLinkageCheck::FixModeKind, StringRef>>
52 if (SM.isInMainFile(L))
55 L = SM.getIncludeLoc(SM.getFileID(L));
65AST_MATCHER(Decl, isFirstDecl) {
return Node.isFirstDecl(); }
67AST_MATCHER(FunctionDecl, hasBody) {
return Node.hasBody(); }
69AST_MATCHER(Decl, isInImportableModuleUnit) {
70 if (
const Module *OwningModule = Node.getOwningModule())
71 if (OwningModule->Kind == Module::ModuleInterfaceUnit ||
72 OwningModule->Kind == Module::ModulePartitionInterface ||
73 OwningModule->Kind == Module::ModulePartitionImplementation)
79 HeaderFileExtensions) {
80 return llvm::all_of(Node.redecls(), [&](
const Decl *D) {
81 return isInMainFile(D->getLocation(),
82 Finder->getASTContext().getSourceManager(),
83 *HeaderFileExtensions);
88 AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
90 return Node.getStorageClass() == SC_Extern;
93AST_MATCHER(FunctionDecl, isAllocationOrDeallocationOverloadedFunction) {
100 static const llvm::DenseSet<OverloadedOperatorKind> OverloadedOperators{
101 OverloadedOperatorKind::OO_New,
102 OverloadedOperatorKind::OO_Array_New,
103 OverloadedOperatorKind::OO_Delete,
104 OverloadedOperatorKind::OO_Array_Delete,
106 return OverloadedOperators.contains(Node.getOverloadedOperator());
110 AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
112 return Finder->getASTContext().getLangOpts().CPlusPlus && Node.isExternC();
115AST_MATCHER(TagDecl, hasNameForLinkage) {
return Node.hasNameForLinkage(); }
117AST_MATCHER(CXXRecordDecl, isExplicitTemplateInstantiation) {
118 return Node.getTemplateSpecializationKind() ==
119 TSK_ExplicitInstantiationDefinition;
128 AnalyzeFunctions(Options.get(
"AnalyzeFunctions", true)),
129 AnalyzeVariables(Options.get(
"AnalyzeVariables", true)),
130 AnalyzeTypes(Options.get(
"AnalyzeTypes", true)) {
131 if (!AnalyzeFunctions && !AnalyzeVariables && !AnalyzeTypes)
133 "the 'misc-use-internal-linkage' check will not perform any "
134 "analysis because its 'AnalyzeFunctions', 'AnalyzeVariables', "
135 "and 'AnalyzeTypes' options have all been set to false");
139 Options.store(Opts,
"FixMode", FixMode);
140 Options.store(Opts,
"AnalyzeFunctions", AnalyzeFunctions);
141 Options.store(Opts,
"AnalyzeVariables", AnalyzeVariables);
142 Options.store(Opts,
"AnalyzeTypes", AnalyzeTypes);
147 allOf(isFirstDecl(), isAllRedeclsInMainFile(&getHeaderFileExtensions()),
148 unless(anyOf(isInAnonymousNamespace(), isInImportableModuleUnit(),
149 hasAncestor(decl(friendDecl())))));
151 if (AnalyzeFunctions)
156 isExplicitlyExternC(), isStaticStorageClass(),
157 isExternStorageClass(), isExplicitTemplateSpecialization(),
158 cxxMethodDecl(), isConsteval(),
159 isAllocationOrDeallocationOverloadedFunction(), isMain())))
163 if (AnalyzeVariables)
165 varDecl(Common, hasGlobalStorage(),
166 unless(anyOf(isExplicitlyExternC(), isStaticStorageClass(),
167 isExternStorageClass(),
168 isExplicitTemplateSpecialization(),
169 hasThreadStorageDuration())))
173 if (getLangOpts().CPlusPlus && AnalyzeTypes)
175 tagDecl(Common, isDefinition(), hasNameForLinkage(),
176 hasDeclContext(anyOf(translationUnitDecl(), namespaceDecl())),
178 classTemplatePartialSpecializationDecl(),
179 cxxRecordDecl(anyOf(isExplicitTemplateSpecialization(),
180 isExplicitTemplateInstantiation())))))
186 "%0 %1 can be made static %select{|or moved into an anonymous namespace }2"
187 "to enforce internal linkage";
190 if (
const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(
"fn")) {
191 const DiagnosticBuilder DB = diag(FD->getLocation(),
Message)
192 <<
"function" << FD << getLangOpts().CPlusPlus;
193 const SourceLocation FixLoc = FD->getInnerLocStart();
194 if (FixLoc.isInvalid() || FixLoc.isMacroID())
197 DB << FixItHint::CreateInsertion(FixLoc,
"static ");
200 if (
const auto *VD = Result.Nodes.getNodeAs<VarDecl>(
"var")) {
204 if (getLangOpts().CPlusPlus && VD->getType().isConstQualified())
207 const DiagnosticBuilder DB = diag(VD->getLocation(),
Message)
208 <<
"variable" << VD << getLangOpts().CPlusPlus;
209 const SourceLocation FixLoc = VD->getInnerLocStart();
210 if (FixLoc.isInvalid() || FixLoc.isMacroID())
213 DB << FixItHint::CreateInsertion(FixLoc,
"static ");
216 if (
const auto *TD = Result.Nodes.getNodeAs<TagDecl>(
"tag")) {
217 diag(TD->getLocation(),
"%0 %1 can be moved into an anonymous namespace "
218 "to enforce internal linkage")
219 << TD->getKindName() << TD;
222 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_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
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...