1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 #ifndef LO_CLANG_SHARED_PLUGINS
20 : public loplugin::FilteringPlugin
<BadStatics
>
24 explicit BadStatics(loplugin::InstantiationData
const& rData
):
25 FilteringPlugin(rData
) {}
27 bool preRun() override
{
28 return compiler
.getLangOpts().CPlusPlus
; // no non-trivial dtors in C
33 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
37 static std::pair
<bool, std::vector
<FieldDecl
const*>> isBadStaticType(
38 QualType
const& rpType
, std::vector
<FieldDecl
const*> & chain
,
39 std::vector
<QualType
> const& rParents
)
42 if (rpType
->isAnyPointerType()) {
43 pt
= rpType
->getPointeeType();
44 } else if (auto at
= rpType
->getAsArrayTypeUnsafe()) {
45 pt
= at
->getElementType();
46 } else if (auto rt
= rpType
->getAs
<ReferenceType
>()) {
47 pt
= rt
->getPointeeType();
50 QualType
const pPointee(pt
.getUnqualifiedType().getCanonicalType());
51 auto const iter(std::find(rParents
.begin(), rParents
.end(), pPointee
));
52 if (iter
== rParents
.end())
54 std::vector
<QualType
> copy(rParents
);
55 copy
.push_back(rpType
.getUnqualifiedType().getCanonicalType());
56 return isBadStaticType(pt
, chain
, copy
);
58 return std::make_pair(false, std::vector
<FieldDecl
const*>());
61 RecordType
const*const pRecordType(rpType
->getAs
<RecordType
>());
63 return std::make_pair(false, std::vector
<FieldDecl
const*>());
65 auto const type
= loplugin::TypeCheck(rpType
);
66 if ( type
.Class("Image").GlobalNamespace()
67 || type
.Class("Bitmap").GlobalNamespace()
68 || type
.Class("BitmapEx").GlobalNamespace()
69 || type
.Class("VclPtr").GlobalNamespace()
72 return std::make_pair(true, chain
);
74 if (type
.Class("array").StdNamespace()
75 || type
.Class("deque").StdNamespace()
76 || type
.Class("forward_list").StdNamespace()
77 || type
.Class("initializer_list").StdNamespace()
78 || type
.Class("list").StdNamespace()
79 || type
.Class("multiset").StdNamespace()
80 || type
.Class("set").StdNamespace()
81 || type
.Class("unordered_multiset").StdNamespace()
82 || type
.Class("unordered_set").StdNamespace()
83 || type
.Class("vector").StdNamespace())
85 std::vector
<QualType
> copy(rParents
);
86 copy
.push_back(rpType
.getUnqualifiedType().getCanonicalType());
87 auto ctsd
= dyn_cast
<ClassTemplateSpecializationDecl
>(
88 pRecordType
->getDecl());
89 assert(ctsd
!= nullptr);
90 auto const & args
= ctsd
->getTemplateArgs();
91 assert(args
.size() >= 1);
92 return isBadStaticType(args
.get(0).getAsType(), chain
, copy
);
94 if (type
.Class("map").StdNamespace()
95 || type
.Class("multimap").StdNamespace()
96 || type
.Class("unordered_map").StdNamespace()
97 || type
.Class("unordered_multimap").StdNamespace())
99 std::vector
<QualType
> copy(rParents
);
100 copy
.push_back(rpType
.getUnqualifiedType().getCanonicalType());
101 auto ctsd
= dyn_cast
<ClassTemplateSpecializationDecl
>(
102 pRecordType
->getDecl());
103 assert(ctsd
!= nullptr);
104 auto const & args
= ctsd
->getTemplateArgs();
105 assert(args
.size() >= 2);
106 auto ret
= isBadStaticType(args
.get(0).getAsType(), chain
, copy
);
110 return isBadStaticType(args
.get(1).getAsType(), chain
, copy
);
112 RecordDecl
const*const pDefinition(pRecordType
->getDecl()->getDefinition());
113 if (!pDefinition
) { // maybe no definition if it's a pointer/reference
114 return std::make_pair(false, std::vector
<FieldDecl
const*>());
116 if ( type
.Class("DeleteOnDeinit").Namespace("vcl").GlobalNamespace()
117 || type
.Class("weak_ptr").StdNamespace() // not owning
118 || type
.Class("ImplWallpaper").GlobalNamespace() // very odd static instance here
119 || type
.Class("Application").GlobalNamespace() // numerous odd subclasses in vclmain::createApplication()
120 || type
.Class("DemoMtfApp").AnonymousNamespace().GlobalNamespace() // one of these Application with own VclPtr
123 return std::make_pair(false, std::vector
<FieldDecl
const*>());
125 std::vector
<QualType
> copy(rParents
);
126 copy
.push_back(rpType
.getUnqualifiedType().getCanonicalType());
127 CXXRecordDecl
const*const pDecl(dyn_cast
<CXXRecordDecl
>(pDefinition
));
129 for (auto it
= pDecl
->field_begin(); it
!= pDecl
->field_end(); ++it
) {
130 chain
.push_back(*it
);
131 auto const ret(isBadStaticType((*it
)->getType(), chain
, copy
));
137 for (auto it
= pDecl
->bases_begin(); it
!= pDecl
->bases_end(); ++it
) {
138 auto const ret(isBadStaticType((*it
).getType(), chain
, copy
));
143 for (auto it
= pDecl
->vbases_begin(); it
!= pDecl
->vbases_end(); ++it
) {
144 auto const ret(isBadStaticType((*it
).getType(), chain
, copy
));
149 return std::make_pair(false, std::vector
<FieldDecl
const*>());
152 bool VisitVarDecl(VarDecl
const*const pVarDecl
)
154 if (ignoreLocation(pVarDecl
)) {
158 if (pVarDecl
->hasGlobalStorage()
159 && pVarDecl
->isThisDeclarationADefinition())
161 auto const name(pVarDecl
->getName());
162 if ( name
== "s_pPreviousView" // not an owning pointer
163 || name
== "s_pDefCollapsed" // SvImpLBox::~SvImpLBox()
164 || name
== "s_pDefExpanded" // SvImpLBox::~SvImpLBox()
165 || name
== "g_pDDSource" // SvTreeListBox::dispose()
166 || name
== "g_pDDTarget" // SvTreeListBox::dispose()
167 || name
== "g_pSfxApplication" // SfxApplication::~SfxApplication()
168 || name
== "s_SidebarResourceManagerInstance" // ResourceManager::disposeDecks()
169 || name
== "s_pGallery" // this is not entirely clear but apparently the GalleryThemeCacheEntry are deleted by GalleryBrowser2::SelectTheme() or GalleryBrowser2::dispose()
170 || name
== "s_ExtMgr" // TheExtensionManager::disposing()
171 || name
== "s_pDocLockedInsertingLinks" // not owning
172 || name
== "s_pVout" // FrameFinit()
173 || name
== "s_pPaintQueue" // SwPaintQueue::Remove()
174 || name
== "gProp" // only owned (VclPtr) member cleared again
175 || name
== "g_OszCtrl" // SwCrsrOszControl::Exit()
176 || name
== "g_pSpellIter" // SwEditShell::SpellEnd()
177 || name
== "g_pConvIter" // SwEditShell::SpellEnd()
178 || name
== "g_pHyphIter" // SwEditShell::HyphEnd()
179 || name
== "xFieldEditEngine" // ScGlobal::Clear()
180 || name
== "xDrawClipDocShellRef" // ScGlobal::Clear()
181 || name
== "s_ImageTree"
182 // ImageTree::get(), ImageTree::shutDown()
183 || name
== "s_pMouseFrame"
184 // vcl/osx/salframeview.mm, mouseEntered/Exited, not owning
185 || name
== "pCurrentMenuBar"
186 // vcl/osx/salmenu.cxx, AquaSalMenu::set/unsetMainMenu, not
188 || name
== "s_pCaptureFrame" // vcl/osx/salframe.cxx, not owning
190 // sw/source/core/text/blink.cxx, _TextFinit()
191 || name
== "s_pIconCache"
192 // sd/source/ui/tools/IconCache.cxx, leaked
193 || name
== "maInstanceMap"
194 // sd/source/ui/framework/tools/FrameworkHelper.cxx, would
195 // leak ViewShellBase* keys if that map is not empty at exit
196 || name
== "theAddInAsyncTbl"
197 // sc/source/core/tool/adiasync.cxx, would leak
198 // ScAddInAsync* keys if that set is not empty at exit
199 || name
== "g_aWindowList"
200 //vcl/unx/gtk3/a11y/atkutil.cxx, asserted empty at exit
201 || name
== "gFontPreviewVirDevs"
202 //svtools/source/control/ctrlbox.cxx, empty at exit
203 || name
== "gStylePreviewCache" // svx/source/tbxctrls/StylesPreviewWindow.cxx
204 || name
== "aLogger" // FormulaLogger& FormulaLogger::get() in sc/source/core/tool/formulalogger.cxx
205 || name
== "s_aUncommittedRegistrations" // sw/source/uibase/dbui/dbmgr.cxx
206 || (loplugin::DeclCheck(pVarDecl
).Var("aAllListeners")
207 .Class("ScAddInListener").GlobalNamespace()) // not owning
208 || (loplugin::DeclCheck(pVarDecl
).Var("maThreadSpecific")
209 .Class("ScDocument").GlobalNamespace()) // not owning
210 || name
== "s_aLOKWindowsMap" // LOK only, guarded by assert, and LOK never tries to perform a VCL cleanup
211 || name
== "s_aLOKWeldBuildersMap" // LOK only, similar case as above
212 || name
== "s_aLOKPopupsMap" // LOK only, similar case as above
213 || name
== "gNotebookBarManager" // LOK only case, when notebookbar is closed - VclPtr instance is removed
214 || name
== "gStaticManager" // vcl/source/graphic/Manager.cxx - stores non-owning pointers
215 || name
== "aThreadedInterpreterPool" // ScInterpreterContext(Pool), not owning
216 || name
== "aNonThreadedInterpreterPool" // ScInterpreterContext(Pool), not owning
217 || name
== "lcl_parserContext" // getParserContext(), the chain from this to a VclPtr is not owning
218 || name
== "aReaderWriter" // /home/noel/libo/sw/source/filter/basflt/fltini.cxx, non-owning
220 // Windows-only extensions/source/scanner/scanwin.cxx, problematic
221 // Twain::mpThread -> ShimListenerThread::mxTopWindow released via Twain::Reset
223 || name
== "g_newReadOnlyDocs"
224 // sfx2/source/doc/docfile.cxx, warning about map's key
225 || name
== "g_existingReadOnlyDocs"
226 // sfx2/source/doc/docfile.cxx, warning about map's key
227 || name
== "gaFramesArr_Impl"
228 // sfx2/source/view/frame.cxx, vector of pointer, so not a problem, nothing is going to happen on shutdown
229 || name
== "g_pOLELRU_Cache" || name
== "s_aTableColumnsMap"
231 || name
== "SINGLETON"
232 // TheAquaA11yFocusTracker in vcl/osx/a11yfocustracker.cxx,
233 // AquaA11yFocusTracker::m_aDocumentWindowList elements symmetrically added and
234 // removed in AquaA11yFocusTracker::window_got_focus and
235 // AquaA11yFocusTracker::WindowEventHandler (TODO: is that guaranteed?)
236 || (loplugin::DeclCheck(pVarDecl
).Var("maEditViewHistory")
237 .Class("LOKEditViewHistory").GlobalNamespace())
238 // sfx2/lokhelper.hxx, only handling pointers, not owning
239 ) // these variables appear unproblematic
243 // these two are fairly harmless because they're both empty objects
244 if ( name
== "s_xEmptyController" // svx/source/fmcomp/gridcell.cxx
245 || name
== "xCell" // svx/source/table/svdotable.cxx
250 // ignore pointers, nothing happens to them on shutdown
251 QualType
const pCanonical(pVarDecl
->getType().getUnqualifiedType().getCanonicalType());
252 if (pCanonical
->isPointerType()) {
255 std::vector
<FieldDecl
const*> pad
;
256 auto const ret(isBadStaticType(pVarDecl
->getType(), pad
,
257 std::vector
<QualType
>()));
259 report(DiagnosticsEngine::Warning
,
260 "bad static variable causes crash on shutdown",
261 pVarDecl
->getLocation())
262 << pVarDecl
->getSourceRange();
263 if (!isUnitTestMode())
265 for (auto i
: ret
.second
) {
266 report(DiagnosticsEngine::Note
,
267 "... due to this member of %0",
269 << i
->getParent() << i
->getSourceRange();
280 loplugin::Plugin::Registration
<BadStatics
> badstatics("badstatics");
284 #endif // LO_CLANG_SHARED_PLUGINS
286 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */