blob: 0b837d245f5faad1d18b402b6da2a2daed0665a0 [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)),
Alexander Kornienkob1c74322017-07-20 12:02:03 +000035 RawStringHeaderFileExtensions(Options.getLocalOrGlobal(
36 "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) {
Haojian Wuc2d75772016-02-05 11:23:59 +000037 if (!utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +000038 HeaderFileExtensions, ',')) {
Haojian Wuc2d75772016-02-05 11:23:59 +000039 // FIXME: Find a more suitable way to handle invalid configuration
40 // options.
41 llvm::errs() << "Invalid header file extension: "
42 << RawStringHeaderFileExtensions << "\n";
43 }
44}
Alexander Kornienkob816ba02016-01-08 16:37:11 +000045
46void DefinitionsInHeadersCheck::storeOptions(
47 ClangTidyOptions::OptionMap &Opts) {
48 Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
Haojian Wuc2d75772016-02-05 11:23:59 +000049 Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
Alexander Kornienkob816ba02016-01-08 16:37:11 +000050}
51
52void DefinitionsInHeadersCheck::registerMatchers(MatchFinder *Finder) {
Haojian Wu3d1d0762016-02-08 16:05:39 +000053 if (!getLangOpts().CPlusPlus)
54 return;
Haojian Wuba992cf2016-06-07 08:55:38 +000055 auto DefinitionMatcher =
56 anyOf(functionDecl(isDefinition(), unless(isDeleted())),
57 varDecl(isDefinition()));
Alexander Kornienkob816ba02016-01-08 16:37:11 +000058 if (UseHeaderFileExtension) {
Haojian Wuba992cf2016-06-07 08:55:38 +000059 Finder->addMatcher(namedDecl(DefinitionMatcher,
60 usesHeaderFileExtension(HeaderFileExtensions))
61 .bind("name-decl"),
62 this);
Alexander Kornienkob816ba02016-01-08 16:37:11 +000063 } else {
64 Finder->addMatcher(
Haojian Wuba992cf2016-06-07 08:55:38 +000065 namedDecl(DefinitionMatcher,
Haojian Wuc2d75772016-02-05 11:23:59 +000066 anyOf(usesHeaderFileExtension(HeaderFileExtensions),
67 unless(isExpansionInMainFile())))
68 .bind("name-decl"),
Alexander Kornienkob816ba02016-01-08 16:37:11 +000069 this);
70 }
71}
72
73void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
Haojian Wue5d27792016-06-27 08:04:01 +000074 // Don't run the check in failing TUs.
Alexander Kornienko385c2a32017-02-08 16:11:22 +000075 if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
Haojian Wue5d27792016-06-27 08:04:01 +000076 return;
77
Alexander Kornienkob816ba02016-01-08 16:37:11 +000078 // C++ [basic.def.odr] p6:
79 // There can be more than one definition of a class type, enumeration type,
80 // inline function with external linkage, class template, non-static function
81 // template, static data member of a class template, member function of a
82 // class template, or template specialization for which some template
83 // parameters are not specifiedin a program provided that each definition
84 // appears in a different translation unit, and provided the definitions
85 // satisfy the following requirements.
86 const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
87 assert(ND);
Haojian Wu3d1d0762016-02-08 16:05:39 +000088 if (ND->isInvalidDecl())
89 return;
Alexander Kornienkob816ba02016-01-08 16:37:11 +000090
91 // Internal linkage variable definitions are ignored for now:
92 // const int a = 1;
93 // static int b = 1;
94 //
95 // Although these might also cause ODR violations, we can be less certain and
96 // should try to keep the false-positive rate down.
97 if (ND->getLinkageInternal() == InternalLinkage)
98 return;
99
100 if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
101 // Inline functions are allowed.
102 if (FD->isInlined())
103 return;
104 // Function templates are allowed.
105 if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
106 return;
Haojian Wuead59ee2017-02-15 14:10:50 +0000107 // Ignore instantiated functions.
108 if (FD->isTemplateInstantiation())
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000109 return;
110 // Member function of a class template and member function of a nested class
111 // in a class template are allowed.
112 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
113 const auto *DC = MD->getDeclContext();
114 while (DC->isRecord()) {
Haojian Wu29634fe2016-02-03 12:10:27 +0000115 if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
116 if (isa<ClassTemplatePartialSpecializationDecl>(RD))
117 return;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000118 if (RD->getDescribedClassTemplate())
119 return;
Haojian Wu29634fe2016-02-03 12:10:27 +0000120 }
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000121 DC = DC->getParent();
122 }
123 }
124
Haojian Wube2588f2017-02-14 12:39:22 +0000125 bool is_full_spec = FD->getTemplateSpecializationKind() != TSK_Undeclared;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000126 diag(FD->getLocation(),
Haojian Wube2588f2017-02-14 12:39:22 +0000127 "%select{function|full function template specialization}0 %1 defined "
128 "in a header file; function definitions in header files can lead to "
129 "ODR violations")
130 << is_full_spec << FD << FixItHint::CreateInsertion(
Haojian Wu0b067c12016-07-13 13:55:29 +0000131 FD->getReturnTypeSourceRange().getBegin(), "inline ");
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000132 } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
133 // Static data members of a class template are allowed.
134 if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
135 return;
Haojian Wuead59ee2017-02-15 14:10:50 +0000136 // Ignore instantiated static data members of classes.
137 if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000138 return;
139 // Ignore variable definition within function scope.
140 if (VD->hasLocalStorage() || VD->isStaticLocal())
141 return;
Gabor Horvath2990ac12017-06-28 12:47:35 +0000142 // Ignore inline variables.
143 if (VD->isInline())
144 return;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000145
146 diag(VD->getLocation(),
Benjamin Kramera62e2232016-04-07 14:55:25 +0000147 "variable %0 defined in a header file; "
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000148 "variable definitions in header files can lead to ODR violations")
Benjamin Kramera62e2232016-04-07 14:55:25 +0000149 << VD;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000150 }
151}
152
153} // namespace misc
154} // namespace tidy
155} // namespace clang