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 #include <iterator>
14 #include <list>
15 #include <set>
16 
17 #include "clang/Sema/SemaDiagnostic.h"
18 
19 #include "check.hxx"
20 #include "plugin.hxx"
21 
22 namespace
23 {
derivesFromTestFixture(CXXRecordDecl const * decl)24 bool derivesFromTestFixture(CXXRecordDecl const* decl)
25 {
26     static auto const pred = [](CXXBaseSpecifier const& spec) {
27         if (auto const t = spec.getType()->getAs<RecordType>())
28         { // (may be a template parameter)
29             return derivesFromTestFixture(dyn_cast<CXXRecordDecl>(t->getDecl()));
30         }
31         return false;
32     };
33     return loplugin::DeclCheck(decl).Class("TestFixture").Namespace("CppUnit").GlobalNamespace()
34            || std::any_of(decl->bases_begin(), decl->bases_end(), pred)
35            || std::any_of(decl->vbases_begin(), decl->vbases_end(), pred);
36 }
37 
isInjectedFunction(FunctionDecl const * decl)38 bool isInjectedFunction(FunctionDecl const* decl)
39 {
40     for (auto d = decl->redecls_begin(); d != decl->redecls_end(); ++d)
41     {
42         auto const c = d->getLexicalDeclContext();
43         if (!(c->isFunctionOrMethod() || c->isRecord()))
44         {
45             return false;
46         }
47     }
48     return true;
49 }
50 
51 // Whether type1 mentions type2 (in a way relevant for argument-dependent name lookup):
mentions(QualType type1,QualType type2)52 bool mentions(QualType type1, QualType type2)
53 {
54     auto t1 = type1;
55     for (;;)
56     {
57         if (auto const t2 = t1->getAs<ReferenceType>())
58         {
59             t1 = t2->getPointeeType();
60         }
61         else if (auto const t3 = t1->getAs<clang::PointerType>())
62         {
63             t1 = t3->getPointeeType();
64         }
65         else if (auto const t4 = t1->getAsArrayTypeUnsafe())
66         {
67             t1 = t4->getElementType();
68         }
69         else
70         {
71             break;
72         }
73     }
74     if (t1.getCanonicalType().getTypePtr() == type2.getTypePtr())
75     {
76         return true;
77     }
78     if (auto const t2 = t1->getAs<TemplateSpecializationType>())
79     {
80         for (auto a = t2->begin(); a != t2->end(); ++a)
81         {
82             if (a->getKind() != TemplateArgument::Type)
83             {
84                 continue;
85             }
86             if (mentions(a->getAsType(), type2))
87             {
88                 return true;
89             }
90         }
91         auto const t3 = t2->desugar();
92         if (t3.getTypePtr() == t2)
93         {
94             return false;
95         }
96         return mentions(t3, type2);
97     }
98     if (auto const t2 = t1->getAs<FunctionProtoType>())
99     {
100         if (mentions(t2->getReturnType(), type2))
101         {
102             return true;
103         }
104         for (auto t3 = t2->param_type_begin(); t3 != t2->param_type_end(); ++t3)
105         {
106             if (mentions(*t3, type2))
107             {
108                 return true;
109             }
110         }
111         return false;
112     }
113     if (auto const t2 = t1->getAs<MemberPointerType>())
114     {
115         if (t2->getClass()->getUnqualifiedDesugaredType() == type2.getTypePtr())
116         {
117             return true;
118         }
119         return mentions(t2->getPointeeType(), type2);
120     }
121     return false;
122 }
123 
hasSalDllpublicExportAttr(Decl const * decl)124 bool hasSalDllpublicExportAttr(Decl const* decl)
125 {
126     if (auto const attr = decl->getAttr<VisibilityAttr>())
127     {
128         return attr->getVisibility() == VisibilityAttr::Default;
129     }
130     return decl->hasAttr<DLLExportAttr>();
131 }
132 
133 class External : public loplugin::FilteringPlugin<External>
134 {
135 public:
External(loplugin::InstantiationData const & data)136     explicit External(loplugin::InstantiationData const& data)
137         : FilteringPlugin(data)
138     {
139     }
140 
run()141     void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
142 
VisitTagDecl(TagDecl * decl)143     bool VisitTagDecl(TagDecl* decl)
144     {
145         if (isa<ClassTemplateSpecializationDecl>(decl))
146         {
147             return true;
148         }
149         if (!decl->isThisDeclarationADefinition())
150         {
151             return true;
152         }
153         if (isa<CXXRecordDecl>(decl->getDeclContext()))
154         {
155             return true;
156         }
157         if (!compiler.getLangOpts().CPlusPlus)
158         {
159             return true;
160         }
161         if (auto const d = dyn_cast<CXXRecordDecl>(decl))
162         {
163             if (d->getDescribedClassTemplate() != nullptr)
164             {
165                 return true;
166             }
167             if (hasSalDllpublicExportAttr(d))
168             {
169                 // If the class definition has explicit default visibility, then assume that it
170                 // needs to be present (e.g., a backwards-compatibility stub like in
171                 // cppuhelper/source/compat.cxx):
172                 return true;
173             }
174             if (derivesFromTestFixture(d))
175             {
176                 // The names of CppUnit tests (that can be specified with CPPUNIT_TEST_NAME) are
177                 // tied to the fully-qualified names of classes derived from CppUnit::TestFixture,
178                 // so avoid unnamed namespaces in those classes' names:
179                 return true;
180             }
181         }
182         return handleDeclaration(decl);
183     }
184 
VisitFunctionDecl(FunctionDecl * decl)185     bool VisitFunctionDecl(FunctionDecl* decl)
186     {
187         if (isa<CXXMethodDecl>(decl))
188         {
189             return true;
190         }
191         if (decl->getTemplatedKind() != FunctionDecl::TK_NonTemplate)
192         {
193             return true;
194         }
195         if (!decl->isThisDeclarationADefinition())
196         {
197             return true;
198         }
199         if (decl->isMain() || decl->isMSVCRTEntryPoint())
200         {
201             return true;
202         }
203         if (loplugin::hasCLanguageLinkageType(decl)
204             && loplugin::DeclCheck(decl).Function("_DllMainCRTStartup").GlobalNamespace())
205         {
206             return true;
207         }
208         // If the function definition is explicit marked SAL_DLLPUBLIC_EXPORT or similar, then
209         // assume that it needs to be present (e.g., only called via dlopen, or a backwards-
210         // compatibility stub like in sal/osl/all/compat.cxx):
211         if (hasSalDllpublicExportAttr(decl))
212         {
213             return true;
214         }
215         auto const canon = decl->getCanonicalDecl();
216         if (loplugin::hasCLanguageLinkageType(canon)
217             && (canon->hasAttr<ConstructorAttr>() || canon->hasAttr<DestructorAttr>()))
218         {
219             return true;
220         }
221         if (compiler.getDiagnostics().getDiagnosticLevel(diag::warn_unused_function,
222                                                          decl->getLocation())
223             < DiagnosticsEngine::Warning)
224         {
225             // Don't warn about e.g.
226             //
227             //  G_DEFINE_TYPE (GLOAction, g_lo_action, G_TYPE_OBJECT);
228             //
229             // in vcl/unx/gtk/gloactiongroup.cxx (which expands to non-static g_lo_action_get_type
230             // function definition), which is already wrapped in
231             //
232             //  #pragma GCC diagnostic ignored "-Wunused-function"
233             return true;
234         }
235         if (isInjectedFunction(decl))
236         {
237             return true;
238         }
239         return handleDeclaration(decl);
240     }
241 
VisitVarDecl(VarDecl * decl)242     bool VisitVarDecl(VarDecl* decl)
243     {
244         if (decl->isStaticDataMember())
245         {
246             return true;
247         }
248         if (isa<VarTemplateSpecializationDecl>(decl))
249         {
250             return true;
251         }
252         if (!decl->isThisDeclarationADefinition())
253         {
254             return true;
255         }
256         if (loplugin::DeclCheck(decl).Var("_pRawDllMain").GlobalNamespace())
257         {
258             return true;
259         }
260         return handleDeclaration(decl);
261     }
262 
VisitClassTemplateDecl(ClassTemplateDecl * decl)263     bool VisitClassTemplateDecl(ClassTemplateDecl* decl)
264     {
265         if (!decl->isThisDeclarationADefinition())
266         {
267             return true;
268         }
269         if (isa<CXXRecordDecl>(decl->getDeclContext()))
270         {
271             return true;
272         }
273         return handleDeclaration(decl);
274     }
275 
VisitFunctionTemplateDecl(FunctionTemplateDecl * decl)276     bool VisitFunctionTemplateDecl(FunctionTemplateDecl* decl)
277     {
278         if (!decl->isThisDeclarationADefinition())
279         {
280             return true;
281         }
282         if (isa<CXXRecordDecl>(decl->getDeclContext()))
283         {
284             return true;
285         }
286         if (isInjectedFunction(decl->getTemplatedDecl()))
287         {
288             return true;
289         }
290         return handleDeclaration(decl);
291     }
292 
VisitVarTemplateDecl(VarTemplateDecl * decl)293     bool VisitVarTemplateDecl(VarTemplateDecl* decl)
294     {
295         if (!decl->isThisDeclarationADefinition())
296         {
297             return true;
298         }
299         return handleDeclaration(decl);
300     }
301 
302 private:
reportSpecializations(T specializations)303     template <typename T> void reportSpecializations(T specializations)
304     {
305         for (auto const d : specializations)
306         {
307             auto const k = d->getTemplateSpecializationKind();
308             if (isTemplateExplicitInstantiationOrSpecialization(k))
309             {
310                 report(DiagnosticsEngine::Note,
311                        "explicit %select{instantiation|specialization}0 is here", d->getLocation())
312                     << (k == TSK_ExplicitSpecialization) << d->getSourceRange();
313             }
314         }
315     }
316 
computeAffectedTypes(Decl const * decl,std::vector<QualType> * affected)317     void computeAffectedTypes(Decl const* decl, std::vector<QualType>* affected)
318     {
319         assert(affected != nullptr);
320         if (auto const d = dyn_cast<EnumDecl>(decl))
321         {
322             affected->push_back(compiler.getASTContext().getEnumType(d));
323         }
324         else
325         {
326             //TODO: Derived types are also affected!
327             CXXRecordDecl const* rec;
328             if (auto const d = dyn_cast<ClassTemplateDecl>(decl))
329             {
330                 rec = d->getTemplatedDecl();
331             }
332             else
333             {
334                 rec = cast<CXXRecordDecl>(decl);
335             }
336             affected->push_back(compiler.getASTContext().getRecordType(rec));
337             for (auto d = rec->decls_begin(); d != rec->decls_end(); ++d)
338             {
339                 if (*d != (*d)->getCanonicalDecl())
340                 {
341                     continue;
342                 }
343                 if (isa<TagDecl>(*d) || isa<ClassTemplateDecl>(*d))
344                 {
345                     if (auto const d1 = dyn_cast<RecordDecl>(*d))
346                     {
347                         if (d1->isInjectedClassName())
348                         {
349                             continue;
350                         }
351                     }
352                     computeAffectedTypes(*d, affected);
353                 }
354             }
355         }
356     }
357 
reportAssociatingFunctions(std::vector<QualType> const & affected,Decl * decl)358     void reportAssociatingFunctions(std::vector<QualType> const& affected, Decl* decl)
359     {
360         auto c = decl->getDeclContext();
361         while (isa<LinkageSpecDecl>(c) || c->isInlineNamespace())
362         {
363             c = c->getParent();
364         }
365         assert(c->isTranslationUnit() || c->isNamespace());
366         SmallVector<DeclContext*, 2> parts;
367         c->collectAllContexts(parts);
368         std::list<DeclContext const*> ctxs;
369         std::copy(parts.begin(), parts.end(),
370                   std::back_insert_iterator<std::list<DeclContext const*>>(ctxs));
371         if (auto const d = dyn_cast<CXXRecordDecl>(decl))
372         {
373             // To find friend functions declared in the class:
374             ctxs.push_back(d);
375         }
376         std::set<FunctionDecl const*> fdecls; // to report every function just once
377         for (auto ctx = ctxs.begin(); ctx != ctxs.end(); ++ctx)
378         {
379             for (auto i = (*ctx)->decls_begin(); i != (*ctx)->decls_end(); ++i)
380             {
381                 auto d = *i;
382                 if (auto const d1 = dyn_cast<LinkageSpecDecl>(d))
383                 {
384                     ctxs.push_back(d1);
385                     continue;
386                 }
387                 if (auto const d1 = dyn_cast<NamespaceDecl>(d))
388                 {
389                     if (d1->isInline())
390                     {
391                         ctxs.push_back(d1);
392                     }
393                     continue;
394                 }
395                 if (auto const d1 = dyn_cast<FriendDecl>(d))
396                 {
397                     d = d1->getFriendDecl();
398                     if (d == nullptr) // happens for 'friend struct S;'
399                     {
400                         continue;
401                     }
402                 }
403                 FunctionDecl const* f;
404                 if (auto const d1 = dyn_cast<FunctionTemplateDecl>(d))
405                 {
406                     f = d1->getTemplatedDecl();
407                 }
408                 else
409                 {
410                     f = dyn_cast<FunctionDecl>(d);
411                     if (f == nullptr)
412                     {
413                         continue;
414                     }
415                 }
416                 if (!fdecls.insert(f->getCanonicalDecl()).second)
417                 {
418                     continue;
419                 }
420                 if (isa<CXXMethodDecl>(f))
421                 {
422                     continue;
423                 }
424                 for (auto const& t : affected)
425                 {
426                     auto const tc = t.getCanonicalType();
427                     for (auto p = f->param_begin(); p != f->param_end(); ++p)
428                     {
429                         if (mentions((*p)->getType(), tc))
430                         {
431                             report(DiagnosticsEngine::Note,
432                                    "a %select{function|function template|function template "
433                                    "specialization}0 associating %1 is declared here",
434                                    f->getLocation())
435                                 << (f->isFunctionTemplateSpecialization()
436                                         ? 2
437                                         : f->getDescribedFunctionTemplate() != nullptr ? 1 : 0)
438                                 << t << f->getSourceRange();
439                             for (auto f1 = f->redecls_begin(); f1 != f->redecls_end(); ++f1)
440                             {
441                                 if (*f1 == f)
442                                 {
443                                     continue;
444                                 }
445                                 report(DiagnosticsEngine::Note, "another declaration is here",
446                                        f1->getLocation())
447                                     << f1->getSourceRange();
448                             }
449                             break;
450                         }
451                     }
452                 }
453             }
454         }
455     }
456 
reportAssociatingFunctions(Decl * decl)457     void reportAssociatingFunctions(Decl* decl)
458     {
459         std::vector<QualType> affected; // enum/class/class template + recursively affected members
460         computeAffectedTypes(decl, &affected);
461         reportAssociatingFunctions(affected, decl);
462     }
463 
handleDeclaration(NamedDecl * decl)464     bool handleDeclaration(NamedDecl* decl)
465     {
466         if (ignoreLocation(decl))
467         {
468             return true;
469         }
470         if (decl->getLinkageInternal() < ModuleLinkage)
471         {
472             return true;
473         }
474         // In some cases getLinkageInternal() arguably wrongly reports ExternalLinkage, see the
475         // commit message of <https://github.com/llvm/llvm-project/commit/
476         // df963a38a9e27fc43b485dfdf52bc1b090087e06> "DR1113: anonymous namespaces formally give
477         // their contents internal linkage":
478         //
479         //  "We still deviate from the standard in one regard here: extern "C" declarations
480         //   in anonymous namespaces are still granted external linkage. Changing those does
481         //   not appear to have been an intentional consequence of the standard change in
482         //   DR1113."
483         //
484         // Do not warn about such "wrongly external" declarations here:
485         if (decl->isInAnonymousNamespace())
486         {
487             return true;
488         }
489         for (Decl const* d = decl; d != nullptr; d = d->getPreviousDecl())
490         {
491             if (!compiler.getSourceManager().isInMainFile(d->getLocation()))
492             {
493                 return true;
494             }
495         }
496         if (compiler.getSourceManager().isMacroBodyExpansion(decl->getLocation()))
497         {
498             if (Lexer::getImmediateMacroName(decl->getLocation(), compiler.getSourceManager(),
499                                              compiler.getLangOpts())
500                 == "MDDS_MTV_DEFINE_ELEMENT_CALLBACKS")
501             {
502                 // Even wrapping in an unnamed namespace or sneaking "static" into the macro
503                 // wouldn't help, as then some of the functions it defines would be flagged as
504                 // unused:
505                 return true;
506             }
507         }
508         else if (compiler.getSourceManager().isMacroArgExpansion(decl->getLocation()))
509         {
510             if (Lexer::getImmediateMacroName(decl->getLocation(), compiler.getSourceManager(),
511                                              compiler.getLangOpts())
512                 == "DEFINE_GUID")
513             {
514                 // Windows, guiddef.h:
515                 return true;
516             }
517         }
518         TypedefNameDecl const* typedefed = nullptr;
519         if (auto const d = dyn_cast<TagDecl>(decl))
520         {
521             typedefed = d->getTypedefNameForAnonDecl();
522         }
523         bool canStatic;
524         if (auto const d = dyn_cast<CXXRecordDecl>(decl))
525         {
526             canStatic = d->isUnion() && d->isAnonymousStructOrUnion();
527         }
528         else
529         {
530             canStatic = isa<FunctionDecl>(decl) || isa<VarDecl>(decl)
531                         || isa<FunctionTemplateDecl>(decl) || isa<VarTemplateDecl>(decl);
532         }
533         // In general, moving functions into an unnamed namespace can: break ADL like in
534         //
535         //   struct S1 { int f() { return 1; } };
536         //   int f(S1 s) { return s.f(); }
537         //   namespace N {
538         //     struct S2: S1 { int f() { return 0; } };
539         //     int f(S2 s) { return s.f(); } // [*]
540         //   }
541         //   int main() { return f(N::S2()); }
542         //
543         // changing from returning 0 to returning 1 when [*] is moved into an unnamed namespace; can
544         // conflict with function declarations in the moved function like in
545         //
546         //   int f(int) { return 0; }
547         //   namespace { int f(int) { return 1; } }
548         //   int g() { // [*]
549         //     int f(int);
550         //     return f(0);
551         //   }
552         //   int main() { return g(); }
553         //
554         // changing from returning 0 to returning 1 when [*] is moved into an unnamed namespace; and
555         // can conflict with overload resolution in general like in
556         //
557         //   int f(int) { return 0; }
558         //   namespace { int f(...) { return 1; } }
559         //   int g() { return f(0); } // [*]
560         //   int main() { return g(); }
561         //
562         // changing from returning 0 to returning 1 when [*] is moved into an unnamed namespace:
563         auto const canUnnamed = compiler.getLangOpts().CPlusPlus
564                                 && !(isa<FunctionDecl>(decl) || isa<FunctionTemplateDecl>(decl));
565         assert(canStatic || canUnnamed);
566         report(
567             DiagnosticsEngine::Warning,
568             ("externally available%select{| typedef'ed}0 entity %1 is not previously declared in an"
569              " included file (if it is only used in this translation unit,"
570              " %select{|make it static}2%select{| or }3%select{|put it in an unnamed namespace}4;"
571              " otherwise, provide a declaration of it in an included file)"),
572             decl->getLocation())
573             << (typedefed != nullptr) << (typedefed == nullptr ? decl : typedefed) << canStatic
574             << (canStatic && canUnnamed) << canUnnamed << decl->getSourceRange();
575         for (auto d = decl->redecls_begin(); d != decl->redecls_end(); ++d)
576         {
577             if (*d == decl)
578             {
579                 continue;
580             }
581             report(DiagnosticsEngine::Note, "another declaration is here", d->getLocation())
582                 << d->getSourceRange();
583         }
584         //TODO: Class template specializations can be in the enclosing namespace, so no need to
585         // list them here (as they won't need to be put into the unnamed namespace too, unlike for
586         // specializations of function and variable templates); and explicit function template
587         // specializations cannot have storage-class specifiers, so as we only suggest to make
588         // function templates static (but not to move them into an unnamed namespace), no need to
589         // list function template specializations here, either:
590         if (auto const d = dyn_cast<VarTemplateDecl>(decl))
591         {
592             reportSpecializations(d->specializations());
593         }
594         if (isa<TagDecl>(decl) || isa<ClassTemplateDecl>(decl))
595         {
596             reportAssociatingFunctions(decl);
597         }
598         return true;
599     }
600 };
601 
602 loplugin::Plugin::Registration<External> external("external");
603 
604 } // namespace
605 
606 #endif // LO_CLANG_SHARED_PLUGINS
607 
608 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
609