blob: 77ca26fdb34f6909e5bcf640f068beded8b4e59d [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) {
Haojian Wu3d1d0762016-02-08 16:05:39 +000054 if (!getLangOpts().CPlusPlus)
55 return;
Haojian Wuba992cf2016-06-07 08:55:38 +000056 auto DefinitionMatcher =
57 anyOf(functionDecl(isDefinition(), unless(isDeleted())),
58 varDecl(isDefinition()));
Alexander Kornienkob816ba02016-01-08 16:37:11 +000059 if (UseHeaderFileExtension) {
Haojian Wuba992cf2016-06-07 08:55:38 +000060 Finder->addMatcher(namedDecl(DefinitionMatcher,
61 usesHeaderFileExtension(HeaderFileExtensions))
62 .bind("name-decl"),
63 this);
Alexander Kornienkob816ba02016-01-08 16:37:11 +000064 } else {
65 Finder->addMatcher(
Haojian Wuba992cf2016-06-07 08:55:38 +000066 namedDecl(DefinitionMatcher,
Haojian Wuc2d75772016-02-05 11:23:59 +000067 anyOf(usesHeaderFileExtension(HeaderFileExtensions),
68 unless(isExpansionInMainFile())))
69 .bind("name-decl"),
Alexander Kornienkob816ba02016-01-08 16:37:11 +000070 this);
71 }
72}
73
74void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
Haojian Wue5d27792016-06-27 08:04:01 +000075 // Don't run the check in failing TUs.
76 if (Result.Context->getDiagnostics().hasErrorOccurred())
77 return;
78
Alexander Kornienkob816ba02016-01-08 16:37:11 +000079 // C++ [basic.def.odr] p6:
80 // There can be more than one definition of a class type, enumeration type,
81 // inline function with external linkage, class template, non-static function
82 // template, static data member of a class template, member function of a
83 // class template, or template specialization for which some template
84 // parameters are not specifiedin a program provided that each definition
85 // appears in a different translation unit, and provided the definitions
86 // satisfy the following requirements.
87 const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
88 assert(ND);
Haojian Wu3d1d0762016-02-08 16:05:39 +000089 if (ND->isInvalidDecl())
90 return;
Alexander Kornienkob816ba02016-01-08 16:37:11 +000091
92 // Internal linkage variable definitions are ignored for now:
93 // const int a = 1;
94 // static int b = 1;
95 //
96 // Although these might also cause ODR violations, we can be less certain and
97 // should try to keep the false-positive rate down.
98 if (ND->getLinkageInternal() == InternalLinkage)
99 return;
100
101 if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
102 // Inline functions are allowed.
103 if (FD->isInlined())
104 return;
105 // Function templates are allowed.
106 if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
107 return;
108 // Function template full specialization is prohibited in header file.
109 if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
110 return;
111 // Member function of a class template and member function of a nested class
112 // in a class template are allowed.
113 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
114 const auto *DC = MD->getDeclContext();
115 while (DC->isRecord()) {
Haojian Wu29634fe2016-02-03 12:10:27 +0000116 if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
117 if (isa<ClassTemplatePartialSpecializationDecl>(RD))
118 return;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000119 if (RD->getDescribedClassTemplate())
120 return;
Haojian Wu29634fe2016-02-03 12:10:27 +0000121 }
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000122 DC = DC->getParent();
123 }
124 }
125
126 diag(FD->getLocation(),
Benjamin Kramera62e2232016-04-07 14:55:25 +0000127 "function %0 defined in a header file; "
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000128 "function definitions in header files can lead to ODR violations")
Haojian Wu0b067c12016-07-13 13:55:29 +0000129 << FD << FixItHint::CreateInsertion(
130 FD->getReturnTypeSourceRange().getBegin(), "inline ");
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000131 } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
132 // Static data members of a class template are allowed.
133 if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
134 return;
135 if (VD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
136 return;
137 // Ignore variable definition within function scope.
138 if (VD->hasLocalStorage() || VD->isStaticLocal())
139 return;
140
141 diag(VD->getLocation(),
Benjamin Kramera62e2232016-04-07 14:55:25 +0000142 "variable %0 defined in a header file; "
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000143 "variable definitions in header files can lead to ODR violations")
Benjamin Kramera62e2232016-04-07 14:55:25 +0000144 << VD;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000145 }
146}
147
148} // namespace misc
149} // namespace tidy
150} // namespace clang