1 //===--- DefinitionsInHeadersCheck.cpp - clang-tidy------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "DefinitionsInHeadersCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang {
17 namespace tidy {
18 namespace misc {
19 
20 namespace {
21 
22 AST_MATCHER_P(NamedDecl, usesHeaderFileExtension,
23               utils::HeaderFileExtensionsSet, HeaderFileExtensions) {
24   return utils::isExpansionLocInHeaderFile(
25       Node.getLocStart(), Finder->getASTContext().getSourceManager(),
26       HeaderFileExtensions);
27 }
28 
29 } // namespace
30 
31 DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(StringRef Name,
32                                                      ClangTidyContext *Context)
33     : ClangTidyCheck(Name, Context),
34       UseHeaderFileExtension(Options.get("UseHeaderFileExtension", true)),
35       RawStringHeaderFileExtensions(Options.getLocalOrGlobal(
36           "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) {
37   if (!utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
38                                         HeaderFileExtensions, ',')) {
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 
46 void DefinitionsInHeadersCheck::storeOptions(
47     ClangTidyOptions::OptionMap &Opts) {
48   Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
49   Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
50 }
51 
52 void DefinitionsInHeadersCheck::registerMatchers(MatchFinder *Finder) {
53   if (!getLangOpts().CPlusPlus)
54     return;
55   auto DefinitionMatcher =
56       anyOf(functionDecl(isDefinition(), unless(isDeleted())),
57             varDecl(isDefinition()));
58   if (UseHeaderFileExtension) {
59     Finder->addMatcher(namedDecl(DefinitionMatcher,
60                                  usesHeaderFileExtension(HeaderFileExtensions))
61                            .bind("name-decl"),
62                        this);
63   } else {
64     Finder->addMatcher(
65         namedDecl(DefinitionMatcher,
66                   anyOf(usesHeaderFileExtension(HeaderFileExtensions),
67                         unless(isExpansionInMainFile())))
68             .bind("name-decl"),
69         this);
70   }
71 }
72 
73 void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
74   // Don't run the check in failing TUs.
75   if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
76     return;
77 
78   // C++ [basic.def.odr] p6:
79   // There can be more than one definition of a class type, enumeration type,
80   // inline function with external linkage, class template, non-static function
81   // template, static data member of a class template, member function of a
82   // class template, or template specialization for which some template
83   // parameters are not specifiedin a program provided that each definition
84   // appears in a different translation unit, and provided the definitions
85   // satisfy the following requirements.
86   const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
87   assert(ND);
88   if (ND->isInvalidDecl())
89     return;
90 
91   // Internal linkage variable definitions are ignored for now:
92   //   const int a = 1;
93   //   static int b = 1;
94   //
95   // Although these might also cause ODR violations, we can be less certain and
96   // should try to keep the false-positive rate down.
97   //
98   // FIXME: Should declarations in anonymous namespaces get the same treatment
99   // as static / const declarations?
100   if (!ND->hasExternalFormalLinkage() && !ND->isInAnonymousNamespace())
101     return;
102 
103   if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
104     // Inline functions are allowed.
105     if (FD->isInlined())
106       return;
107     // Function templates are allowed.
108     if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
109       return;
110     // Ignore instantiated functions.
111     if (FD->isTemplateInstantiation())
112       return;
113     // Member function of a class template and member function of a nested class
114     // in a class template are allowed.
115     if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
116       const auto *DC = MD->getDeclContext();
117       while (DC->isRecord()) {
118         if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
119           if (isa<ClassTemplatePartialSpecializationDecl>(RD))
120             return;
121           if (RD->getDescribedClassTemplate())
122             return;
123         }
124         DC = DC->getParent();
125       }
126     }
127 
128     bool is_full_spec = FD->getTemplateSpecializationKind() != TSK_Undeclared;
129     diag(FD->getLocation(),
130          "%select{function|full function template specialization}0 %1 defined "
131          "in a header file; function definitions in header files can lead to "
132          "ODR violations")
133         << is_full_spec << FD << FixItHint::CreateInsertion(
134                      FD->getReturnTypeSourceRange().getBegin(), "inline ");
135   } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
136     // Static data members of a class template are allowed.
137     if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
138       return;
139     // Ignore instantiated static data members of classes.
140     if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
141       return;
142     // Ignore variable definition within function scope.
143     if (VD->hasLocalStorage() || VD->isStaticLocal())
144       return;
145     // Ignore inline variables.
146     if (VD->isInline())
147       return;
148 
149     diag(VD->getLocation(),
150          "variable %0 defined in a header file; "
151          "variable definitions in header files can lead to ODR violations")
152         << VD;
153   }
154 }
155 
156 } // namespace misc
157 } // namespace tidy
158 } // namespace clang
159