clang-tools 20.0.0git
UseInternalLinkageCheck.cpp
Go to the documentation of this file.
1//===--- UseInternalLinkageCheck.cpp - clang-tidy--------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
10#include "../utils/FileExtensionsUtils.h"
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 "llvm/ADT/STLExtras.h"
18
19using namespace clang::ast_matchers;
20
21namespace clang::tidy {
22
23template <>
24struct OptionEnumMapping<misc::UseInternalLinkageCheck::FixModeKind> {
25 static llvm::ArrayRef<
26 std::pair<misc::UseInternalLinkageCheck::FixModeKind, StringRef>>
28 static constexpr std::pair<misc::UseInternalLinkageCheck::FixModeKind,
29 StringRef>
30 Mapping[] = {
33 "UseStatic"},
34 };
35 return {Mapping};
36 }
37};
38
39} // namespace clang::tidy
40
41namespace clang::tidy::misc {
42
43namespace {
44
45AST_MATCHER(Decl, isFirstDecl) { return Node.isFirstDecl(); }
46
47static bool isInMainFile(SourceLocation L, SourceManager &SM,
48 const FileExtensionsSet &HeaderFileExtensions) {
49 for (;;) {
50 if (utils::isSpellingLocInHeaderFile(L, SM, HeaderFileExtensions))
51 return false;
52 if (SM.isInMainFile(L))
53 return true;
54 // not in header file but not in main file
55 L = SM.getIncludeLoc(SM.getFileID(L));
56 if (L.isValid())
57 continue;
58 // Conservative about the unknown
59 return false;
60 }
61}
62
63AST_MATCHER_P(Decl, isAllRedeclsInMainFile, FileExtensionsSet,
64 HeaderFileExtensions) {
65 return llvm::all_of(Node.redecls(), [&](const Decl *D) {
66 return isInMainFile(D->getLocation(),
67 Finder->getASTContext().getSourceManager(),
68 HeaderFileExtensions);
69 });
70}
71
72AST_POLYMORPHIC_MATCHER(isExternStorageClass,
73 AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
74 VarDecl)) {
75 return Node.getStorageClass() == SC_Extern;
76}
77
78} // namespace
79
81 ClangTidyContext *Context)
82 : ClangTidyCheck(Name, Context),
83 HeaderFileExtensions(Context->getHeaderFileExtensions()),
84 FixMode(Options.get("FixMode", FixModeKind::UseStatic)) {}
85
87 Options.store(Opts, "FixMode", FixMode);
88}
89
91 auto Common =
92 allOf(isFirstDecl(), isAllRedeclsInMainFile(HeaderFileExtensions),
93 unless(anyOf(
94 // 1. internal linkage
95 isStaticStorageClass(), isInAnonymousNamespace(),
96 // 2. explicit external linkage
97 isExternStorageClass(), isExternC(),
98 // 3. template
99 isExplicitTemplateSpecialization(),
100 // 4. friend
101 hasAncestor(friendDecl()))));
102 Finder->addMatcher(
103 functionDecl(Common, unless(cxxMethodDecl()), unless(isMain()))
104 .bind("fn"),
105 this);
106 Finder->addMatcher(varDecl(Common, hasGlobalStorage()).bind("var"), this);
107}
108
109static constexpr StringRef Message =
110 "%0 %1 can be made static or moved into an anonymous namespace "
111 "to enforce internal linkage";
112
113void UseInternalLinkageCheck::check(const MatchFinder::MatchResult &Result) {
114 if (const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("fn")) {
115 DiagnosticBuilder DB = diag(FD->getLocation(), Message) << "function" << FD;
116 SourceLocation FixLoc = FD->getTypeSpecStartLoc();
117 if (FixLoc.isInvalid() || FixLoc.isMacroID())
118 return;
119 if (FixMode == FixModeKind::UseStatic)
120 DB << FixItHint::CreateInsertion(FixLoc, "static ");
121 return;
122 }
123 if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var")) {
124 // In C++, const variables at file scope have implicit internal linkage,
125 // so we should not warn there. This is not the case in C.
126 // https://eel.is/c++draft/diff#basic-3
127 if (getLangOpts().CPlusPlus && VD->getType().isConstQualified())
128 return;
129
130 DiagnosticBuilder DB = diag(VD->getLocation(), Message) << "variable" << VD;
131 SourceLocation FixLoc = VD->getTypeSpecStartLoc();
132 if (FixLoc.isInvalid() || FixLoc.isMacroID())
133 return;
134 if (FixMode == FixModeKind::UseStatic)
135 DB << FixItHint::CreateInsertion(FixLoc, "static ");
136 return;
137 }
138 llvm_unreachable("");
139}
140
141} // namespace clang::tidy::misc
const FunctionDecl * Decl
llvm::SmallString< 256U > Name
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
UseInternalLinkageCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
AST_POLYMORPHIC_MATCHER(isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc))
Matches AST nodes that were found within Abseil files.
Definition: AbseilMatcher.h:30
AST_MATCHER_P(UserDefinedLiteral, hasLiteral, clang::ast_matchers::internal::Matcher< Expr >, InnerMatcher)
AST_MATCHER(Decl, declHasNoReturnAttr)
matches a Decl if it has a "no return" attribute of any kind
static constexpr StringRef Message
bool isSpellingLocInHeaderFile(SourceLocation Loc, SourceManager &SM, const FileExtensionsSet &HeaderFileExtensions)
Checks whether spelling 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...