clang-tools  11.0.0
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  // FIXME: Find a more suitable way to handle invalid configuration
40  // options.
41  llvm::errs() << "Invalid header file extension: "
42  << RawStringHeaderFileExtensions << "\n";
43  }
44 }
45 
48  Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
49  Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
50 }
51 
53  auto DefinitionMatcher =
54  anyOf(functionDecl(isDefinition(), unless(isDeleted())),
55  varDecl(isDefinition()));
56  if (UseHeaderFileExtension) {
57  Finder->addMatcher(namedDecl(DefinitionMatcher,
58  usesHeaderFileExtension(HeaderFileExtensions))
59  .bind("name-decl"),
60  this);
61  } else {
62  Finder->addMatcher(
63  namedDecl(DefinitionMatcher,
64  anyOf(usesHeaderFileExtension(HeaderFileExtensions),
65  unless(isExpansionInMainFile())))
66  .bind("name-decl"),
67  this);
68  }
69 }
70 
71 void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
72  // Don't run the check in failing TUs.
73  if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
74  return;
75 
76  // C++ [basic.def.odr] p6:
77  // There can be more than one definition of a class type, enumeration type,
78  // inline function with external linkage, class template, non-static function
79  // template, static data member of a class template, member function of a
80  // class template, or template specialization for which some template
81  // parameters are not specifiedin a program provided that each definition
82  // appears in a different translation unit, and provided the definitions
83  // satisfy the following requirements.
84  const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
85  assert(ND);
86  if (ND->isInvalidDecl())
87  return;
88 
89  // Internal linkage variable definitions are ignored for now:
90  // const int a = 1;
91  // static int b = 1;
92  //
93  // Although these might also cause ODR violations, we can be less certain and
94  // should try to keep the false-positive rate down.
95  //
96  // FIXME: Should declarations in anonymous namespaces get the same treatment
97  // as static / const declarations?
98  if (!ND->hasExternalFormalLinkage() && !ND->isInAnonymousNamespace())
99  return;
100 
101  if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
102  // Inline functions are allowed.
103  if (FD->isInlined())
104  return;
105  // Function templates are allowed.
106  if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
107  return;
108  // Ignore instantiated functions.
109  if (FD->isTemplateInstantiation())
110  return;
111  // Member function of a class template and member function of a nested class
112  // in a class template are allowed.
113  if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
114  const auto *DC = MD->getDeclContext();
115  while (DC->isRecord()) {
116  if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
117  if (isa<ClassTemplatePartialSpecializationDecl>(RD))
118  return;
119  if (RD->getDescribedClassTemplate())
120  return;
121  }
122  DC = DC->getParent();
123  }
124  }
125 
126  bool IsFullSpec = FD->getTemplateSpecializationKind() != TSK_Undeclared;
127  diag(FD->getLocation(),
128  "%select{function|full function template specialization}0 %1 defined "
129  "in a header file; function definitions in header files can lead to "
130  "ODR violations")
131  << IsFullSpec << FD;
132  diag(FD->getLocation(), /*FixDescription=*/"make as 'inline'",
133  DiagnosticIDs::Note)
134  << FixItHint::CreateInsertion(FD->getInnerLocStart(), "inline ");
135  } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
136  // C++14 variable templates are allowed.
137  if (VD->getDescribedVarTemplate())
138  return;
139  // Static data members of a class template are allowed.
140  if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
141  return;
142  // Ignore instantiated static data members of classes.
143  if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
144  return;
145  // Ignore variable definition within function scope.
146  if (VD->hasLocalStorage() || VD->isStaticLocal())
147  return;
148  // Ignore inline variables.
149  if (VD->isInline())
150  return;
151 
152  diag(VD->getLocation(),
153  "variable %0 defined in a header file; "
154  "variable definitions in header files can lead to ODR violations")
155  << VD;
156  }
157 }
158 
159 } // namespace misc
160 } // namespace tidy
161 } // namespace clang
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:46
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:114
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:71
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::readability::AST_MATCHER_P
AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl, ast_matchers::internal::Matcher< CXXMethodDecl >, InnerMatcher)
Definition: ConvertMemberFunctionsToStatic.cpp:53
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:471
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:76
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
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:55
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:52
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:152
clang::tidy::utils::defaultHeaderFileExtensions
StringRef defaultHeaderFileExtensions()
Returns recommended default value for the list of header file extensions.
Definition: FileExtensionsUtils.h:38
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:111
clang::tidy::utils::FileExtensionsSet
llvm::SmallSet< llvm::StringRef, 5 > FileExtensionsSet
Definition: FileExtensionsUtils.h:22