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