Version 24.2.2.2, tag libreoffice-24.2.2.2
[LibreOffice.git] / compilerplugins / clang / external.cxx
blob8c873355390797348339644c18060b4f0a014e1b
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.
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
11 #include <algorithm>
12 #include <cassert>
13 #include <iterator>
14 #include <list>
15 #include <set>
17 #include "clang/Sema/SemaDiagnostic.h"
19 #include "check.hxx"
20 #include "compat.hxx"
21 #include "plugin.hxx"
23 namespace
25 bool derivesFromTestFixture(CXXRecordDecl const* decl)
27 static auto const pred = [](CXXBaseSpecifier const& spec) {
28 if (auto const t = spec.getType()->getAs<RecordType>())
29 { // (may be a template parameter)
30 return derivesFromTestFixture(dyn_cast<CXXRecordDecl>(t->getDecl()));
32 return false;
34 return loplugin::DeclCheck(decl).Class("TestFixture").Namespace("CppUnit").GlobalNamespace()
35 || std::any_of(decl->bases_begin(), decl->bases_end(), pred)
36 || std::any_of(decl->vbases_begin(), decl->vbases_end(), pred);
39 bool isInjectedFunction(FunctionDecl const* decl)
41 for (auto d = decl->redecls_begin(); d != decl->redecls_end(); ++d)
43 auto const c = d->getLexicalDeclContext();
44 if (!(c->isFunctionOrMethod() || c->isRecord()))
46 return false;
49 return true;
52 // Whether type1 mentions type2 (in a way relevant for argument-dependent name lookup):
53 bool mentions(QualType type1, QualType type2)
55 auto t1 = type1;
56 for (;;)
58 if (auto const t2 = t1->getAs<ReferenceType>())
60 t1 = t2->getPointeeType();
62 else if (auto const t3 = t1->getAs<clang::PointerType>())
64 t1 = t3->getPointeeType();
66 else if (auto const t4 = t1->getAsArrayTypeUnsafe())
68 t1 = t4->getElementType();
70 else
72 break;
75 if (t1.getCanonicalType().getTypePtr() == type2.getTypePtr())
77 return true;
79 if (auto const t2 = t1->getAs<TemplateSpecializationType>())
81 auto const args = t2->template_arguments();
82 for (auto a = args.begin(); a != args.end(); ++a)
84 if (a->getKind() != TemplateArgument::Type)
86 continue;
88 if (mentions(a->getAsType(), type2))
90 return true;
93 auto const t3 = t2->desugar();
94 if (t3.getTypePtr() == t2)
96 return false;
98 return mentions(t3, type2);
100 if (auto const t2 = t1->getAs<FunctionProtoType>())
102 if (mentions(t2->getReturnType(), type2))
104 return true;
106 for (auto t3 = t2->param_type_begin(); t3 != t2->param_type_end(); ++t3)
108 if (mentions(*t3, type2))
110 return true;
113 return false;
115 if (auto const t2 = t1->getAs<MemberPointerType>())
117 if (t2->getClass()->getUnqualifiedDesugaredType() == type2.getTypePtr())
119 return true;
121 return mentions(t2->getPointeeType(), type2);
123 return false;
126 bool hasSalDllpublicExportAttr(Decl const* decl)
128 if (auto const attr = decl->getAttr<VisibilityAttr>())
130 return attr->getVisibility() == VisibilityAttr::Default;
132 return decl->hasAttr<DLLExportAttr>();
135 class External : public loplugin::FilteringPlugin<External>
137 public:
138 explicit External(loplugin::InstantiationData const& data)
139 : FilteringPlugin(data)
143 void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
145 bool VisitTagDecl(TagDecl* decl)
147 if (isa<ClassTemplateSpecializationDecl>(decl))
149 return true;
151 if (!decl->isThisDeclarationADefinition())
153 return true;
155 if (isa<CXXRecordDecl>(decl->getDeclContext()))
157 return true;
159 if (!compiler.getLangOpts().CPlusPlus)
161 return true;
163 if (auto const d = dyn_cast<CXXRecordDecl>(decl))
165 if (d->getDescribedClassTemplate() != nullptr)
167 return true;
169 if (hasSalDllpublicExportAttr(d))
171 // If the class definition has explicit default visibility, then assume that it
172 // needs to be present (e.g., a backwards-compatibility stub like in
173 // cppuhelper/source/compat.cxx):
174 return true;
176 if (derivesFromTestFixture(d))
178 // The names of CppUnit tests (that can be specified with CPPUNIT_TEST_NAME) are
179 // tied to the fully-qualified names of classes derived from CppUnit::TestFixture,
180 // so avoid unnamed namespaces in those classes' names:
181 return true;
184 return handleDeclaration(decl);
187 bool VisitFunctionDecl(FunctionDecl* decl)
189 if (isa<CXXMethodDecl>(decl))
191 return true;
193 if (decl->getTemplatedKind() != FunctionDecl::TK_NonTemplate)
195 return true;
197 if (!decl->isThisDeclarationADefinition())
199 return true;
201 if (decl->isMain() || decl->isMSVCRTEntryPoint())
203 return true;
205 if (loplugin::hasCLanguageLinkageType(decl)
206 && loplugin::DeclCheck(decl).Function("_DllMainCRTStartup").GlobalNamespace())
208 return true;
210 // If the function definition is explicit marked SAL_DLLPUBLIC_EXPORT or similar, then
211 // assume that it needs to be present (e.g., only called via dlopen, or a backwards-
212 // compatibility stub like in sal/osl/all/compat.cxx):
213 if (hasSalDllpublicExportAttr(decl))
215 return true;
217 auto const canon = decl->getCanonicalDecl();
218 if (loplugin::hasCLanguageLinkageType(canon)
219 && (canon->hasAttr<ConstructorAttr>() || canon->hasAttr<DestructorAttr>()))
221 return true;
223 if (compiler.getDiagnostics().getDiagnosticLevel(diag::warn_unused_function,
224 decl->getLocation())
225 < DiagnosticsEngine::Warning)
227 // Don't warn about e.g.
229 // G_DEFINE_TYPE (GLOAction, g_lo_action, G_TYPE_OBJECT);
231 // in vcl/unx/gtk/gloactiongroup.cxx (which expands to non-static g_lo_action_get_type
232 // function definition), which is already wrapped in
234 // #pragma GCC diagnostic ignored "-Wunused-function"
235 return true;
237 if (isInjectedFunction(decl))
239 return true;
241 return handleDeclaration(decl);
244 bool VisitVarDecl(VarDecl* decl)
246 if (decl->isStaticDataMember())
248 return true;
250 if (isa<VarTemplateSpecializationDecl>(decl))
252 return true;
254 if (!decl->isThisDeclarationADefinition())
256 return true;
258 if (loplugin::DeclCheck(decl).Var("_pRawDllMain").GlobalNamespace())
260 return true;
262 return handleDeclaration(decl);
265 bool VisitClassTemplateDecl(ClassTemplateDecl* decl)
267 if (!decl->isThisDeclarationADefinition())
269 return true;
271 if (isa<CXXRecordDecl>(decl->getDeclContext()))
273 return true;
275 return handleDeclaration(decl);
278 bool VisitFunctionTemplateDecl(FunctionTemplateDecl* decl)
280 if (!decl->isThisDeclarationADefinition())
282 return true;
284 if (isa<CXXRecordDecl>(decl->getDeclContext()))
286 return true;
288 if (isInjectedFunction(decl->getTemplatedDecl()))
290 return true;
292 return handleDeclaration(decl);
295 bool VisitVarTemplateDecl(VarTemplateDecl* decl)
297 if (!decl->isThisDeclarationADefinition())
299 return true;
301 return handleDeclaration(decl);
304 private:
305 template <typename T> void reportSpecializations(T specializations)
307 for (auto const d : specializations)
309 auto const k = d->getTemplateSpecializationKind();
310 if (isTemplateExplicitInstantiationOrSpecialization(k))
312 report(DiagnosticsEngine::Note,
313 "explicit %select{instantiation|specialization}0 is here", d->getLocation())
314 << (k == TSK_ExplicitSpecialization) << d->getSourceRange();
319 void computeAffectedTypes(Decl const* decl, std::vector<QualType>* affected)
321 assert(affected != nullptr);
322 if (auto const d = dyn_cast<EnumDecl>(decl))
324 affected->push_back(compiler.getASTContext().getEnumType(d));
326 else
328 //TODO: Derived types are also affected!
329 CXXRecordDecl const* rec;
330 if (auto const d = dyn_cast<ClassTemplateDecl>(decl))
332 rec = d->getTemplatedDecl();
334 else
336 rec = cast<CXXRecordDecl>(decl);
338 affected->push_back(compiler.getASTContext().getRecordType(rec));
339 for (auto d = rec->decls_begin(); d != rec->decls_end(); ++d)
341 if (*d != (*d)->getCanonicalDecl())
343 continue;
345 if (isa<TagDecl>(*d) || isa<ClassTemplateDecl>(*d))
347 if (auto const d1 = dyn_cast<RecordDecl>(*d))
349 if (d1->isInjectedClassName())
351 continue;
354 computeAffectedTypes(*d, affected);
360 void reportAssociatingFunctions(std::vector<QualType> const& affected, Decl* decl)
362 auto c = decl->getDeclContext();
363 while (isa<LinkageSpecDecl>(c) || c->isInlineNamespace())
365 c = c->getParent();
367 assert(c->isTranslationUnit() || c->isNamespace());
368 SmallVector<DeclContext*, 2> parts;
369 c->collectAllContexts(parts);
370 std::list<DeclContext const*> ctxs;
371 std::copy(parts.begin(), parts.end(),
372 std::back_insert_iterator<std::list<DeclContext const*>>(ctxs));
373 if (auto const d = dyn_cast<CXXRecordDecl>(decl))
375 // To find friend functions declared in the class:
376 ctxs.push_back(d);
378 std::set<FunctionDecl const*> fdecls; // to report every function just once
379 for (auto ctx = ctxs.begin(); ctx != ctxs.end(); ++ctx)
381 for (auto i = (*ctx)->decls_begin(); i != (*ctx)->decls_end(); ++i)
383 auto d = *i;
384 if (auto const d1 = dyn_cast<LinkageSpecDecl>(d))
386 ctxs.push_back(d1);
387 continue;
389 if (auto const d1 = dyn_cast<NamespaceDecl>(d))
391 if (d1->isInline())
393 ctxs.push_back(d1);
395 continue;
397 if (auto const d1 = dyn_cast<FriendDecl>(d))
399 d = d1->getFriendDecl();
400 if (d == nullptr) // happens for 'friend struct S;'
402 continue;
405 FunctionDecl const* f;
406 if (auto const d1 = dyn_cast<FunctionTemplateDecl>(d))
408 f = d1->getTemplatedDecl();
410 else
412 f = dyn_cast<FunctionDecl>(d);
413 if (f == nullptr)
415 continue;
418 if (!fdecls.insert(f->getCanonicalDecl()).second)
420 continue;
422 if (isa<CXXMethodDecl>(f))
424 continue;
426 for (auto const& t : affected)
428 auto const tc = t.getCanonicalType();
429 for (auto p = f->param_begin(); p != f->param_end(); ++p)
431 if (mentions((*p)->getType(), tc))
433 report(DiagnosticsEngine::Note,
434 "a %select{function|function template|function template "
435 "specialization}0 associating %1 is declared here",
436 f->getLocation())
437 << (f->isFunctionTemplateSpecialization()
439 : f->getDescribedFunctionTemplate() != nullptr ? 1 : 0)
440 << t << f->getSourceRange();
441 for (auto f1 = f->redecls_begin(); f1 != f->redecls_end(); ++f1)
443 if (*f1 == f)
445 continue;
447 report(DiagnosticsEngine::Note, "another declaration is here",
448 f1->getLocation())
449 << f1->getSourceRange();
451 break;
459 void reportAssociatingFunctions(Decl* decl)
461 std::vector<QualType> affected; // enum/class/class template + recursively affected members
462 computeAffectedTypes(decl, &affected);
463 reportAssociatingFunctions(affected, decl);
466 bool handleDeclaration(NamedDecl* decl)
468 if (ignoreLocation(decl))
470 return true;
472 if (decl->getLinkageInternal() < compat::Linkage::Module)
474 return true;
476 // In some cases getLinkageInternal() arguably wrongly reports ExternalLinkage, see the
477 // commit message of <https://github.com/llvm/llvm-project/commit/
478 // df963a38a9e27fc43b485dfdf52bc1b090087e06> "DR1113: anonymous namespaces formally give
479 // their contents internal linkage":
481 // "We still deviate from the standard in one regard here: extern "C" declarations
482 // in anonymous namespaces are still granted external linkage. Changing those does
483 // not appear to have been an intentional consequence of the standard change in
484 // DR1113."
486 // Do not warn about such "wrongly external" declarations here:
487 if (decl->isInAnonymousNamespace())
489 return true;
491 for (Decl const* d = decl; d != nullptr; d = d->getPreviousDecl())
493 if (!compiler.getSourceManager().isInMainFile(d->getLocation()))
495 return true;
498 if (compiler.getSourceManager().isMacroBodyExpansion(decl->getLocation()))
500 if (Lexer::getImmediateMacroName(decl->getLocation(), compiler.getSourceManager(),
501 compiler.getLangOpts())
502 == "MDDS_MTV_DEFINE_ELEMENT_CALLBACKS")
504 // Even wrapping in an unnamed namespace or sneaking "static" into the macro
505 // wouldn't help, as then some of the functions it defines would be flagged as
506 // unused:
507 return true;
510 else if (compiler.getSourceManager().isMacroArgExpansion(decl->getLocation()))
512 if (Lexer::getImmediateMacroName(decl->getLocation(), compiler.getSourceManager(),
513 compiler.getLangOpts())
514 == "DEFINE_GUID")
516 // Windows, guiddef.h:
517 return true;
520 TypedefNameDecl const* typedefed = nullptr;
521 if (auto const d = dyn_cast<TagDecl>(decl))
523 typedefed = d->getTypedefNameForAnonDecl();
525 bool canStatic;
526 if (auto const d = dyn_cast<CXXRecordDecl>(decl))
528 canStatic = d->isUnion() && d->isAnonymousStructOrUnion();
530 else
532 canStatic = isa<FunctionDecl>(decl) || isa<VarDecl>(decl)
533 || isa<FunctionTemplateDecl>(decl) || isa<VarTemplateDecl>(decl);
535 // In general, moving functions into an unnamed namespace can: break ADL like in
537 // struct S1 { int f() { return 1; } };
538 // int f(S1 s) { return s.f(); }
539 // namespace N {
540 // struct S2: S1 { int f() { return 0; } };
541 // int f(S2 s) { return s.f(); } // [*]
542 // }
543 // int main() { return f(N::S2()); }
545 // changing from returning 0 to returning 1 when [*] is moved into an unnamed namespace; can
546 // conflict with function declarations in the moved function like in
548 // int f(int) { return 0; }
549 // namespace { int f(int) { return 1; } }
550 // int g() { // [*]
551 // int f(int);
552 // return f(0);
553 // }
554 // int main() { return g(); }
556 // changing from returning 0 to returning 1 when [*] is moved into an unnamed namespace; and
557 // can conflict with overload resolution in general like in
559 // int f(int) { return 0; }
560 // namespace { int f(...) { return 1; } }
561 // int g() { return f(0); } // [*]
562 // int main() { return g(); }
564 // changing from returning 0 to returning 1 when [*] is moved into an unnamed namespace:
565 auto const canUnnamed = compiler.getLangOpts().CPlusPlus
566 && !(isa<FunctionDecl>(decl) || isa<FunctionTemplateDecl>(decl));
567 assert(canStatic || canUnnamed);
568 report(
569 DiagnosticsEngine::Warning,
570 ("externally available%select{| typedef'ed}0 entity %1 is not previously declared in an"
571 " included file (if it is only used in this translation unit,"
572 " %select{|make it static}2%select{| or }3%select{|put it in an unnamed namespace}4;"
573 " otherwise, provide a declaration of it in an included file)"),
574 decl->getLocation())
575 << (typedefed != nullptr) << (typedefed == nullptr ? decl : typedefed) << canStatic
576 << (canStatic && canUnnamed) << canUnnamed << decl->getSourceRange();
577 for (auto d = decl->redecls_begin(); d != decl->redecls_end(); ++d)
579 if (*d == decl)
581 continue;
583 report(DiagnosticsEngine::Note, "another declaration is here", d->getLocation())
584 << d->getSourceRange();
586 //TODO: Class template specializations can be in the enclosing namespace, so no need to
587 // list them here (as they won't need to be put into the unnamed namespace too, unlike for
588 // specializations of function and variable templates); and explicit function template
589 // specializations cannot have storage-class specifiers, so as we only suggest to make
590 // function templates static (but not to move them into an unnamed namespace), no need to
591 // list function template specializations here, either:
592 if (auto const d = dyn_cast<VarTemplateDecl>(decl))
594 reportSpecializations(d->specializations());
596 if (isa<TagDecl>(decl) || isa<ClassTemplateDecl>(decl))
598 reportAssociatingFunctions(decl);
600 return true;
604 loplugin::Plugin::Registration<External> external("external");
606 } // namespace
608 #endif // LO_CLANG_SHARED_PLUGINS
610 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */