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::HeaderFileExtensionsSet,HeaderFileExtensions)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 
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::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 
storeOptions(ClangTidyOptions::OptionMap & Opts)45 void DefinitionsInHeadersCheck::storeOptions(
46     ClangTidyOptions::OptionMap &Opts) {
47   Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
48   Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
49 }
50 
registerMatchers(MatchFinder * Finder)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 
check(const MatchFinder::MatchResult & Result)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->getReturnTypeSourceRange().getBegin(),
136                                       "inline ");
137   } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
138     // Static data members of a class template are allowed.
139     if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
140       return;
141     // Ignore instantiated static data members of classes.
142     if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
143       return;
144     // Ignore variable definition within function scope.
145     if (VD->hasLocalStorage() || VD->isStaticLocal())
146       return;
147     // Ignore inline variables.
148     if (VD->isInline())
149       return;
150 
151     diag(VD->getLocation(),
152          "variable %0 defined in a header file; "
153          "variable definitions in header files can lead to ODR violations")
154         << VD;
155   }
156 }
157 
158 } // namespace misc
159 } // namespace tidy
160 } // namespace clang
161