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