DefinitionsInHeadersCheck.cpp 6.1 KB

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