brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.0 KiB · c10ee1d Raw
137 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 "DefinitionsInHeadersCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::misc {16 17namespace {18 19AST_MATCHER_P(NamedDecl, usesHeaderFileExtension, FileExtensionsSet,20              HeaderFileExtensions) {21  return utils::isExpansionLocInHeaderFile(22      Node.getBeginLoc(), Finder->getASTContext().getSourceManager(),23      HeaderFileExtensions);24}25 26} // namespace27 28DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(StringRef Name,29                                                     ClangTidyContext *Context)30    : ClangTidyCheck(Name, Context),31      HeaderFileExtensions(Context->getHeaderFileExtensions()) {}32 33void DefinitionsInHeadersCheck::registerMatchers(MatchFinder *Finder) {34  auto DefinitionMatcher =35      anyOf(functionDecl(isDefinition(), unless(isDeleted())),36            varDecl(isDefinition()));37  Finder->addMatcher(namedDecl(DefinitionMatcher,38                               usesHeaderFileExtension(HeaderFileExtensions))39                         .bind("name-decl"),40                     this);41}42 43void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {44  // Don't run the check in failing TUs.45  if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())46    return;47 48  // C++ [basic.def.odr] p6:49  // There can be more than one definition of a class type, enumeration type,50  // inline function with external linkage, class template, non-static function51  // template, static data member of a class template, member function of a52  // class template, or template specialization for which some template53  // parameters are not specifiedin a program provided that each definition54  // appears in a different translation unit, and provided the definitions55  // satisfy the following requirements.56  const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");57  assert(ND);58  if (ND->isInvalidDecl())59    return;60 61  // Internal linkage variable definitions are ignored for now:62  //   const int a = 1;63  //   static int b = 1;64  //   namespace { int c = 1; }65  //66  // Although these might also cause ODR violations, we can be less certain and67  // should try to keep the false-positive rate down.68  if (!ND->hasExternalFormalLinkage() || ND->isInAnonymousNamespace())69    return;70 71  if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {72    // Inline functions are allowed.73    if (FD->isInlined())74      return;75    // Function templates are allowed.76    if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)77      return;78    // Ignore instantiated functions.79    if (FD->isTemplateInstantiation())80      return;81    // Member function of a class template and member function of a nested class82    // in a class template are allowed.83    if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {84      const auto *DC = MD->getDeclContext();85      while (DC->isRecord()) {86        if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {87          if (isa<ClassTemplatePartialSpecializationDecl>(RD))88            return;89          if (RD->getDescribedClassTemplate())90            return;91        }92        DC = DC->getParent();93      }94    }95 96    const bool IsFullSpec =97        FD->getTemplateSpecializationKind() != TSK_Undeclared;98    diag(FD->getLocation(),99         "%select{function|full function template specialization}0 %1 defined "100         "in a header file; function definitions in header files can lead to "101         "ODR violations")102        << IsFullSpec << FD;103    // inline is not allowed for main function.104    if (FD->isMain())105      return;106    diag(FD->getLocation(), "mark the definition as 'inline'",107         DiagnosticIDs::Note)108        << FixItHint::CreateInsertion(FD->getInnerLocStart(), "inline ");109  } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {110    // C++14 variable templates are allowed.111    if (VD->getDescribedVarTemplate())112      return;113    // Static data members of a class template are allowed.114    if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())115      return;116    // Ignore instantiated static data members of classes.117    if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))118      return;119    // Ignore variable definition within function scope.120    if (VD->hasLocalStorage() || VD->isStaticLocal())121      return;122    // Ignore inline variables.123    if (VD->isInline())124      return;125    // Ignore partial specializations.126    if (isa<VarTemplatePartialSpecializationDecl>(VD))127      return;128 129    diag(VD->getLocation(),130         "variable %0 defined in a header file; "131         "variable definitions in header files can lead to ODR violations")132        << VD;133  }134}135 136} // namespace clang::tidy::misc137