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::HeaderFileExtensionsSet,HeaderFileExtensions)21 AST_MATCHER_P(NamedDecl, usesHeaderFileExtension,
22 utils::HeaderFileExtensionsSet, 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::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
37 HeaderFileExtensions, ',')) {
38 // FIXME: Find a more suitable way to handle invalid configuration
39 // options.
40 llvm::errs() << "Invalid header file extension: "
41 << RawStringHeaderFileExtensions << "\n";
42 }
43 }
44
storeOptions(ClangTidyOptions::OptionMap & Opts)45 void DefinitionsInHeadersCheck::storeOptions(
46 ClangTidyOptions::OptionMap &Opts) {
47 Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
48 Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
49 }
50
registerMatchers(MatchFinder * Finder)51 void DefinitionsInHeadersCheck::registerMatchers(MatchFinder *Finder) {
52 if (!getLangOpts().CPlusPlus)
53 return;
54 auto DefinitionMatcher =
55 anyOf(functionDecl(isDefinition(), unless(isDeleted())),
56 varDecl(isDefinition()));
57 if (UseHeaderFileExtension) {
58 Finder->addMatcher(namedDecl(DefinitionMatcher,
59 usesHeaderFileExtension(HeaderFileExtensions))
60 .bind("name-decl"),
61 this);
62 } else {
63 Finder->addMatcher(
64 namedDecl(DefinitionMatcher,
65 anyOf(usesHeaderFileExtension(HeaderFileExtensions),
66 unless(isExpansionInMainFile())))
67 .bind("name-decl"),
68 this);
69 }
70 }
71
check(const MatchFinder::MatchResult & Result)72 void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
73 // Don't run the check in failing TUs.
74 if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
75 return;
76
77 // C++ [basic.def.odr] p6:
78 // There can be more than one definition of a class type, enumeration type,
79 // inline function with external linkage, class template, non-static function
80 // template, static data member of a class template, member function of a
81 // class template, or template specialization for which some template
82 // parameters are not specifiedin a program provided that each definition
83 // appears in a different translation unit, and provided the definitions
84 // satisfy the following requirements.
85 const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
86 assert(ND);
87 if (ND->isInvalidDecl())
88 return;
89
90 // Internal linkage variable definitions are ignored for now:
91 // const int a = 1;
92 // static int b = 1;
93 //
94 // Although these might also cause ODR violations, we can be less certain and
95 // should try to keep the false-positive rate down.
96 //
97 // FIXME: Should declarations in anonymous namespaces get the same treatment
98 // as static / const declarations?
99 if (!ND->hasExternalFormalLinkage() && !ND->isInAnonymousNamespace())
100 return;
101
102 if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
103 // Inline functions are allowed.
104 if (FD->isInlined())
105 return;
106 // Function templates are allowed.
107 if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
108 return;
109 // Ignore instantiated functions.
110 if (FD->isTemplateInstantiation())
111 return;
112 // Member function of a class template and member function of a nested class
113 // in a class template are allowed.
114 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
115 const auto *DC = MD->getDeclContext();
116 while (DC->isRecord()) {
117 if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
118 if (isa<ClassTemplatePartialSpecializationDecl>(RD))
119 return;
120 if (RD->getDescribedClassTemplate())
121 return;
122 }
123 DC = DC->getParent();
124 }
125 }
126
127 bool IsFullSpec = FD->getTemplateSpecializationKind() != TSK_Undeclared;
128 diag(FD->getLocation(),
129 "%select{function|full function template specialization}0 %1 defined "
130 "in a header file; function definitions in header files can lead to "
131 "ODR violations")
132 << IsFullSpec << FD;
133 diag(FD->getLocation(), /*FixDescription=*/"make as 'inline'",
134 DiagnosticIDs::Note)
135 << FixItHint::CreateInsertion(FD->getReturnTypeSourceRange().getBegin(),
136 "inline ");
137 } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
138 // Static data members of a class template are allowed.
139 if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
140 return;
141 // Ignore instantiated static data members of classes.
142 if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
143 return;
144 // Ignore variable definition within function scope.
145 if (VD->hasLocalStorage() || VD->isStaticLocal())
146 return;
147 // Ignore inline variables.
148 if (VD->isInline())
149 return;
150
151 diag(VD->getLocation(),
152 "variable %0 defined in a header file; "
153 "variable definitions in header files can lead to ODR violations")
154 << VD;
155 }
156 }
157
158 } // namespace misc
159 } // namespace tidy
160 } // namespace clang
161