blob: 1ee97576c7ad12634b95e4c92bb8a44558e965da [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;
Alexander Kornienkob816ba02016-01-08 16:37:11 +000056 if (UseHeaderFileExtension) {
57 Finder->addMatcher(
58 namedDecl(anyOf(functionDecl(isDefinition()), varDecl(isDefinition())),
Haojian Wuc2d75772016-02-05 11:23:59 +000059 usesHeaderFileExtension(HeaderFileExtensions))
60 .bind("name-decl"),
Alexander Kornienkob816ba02016-01-08 16:37:11 +000061 this);
62 } else {
63 Finder->addMatcher(
64 namedDecl(anyOf(functionDecl(isDefinition()), varDecl(isDefinition())),
Haojian Wuc2d75772016-02-05 11:23:59 +000065 anyOf(usesHeaderFileExtension(HeaderFileExtensions),
66 unless(isExpansionInMainFile())))
67 .bind("name-decl"),
Alexander Kornienkob816ba02016-01-08 16:37:11 +000068 this);
69 }
70}
71
72void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
73 // C++ [basic.def.odr] p6:
74 // There can be more than one definition of a class type, enumeration type,
75 // inline function with external linkage, class template, non-static function
76 // template, static data member of a class template, member function of a
77 // class template, or template specialization for which some template
78 // parameters are not specifiedin a program provided that each definition
79 // appears in a different translation unit, and provided the definitions
80 // satisfy the following requirements.
81 const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
82 assert(ND);
Haojian Wu3d1d0762016-02-08 16:05:39 +000083 if (ND->isInvalidDecl())
84 return;
Alexander Kornienkob816ba02016-01-08 16:37:11 +000085
86 // Internal linkage variable definitions are ignored for now:
87 // const int a = 1;
88 // static int b = 1;
89 //
90 // Although these might also cause ODR violations, we can be less certain and
91 // should try to keep the false-positive rate down.
92 if (ND->getLinkageInternal() == InternalLinkage)
93 return;
94
95 if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
96 // Inline functions are allowed.
97 if (FD->isInlined())
98 return;
99 // Function templates are allowed.
100 if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
101 return;
102 // Function template full specialization is prohibited in header file.
103 if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
104 return;
105 // Member function of a class template and member function of a nested class
106 // in a class template are allowed.
107 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
108 const auto *DC = MD->getDeclContext();
109 while (DC->isRecord()) {
Haojian Wu29634fe2016-02-03 12:10:27 +0000110 if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
111 if (isa<ClassTemplatePartialSpecializationDecl>(RD))
112 return;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000113 if (RD->getDescribedClassTemplate())
114 return;
Haojian Wu29634fe2016-02-03 12:10:27 +0000115 }
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000116 DC = DC->getParent();
117 }
118 }
119
120 diag(FD->getLocation(),
Benjamin Kramera62e2232016-04-07 14:55:25 +0000121 "function %0 defined in a header file; "
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000122 "function definitions in header files can lead to ODR violations")
Benjamin Kramera62e2232016-04-07 14:55:25 +0000123 << FD << FixItHint::CreateInsertion(FD->getSourceRange().getBegin(),
124 "inline ");
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000125 } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
126 // Static data members of a class template are allowed.
127 if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
128 return;
129 if (VD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
130 return;
131 // Ignore variable definition within function scope.
132 if (VD->hasLocalStorage() || VD->isStaticLocal())
133 return;
134
135 diag(VD->getLocation(),
Benjamin Kramera62e2232016-04-07 14:55:25 +0000136 "variable %0 defined in a header file; "
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000137 "variable definitions in header files can lead to ODR violations")
Benjamin Kramera62e2232016-04-07 14:55:25 +0000138 << VD;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000139 }
140}
141
142} // namespace misc
143} // namespace tidy
144} // namespace clang