blob: f4dab397449a8d2c7e24eb001464b5c679a69349 [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(
Stephen Kelly43465bf2018-08-09 22:42:26 +000025 Node.getBeginLoc(), Finder->getASTContext().getSourceManager(),
Haojian Wuc2d75772016-02-05 11:23:59 +000026 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.
Richard Smith2ccecb92017-09-22 23:47:20 +000097 //
98 // FIXME: Should declarations in anonymous namespaces get the same treatment
99 // as static / const declarations?
100 if (!ND->hasExternalFormalLinkage() && !ND->isInAnonymousNamespace())
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000101 return;
102
103 if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
104 // Inline functions are allowed.
105 if (FD->isInlined())
106 return;
107 // Function templates are allowed.
108 if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
109 return;
Haojian Wuead59ee2017-02-15 14:10:50 +0000110 // Ignore instantiated functions.
111 if (FD->isTemplateInstantiation())
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000112 return;
113 // Member function of a class template and member function of a nested class
114 // in a class template are allowed.
115 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
116 const auto *DC = MD->getDeclContext();
117 while (DC->isRecord()) {
Haojian Wu29634fe2016-02-03 12:10:27 +0000118 if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
119 if (isa<ClassTemplatePartialSpecializationDecl>(RD))
120 return;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000121 if (RD->getDescribedClassTemplate())
122 return;
Haojian Wu29634fe2016-02-03 12:10:27 +0000123 }
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000124 DC = DC->getParent();
125 }
126 }
127
Haojian Wube2588f2017-02-14 12:39:22 +0000128 bool is_full_spec = FD->getTemplateSpecializationKind() != TSK_Undeclared;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000129 diag(FD->getLocation(),
Haojian Wube2588f2017-02-14 12:39:22 +0000130 "%select{function|full function template specialization}0 %1 defined "
131 "in a header file; function definitions in header files can lead to "
132 "ODR violations")
133 << is_full_spec << FD << FixItHint::CreateInsertion(
Haojian Wu0b067c12016-07-13 13:55:29 +0000134 FD->getReturnTypeSourceRange().getBegin(), "inline ");
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000135 } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
136 // Static data members of a class template are allowed.
137 if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
138 return;
Haojian Wuead59ee2017-02-15 14:10:50 +0000139 // Ignore instantiated static data members of classes.
140 if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000141 return;
142 // Ignore variable definition within function scope.
143 if (VD->hasLocalStorage() || VD->isStaticLocal())
144 return;
Gabor Horvath2990ac12017-06-28 12:47:35 +0000145 // Ignore inline variables.
146 if (VD->isInline())
147 return;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000148
149 diag(VD->getLocation(),
Benjamin Kramera62e2232016-04-07 14:55:25 +0000150 "variable %0 defined in a header file; "
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000151 "variable definitions in header files can lead to ODR violations")
Benjamin Kramera62e2232016-04-07 14:55:25 +0000152 << VD;
Alexander Kornienkob816ba02016-01-08 16:37:11 +0000153 }
154}
155
156} // namespace misc
157} // namespace tidy
158} // namespace clang