clang-tools 23.0.0git
DefinitionsInHeadersCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13
14using namespace clang::ast_matchers;
15
16namespace clang::tidy::misc {
17
18namespace {
19
20AST_MATCHER_P(NamedDecl, usesHeaderFileExtension, FileExtensionsSet,
21 HeaderFileExtensions) {
23 Node.getBeginLoc(), Finder->getASTContext().getSourceManager(),
24 HeaderFileExtensions);
25}
26
27} // namespace
28
32
34 auto DefinitionMatcher =
35 anyOf(functionDecl(isDefinition(), unless(isDeleted())),
36 varDecl(isDefinition()));
37 Finder->addMatcher(
38 namedDecl(DefinitionMatcher,
39 usesHeaderFileExtension(getHeaderFileExtensions()))
40 .bind("name-decl"),
41 this);
42}
43
44void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
45 // Don't run the check in failing TUs.
46 if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
47 return;
48
49 // C++ [basic.def.odr] p6:
50 // There can be more than one definition of a class type, enumeration type,
51 // inline function with external linkage, class template, non-static function
52 // template, static data member of a class template, member function of a
53 // class template, or template specialization for which some template
54 // parameters are not specifiedin a program provided that each definition
55 // appears in a different translation unit, and provided the definitions
56 // satisfy the following requirements.
57 const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
58 assert(ND);
59 if (ND->isInvalidDecl())
60 return;
61
62 // Internal linkage variable definitions are ignored for now:
63 // const int a = 1;
64 // static int b = 1;
65 // namespace { int c = 1; }
66 //
67 // Although these might also cause ODR violations, we can be less certain and
68 // should try to keep the false-positive rate down.
69 if (!ND->hasExternalFormalLinkage() || ND->isInAnonymousNamespace())
70 return;
71
72 if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
73 // Inline functions are allowed.
74 if (FD->isInlined())
75 return;
76 // Function templates are allowed.
77 if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
78 return;
79 // Ignore instantiated functions.
80 if (FD->isTemplateInstantiation())
81 return;
82 // Member function of a class template and member function of a nested class
83 // in a class template are allowed.
84 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
85 const auto *DC = MD->getDeclContext();
86 while (DC->isRecord()) {
87 if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
88 if (isa<ClassTemplatePartialSpecializationDecl>(RD))
89 return;
90 if (RD->getDescribedClassTemplate())
91 return;
92 }
93 DC = DC->getParent();
94 }
95 }
96
97 const bool IsFullSpec =
98 FD->getTemplateSpecializationKind() != TSK_Undeclared;
99 diag(FD->getLocation(),
100 "%select{function|full function template specialization}0 %1 defined "
101 "in a header file; function definitions in header files can lead to "
102 "ODR violations")
103 << IsFullSpec << FD;
104 // inline is not allowed for main function.
105 if (FD->isMain())
106 return;
107 diag(FD->getLocation(), "mark the definition as 'inline'",
108 DiagnosticIDs::Note)
109 << FixItHint::CreateInsertion(FD->getInnerLocStart(), "inline ");
110 } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
111 // C++14 variable templates are allowed.
112 if (VD->getDescribedVarTemplate())
113 return;
114 // Static data members of a class template are allowed.
115 if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
116 return;
117 // Ignore instantiated static data members of classes.
118 if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
119 return;
120 // Ignore variable definition within function scope.
121 if (VD->hasLocalStorage() || VD->isStaticLocal())
122 return;
123 // Ignore inline variables.
124 if (VD->isInline())
125 return;
126 // Ignore partial specializations.
127 if (isa<VarTemplatePartialSpecializationDecl>(VD))
128 return;
129
130 diag(VD->getLocation(),
131 "variable %0 defined in a header file; "
132 "variable definitions in header files can lead to ODR violations")
133 << VD;
134 }
135}
136
137} // namespace clang::tidy::misc
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
DefinitionsInHeadersCheck(StringRef Name, ClangTidyContext *Context)
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
bool isExpansionLocInHeaderFile(SourceLocation Loc, const SourceManager &SM, const FileExtensionsSet &HeaderFileExtensions)
Checks whether expansion location of Loc is in header file.
llvm::SmallSet< llvm::StringRef, 5 > FileExtensionsSet