blob: e91cc9573f71e5f25a8fe25ea5e93fe78db9d407 [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) {
75 // C++ [basic.def.odr] p6:
76 // There can be more than one definition of a class type, enumeration type,
77 // inline function with external linkage, class template, non-static function
78 // template, static data member of a class template, member function of a
79 // class template, or template specialization for which some template
80 // parameters are not specifiedin a program provided that each definition
81 // appears in a different translation unit, and provided the definitions
82 // satisfy the following requirements.
83 const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
84 assert(ND);
Haojian Wu3d1d0762016-02-08 16:05:39 +000085 if (ND->isInvalidDecl())
86 return;
Alexander Kornienkob816ba02016-01-08 16:37:11 +000087
88 // Internal linkage variable definitions are ignored for now:
89 // const int a = 1;
90 // static int b = 1;
91 //
92 // Although these might also cause ODR violations, we can be less certain and
93 // should try to keep the false-positive rate down.
94 if (ND->getLinkageInternal() == InternalLinkage)
95 return;
96
97 if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
98 // Inline functions are allowed.
99 if (FD->isInlined())
100 return;
101 // Function templates are allowed.
102 if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
103 return;
104 // Function template full specialization is prohibited in header file.
105 if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
106 return;
107 // Member function of a class template and member function of a nested class
108 // in a class template are allowed.
109 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
110 const auto *DC = MD->getDeclContext();
111 while (DC->isRecord()) {
Haojian Wu29634fe2016-02-03 12:10:27 +0000112 if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
113 if (isa<ClassTemplatePartialSpecializationDecl>(RD))
114 return;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000115 if (RD->getDescribedClassTemplate())
116 return;
Haojian Wu29634fe2016-02-03 12:10:27 +0000117 }
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000118 DC = DC->getParent();
119 }
120 }
121
122 diag(FD->getLocation(),
Benjamin Kramera62e2232016-04-07 14:55:25 +0000123 "function %0 defined in a header file; "
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000124 "function definitions in header files can lead to ODR violations")
Benjamin Kramera62e2232016-04-07 14:55:25 +0000125 << FD << FixItHint::CreateInsertion(FD->getSourceRange().getBegin(),
126 "inline ");
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000127 } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
128 // Static data members of a class template are allowed.
129 if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
130 return;
131 if (VD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
132 return;
133 // Ignore variable definition within function scope.
134 if (VD->hasLocalStorage() || VD->isStaticLocal())
135 return;
136
137 diag(VD->getLocation(),
Benjamin Kramera62e2232016-04-07 14:55:25 +0000138 "variable %0 defined in a header file; "
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000139 "variable definitions in header files can lead to ODR violations")
Benjamin Kramera62e2232016-04-07 14:55:25 +0000140 << VD;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000141 }
142}
143
144} // namespace misc
145} // namespace tidy
146} // namespace clang