2015-10-30 16:05:42 +01:00
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2.0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
*/
2016-10-08 23:55:08 +02:00
# include <cassert>
2016-06-28 18:54:31 +02:00
# include "check.hxx"
2015-10-30 16:05:42 +01:00
# include "plugin.hxx"
namespace {
class BadStatics
: public clang : : RecursiveASTVisitor < BadStatics >
, public loplugin : : Plugin
{
public :
2017-11-07 11:50:47 +01:00
explicit BadStatics ( loplugin : : InstantiationData const & rData ) :
Plugin ( rData ) { }
2015-10-30 16:05:42 +01:00
void run ( ) override {
if ( compiler . getLangOpts ( ) . CPlusPlus ) { // no non-trivial dtors in C
TraverseDecl ( compiler . getASTContext ( ) . getTranslationUnitDecl ( ) ) ;
}
}
2015-11-17 15:05:34 +01:00
static std : : pair < bool , std : : vector < FieldDecl const * > > isBadStaticType (
QualType const & rpType , std : : vector < FieldDecl const * > & chain ,
2015-11-12 22:33:36 +01:00
std : : vector < QualType > const & rParents )
2015-11-05 13:16:50 +01:00
{
2016-10-08 23:55:08 +02:00
QualType pt ;
if ( rpType - > isAnyPointerType ( ) ) {
pt = rpType - > getPointeeType ( ) ;
} else if ( auto at = rpType - > getAsArrayTypeUnsafe ( ) ) {
pt = at - > getElementType ( ) ;
} else if ( auto rt = rpType - > getAs < ReferenceType > ( ) ) {
pt = rt - > getPointeeType ( ) ;
}
if ( ! pt . isNull ( ) ) {
QualType const pPointee ( pt . getUnqualifiedType ( ) . getCanonicalType ( ) ) ;
2015-11-12 22:33:36 +01:00
auto const iter ( std : : find ( rParents . begin ( ) , rParents . end ( ) , pPointee ) ) ;
if ( iter = = rParents . end ( ) )
{
std : : vector < QualType > copy ( rParents ) ;
2016-10-08 23:55:08 +02:00
copy . push_back ( rpType . getUnqualifiedType ( ) . getCanonicalType ( ) ) ;
return isBadStaticType ( pt , chain , copy ) ;
2015-11-12 22:33:36 +01:00
} else {
2015-11-17 15:05:34 +01:00
return std : : make_pair ( false , std : : vector < FieldDecl const * > ( ) ) ;
2015-11-12 22:33:36 +01:00
}
}
2016-10-08 23:55:08 +02:00
RecordType const * const pRecordType ( rpType - > getAs < RecordType > ( ) ) ;
2015-11-05 13:16:50 +01:00
if ( ! pRecordType ) {
2015-11-17 15:05:34 +01:00
return std : : make_pair ( false , std : : vector < FieldDecl const * > ( ) ) ;
2015-11-05 13:16:50 +01:00
}
2015-12-08 22:56:02 +01:00
auto const type = loplugin : : TypeCheck ( rpType ) ;
if ( type . Class ( " Image " ) . GlobalNamespace ( )
| | type . Class ( " Bitmap " ) . GlobalNamespace ( )
| | type . Class ( " BitmapEx " ) . GlobalNamespace ( )
2016-04-14 15:07:57 +02:00
| | type . Class ( " VclPtr " ) . GlobalNamespace ( )
2015-11-05 13:16:50 +01:00
)
{
2015-11-17 15:05:34 +01:00
return std : : make_pair ( true , chain ) ;
2015-11-05 13:16:50 +01:00
}
2016-10-08 23:55:08 +02:00
if ( type . Class ( " array " ) . StdNamespace ( )
| | type . Class ( " deque " ) . StdNamespace ( )
| | type . Class ( " forward_list " ) . StdNamespace ( )
| | type . Class ( " initializer_list " ) . StdNamespace ( )
| | type . Class ( " list " ) . StdNamespace ( )
| | type . Class ( " multiset " ) . StdNamespace ( )
| | type . Class ( " set " ) . StdNamespace ( )
| | type . Class ( " unordered_multiset " ) . StdNamespace ( )
| | type . Class ( " unordered_set " ) . StdNamespace ( )
| | type . Class ( " vector " ) . StdNamespace ( ) )
{
std : : vector < QualType > copy ( rParents ) ;
copy . push_back ( rpType . getUnqualifiedType ( ) . getCanonicalType ( ) ) ;
auto ctsd = dyn_cast < ClassTemplateSpecializationDecl > (
pRecordType - > getDecl ( ) ) ;
assert ( ctsd ! = nullptr ) ;
auto const & args = ctsd - > getTemplateArgs ( ) ;
assert ( args . size ( ) > = 1 ) ;
return isBadStaticType ( args . get ( 0 ) . getAsType ( ) , chain , copy ) ;
}
if ( type . Class ( " map " ) . StdNamespace ( )
| | type . Class ( " multimap " ) . StdNamespace ( )
| | type . Class ( " unordered_map " ) . StdNamespace ( )
| | type . Class ( " unordered_multimap " ) . StdNamespace ( ) )
{
std : : vector < QualType > copy ( rParents ) ;
copy . push_back ( rpType . getUnqualifiedType ( ) . getCanonicalType ( ) ) ;
auto ctsd = dyn_cast < ClassTemplateSpecializationDecl > (
pRecordType - > getDecl ( ) ) ;
assert ( ctsd ! = nullptr ) ;
auto const & args = ctsd - > getTemplateArgs ( ) ;
assert ( args . size ( ) > = 2 ) ;
auto ret = isBadStaticType ( args . get ( 0 ) . getAsType ( ) , chain , copy ) ;
if ( ret . first ) {
return ret ;
}
return isBadStaticType ( args . get ( 1 ) . getAsType ( ) , chain , copy ) ;
}
2015-11-05 13:16:50 +01:00
RecordDecl const * const pDefinition ( pRecordType - > getDecl ( ) - > getDefinition ( ) ) ;
2015-11-12 22:33:36 +01:00
if ( ! pDefinition ) { // maybe no definition if it's a pointer/reference
2015-11-17 15:05:34 +01:00
return std : : make_pair ( false , std : : vector < FieldDecl const * > ( ) ) ;
2015-11-12 22:33:36 +01:00
}
2015-12-08 22:56:02 +01:00
if ( type . Class ( " DeleteOnDeinit " ) . Namespace ( " vcl " ) . GlobalNamespace ( )
| | type . Class ( " weak_ptr " ) . StdNamespace ( ) // not owning
| | type . Class ( " ImplWallpaper " ) . GlobalNamespace ( ) // very odd static instance here
| | type . Class ( " Application " ) . GlobalNamespace ( ) // numerous odd subclasses in vclmain::createApplication()
| | type . Class ( " DemoMtfApp " ) . GlobalNamespace ( ) // one of these Application with own VclPtr
2015-11-12 22:33:36 +01:00
)
{
2015-11-17 15:05:34 +01:00
return std : : make_pair ( false , std : : vector < FieldDecl const * > ( ) ) ;
2015-11-12 22:33:36 +01:00
}
std : : vector < QualType > copy ( rParents ) ;
2016-10-08 23:55:08 +02:00
copy . push_back ( rpType . getUnqualifiedType ( ) . getCanonicalType ( ) ) ;
2015-11-05 13:16:50 +01:00
CXXRecordDecl const * const pDecl ( dyn_cast < CXXRecordDecl > ( pDefinition ) ) ;
assert ( pDecl ) ;
for ( auto it = pDecl - > field_begin ( ) ; it ! = pDecl - > field_end ( ) ; + + it ) {
2015-11-17 15:05:34 +01:00
chain . push_back ( * it ) ;
auto const ret ( isBadStaticType ( ( * it ) - > getType ( ) , chain , copy ) ) ;
chain . pop_back ( ) ;
2015-11-05 13:16:50 +01:00
if ( ret . first ) {
return ret ;
}
}
for ( auto it = pDecl - > bases_begin ( ) ; it ! = pDecl - > bases_end ( ) ; + + it ) {
2015-11-17 15:05:34 +01:00
auto const ret ( isBadStaticType ( ( * it ) . getType ( ) , chain , copy ) ) ;
2015-11-05 13:16:50 +01:00
if ( ret . first ) {
return ret ;
}
}
for ( auto it = pDecl - > vbases_begin ( ) ; it ! = pDecl - > vbases_end ( ) ; + + it ) {
2015-11-17 15:05:34 +01:00
auto const ret ( isBadStaticType ( ( * it ) . getType ( ) , chain , copy ) ) ;
2015-11-05 13:16:50 +01:00
if ( ret . first ) {
return ret ;
}
}
2015-11-17 15:05:34 +01:00
return std : : make_pair ( false , std : : vector < FieldDecl const * > ( ) ) ;
2015-11-05 13:16:50 +01:00
}
2015-10-30 16:05:42 +01:00
bool VisitVarDecl ( VarDecl const * const pVarDecl )
{
if ( ignoreLocation ( pVarDecl ) ) {
return true ;
}
2015-11-05 13:16:50 +01:00
if ( pVarDecl - > hasGlobalStorage ( )
& & pVarDecl - > isThisDeclarationADefinition ( ) )
{
2015-11-12 22:33:36 +01:00
auto const name ( pVarDecl - > getName ( ) ) ;
if ( name = = " g_pI18NStatusInstance " // I18NStatus::free()
| | name = = " s_pPreviousView " // not a owning pointer
| | name = = " s_pDefCollapsed " // SvImpLBox::~SvImpLBox()
| | name = = " s_pDefExpanded " // SvImpLBox::~SvImpLBox()
| | name = = " g_pDDSource " // SvTreeListBox::dispose()
| | name = = " g_pDDTarget " // SvTreeListBox::dispose()
| | name = = " g_pSfxApplication " // SfxApplication::~SfxApplication()
| | name = = " s_SidebarResourceManagerInstance " // ResourceManager::disposeDecks()
| | name = = " s_pGallery " // this is not entirely clear but apparently the GalleryThemeCacheEntry are deleted by GalleryBrowser2::SelectTheme() or GalleryBrowser2::dispose()
| | name = = " s_ExtMgr " // TheExtensionManager::disposing()
| | name = = " s_pDocLockedInsertingLinks " // not owning
2016-04-22 10:08:07 +02:00
| | name = = " s_pVout " // FrameFinit()
2015-11-12 22:33:36 +01:00
| | name = = " s_pPaintQueue " // SwPaintQueue::Remove()
| | name = = " gProp " // only owned (VclPtr) member cleared again
| | name = = " g_OszCtrl " // SwCrsrOszControl::Exit()
| | name = = " g_pSpellIter " // SwEditShell::SpellEnd()
| | name = = " g_pConvIter " // SwEditShell::SpellEnd()
| | name = = " g_pHyphIter " // SwEditShell::HyphEnd()
| | name = = " pFieldEditEngine " // ScGlobal::Clear()
| | name = = " xDrawClipDocShellRef " // ScGlobal::Clear()
2016-12-18 14:31:14 +01:00
| | name = = " s_ImageTree "
// ImageTree::get(), ImageTree::shutDown()
2015-11-17 16:41:40 +01:00
| | name = = " s_pMouseFrame "
// vcl/osx/salframeview.mm, mouseEntered/Exited, not owning
| | name = = " pCurrentMenuBar "
// vcl/osx/salmenu.cxx, AquaSalMenu::set/unsetMainMenu, not
// owning
| | name = = " s_pCaptureFrame " // vcl/osx/salframe.cxx, not owning
2015-11-17 19:38:41 +01:00
| | name = = " pBlink "
// sw/source/core/text/blink.cxx, _TextFinit()
2015-11-17 23:43:26 +01:00
| | name = = " s_pIconCache "
2015-11-17 21:31:46 +01:00
// sd/source/ui/tools/IconCache.cxx, leaked
2016-06-29 21:51:28 +02:00
| | name = = " maInstanceMap "
// sd/source/ui/framework/tools/FrameworkHelper.cxx, would
// leak ViewShellBase* keys if that map is not empty at exit
| | name = = " theAddInAsyncTbl "
// sc/source/core/tool/adiasync.cxx, would leak
// ScAddInAsync* keys if that set is not empty at exit
2016-06-30 12:53:40 +02:00
| | name = = " g_aWindowList "
//vcl/unx/gtk/a11y/atkutil.cxx, asserted empty at exit
2016-12-12 12:13:27 +02:00
| | name = = " aLogger " // FormulaLogger& FormulaLogger::get() in sc/source/core/tool/formulalogger.cxx
2017-09-08 10:57:55 +02:00
| | name = = " m_aUncommitedRegistrations " // sw/source/uibase/dbui/dbmgr.cxx
2016-10-08 23:49:29 +02:00
| | ( loplugin : : DeclCheck ( pVarDecl ) . Var ( " aAllListeners " )
. Class ( " ScAddInListener " ) . GlobalNamespace ( ) ) // not owning
2017-11-29 14:19:54 +01:00
| | name = = " s_pLOKWindowsMap " // LOK only, guarded by assert, and LOK never tries to perform a VCL cleanup
2018-04-04 17:51:26 +09:00
| | name = = " gStaticManager " // vcl/source/graphic/Manager.cxx - stores non-owning pointers
2015-11-12 22:33:36 +01:00
) // these variables appear unproblematic
{
return true ;
}
2016-04-18 11:21:44 +02:00
// these two are fairly harmless because they're both empty objects
if ( name = = " s_xEmptyController " // svx/source/fmcomp/gridcell.cxx
2016-04-14 15:07:57 +02:00
| | name = = " xCell " // svx/source/table/svdotable.cxx
)
{
return true ;
}
// ignore pointers, nothing happens to them on shutdown
QualType const pCanonical ( pVarDecl - > getType ( ) . getUnqualifiedType ( ) . getCanonicalType ( ) ) ;
if ( pCanonical - > isPointerType ( ) ) {
return true ;
}
2015-11-17 15:05:34 +01:00
std : : vector < FieldDecl const * > pad ;
auto const ret ( isBadStaticType ( pVarDecl - > getType ( ) , pad ,
2015-11-12 22:33:36 +01:00
std : : vector < QualType > ( ) ) ) ;
2015-11-05 13:16:50 +01:00
if ( ret . first ) {
2015-10-30 16:05:42 +01:00
report ( DiagnosticsEngine : : Warning ,
" bad static variable causes crash on shutdown " ,
pVarDecl - > getLocation ( ) )
< < pVarDecl - > getSourceRange ( ) ;
2017-09-21 16:37:40 +02:00
if ( ! isUnitTestMode ( ) )
{
for ( auto i : ret . second ) {
report ( DiagnosticsEngine : : Note ,
" ... due to this member of %0 " ,
i - > getLocation ( ) )
< < i - > getParent ( ) < < i - > getSourceRange ( ) ;
}
2015-11-05 13:16:50 +01:00
}
2015-10-30 16:05:42 +01:00
}
}
return true ;
}
} ;
loplugin : : Plugin : : Registration < BadStatics > X ( " badstatics " ) ;
} // namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */