clang-tools  3.8.0
DefinitionsInHeadersCheck.cpp
Go to the documentation of this file.
1 //===--- DefinitionsInHeadersCheck.cpp - clang-tidy------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang {
17 namespace tidy {
18 namespace misc {
19 
20 namespace {
21 
22 AST_MATCHER(NamedDecl, isHeaderFileExtension) {
23  SourceManager& SM = Finder->getASTContext().getSourceManager();
24  SourceLocation ExpansionLoc = SM.getExpansionLoc(Node.getLocStart());
25  StringRef Filename = SM.getFilename(ExpansionLoc);
26  return Filename.endswith(".h") || Filename.endswith(".hh") ||
27  Filename.endswith(".hpp") || Filename.endswith(".hxx") ||
28  llvm::sys::path::extension(Filename).empty();
29 }
30 
31 } // namespace
32 
33 DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(
34  StringRef Name, ClangTidyContext *Context)
35  : ClangTidyCheck(Name, Context),
36  UseHeaderFileExtension(Options.get("UseHeaderFileExtension", true)) {}
37 
40  Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
41 }
42 
44  if (UseHeaderFileExtension) {
45  Finder->addMatcher(
46  namedDecl(anyOf(functionDecl(isDefinition()), varDecl(isDefinition())),
47  isHeaderFileExtension()).bind("name-decl"),
48  this);
49  } else {
50  Finder->addMatcher(
51  namedDecl(anyOf(functionDecl(isDefinition()), varDecl(isDefinition())),
52  anyOf(isHeaderFileExtension(),
53  unless(isExpansionInMainFile()))).bind("name-decl"),
54  this);
55  }
56 }
57 
58 void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
59  // C++ [basic.def.odr] p6:
60  // There can be more than one definition of a class type, enumeration type,
61  // inline function with external linkage, class template, non-static function
62  // template, static data member of a class template, member function of a
63  // class template, or template specialization for which some template
64  // parameters are not specifiedin a program provided that each definition
65  // appears in a different translation unit, and provided the definitions
66  // satisfy the following requirements.
67  const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
68  assert(ND);
69 
70  // Internal linkage variable definitions are ignored for now:
71  // const int a = 1;
72  // static int b = 1;
73  //
74  // Although these might also cause ODR violations, we can be less certain and
75  // should try to keep the false-positive rate down.
76  if (ND->getLinkageInternal() == InternalLinkage)
77  return;
78 
79  if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
80  // Inline functions are allowed.
81  if (FD->isInlined())
82  return;
83  // Function templates are allowed.
84  if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
85  return;
86  // Function template full specialization is prohibited in header file.
87  if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
88  return;
89  // Member function of a class template and member function of a nested class
90  // in a class template are allowed.
91  if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
92  const auto *DC = MD->getDeclContext();
93  while (DC->isRecord()) {
94  if (const auto *RD = dyn_cast<CXXRecordDecl>(DC))
95  if (RD->getDescribedClassTemplate())
96  return;
97  DC = DC->getParent();
98  }
99  }
100 
101  diag(FD->getLocation(),
102  "function '%0' defined in a header file; "
103  "function definitions in header files can lead to ODR violations")
104  << FD->getNameInfo().getName().getAsString()
105  << FixItHint::CreateInsertion(FD->getSourceRange().getBegin(),
106  "inline ");
107  } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
108  // Static data members of a class template are allowed.
109  if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
110  return;
111  if (VD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
112  return;
113  // Ignore variable definition within function scope.
114  if (VD->hasLocalStorage() || VD->isStaticLocal())
115  return;
116 
117  diag(VD->getLocation(),
118  "variable '%0' defined in a header file; "
119  "variable definitions in header files can lead to ODR violations")
120  << VD->getName();
121  }
122 }
123 
124 } // namespace misc
125 } // namespace tidy
126 } // namespace clang
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
StringHandle Name
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
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.
Definition: ClangTidy.h:102
AST_MATCHER(Stmt, isInsideOfRangeBeginEndStmt)
SourceManager & SM
std::string Filename
Filename as a string.
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: ClangTidy.cpp:344
std::map< std::string, std::string > OptionMap
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register ASTMatchers with Finder.
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
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's name.
Definition: ClangTidy.cpp:323
const NamedDecl * Result
Definition: USRFinder.cpp:121