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