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
9#include "UseInternalLinkageCheck.h"
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 "clang/Lex/Token.h"
18#include "llvm/ADT/DenseSet.h"
19#include "llvm/ADT/STLExtras.h"
20#include "llvm/ADT/SmallVector.h"
21
22using namespace clang::ast_matchers;
23
24namespace clang::tidy {
25
26template <>
27struct OptionEnumMapping<misc::UseInternalLinkageCheck::FixModeKind> {
28 static llvm::ArrayRef<
29 std::pair<misc::UseInternalLinkageCheck::FixModeKind, StringRef>>
30 getEnumMapping() {
31 static constexpr std::pair<misc::UseInternalLinkageCheck::FixModeKind,
32 StringRef>
33 Mapping[] = {
34 {misc::UseInternalLinkageCheck::FixModeKind::None, "None"},
35 {misc::UseInternalLinkageCheck::FixModeKind::UseStatic,
36 "UseStatic"},
37 };
38 return {Mapping};
39 }
40};
41
42} // namespace clang::tidy
43
44namespace clang::tidy::misc {
45
46namespace {
47
48AST_MATCHER(Decl, isFirstDecl) { return Node.isFirstDecl(); }
49
50AST_MATCHER(FunctionDecl, hasBody) { return Node.hasBody(); }
51
52static bool isInMainFile(SourceLocation L, SourceManager &SM,
53 const FileExtensionsSet &HeaderFileExtensions) {
54 for (;;) {
55 if (utils::isExpansionLocInHeaderFile(Loc: L, SM, HeaderFileExtensions))
56 return false;
57 if (SM.isInMainFile(Loc: L))
58 return true;
59 // not in header file but not in main file
60 L = SM.getIncludeLoc(FID: SM.getFileID(SpellingLoc: L));
61 if (L.isValid())
62 continue;
63 // Conservative about the unknown
64 return false;
65 }
66}
67
68AST_MATCHER_P(Decl, isAllRedeclsInMainFile, FileExtensionsSet,
69 HeaderFileExtensions) {
70 return llvm::all_of(Range: Node.redecls(), P: [&](const Decl *D) {
71 return isInMainFile(L: D->getLocation(),
72 SM&: Finder->getASTContext().getSourceManager(),
73 HeaderFileExtensions);
74 });
75}
76
77AST_POLYMORPHIC_MATCHER(isExternStorageClass,
78 AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
79 VarDecl)) {
80 return Node.getStorageClass() == SC_Extern;
81}
82
83AST_MATCHER(FunctionDecl, isAllocationOrDeallocationOverloadedFunction) {
84 // [basic.stc.dynamic.allocation]
85 // An allocation function that is not a class member function shall belong to
86 // the global scope and not have a name with internal linkage.
87 // [basic.stc.dynamic.deallocation]
88 // A deallocation function that is not a class member function shall belong to
89 // the global scope and not have a name with internal linkage.
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,
95 };
96 return OverloadedOperators.contains(V: Node.getOverloadedOperator());
97}
98
99} // namespace
100
101UseInternalLinkageCheck::UseInternalLinkageCheck(StringRef Name,
102 ClangTidyContext *Context)
103 : ClangTidyCheck(Name, Context),
104 HeaderFileExtensions(Context->getHeaderFileExtensions()),
105 FixMode(Options.get(LocalName: "FixMode", Default: FixModeKind::UseStatic)) {}
106
107void UseInternalLinkageCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
108 Options.store(Options&: Opts, LocalName: "FixMode", Value: FixMode);
109}
110
111void UseInternalLinkageCheck::registerMatchers(MatchFinder *Finder) {
112 auto Common =
113 allOf(isFirstDecl(), isAllRedeclsInMainFile(HeaderFileExtensions),
114 unless(anyOf(
115 // 1. internal linkage
116 isStaticStorageClass(), isInAnonymousNamespace(),
117 // 2. explicit external linkage
118 isExternStorageClass(), isExternC(),
119 // 3. template
120 isExplicitTemplateSpecialization(),
121 hasAncestor(decl(anyOf(
122 // 4. friend
123 friendDecl(),
124 // 5. module export decl
125 exportDecl()))))));
126 Finder->addMatcher(
127 NodeMatch: functionDecl(Common, hasBody(),
128 unless(anyOf(cxxMethodDecl(), isConsteval(),
129 isAllocationOrDeallocationOverloadedFunction(),
130 isMain())))
131 .bind(ID: "fn"),
132 Action: this);
133 Finder->addMatcher(
134 NodeMatch: varDecl(Common, hasGlobalStorage(), unless(hasThreadStorageDuration()))
135 .bind(ID: "var"),
136 Action: this);
137}
138
139static constexpr StringRef Message =
140 "%0 %1 can be made static or moved into an anonymous namespace "
141 "to enforce internal linkage";
142
143void UseInternalLinkageCheck::check(const MatchFinder::MatchResult &Result) {
144 if (const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(ID: "fn")) {
145 DiagnosticBuilder DB = diag(FD->getLocation(), Message) << "function" << FD;
146 const SourceLocation FixLoc = FD->getInnerLocStart();
147 if (FixLoc.isInvalid() || FixLoc.isMacroID())
148 return;
149 if (FixMode == FixModeKind::UseStatic)
150 DB << FixItHint::CreateInsertion(InsertionLoc: FixLoc, Code: "static ");
151 return;
152 }
153 if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>(ID: "var")) {
154 // In C++, const variables at file scope have implicit internal linkage,
155 // so we should not warn there. This is not the case in C.
156 // https://eel.is/c++draft/diff#basic-3
157 if (getLangOpts().CPlusPlus && VD->getType().isConstQualified())
158 return;
159
160 DiagnosticBuilder DB = diag(VD->getLocation(), Message) << "variable" << VD;
161 const SourceLocation FixLoc = VD->getInnerLocStart();
162 if (FixLoc.isInvalid() || FixLoc.isMacroID())
163 return;
164 if (FixMode == FixModeKind::UseStatic)
165 DB << FixItHint::CreateInsertion(InsertionLoc: FixLoc, Code: "static ");
166 return;
167 }
168 llvm_unreachable("");
169}
170
171} // namespace clang::tidy::misc
172

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.cpp