blob: e6d7567ec93f05bd79d8d0117b5c8a20c836fd04 [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
Haojian Wuc2d75772016-02-05 11:23:59 +000022AST_MATCHER_P(NamedDecl, usesHeaderFileExtension,
23 utils::HeaderFileExtensionsSet, HeaderFileExtensions) {
24 return utils::isExpansionLocInHeaderFile(
25 Node.getLocStart(), Finder->getASTContext().getSourceManager(),
26 HeaderFileExtensions);
Alexander Kornienkob816ba02016-01-08 16:37:11 +000027}
28
29} // namespace
30
Haojian Wuc2d75772016-02-05 11:23:59 +000031DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(StringRef Name,
32 ClangTidyContext *Context)
33 : ClangTidyCheck(Name, Context),
34 UseHeaderFileExtension(Options.get("UseHeaderFileExtension", true)),
35 RawStringHeaderFileExtensions(
36 Options.getLocalOrGlobal("HeaderFileExtensions", ",h,hh,hpp,hxx")) {
37 if (!utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
38 HeaderFileExtensions,
39 ',')) {
40 // FIXME: Find a more suitable way to handle invalid configuration
41 // options.
42 llvm::errs() << "Invalid header file extension: "
43 << RawStringHeaderFileExtensions << "\n";
44 }
45}
Alexander Kornienkob816ba02016-01-08 16:37:11 +000046
47void DefinitionsInHeadersCheck::storeOptions(
48 ClangTidyOptions::OptionMap &Opts) {
49 Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
Haojian Wuc2d75772016-02-05 11:23:59 +000050 Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
Alexander Kornienkob816ba02016-01-08 16:37:11 +000051}
52
53void DefinitionsInHeadersCheck::registerMatchers(MatchFinder *Finder) {
54 if (UseHeaderFileExtension) {
55 Finder->addMatcher(
56 namedDecl(anyOf(functionDecl(isDefinition()), varDecl(isDefinition())),
Haojian Wuc2d75772016-02-05 11:23:59 +000057 usesHeaderFileExtension(HeaderFileExtensions))
58 .bind("name-decl"),
Alexander Kornienkob816ba02016-01-08 16:37:11 +000059 this);
60 } else {
61 Finder->addMatcher(
62 namedDecl(anyOf(functionDecl(isDefinition()), varDecl(isDefinition())),
Haojian Wuc2d75772016-02-05 11:23:59 +000063 anyOf(usesHeaderFileExtension(HeaderFileExtensions),
64 unless(isExpansionInMainFile())))
65 .bind("name-decl"),
Alexander Kornienkob816ba02016-01-08 16:37:11 +000066 this);
67 }
68}
69
70void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
71 // C++ [basic.def.odr] p6:
72 // There can be more than one definition of a class type, enumeration type,
73 // inline function with external linkage, class template, non-static function
74 // template, static data member of a class template, member function of a
75 // class template, or template specialization for which some template
76 // parameters are not specifiedin a program provided that each definition
77 // appears in a different translation unit, and provided the definitions
78 // satisfy the following requirements.
79 const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
80 assert(ND);
81
82 // Internal linkage variable definitions are ignored for now:
83 // const int a = 1;
84 // static int b = 1;
85 //
86 // Although these might also cause ODR violations, we can be less certain and
87 // should try to keep the false-positive rate down.
88 if (ND->getLinkageInternal() == InternalLinkage)
89 return;
90
91 if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
92 // Inline functions are allowed.
93 if (FD->isInlined())
94 return;
95 // Function templates are allowed.
96 if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
97 return;
98 // Function template full specialization is prohibited in header file.
99 if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
100 return;
101 // Member function of a class template and member function of a nested class
102 // in a class template are allowed.
103 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
104 const auto *DC = MD->getDeclContext();
105 while (DC->isRecord()) {
Haojian Wu29634fe2016-02-03 12:10:27 +0000106 if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
107 if (isa<ClassTemplatePartialSpecializationDecl>(RD))
108 return;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000109 if (RD->getDescribedClassTemplate())
110 return;
Haojian Wu29634fe2016-02-03 12:10:27 +0000111 }
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000112 DC = DC->getParent();
113 }
114 }
115
116 diag(FD->getLocation(),
117 "function '%0' defined in a header file; "
118 "function definitions in header files can lead to ODR violations")
119 << FD->getNameInfo().getName().getAsString()
120 << FixItHint::CreateInsertion(FD->getSourceRange().getBegin(),
121 "inline ");
122 } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
123 // Static data members of a class template are allowed.
124 if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
125 return;
126 if (VD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
127 return;
128 // Ignore variable definition within function scope.
129 if (VD->hasLocalStorage() || VD->isStaticLocal())
130 return;
131
132 diag(VD->getLocation(),
133 "variable '%0' defined in a header file; "
134 "variable definitions in header files can lead to ODR violations")
135 << VD->getName();
136 }
137}
138
139} // namespace misc
140} // namespace tidy
141} // namespace clang