Files
TheRock/compiler/amd-llvm/clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

226 lines
8.5 KiB
C++

//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "UseInternalLinkageCheck.h"
#include "../utils/FileExtensionsUtils.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchersMacros.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Lex/Token.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
using namespace clang::ast_matchers;
namespace clang::tidy {
template <>
struct OptionEnumMapping<misc::UseInternalLinkageCheck::FixModeKind> {
static llvm::ArrayRef<
std::pair<misc::UseInternalLinkageCheck::FixModeKind, StringRef>>
getEnumMapping() {
static constexpr std::pair<misc::UseInternalLinkageCheck::FixModeKind,
StringRef>
Mapping[] = {
{misc::UseInternalLinkageCheck::FixModeKind::None, "None"},
{misc::UseInternalLinkageCheck::FixModeKind::UseStatic,
"UseStatic"},
};
return {Mapping};
}
};
} // namespace clang::tidy
namespace clang::tidy::misc {
static bool isInMainFile(SourceLocation L, SourceManager &SM,
const FileExtensionsSet &HeaderFileExtensions) {
for (;;) {
if (utils::isExpansionLocInHeaderFile(L, SM, HeaderFileExtensions))
return false;
if (SM.isInMainFile(L))
return true;
// not in header file but not in main file
L = SM.getIncludeLoc(SM.getFileID(L));
if (L.isValid())
continue;
// Conservative about the unknown
return false;
}
}
namespace {
AST_MATCHER(Decl, isFirstDecl) { return Node.isFirstDecl(); }
AST_MATCHER(FunctionDecl, hasBody) { return Node.hasBody(); }
AST_MATCHER(Decl, isInImportableModuleUnit) {
if (const Module *OwningModule = Node.getOwningModule())
if (OwningModule->Kind == Module::ModuleInterfaceUnit ||
OwningModule->Kind == Module::ModulePartitionInterface ||
OwningModule->Kind == Module::ModulePartitionImplementation)
return true;
return false;
}
AST_MATCHER_P(Decl, isAllRedeclsInMainFile, const FileExtensionsSet *,
HeaderFileExtensions) {
return llvm::all_of(Node.redecls(), [&](const Decl *D) {
return isInMainFile(D->getLocation(),
Finder->getASTContext().getSourceManager(),
*HeaderFileExtensions);
});
}
AST_POLYMORPHIC_MATCHER(isExternStorageClass,
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
VarDecl)) {
return Node.getStorageClass() == SC_Extern;
}
AST_MATCHER(FunctionDecl, isAllocationOrDeallocationOverloadedFunction) {
// [basic.stc.dynamic.allocation]
// An allocation function that is not a class member function shall belong to
// the global scope and not have a name with internal linkage.
// [basic.stc.dynamic.deallocation]
// A deallocation function that is not a class member function shall belong to
// the global scope and not have a name with internal linkage.
static const llvm::DenseSet<OverloadedOperatorKind> OverloadedOperators{
OverloadedOperatorKind::OO_New,
OverloadedOperatorKind::OO_Array_New,
OverloadedOperatorKind::OO_Delete,
OverloadedOperatorKind::OO_Array_Delete,
};
return OverloadedOperators.contains(Node.getOverloadedOperator());
}
AST_POLYMORPHIC_MATCHER(isExplicitlyExternC,
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
VarDecl)) {
return Finder->getASTContext().getLangOpts().CPlusPlus && Node.isExternC();
}
AST_MATCHER(TagDecl, hasNameForLinkage) { return Node.hasNameForLinkage(); }
AST_MATCHER(CXXRecordDecl, isExplicitTemplateInstantiation) {
return Node.getTemplateSpecializationKind() ==
TSK_ExplicitInstantiationDefinition;
}
} // namespace
UseInternalLinkageCheck::UseInternalLinkageCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
FixMode(Options.get("FixMode", FixModeKind::UseStatic)),
AnalyzeFunctions(Options.get("AnalyzeFunctions", true)),
AnalyzeVariables(Options.get("AnalyzeVariables", true)),
AnalyzeTypes(Options.get("AnalyzeTypes", true)) {
if (!AnalyzeFunctions && !AnalyzeVariables && !AnalyzeTypes)
configurationDiag(
"the 'misc-use-internal-linkage' check will not perform any "
"analysis because its 'AnalyzeFunctions', 'AnalyzeVariables', "
"and 'AnalyzeTypes' options have all been set to false");
}
void UseInternalLinkageCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "FixMode", FixMode);
Options.store(Opts, "AnalyzeFunctions", AnalyzeFunctions);
Options.store(Opts, "AnalyzeVariables", AnalyzeVariables);
Options.store(Opts, "AnalyzeTypes", AnalyzeTypes);
}
void UseInternalLinkageCheck::registerMatchers(MatchFinder *Finder) {
const auto Common =
allOf(isFirstDecl(), isAllRedeclsInMainFile(&getHeaderFileExtensions()),
unless(anyOf(isInAnonymousNamespace(), isInImportableModuleUnit(),
hasAncestor(decl(friendDecl())))));
if (AnalyzeFunctions)
Finder->addMatcher(
functionDecl(
Common, hasBody(),
unless(anyOf(
isExplicitlyExternC(), isStaticStorageClass(),
isExternStorageClass(), isExplicitTemplateSpecialization(),
cxxMethodDecl(), isConsteval(),
isAllocationOrDeallocationOverloadedFunction(), isMain())))
.bind("fn"),
this);
if (AnalyzeVariables)
Finder->addMatcher(
varDecl(Common, hasGlobalStorage(),
unless(anyOf(isExplicitlyExternC(), isStaticStorageClass(),
isExternStorageClass(),
isExplicitTemplateSpecialization(),
hasThreadStorageDuration())))
.bind("var"),
this);
if (getLangOpts().CPlusPlus && AnalyzeTypes)
Finder->addMatcher(
tagDecl(Common, isDefinition(), hasNameForLinkage(),
hasDeclContext(anyOf(translationUnitDecl(), namespaceDecl())),
unless(anyOf(
classTemplatePartialSpecializationDecl(),
cxxRecordDecl(anyOf(isExplicitTemplateSpecialization(),
isExplicitTemplateInstantiation())))))
.bind("tag"),
this);
}
static constexpr StringRef Message =
"%0 %1 can be made static %select{|or moved into an anonymous namespace }2"
"to enforce internal linkage";
void UseInternalLinkageCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("fn")) {
const DiagnosticBuilder DB = diag(FD->getLocation(), Message)
<< "function" << FD << getLangOpts().CPlusPlus;
const SourceLocation FixLoc = FD->getInnerLocStart();
if (FixLoc.isInvalid() || FixLoc.isMacroID())
return;
if (FixMode == FixModeKind::UseStatic)
DB << FixItHint::CreateInsertion(FixLoc, "static ");
return;
}
if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var")) {
// In C++, const variables at file scope have implicit internal linkage,
// so we should not warn there. This is not the case in C.
// https://eel.is/c++draft/diff#basic-3
if (getLangOpts().CPlusPlus && VD->getType().isConstQualified())
return;
const DiagnosticBuilder DB = diag(VD->getLocation(), Message)
<< "variable" << VD << getLangOpts().CPlusPlus;
const SourceLocation FixLoc = VD->getInnerLocStart();
if (FixLoc.isInvalid() || FixLoc.isMacroID())
return;
if (FixMode == FixModeKind::UseStatic)
DB << FixItHint::CreateInsertion(FixLoc, "static ");
return;
}
if (const auto *TD = Result.Nodes.getNodeAs<TagDecl>("tag")) {
diag(TD->getLocation(), "%0 %1 can be moved into an anonymous namespace "
"to enforce internal linkage")
<< TD->getKindName() << TD;
return;
}
llvm_unreachable("");
}
} // namespace clang::tidy::misc