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