clang-tools 10.0.0

DefinitionsInHeadersCheck.cpp
Go to the documentation of this file.
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
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
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
45 void DefinitionsInHeadersCheck::storeOptions(
46 ClangTidyOptions::OptionMap &Opts) {
47 Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
48 Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
49 }
50
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
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->getInnerLocStart(), "inline ");
136 } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
137 // Static data members of a class template are allowed.
138 if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
139 return;
140 // Ignore instantiated static data members of classes.
141 if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
142 return;
143 // Ignore variable definition within function scope.
144 if (VD->hasLocalStorage() || VD->isStaticLocal())
145 return;
146 // Ignore inline variables.
147 if (VD->isInline())
148 return;
149
150 diag(VD->getLocation(),
151 "variable %0 defined in a header file; "
152 "variable definitions in header files can lead to ODR violations")
153 << VD;
154 }
155 }
156
157 } // namespace misc
158 } // namespace tidy
159 } // namespace clang
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::misc::DefinitionsInHeadersCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: DefinitionsInHeadersCheck.cpp:72
clang::tidy::utils::parseHeaderFileExtensions
bool parseHeaderFileExtensions(StringRef AllHeaderFileExtensions, HeaderFileExtensionsSet &HeaderFileExtensions, char delimiter)
Parses header file extensions from a semicolon-separated list.
Definition: HeaderFileExtensionsUtils.cpp:41
clang::tidy::utils::isExpansionLocInHeaderFile
bool isExpansionLocInHeaderFile(SourceLocation Loc, const SourceManager &SM, const HeaderFileExtensionsSet &HeaderFileExtensions)
Checks whether expansion location of Loc is in header file.
Definition: HeaderFileExtensionsUtils.cpp:17
clang::tidy::misc::DefinitionsInHeadersCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: DefinitionsInHeadersCheck.cpp:45
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:47
clang::tidy::ClangTidyCheck::getLangOpts
const LangOptions & getLangOpts() const
Returns the language options from the context.
Definition: ClangTidyCheck.h:188
clang::tidy::utils::defaultHeaderFileExtensions
StringRef defaultHeaderFileExtensions()
Returns recommended default value for the list of header file extensions.
Definition: HeaderFileExtensionsUtils.h:40
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:184
clang::tidy::utils::HeaderFileExtensionsSet
llvm::SmallSet< llvm::StringRef, 5 > HeaderFileExtensionsSet
Definition: HeaderFileExtensionsUtils.h:21
clang::tidy::ClangTidyCheck::OptionsView::store
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidyCheck.cpp:58
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:97
clang::tidy::misc::DefinitionsInHeadersCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: DefinitionsInHeadersCheck.cpp:51
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::doc::MD
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for MD output.")
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:77
DefinitionsInHeadersCheck.h
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidyCheck.cpp:21
clang::tidy::readability::AST_MATCHER_P
AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl, ast_matchers::internal::Matcher< CXXMethodDecl >, InnerMatcher)
Definition: ConvertMemberFunctionsToStatic.cpp:52