1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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/.
9 #ifndef LO_CLANG_SHARED_PLUGINS
17 #include "clang/Sema/SemaDiagnostic.h"
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()));
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()))
52 // Whether type1 mentions type2 (in a way relevant for argument-dependent name lookup):
53 bool mentions(QualType type1
, QualType type2
)
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();
75 if (t1
.getCanonicalType().getTypePtr() == type2
.getTypePtr())
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
)
88 if (mentions(a
->getAsType(), type2
))
93 auto const t3
= t2
->desugar();
94 if (t3
.getTypePtr() == t2
)
98 return mentions(t3
, type2
);
100 if (auto const t2
= t1
->getAs
<FunctionProtoType
>())
102 if (mentions(t2
->getReturnType(), type2
))
106 for (auto t3
= t2
->param_type_begin(); t3
!= t2
->param_type_end(); ++t3
)
108 if (mentions(*t3
, type2
))
115 if (auto const t2
= t1
->getAs
<MemberPointerType
>())
117 if (t2
->getClass()->getUnqualifiedDesugaredType() == type2
.getTypePtr())
121 return mentions(t2
->getPointeeType(), type2
);
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
>
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
))
151 if (!decl
->isThisDeclarationADefinition())
155 if (isa
<CXXRecordDecl
>(decl
->getDeclContext()))
159 if (!compiler
.getLangOpts().CPlusPlus
)
163 if (auto const d
= dyn_cast
<CXXRecordDecl
>(decl
))
165 if (d
->getDescribedClassTemplate() != nullptr)
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):
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:
184 return handleDeclaration(decl
);
187 bool VisitFunctionDecl(FunctionDecl
* decl
)
189 if (isa
<CXXMethodDecl
>(decl
))
193 if (decl
->getTemplatedKind() != FunctionDecl::TK_NonTemplate
)
197 if (!decl
->isThisDeclarationADefinition())
201 if (decl
->isMain() || decl
->isMSVCRTEntryPoint())
205 if (loplugin::hasCLanguageLinkageType(decl
)
206 && loplugin::DeclCheck(decl
).Function("_DllMainCRTStartup").GlobalNamespace())
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
))
217 auto const canon
= decl
->getCanonicalDecl();
218 if (loplugin::hasCLanguageLinkageType(canon
)
219 && (canon
->hasAttr
<ConstructorAttr
>() || canon
->hasAttr
<DestructorAttr
>()))
223 if (compiler
.getDiagnostics().getDiagnosticLevel(diag::warn_unused_function
,
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"
237 if (isInjectedFunction(decl
))
241 return handleDeclaration(decl
);
244 bool VisitVarDecl(VarDecl
* decl
)
246 if (decl
->isStaticDataMember())
250 if (isa
<VarTemplateSpecializationDecl
>(decl
))
254 if (!decl
->isThisDeclarationADefinition())
258 if (loplugin::DeclCheck(decl
).Var("_pRawDllMain").GlobalNamespace())
262 return handleDeclaration(decl
);
265 bool VisitClassTemplateDecl(ClassTemplateDecl
* decl
)
267 if (!decl
->isThisDeclarationADefinition())
271 if (isa
<CXXRecordDecl
>(decl
->getDeclContext()))
275 return handleDeclaration(decl
);
278 bool VisitFunctionTemplateDecl(FunctionTemplateDecl
* decl
)
280 if (!decl
->isThisDeclarationADefinition())
284 if (isa
<CXXRecordDecl
>(decl
->getDeclContext()))
288 if (isInjectedFunction(decl
->getTemplatedDecl()))
292 return handleDeclaration(decl
);
295 bool VisitVarTemplateDecl(VarTemplateDecl
* decl
)
297 if (!decl
->isThisDeclarationADefinition())
301 return handleDeclaration(decl
);
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
));
328 //TODO: Derived types are also affected!
329 CXXRecordDecl
const* rec
;
330 if (auto const d
= dyn_cast
<ClassTemplateDecl
>(decl
))
332 rec
= d
->getTemplatedDecl();
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())
345 if (isa
<TagDecl
>(*d
) || isa
<ClassTemplateDecl
>(*d
))
347 if (auto const d1
= dyn_cast
<RecordDecl
>(*d
))
349 if (d1
->isInjectedClassName())
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())
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:
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
)
384 if (auto const d1
= dyn_cast
<LinkageSpecDecl
>(d
))
389 if (auto const d1
= dyn_cast
<NamespaceDecl
>(d
))
397 if (auto const d1
= dyn_cast
<FriendDecl
>(d
))
399 d
= d1
->getFriendDecl();
400 if (d
== nullptr) // happens for 'friend struct S;'
405 FunctionDecl
const* f
;
406 if (auto const d1
= dyn_cast
<FunctionTemplateDecl
>(d
))
408 f
= d1
->getTemplatedDecl();
412 f
= dyn_cast
<FunctionDecl
>(d
);
418 if (!fdecls
.insert(f
->getCanonicalDecl()).second
)
422 if (isa
<CXXMethodDecl
>(f
))
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",
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
)
447 report(DiagnosticsEngine::Note
, "another declaration is here",
449 << f1
->getSourceRange();
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
))
472 if (decl
->getLinkageInternal() < compat::Linkage::Module
)
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
486 // Do not warn about such "wrongly external" declarations here:
487 if (decl
->isInAnonymousNamespace())
491 for (Decl
const* d
= decl
; d
!= nullptr; d
= d
->getPreviousDecl())
493 if (!compiler
.getSourceManager().isInMainFile(d
->getLocation()))
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
510 else if (compiler
.getSourceManager().isMacroArgExpansion(decl
->getLocation()))
512 if (Lexer::getImmediateMacroName(decl
->getLocation(), compiler
.getSourceManager(),
513 compiler
.getLangOpts())
516 // Windows, guiddef.h:
520 TypedefNameDecl
const* typedefed
= nullptr;
521 if (auto const d
= dyn_cast
<TagDecl
>(decl
))
523 typedefed
= d
->getTypedefNameForAnonDecl();
526 if (auto const d
= dyn_cast
<CXXRecordDecl
>(decl
))
528 canStatic
= d
->isUnion() && d
->isAnonymousStructOrUnion();
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(); }
540 // struct S2: S1 { int f() { return 0; } };
541 // int f(S2 s) { return s.f(); } // [*]
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; } }
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
);
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)"),
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
)
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
);
604 loplugin::Plugin::Registration
<External
> external("external");
608 #endif // LO_CLANG_SHARED_PLUGINS
610 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */