1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 #ifndef LO_CLANG_SHARED_PLUGINS
10 
11 #include <algorithm>
12 #include <cassert>
13 
14 #include "clang/Sema/SemaDiagnostic.h"
15 
16 #include "check.hxx"
17 #include "plugin.hxx"
18 
19 namespace
20 {
derivesFromTestFixture(CXXRecordDecl const * decl)21 bool derivesFromTestFixture(CXXRecordDecl const* decl)
22 {
23     static auto const pred = [](CXXBaseSpecifier const& spec) {
24         if (auto const t = spec.getType()->getAs<RecordType>())
25         { // (may be a template parameter)
26             return derivesFromTestFixture(dyn_cast<CXXRecordDecl>(t->getDecl()));
27         }
28         return false;
29     };
30     return loplugin::DeclCheck(decl).Class("TestFixture").Namespace("CppUnit").GlobalNamespace()
31            || std::any_of(decl->bases_begin(), decl->bases_end(), pred)
32            || std::any_of(decl->vbases_begin(), decl->vbases_end(), pred);
33 }
34 
35 class External : public loplugin::FilteringPlugin<External>
36 {
37 public:
External(loplugin::InstantiationData const & data)38     explicit External(loplugin::InstantiationData const& data)
39         : FilteringPlugin(data)
40     {
41     }
42 
run()43     void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
44 
VisitTagDecl(TagDecl const * decl)45     bool VisitTagDecl(TagDecl const* decl)
46     {
47         /*TODO:*/
48         return true; // in general, moving classes or enumerations into an unnamed namespace can break ADL
49         if (isa<ClassTemplateSpecializationDecl>(decl))
50         {
51             return true;
52         }
53         if (!decl->isThisDeclarationADefinition())
54         {
55             return true;
56         }
57         if (isa<CXXRecordDecl>(decl->getDeclContext()))
58         {
59             return true;
60         }
61         if (!compiler.getLangOpts().CPlusPlus)
62         {
63             return true;
64         }
65         if (auto const d = dyn_cast<CXXRecordDecl>(decl))
66         {
67             if (d->getDescribedClassTemplate() != nullptr)
68             {
69                 return true;
70             }
71             if (auto const attr = d->getAttr<VisibilityAttr>())
72             {
73                 if (attr->getVisibility() == VisibilityAttr::Default)
74                 {
75                     // If the class definition has explicit default visibility, then assume that it
76                     // needs to be present (e.g., a backwards-compatibility stub like in
77                     // cppuhelper/source/compat.cxx):
78                     return true;
79                 }
80             }
81             if (derivesFromTestFixture(d))
82             {
83                 // The names of CppUnit tests (that can be specified with CPPUNIT_TEST_NAME) are
84                 // tied to the fully-qualified names of classes derived from CppUnit::TestFixture,
85                 // so avoid unnamed namespaces in those classes' names:
86                 return true;
87             }
88         }
89         return handleDeclaration(decl);
90     }
91 
VisitFunctionDecl(FunctionDecl const * decl)92     bool VisitFunctionDecl(FunctionDecl const* decl)
93     {
94         if (isa<CXXMethodDecl>(decl))
95         {
96             return true;
97         }
98         if (decl->getTemplatedKind() != FunctionDecl::TK_NonTemplate)
99         {
100             return true;
101         }
102         if (!decl->isThisDeclarationADefinition())
103         {
104             return true;
105         }
106         if (decl->isMain() || decl->isMSVCRTEntryPoint())
107         {
108             return true;
109         }
110         if (loplugin::hasCLanguageLinkageType(decl)
111             && loplugin::DeclCheck(decl).Function("_DllMainCRTStartup").GlobalNamespace())
112         {
113             return true;
114         }
115         // If the function definition is explicit marked SAL_DLLPUBLIC_EXPORT or similar, then
116         // assume that it needs to be present (e.g., only called via dlopen, or a backwards-
117         // compatibility stub like in sal/osl/all/compat.cxx):
118         if (auto const attr = decl->getAttr<VisibilityAttr>())
119         {
120             if (attr->getVisibility() == VisibilityAttr::Default)
121             {
122                 return true;
123             }
124         }
125         else if (decl->hasAttr<DLLExportAttr>())
126         {
127             return true;
128         }
129         auto const canon = decl->getCanonicalDecl();
130         if (loplugin::hasCLanguageLinkageType(canon)
131             && (canon->hasAttr<ConstructorAttr>() || canon->hasAttr<DestructorAttr>()))
132         {
133             return true;
134         }
135         if (compiler.getDiagnostics().getDiagnosticLevel(diag::warn_unused_function,
136                                                          decl->getLocation())
137             < DiagnosticsEngine::Warning)
138         {
139             // Don't warn about e.g.
140             //
141             //  G_DEFINE_TYPE (GLOAction, g_lo_action, G_TYPE_OBJECT);
142             //
143             // in vcl/unx/gtk/gloactiongroup.cxx (which expands to non-static g_lo_action_get_type
144             // function definition), which is already wrapped in
145             //
146             //  #pragma GCC diagnostic ignored "-Wunused-function"
147             return true;
148         }
149         return handleDeclaration(decl);
150     }
151 
VisitVarDecl(VarDecl const * decl)152     bool VisitVarDecl(VarDecl const* decl)
153     {
154         if (decl->isStaticDataMember())
155         {
156             return true;
157         }
158         if (isa<VarTemplateSpecializationDecl>(decl))
159         {
160             return true;
161         }
162         if (!decl->isThisDeclarationADefinition())
163         {
164             return true;
165         }
166         if (loplugin::DeclCheck(decl).Var("_pRawDllMain").GlobalNamespace())
167         {
168             return true;
169         }
170         return handleDeclaration(decl);
171     }
172 
VisitClassTemplateDecl(ClassTemplateDecl const * decl)173     bool VisitClassTemplateDecl(ClassTemplateDecl const* decl)
174     {
175         /*TODO:*/
176         return true; // in general, moving classes or enumerations into an unnamed namespace can break ADL
177         if (!decl->isThisDeclarationADefinition())
178         {
179             return true;
180         }
181         if (isa<CXXRecordDecl>(decl->getDeclContext()))
182         {
183             return true;
184         }
185         return handleDeclaration(decl);
186     }
187 
VisitFunctionTemplateDecl(FunctionTemplateDecl const * decl)188     bool VisitFunctionTemplateDecl(FunctionTemplateDecl const* decl)
189     {
190         if (!decl->isThisDeclarationADefinition())
191         {
192             return true;
193         }
194         if (isa<CXXRecordDecl>(decl->getDeclContext()))
195         {
196             return true;
197         }
198         return handleDeclaration(decl);
199     }
200 
VisitVarTemplateDecl(VarTemplateDecl const * decl)201     bool VisitVarTemplateDecl(VarTemplateDecl const* decl)
202     {
203         if (!decl->isThisDeclarationADefinition())
204         {
205             return true;
206         }
207         return handleDeclaration(decl);
208     }
209 
210 private:
reportSpecializations(T specializations)211     template <typename T> void reportSpecializations(T specializations)
212     {
213         for (auto const d : specializations)
214         {
215             auto const k = d->getTemplateSpecializationKind();
216             if (isTemplateExplicitInstantiationOrSpecialization(k))
217             {
218                 report(DiagnosticsEngine::Note,
219                        "explicit %select{instantiation|specialization}0 is here", d->getLocation())
220                     << (k == TSK_ExplicitSpecialization) << d->getSourceRange();
221             }
222         }
223     }
224 
handleDeclaration(NamedDecl const * decl)225     bool handleDeclaration(NamedDecl const* decl)
226     {
227         if (ignoreLocation(decl))
228         {
229             return true;
230         }
231         if (decl->getLinkageInternal() < ModuleLinkage)
232         {
233             return true;
234         }
235         //TODO: in some cases getLinkageInternal() appears to report ExternalLinkage instead of
236         // UniqueExternalLinkage:
237         if (decl->isInAnonymousNamespace())
238         {
239             return true;
240         }
241         for (Decl const* d = decl; d != nullptr; d = d->getPreviousDecl())
242         {
243             if (!compiler.getSourceManager().isInMainFile(d->getLocation()))
244             {
245                 return true;
246             }
247         }
248         if (compiler.getSourceManager().isMacroBodyExpansion(decl->getLocation()))
249         {
250             if (Lexer::getImmediateMacroName(decl->getLocation(), compiler.getSourceManager(),
251                                              compiler.getLangOpts())
252                 == "MDDS_MTV_DEFINE_ELEMENT_CALLBACKS")
253             {
254                 // Even wrapping in an unnamed namespace or sneaking "static" into the macro
255                 // wouldn't help, as then some of the functions it defines would be flagged as
256                 // unused:
257                 return true;
258             }
259         }
260         else if (compiler.getSourceManager().isMacroArgExpansion(decl->getLocation()))
261         {
262             if (Lexer::getImmediateMacroName(decl->getLocation(), compiler.getSourceManager(),
263                                              compiler.getLangOpts())
264                 == "DEFINE_GUID")
265             {
266                 // Windows, guiddef.h:
267                 return true;
268             }
269         }
270         TypedefNameDecl const* typedefed = nullptr;
271         if (auto const d = dyn_cast<TagDecl>(decl))
272         {
273             typedefed = d->getTypedefNameForAnonDecl();
274         }
275         bool canStatic;
276         if (auto const d = dyn_cast<CXXRecordDecl>(decl))
277         {
278             canStatic = d->isUnion() && d->isAnonymousStructOrUnion();
279         }
280         else
281         {
282             canStatic = isa<FunctionDecl>(decl) || isa<VarDecl>(decl)
283                         || isa<FunctionTemplateDecl>(decl) || isa<VarTemplateDecl>(decl);
284         }
285         auto const canUnnamed = compiler.getLangOpts().CPlusPlus
286                                 && !(isa<FunctionDecl>(decl) || isa<FunctionTemplateDecl>(decl));
287         // in general, moving functions into an unnamed namespace can break ADL
288         assert(canStatic || canUnnamed);
289         report(
290             DiagnosticsEngine::Warning,
291             ("externally available%select{| typedef'ed}0 entity %1 is not previously declared in an"
292              " included file (if it is only used in this translation unit,"
293              " %select{|make it static}2%select{| or }3%select{|put it in an unnamed namespace}4;"
294              " otherwise, provide a declaration of it in an included file)"),
295             decl->getLocation())
296             << (typedefed != nullptr) << (typedefed == nullptr ? decl : typedefed) << canStatic
297             << (canStatic && canUnnamed) << canUnnamed << decl->getSourceRange();
298         for (auto d = decl->getPreviousDecl(); d != nullptr; d = d->getPreviousDecl())
299         {
300             report(DiagnosticsEngine::Note, "previous declaration is here", d->getLocation())
301                 << d->getSourceRange();
302         }
303         //TODO: Class template specializations can be in the enclosing namespace, so no need to
304         // list them here (as they won't need to be put into the unnamed namespace too, unlike for
305         // specializations of function and variable templates); and explicit function template
306         // specializations cannot have storage-class specifiers, so as we only suggest to make
307         // function templates static (but not to move them into an unnamed namespace), no need to
308         // list function template specializations here, either:
309         if (auto const d = dyn_cast<VarTemplateDecl>(decl))
310         {
311             reportSpecializations(d->specializations());
312         }
313         return true;
314     }
315 };
316 
317 loplugin::Plugin::Registration<External> external("external");
318 
319 } // namespace
320 
321 #endif // LO_CLANG_SHARED_PLUGINS
322 
323 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
324