xref: /llvm-project/clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.cpp (revision 6504546abcd38159256c3030286b1c02b401c4f8)
1c4f83a00SCongcong Cai //===--- UseInternalLinkageCheck.cpp - clang-tidy--------------------------===//
2c4f83a00SCongcong Cai //
3c4f83a00SCongcong Cai // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c4f83a00SCongcong Cai // See https://llvm.org/LICENSE.txt for license information.
5c4f83a00SCongcong Cai // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c4f83a00SCongcong Cai //
7c4f83a00SCongcong Cai //===----------------------------------------------------------------------===//
8c4f83a00SCongcong Cai 
9c4f83a00SCongcong Cai #include "UseInternalLinkageCheck.h"
10c4f83a00SCongcong Cai #include "../utils/FileExtensionsUtils.h"
11c4f83a00SCongcong Cai #include "clang/AST/Decl.h"
12c4f83a00SCongcong Cai #include "clang/ASTMatchers/ASTMatchFinder.h"
13c4f83a00SCongcong Cai #include "clang/ASTMatchers/ASTMatchers.h"
14c4f83a00SCongcong Cai #include "clang/ASTMatchers/ASTMatchersMacros.h"
15c4f83a00SCongcong Cai #include "clang/Basic/SourceLocation.h"
16c4f83a00SCongcong Cai #include "clang/Basic/Specifiers.h"
176f21a7bdSCongcong Cai #include "clang/Lex/Token.h"
18f89fa238SCongcong Cai #include "llvm/ADT/DenseSet.h"
19c4f83a00SCongcong Cai #include "llvm/ADT/STLExtras.h"
20f89fa238SCongcong Cai #include "llvm/ADT/SmallVector.h"
21c4f83a00SCongcong Cai 
22c4f83a00SCongcong Cai using namespace clang::ast_matchers;
23c4f83a00SCongcong Cai 
24019f5257SCongcong Cai namespace clang::tidy {
25019f5257SCongcong Cai 
26019f5257SCongcong Cai template <>
27019f5257SCongcong Cai struct OptionEnumMapping<misc::UseInternalLinkageCheck::FixModeKind> {
28019f5257SCongcong Cai   static llvm::ArrayRef<
29019f5257SCongcong Cai       std::pair<misc::UseInternalLinkageCheck::FixModeKind, StringRef>>
30019f5257SCongcong Cai   getEnumMapping() {
31019f5257SCongcong Cai     static constexpr std::pair<misc::UseInternalLinkageCheck::FixModeKind,
32019f5257SCongcong Cai                                StringRef>
33019f5257SCongcong Cai         Mapping[] = {
34019f5257SCongcong Cai             {misc::UseInternalLinkageCheck::FixModeKind::None, "None"},
35019f5257SCongcong Cai             {misc::UseInternalLinkageCheck::FixModeKind::UseStatic,
36019f5257SCongcong Cai              "UseStatic"},
37019f5257SCongcong Cai         };
38019f5257SCongcong Cai     return {Mapping};
39019f5257SCongcong Cai   }
40019f5257SCongcong Cai };
41019f5257SCongcong Cai 
42019f5257SCongcong Cai } // namespace clang::tidy
43019f5257SCongcong Cai 
44c4f83a00SCongcong Cai namespace clang::tidy::misc {
45c4f83a00SCongcong Cai 
46c4f83a00SCongcong Cai namespace {
47c4f83a00SCongcong Cai 
48c4f83a00SCongcong Cai AST_MATCHER(Decl, isFirstDecl) { return Node.isFirstDecl(); }
49c4f83a00SCongcong Cai 
50e3aafe40SCongcong Cai AST_MATCHER(FunctionDecl, hasBody) { return Node.hasBody(); }
51e3aafe40SCongcong Cai 
52c4f83a00SCongcong Cai static bool isInMainFile(SourceLocation L, SourceManager &SM,
53c4f83a00SCongcong Cai                          const FileExtensionsSet &HeaderFileExtensions) {
54c4f83a00SCongcong Cai   for (;;) {
55c4f83a00SCongcong Cai     if (utils::isSpellingLocInHeaderFile(L, SM, HeaderFileExtensions))
56c4f83a00SCongcong Cai       return false;
57c4f83a00SCongcong Cai     if (SM.isInMainFile(L))
58c4f83a00SCongcong Cai       return true;
59c4f83a00SCongcong Cai     // not in header file but not in main file
60c4f83a00SCongcong Cai     L = SM.getIncludeLoc(SM.getFileID(L));
61c4f83a00SCongcong Cai     if (L.isValid())
62c4f83a00SCongcong Cai       continue;
63c4f83a00SCongcong Cai     // Conservative about the unknown
64c4f83a00SCongcong Cai     return false;
65c4f83a00SCongcong Cai   }
66c4f83a00SCongcong Cai }
67c4f83a00SCongcong Cai 
68c4f83a00SCongcong Cai AST_MATCHER_P(Decl, isAllRedeclsInMainFile, FileExtensionsSet,
69c4f83a00SCongcong Cai               HeaderFileExtensions) {
70c4f83a00SCongcong Cai   return llvm::all_of(Node.redecls(), [&](const Decl *D) {
71c4f83a00SCongcong Cai     return isInMainFile(D->getLocation(),
72c4f83a00SCongcong Cai                         Finder->getASTContext().getSourceManager(),
73c4f83a00SCongcong Cai                         HeaderFileExtensions);
74c4f83a00SCongcong Cai   });
75c4f83a00SCongcong Cai }
76c4f83a00SCongcong Cai 
77c4f83a00SCongcong Cai AST_POLYMORPHIC_MATCHER(isExternStorageClass,
78c4f83a00SCongcong Cai                         AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
79c4f83a00SCongcong Cai                                                         VarDecl)) {
80c4f83a00SCongcong Cai   return Node.getStorageClass() == SC_Extern;
81c4f83a00SCongcong Cai }
82c4f83a00SCongcong Cai 
83f89fa238SCongcong Cai AST_MATCHER(FunctionDecl, isAllocationOrDeallocationOverloadedFunction) {
84f89fa238SCongcong Cai   // [basic.stc.dynamic.allocation]
85f89fa238SCongcong Cai   // An allocation function that is not a class member function shall belong to
86f89fa238SCongcong Cai   // the global scope and not have a name with internal linkage.
87f89fa238SCongcong Cai   // [basic.stc.dynamic.deallocation]
88f89fa238SCongcong Cai   // A deallocation function that is not a class member function shall belong to
89f89fa238SCongcong Cai   // the global scope and not have a name with internal linkage.
90f89fa238SCongcong Cai   static const llvm::DenseSet<OverloadedOperatorKind> OverloadedOperators{
91f89fa238SCongcong Cai       OverloadedOperatorKind::OO_New,
92f89fa238SCongcong Cai       OverloadedOperatorKind::OO_Array_New,
93f89fa238SCongcong Cai       OverloadedOperatorKind::OO_Delete,
94f89fa238SCongcong Cai       OverloadedOperatorKind::OO_Array_Delete,
95f89fa238SCongcong Cai   };
96f89fa238SCongcong Cai   return OverloadedOperators.contains(Node.getOverloadedOperator());
97f89fa238SCongcong Cai }
98f89fa238SCongcong Cai 
99c4f83a00SCongcong Cai } // namespace
100c4f83a00SCongcong Cai 
101019f5257SCongcong Cai UseInternalLinkageCheck::UseInternalLinkageCheck(StringRef Name,
102019f5257SCongcong Cai                                                  ClangTidyContext *Context)
103019f5257SCongcong Cai     : ClangTidyCheck(Name, Context),
104019f5257SCongcong Cai       HeaderFileExtensions(Context->getHeaderFileExtensions()),
105019f5257SCongcong Cai       FixMode(Options.get("FixMode", FixModeKind::UseStatic)) {}
106019f5257SCongcong Cai 
107019f5257SCongcong Cai void UseInternalLinkageCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
108019f5257SCongcong Cai   Options.store(Opts, "FixMode", FixMode);
109019f5257SCongcong Cai }
110019f5257SCongcong Cai 
111c4f83a00SCongcong Cai void UseInternalLinkageCheck::registerMatchers(MatchFinder *Finder) {
112c4f83a00SCongcong Cai   auto Common =
113c4f83a00SCongcong Cai       allOf(isFirstDecl(), isAllRedeclsInMainFile(HeaderFileExtensions),
114c4f83a00SCongcong Cai             unless(anyOf(
115c4f83a00SCongcong Cai                 // 1. internal linkage
116c4f83a00SCongcong Cai                 isStaticStorageClass(), isInAnonymousNamespace(),
117c4f83a00SCongcong Cai                 // 2. explicit external linkage
118c4f83a00SCongcong Cai                 isExternStorageClass(), isExternC(),
119c4f83a00SCongcong Cai                 // 3. template
120c4f83a00SCongcong Cai                 isExplicitTemplateSpecialization(),
121010317e1SCongcong Cai                 hasAncestor(decl(anyOf(
122c4f83a00SCongcong Cai                     // 4. friend
123010317e1SCongcong Cai                     friendDecl(),
124010317e1SCongcong Cai                     // 5. module export decl
125010317e1SCongcong Cai                     exportDecl()))))));
126c4f83a00SCongcong Cai   Finder->addMatcher(
127f89fa238SCongcong Cai       functionDecl(Common, hasBody(),
128*6504546aSmaflcko                    unless(anyOf(cxxMethodDecl(), isConsteval(),
129f89fa238SCongcong Cai                                 isAllocationOrDeallocationOverloadedFunction(),
130f89fa238SCongcong Cai                                 isMain())))
131c4f83a00SCongcong Cai           .bind("fn"),
132c4f83a00SCongcong Cai       this);
133c4f83a00SCongcong Cai   Finder->addMatcher(varDecl(Common, hasGlobalStorage()).bind("var"), this);
134c4f83a00SCongcong Cai }
135c4f83a00SCongcong Cai 
136c4f83a00SCongcong Cai static constexpr StringRef Message =
137c4f83a00SCongcong Cai     "%0 %1 can be made static or moved into an anonymous namespace "
138c4f83a00SCongcong Cai     "to enforce internal linkage";
139c4f83a00SCongcong Cai 
140c4f83a00SCongcong Cai void UseInternalLinkageCheck::check(const MatchFinder::MatchResult &Result) {
141c4f83a00SCongcong Cai   if (const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("fn")) {
142019f5257SCongcong Cai     DiagnosticBuilder DB = diag(FD->getLocation(), Message) << "function" << FD;
1436f21a7bdSCongcong Cai     const SourceLocation FixLoc = FD->getInnerLocStart();
144019f5257SCongcong Cai     if (FixLoc.isInvalid() || FixLoc.isMacroID())
145019f5257SCongcong Cai       return;
146019f5257SCongcong Cai     if (FixMode == FixModeKind::UseStatic)
147019f5257SCongcong Cai       DB << FixItHint::CreateInsertion(FixLoc, "static ");
148c4f83a00SCongcong Cai     return;
149c4f83a00SCongcong Cai   }
150c4f83a00SCongcong Cai   if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var")) {
15129896db0SCarlos Galvez     // In C++, const variables at file scope have implicit internal linkage,
15229896db0SCarlos Galvez     // so we should not warn there. This is not the case in C.
15329896db0SCarlos Galvez     // https://eel.is/c++draft/diff#basic-3
15429896db0SCarlos Galvez     if (getLangOpts().CPlusPlus && VD->getType().isConstQualified())
15529896db0SCarlos Galvez       return;
15629896db0SCarlos Galvez 
157019f5257SCongcong Cai     DiagnosticBuilder DB = diag(VD->getLocation(), Message) << "variable" << VD;
1586f21a7bdSCongcong Cai     const SourceLocation FixLoc = VD->getInnerLocStart();
159019f5257SCongcong Cai     if (FixLoc.isInvalid() || FixLoc.isMacroID())
160019f5257SCongcong Cai       return;
161019f5257SCongcong Cai     if (FixMode == FixModeKind::UseStatic)
162019f5257SCongcong Cai       DB << FixItHint::CreateInsertion(FixLoc, "static ");
163c4f83a00SCongcong Cai     return;
164c4f83a00SCongcong Cai   }
165c4f83a00SCongcong Cai   llvm_unreachable("");
166c4f83a00SCongcong Cai }
167c4f83a00SCongcong Cai 
168c4f83a00SCongcong Cai } // namespace clang::tidy::misc
169