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