blob: 29fa0f6d5bf0a82f2f8706f004a7af4071df1296 [file] [log] [blame]
Alexander Kornienkob816ba02016-01-08 16:37:11 +00001//===--- 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
10#include "DefinitionsInHeadersCheck.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13
14using namespace clang::ast_matchers;
15
16namespace clang {
17namespace tidy {
18namespace misc {
19
20namespace {
21
22AST_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
33DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(
34 StringRef Name, ClangTidyContext *Context)
35 : ClangTidyCheck(Name, Context),
36 UseHeaderFileExtension(Options.get("UseHeaderFileExtension", true)) {}
37
38void DefinitionsInHeadersCheck::storeOptions(
39 ClangTidyOptions::OptionMap &Opts) {
40 Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
41}
42
43void DefinitionsInHeadersCheck::registerMatchers(MatchFinder *Finder) {
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
58void 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