1 //===--- DefinitionsInHeadersCheck.cpp - clang-tidy------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "DefinitionsInHeadersCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12
13 using namespace clang::ast_matchers;
14
15 namespace clang {
16 namespace tidy {
17 namespace misc {
18
19 namespace {
20
AST_MATCHER_P(NamedDecl,usesHeaderFileExtension,utils::FileExtensionsSet,HeaderFileExtensions)21 AST_MATCHER_P(NamedDecl, usesHeaderFileExtension, utils::FileExtensionsSet,
22 HeaderFileExtensions) {
23 return utils::isExpansionLocInHeaderFile(
24 Node.getBeginLoc(), Finder->getASTContext().getSourceManager(),
25 HeaderFileExtensions);
26 }
27
28 } // namespace
29
DefinitionsInHeadersCheck(StringRef Name,ClangTidyContext * Context)30 DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(StringRef Name,
31 ClangTidyContext *Context)
32 : ClangTidyCheck(Name, Context),
33 UseHeaderFileExtension(Options.get("UseHeaderFileExtension", true)),
34 RawStringHeaderFileExtensions(Options.getLocalOrGlobal(
35 "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) {
36 if (!utils::parseFileExtensions(RawStringHeaderFileExtensions,
37 HeaderFileExtensions,
38 utils::defaultFileExtensionDelimiters())) {
39 // FIXME: Find a more suitable way to handle invalid configuration
40 // options.
41 llvm::errs() << "Invalid header file extension: "
42 << RawStringHeaderFileExtensions << "\n";
43 }
44 }
45
storeOptions(ClangTidyOptions::OptionMap & Opts)46 void DefinitionsInHeadersCheck::storeOptions(
47 ClangTidyOptions::OptionMap &Opts) {
48 Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
49 Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
50 }
51
registerMatchers(MatchFinder * Finder)52 void DefinitionsInHeadersCheck::registerMatchers(MatchFinder *Finder) {
53 auto DefinitionMatcher =
54 anyOf(functionDecl(isDefinition(), unless(isDeleted())),
55 varDecl(isDefinition()));
56 if (UseHeaderFileExtension) {
57 Finder->addMatcher(namedDecl(DefinitionMatcher,
58 usesHeaderFileExtension(HeaderFileExtensions))
59 .bind("name-decl"),
60 this);
61 } else {
62 Finder->addMatcher(
63 namedDecl(DefinitionMatcher,
64 anyOf(usesHeaderFileExtension(HeaderFileExtensions),
65 unless(isExpansionInMainFile())))
66 .bind("name-decl"),
67 this);
68 }
69 }
70
check(const MatchFinder::MatchResult & Result)71 void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
72 // Don't run the check in failing TUs.
73 if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
74 return;
75
76 // C++ [basic.def.odr] p6:
77 // There can be more than one definition of a class type, enumeration type,
78 // inline function with external linkage, class template, non-static function
79 // template, static data member of a class template, member function of a
80 // class template, or template specialization for which some template
81 // parameters are not specifiedin a program provided that each definition
82 // appears in a different translation unit, and provided the definitions
83 // satisfy the following requirements.
84 const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
85 assert(ND);
86 if (ND->isInvalidDecl())
87 return;
88
89 // Internal linkage variable definitions are ignored for now:
90 // const int a = 1;
91 // static int b = 1;
92 //
93 // Although these might also cause ODR violations, we can be less certain and
94 // should try to keep the false-positive rate down.
95 //
96 // FIXME: Should declarations in anonymous namespaces get the same treatment
97 // as static / const declarations?
98 if (!ND->hasExternalFormalLinkage() && !ND->isInAnonymousNamespace())
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 // Ignore instantiated functions.
109 if (FD->isTemplateInstantiation())
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()) {
116 if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
117 if (isa<ClassTemplatePartialSpecializationDecl>(RD))
118 return;
119 if (RD->getDescribedClassTemplate())
120 return;
121 }
122 DC = DC->getParent();
123 }
124 }
125
126 bool IsFullSpec = FD->getTemplateSpecializationKind() != TSK_Undeclared;
127 diag(FD->getLocation(),
128 "%select{function|full function template specialization}0 %1 defined "
129 "in a header file; function definitions in header files can lead to "
130 "ODR violations")
131 << IsFullSpec << FD;
132 diag(FD->getLocation(), /*FixDescription=*/"make as 'inline'",
133 DiagnosticIDs::Note)
134 << FixItHint::CreateInsertion(FD->getInnerLocStart(), "inline ");
135 } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
136 // C++14 variable templates are allowed.
137 if (VD->getDescribedVarTemplate())
138 return;
139 // Static data members of a class template are allowed.
140 if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
141 return;
142 // Ignore instantiated static data members of classes.
143 if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
144 return;
145 // Ignore variable definition within function scope.
146 if (VD->hasLocalStorage() || VD->isStaticLocal())
147 return;
148 // Ignore inline variables.
149 if (VD->isInline())
150 return;
151
152 diag(VD->getLocation(),
153 "variable %0 defined in a header file; "
154 "variable definitions in header files can lead to ODR violations")
155 << VD;
156 }
157 }
158
159 } // namespace misc
160 } // namespace tidy
161 } // namespace clang
162