brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.2 KiB · bad51c6 Raw
174 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//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::tidy43 44namespace clang::tidy::misc {45 46static bool isInMainFile(SourceLocation L, SourceManager &SM,47                         const FileExtensionsSet &HeaderFileExtensions) {48  for (;;) {49    if (utils::isExpansionLocInHeaderFile(L, SM, HeaderFileExtensions))50      return false;51    if (SM.isInMainFile(L))52      return true;53    // not in header file but not in main file54    L = SM.getIncludeLoc(SM.getFileID(L));55    if (L.isValid())56      continue;57    // Conservative about the unknown58    return false;59  }60}61 62namespace {63 64AST_MATCHER(Decl, isFirstDecl) { return Node.isFirstDecl(); }65 66AST_MATCHER(FunctionDecl, hasBody) { return Node.hasBody(); }67 68AST_MATCHER_P(Decl, isAllRedeclsInMainFile, FileExtensionsSet,69              HeaderFileExtensions) {70  return llvm::all_of(Node.redecls(), [&](const Decl *D) {71    return isInMainFile(D->getLocation(),72                        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 to86  // 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 to89  // 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(Node.getOverloadedOperator());97}98 99} // namespace100 101UseInternalLinkageCheck::UseInternalLinkageCheck(StringRef Name,102                                                 ClangTidyContext *Context)103    : ClangTidyCheck(Name, Context),104      HeaderFileExtensions(Context->getHeaderFileExtensions()),105      FixMode(Options.get("FixMode", FixModeKind::UseStatic)) {}106 107void UseInternalLinkageCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {108  Options.store(Opts, "FixMode", FixMode);109}110 111void UseInternalLinkageCheck::registerMatchers(MatchFinder *Finder) {112  auto Common =113      allOf(isFirstDecl(), isAllRedeclsInMainFile(HeaderFileExtensions),114            unless(anyOf(115                // 1. internal linkage116                isStaticStorageClass(), isInAnonymousNamespace(),117                // 2. explicit external linkage118                isExternStorageClass(), isExternC(),119                // 3. template120                isExplicitTemplateSpecialization(),121                hasAncestor(decl(anyOf(122                    // 4. friend123                    friendDecl(),124                    // 5. module export decl125                    exportDecl()))))));126  Finder->addMatcher(127      functionDecl(Common, hasBody(),128                   unless(anyOf(cxxMethodDecl(), isConsteval(),129                                isAllocationOrDeallocationOverloadedFunction(),130                                isMain())))131          .bind("fn"),132      this);133  Finder->addMatcher(134      varDecl(Common, hasGlobalStorage(), unless(hasThreadStorageDuration()))135          .bind("var"),136      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>("fn")) {145    const DiagnosticBuilder DB = diag(FD->getLocation(), Message)146                                 << "function" << FD;147    const SourceLocation FixLoc = FD->getInnerLocStart();148    if (FixLoc.isInvalid() || FixLoc.isMacroID())149      return;150    if (FixMode == FixModeKind::UseStatic)151      DB << FixItHint::CreateInsertion(FixLoc, "static ");152    return;153  }154  if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var")) {155    // In C++, const variables at file scope have implicit internal linkage,156    // so we should not warn there. This is not the case in C.157    // https://eel.is/c++draft/diff#basic-3158    if (getLangOpts().CPlusPlus && VD->getType().isConstQualified())159      return;160 161    const DiagnosticBuilder DB = diag(VD->getLocation(), Message)162                                 << "variable" << VD;163    const SourceLocation FixLoc = VD->getInnerLocStart();164    if (FixLoc.isInvalid() || FixLoc.isMacroID())165      return;166    if (FixMode == FixModeKind::UseStatic)167      DB << FixItHint::CreateInsertion(FixLoc, "static ");168    return;169  }170  llvm_unreachable("");171}172 173} // namespace clang::tidy::misc174