1 //===--- DefinitionsInHeadersCheck.cpp - clang-tidy------------------------===// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "DefinitionsInHeadersCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 13 using namespace clang::ast_matchers; 14 15 namespace clang::tidy::misc { 16 17 namespace { 18 19 AST_MATCHER_P(NamedDecl, usesHeaderFileExtension, FileExtensionsSet, 20 HeaderFileExtensions) { 21 return utils::isExpansionLocInHeaderFile( 22 Node.getBeginLoc(), Finder->getASTContext().getSourceManager(), 23 HeaderFileExtensions); 24 } 25 26 } // namespace 27 28 DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(StringRef Name, 29 ClangTidyContext *Context) 30 : ClangTidyCheck(Name, Context), 31 HeaderFileExtensions(Context->getHeaderFileExtensions()) {} 32 33 void 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 43 void 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 function 51 // template, static data member of a class template, member function of a 52 // class template, or template specialization for which some template 53 // parameters are not specifiedin a program provided that each definition 54 // appears in a different translation unit, and provided the definitions 55 // 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 and 67 // 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 class 82 // 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 bool IsFullSpec = FD->getTemplateSpecializationKind() != TSK_Undeclared; 97 diag(FD->getLocation(), 98 "%select{function|full function template specialization}0 %1 defined " 99 "in a header file; function definitions in header files can lead to " 100 "ODR violations") 101 << IsFullSpec << FD; 102 // inline is not allowed for main function. 103 if (FD->isMain()) 104 return; 105 diag(FD->getLocation(), "mark the definition as 'inline'", 106 DiagnosticIDs::Note) 107 << FixItHint::CreateInsertion(FD->getInnerLocStart(), "inline "); 108 } else if (const auto *VD = dyn_cast<VarDecl>(ND)) { 109 // C++14 variable templates are allowed. 110 if (VD->getDescribedVarTemplate()) 111 return; 112 // Static data members of a class template are allowed. 113 if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember()) 114 return; 115 // Ignore instantiated static data members of classes. 116 if (isTemplateInstantiation(VD->getTemplateSpecializationKind())) 117 return; 118 // Ignore variable definition within function scope. 119 if (VD->hasLocalStorage() || VD->isStaticLocal()) 120 return; 121 // Ignore inline variables. 122 if (VD->isInline()) 123 return; 124 // Ignore partial specializations. 125 if (isa<VarTemplatePartialSpecializationDecl>(VD)) 126 return; 127 128 diag(VD->getLocation(), 129 "variable %0 defined in a header file; " 130 "variable definitions in header files can lead to ODR violations") 131 << VD; 132 } 133 } 134 135 } // namespace clang::tidy::misc 136