1 //===--- DefinitionsInHeadersCheck.cpp - clang-tidy------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "DefinitionsInHeadersCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 using namespace clang::ast_matchers
;
15 namespace clang::tidy::misc
{
19 AST_MATCHER_P(NamedDecl
, usesHeaderFileExtension
, FileExtensionsSet
,
20 HeaderFileExtensions
) {
21 return utils::isExpansionLocInHeaderFile(
22 Node
.getBeginLoc(), Finder
->getASTContext().getSourceManager(),
23 HeaderFileExtensions
);
28 DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(StringRef Name
,
29 ClangTidyContext
*Context
)
30 : ClangTidyCheck(Name
, Context
),
31 UseHeaderFileExtension(Options
.get("UseHeaderFileExtension", true)) {
32 std::optional
<StringRef
> HeaderFileExtensionsOption
=
33 Options
.get("HeaderFileExtensions");
34 RawStringHeaderFileExtensions
=
35 HeaderFileExtensionsOption
.value_or(utils::defaultHeaderFileExtensions());
36 if (HeaderFileExtensionsOption
) {
37 if (!utils::parseFileExtensions(RawStringHeaderFileExtensions
,
39 utils::defaultFileExtensionDelimiters())) {
40 this->configurationDiag("Invalid header file extension: '%0'")
41 << RawStringHeaderFileExtensions
;
44 HeaderFileExtensions
= Context
->getHeaderFileExtensions();
47 void DefinitionsInHeadersCheck::storeOptions(
48 ClangTidyOptions::OptionMap
&Opts
) {
49 Options
.store(Opts
, "UseHeaderFileExtension", UseHeaderFileExtension
);
50 Options
.store(Opts
, "HeaderFileExtensions", RawStringHeaderFileExtensions
);
53 void DefinitionsInHeadersCheck::registerMatchers(MatchFinder
*Finder
) {
54 auto DefinitionMatcher
=
55 anyOf(functionDecl(isDefinition(), unless(isDeleted())),
56 varDecl(isDefinition()));
57 if (UseHeaderFileExtension
) {
58 Finder
->addMatcher(namedDecl(DefinitionMatcher
,
59 usesHeaderFileExtension(HeaderFileExtensions
))
64 namedDecl(DefinitionMatcher
,
65 anyOf(usesHeaderFileExtension(HeaderFileExtensions
),
66 unless(isExpansionInMainFile())))
72 void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult
&Result
) {
73 // Don't run the check in failing TUs.
74 if (Result
.Context
->getDiagnostics().hasUncompilableErrorOccurred())
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");
87 if (ND
->isInvalidDecl())
90 // Internal linkage variable definitions are ignored for now:
93 // namespace { int c = 1; }
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
->hasExternalFormalLinkage() || ND
->isInAnonymousNamespace())
100 if (const auto *FD
= dyn_cast
<FunctionDecl
>(ND
)) {
101 // Inline functions are allowed.
104 // Function templates are allowed.
105 if (FD
->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate
)
107 // Ignore instantiated functions.
108 if (FD
->isTemplateInstantiation())
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()) {
115 if (const auto *RD
= dyn_cast
<CXXRecordDecl
>(DC
)) {
116 if (isa
<ClassTemplatePartialSpecializationDecl
>(RD
))
118 if (RD
->getDescribedClassTemplate())
121 DC
= DC
->getParent();
125 bool IsFullSpec
= FD
->getTemplateSpecializationKind() != TSK_Undeclared
;
126 diag(FD
->getLocation(),
127 "%select{function|full function template specialization}0 %1 defined "
128 "in a header file; function definitions in header files can lead to "
131 // inline is not allowed for main function.
134 diag(FD
->getLocation(), /*Description=*/"make as 'inline'",
136 << FixItHint::CreateInsertion(FD
->getInnerLocStart(), "inline ");
137 } else if (const auto *VD
= dyn_cast
<VarDecl
>(ND
)) {
138 // C++14 variable templates are allowed.
139 if (VD
->getDescribedVarTemplate())
141 // Static data members of a class template are allowed.
142 if (VD
->getDeclContext()->isDependentContext() && VD
->isStaticDataMember())
144 // Ignore instantiated static data members of classes.
145 if (isTemplateInstantiation(VD
->getTemplateSpecializationKind()))
147 // Ignore variable definition within function scope.
148 if (VD
->hasLocalStorage() || VD
->isStaticLocal())
150 // Ignore inline variables.
153 // Ignore partial specializations.
154 if (isa
<VarTemplatePartialSpecializationDecl
>(VD
))
157 diag(VD
->getLocation(),
158 "variable %0 defined in a header file; "
159 "variable definitions in header files can lead to ODR violations")
164 } // namespace clang::tidy::misc