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