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