clang-tools  14.0.0git
DefinitionsInHeadersCheck.cpp
Go to the documentation of this file.
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 
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 
13 using namespace clang::ast_matchers;
14 
15 namespace clang {
16 namespace tidy {
17 namespace misc {
18 
19 namespace {
20 
21 AST_MATCHER_P(NamedDecl, usesHeaderFileExtension, utils::FileExtensionsSet,
22  HeaderFileExtensions) {
24  Node.getBeginLoc(), Finder->getASTContext().getSourceManager(),
25  HeaderFileExtensions);
26 }
27 
28 } // namespace
29 
30 DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(StringRef Name,
31  ClangTidyContext *Context)
32  : ClangTidyCheck(Name, Context),
33  UseHeaderFileExtension(Options.get("UseHeaderFileExtension", true)),
34  RawStringHeaderFileExtensions(Options.getLocalOrGlobal(
35  "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) {
36  if (!utils::parseFileExtensions(RawStringHeaderFileExtensions,
37  HeaderFileExtensions,
39  this->configurationDiag("Invalid header file extension: '%0'")
40  << RawStringHeaderFileExtensions;
41  }
42 }
43 
46  Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
47  Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
48 }
49 
51  auto DefinitionMatcher =
52  anyOf(functionDecl(isDefinition(), unless(isDeleted())),
53  varDecl(isDefinition()));
54  if (UseHeaderFileExtension) {
55  Finder->addMatcher(namedDecl(DefinitionMatcher,
56  usesHeaderFileExtension(HeaderFileExtensions))
57  .bind("name-decl"),
58  this);
59  } else {
60  Finder->addMatcher(
61  namedDecl(DefinitionMatcher,
62  anyOf(usesHeaderFileExtension(HeaderFileExtensions),
63  unless(isExpansionInMainFile())))
64  .bind("name-decl"),
65  this);
66  }
67 }
68 
69 void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
70  // Don't run the check in failing TUs.
71  if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
72  return;
73 
74  // C++ [basic.def.odr] p6:
75  // There can be more than one definition of a class type, enumeration type,
76  // inline function with external linkage, class template, non-static function
77  // template, static data member of a class template, member function of a
78  // class template, or template specialization for which some template
79  // parameters are not specifiedin a program provided that each definition
80  // appears in a different translation unit, and provided the definitions
81  // satisfy the following requirements.
82  const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
83  assert(ND);
84  if (ND->isInvalidDecl())
85  return;
86 
87  // Internal linkage variable definitions are ignored for now:
88  // const int a = 1;
89  // static int b = 1;
90  //
91  // Although these might also cause ODR violations, we can be less certain and
92  // should try to keep the false-positive rate down.
93  //
94  // FIXME: Should declarations in anonymous namespaces get the same treatment
95  // as static / const declarations?
96  if (!ND->hasExternalFormalLinkage() && !ND->isInAnonymousNamespace())
97  return;
98 
99  if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
100  // Inline functions are allowed.
101  if (FD->isInlined())
102  return;
103  // Function templates are allowed.
104  if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
105  return;
106  // Ignore instantiated functions.
107  if (FD->isTemplateInstantiation())
108  return;
109  // Member function of a class template and member function of a nested class
110  // in a class template are allowed.
111  if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
112  const auto *DC = MD->getDeclContext();
113  while (DC->isRecord()) {
114  if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
115  if (isa<ClassTemplatePartialSpecializationDecl>(RD))
116  return;
117  if (RD->getDescribedClassTemplate())
118  return;
119  }
120  DC = DC->getParent();
121  }
122  }
123 
124  bool IsFullSpec = FD->getTemplateSpecializationKind() != TSK_Undeclared;
125  diag(FD->getLocation(),
126  "%select{function|full function template specialization}0 %1 defined "
127  "in a header file; function definitions in header files can lead to "
128  "ODR violations")
129  << IsFullSpec << FD;
130  // inline is not allowed for main function.
131  if (FD->isMain())
132  return;
133  diag(FD->getLocation(), /*FixDescription=*/"make as 'inline'",
134  DiagnosticIDs::Note)
135  << FixItHint::CreateInsertion(FD->getInnerLocStart(), "inline ");
136  } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
137  // C++14 variable templates are allowed.
138  if (VD->getDescribedVarTemplate())
139  return;
140  // Static data members of a class template are allowed.
141  if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
142  return;
143  // Ignore instantiated static data members of classes.
144  if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
145  return;
146  // Ignore variable definition within function scope.
147  if (VD->hasLocalStorage() || VD->isStaticLocal())
148  return;
149  // Ignore inline variables.
150  if (VD->isInline())
151  return;
152 
153  diag(VD->getLocation(),
154  "variable %0 defined in a header file; "
155  "variable definitions in header files can lead to ODR violations")
156  << VD;
157  }
158 }
159 
160 } // namespace misc
161 } // namespace tidy
162 } // namespace clang
clang::tidy::ClangTidyOptions::OptionMap
llvm::StringMap< ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:115
clang::doc::MD
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for MD output.")
clang::tidy::misc::DefinitionsInHeadersCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: DefinitionsInHeadersCheck.cpp:44
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:54
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::misc::DefinitionsInHeadersCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: DefinitionsInHeadersCheck.cpp:69
clang::tidy::utils::parseFileExtensions
bool parseFileExtensions(StringRef AllFileExtensions, FileExtensionsSet &FileExtensions, StringRef Delimiters)
Parses header file extensions from a semicolon-separated list.
Definition: FileExtensionsUtils.cpp:35
clang::tidy::utils::defaultFileExtensionDelimiters
StringRef defaultFileExtensionDelimiters()
Returns recommended default value for the list of file extension delimiters.
Definition: FileExtensionsUtils.h:48
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:416
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:72
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:28
DefinitionsInHeadersCheck.h
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:25
clang::tidy::utils::isExpansionLocInHeaderFile
bool isExpansionLocInHeaderFile(SourceLocation Loc, const SourceManager &SM, const FileExtensionsSet &HeaderFileExtensions)
Checks whether expansion location of Loc is in header file.
Definition: FileExtensionsUtils.cpp:17
clang::tidy::misc::DefinitionsInHeadersCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: DefinitionsInHeadersCheck.cpp:50
clang::tidy::ClangTidyCheck::configurationDiag
DiagnosticBuilder configurationDiag(StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning) const
Adds a diagnostic to report errors in the check's configuration.
Definition: ClangTidyCheck.cpp:36
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::ClangTidyCheck::OptionsView::store
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidyCheck.cpp:120
clang::tidy::utils::defaultHeaderFileExtensions
StringRef defaultHeaderFileExtensions()
Returns recommended default value for the list of header file extensions.
Definition: FileExtensionsUtils.h:38
clang::tidy::bugprone::AST_MATCHER_P
AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N)
Matches functions that have at least the specified amount of parameters.
Definition: EasilySwappableParametersCheck.cpp:1877
clang::tidy::utils::FileExtensionsSet
llvm::SmallSet< llvm::StringRef, 5 > FileExtensionsSet
Definition: FileExtensionsUtils.h:22