2014-07-21 08:27:11 +02: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/.
*/
2017-04-14 08:42:15 +10:00
# include <memory>
2014-07-21 08:27:11 +02:00
# include <string>
2015-03-09 14:29:30 +02:00
# include <iostream>
2014-07-21 08:27:11 +02:00
# include "plugin.hxx"
2016-06-30 14:09:31 +02:00
# include "check.hxx"
2014-07-21 08:27:11 +02:00
# include "clang/AST/CXXInheritance.h"
// Final goal: Checker for VCL widget references. Makes sure that VCL Window subclasses are properly referenced counted and dispose()'ed.
//
// But at the moment it just finds subclasses of Window which are not heap-allocated
//
// TODO do I need to check for local and static variables, too ?
// TODO when we have a dispose() method, verify that the dispose() methods releases all of the Window references
// TODO when we have a dispose() method, verify that it calls the super-class dispose() method at some point.
namespace {
class VCLWidgets :
public RecursiveASTVisitor < VCLWidgets > , public loplugin : : Plugin
{
public :
2017-11-07 11:50:47 +01:00
explicit VCLWidgets ( loplugin : : InstantiationData const & data ) : Plugin ( data )
{ }
2014-07-21 08:27:11 +02:00
virtual void run ( ) override { TraverseDecl ( compiler . getASTContext ( ) . getTranslationUnitDecl ( ) ) ; }
2016-10-28 11:13:37 +02:00
bool shouldVisitTemplateInstantiations ( ) const { return true ; }
2014-07-21 08:27:11 +02:00
2016-10-28 11:13:37 +02:00
bool VisitVarDecl ( const VarDecl * ) ;
2015-02-11 14:42:23 +02:00
bool VisitFieldDecl ( const FieldDecl * ) ;
bool VisitParmVarDecl ( const ParmVarDecl * ) ;
bool VisitFunctionDecl ( const FunctionDecl * ) ;
bool VisitCXXDestructorDecl ( const CXXDestructorDecl * ) ;
2015-03-09 14:29:30 +02:00
bool VisitCXXDeleteExpr ( const CXXDeleteExpr * ) ;
2015-05-18 10:08:00 +02:00
bool VisitCallExpr ( const CallExpr * ) ;
2016-11-10 12:53:02 +02:00
bool VisitDeclRefExpr ( const DeclRefExpr * ) ;
bool VisitCXXConstructExpr ( const CXXConstructExpr * ) ;
bool VisitBinaryOperator ( const BinaryOperator * ) ;
2015-01-15 15:48:23 +02:00
private :
2017-09-11 10:48:12 +02:00
void checkAssignmentForVclPtrToRawConversion ( const SourceLocation & sourceLoc , const clang : : Type * lhsType , const Expr * rhs ) ;
2015-01-15 15:48:23 +02:00
bool isDisposeCallingSuperclassDispose ( const CXXMethodDecl * pMethodDecl ) ;
2015-05-18 10:08:00 +02:00
bool mbCheckingMemcpy = false ;
2014-07-21 08:27:11 +02:00
} ;
2016-05-18 12:25:54 +02:00
# define BASE_REF_COUNTED_CLASS "VclReferenceBase"
2017-12-15 14:20:38 +01:00
bool BaseCheckNotWindowSubclass ( const CXXRecordDecl * BaseDefinition ) {
2016-12-07 07:27:24 +01:00
return ! loplugin : : DeclCheck ( BaseDefinition ) . Class ( BASE_REF_COUNTED_CLASS )
. GlobalNamespace ( ) ;
2015-01-07 10:40:53 +02:00
}
2016-10-28 11:13:37 +02:00
bool isDerivedFromVclReferenceBase ( const CXXRecordDecl * decl ) {
2015-03-13 16:26:50 +02:00
if ( ! decl )
return false ;
2016-12-07 07:27:24 +01:00
if ( loplugin : : DeclCheck ( decl ) . Class ( BASE_REF_COUNTED_CLASS )
. GlobalNamespace ( ) )
{
2015-01-07 10:40:53 +02:00
return true ;
2016-12-07 07:27:24 +01:00
}
2015-03-09 14:29:30 +02:00
if ( ! decl - > hasDefinition ( ) ) {
return false ;
}
2015-01-07 10:40:53 +02:00
if ( // not sure what hasAnyDependentBases() does,
// but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
! decl - > hasAnyDependentBases ( ) & &
2017-12-15 14:20:38 +01:00
! decl - > forallBases ( BaseCheckNotWindowSubclass , true ) ) {
2015-01-07 10:40:53 +02:00
return true ;
2014-07-21 08:27:11 +02:00
}
return false ;
}
2017-09-11 10:48:12 +02:00
bool containsVclReferenceBaseSubclass ( const clang : : Type * pType0 ) ;
2015-03-13 16:26:50 +02:00
2016-10-28 11:13:37 +02:00
bool containsVclReferenceBaseSubclass ( const QualType & qType ) {
2016-12-07 07:27:24 +01:00
auto check = loplugin : : TypeCheck ( qType ) ;
if ( check . Class ( " ScopedVclPtr " ) . GlobalNamespace ( )
| | check . Class ( " ScopedVclPtrInstance " ) . GlobalNamespace ( )
| | check . Class ( " VclPtr " ) . GlobalNamespace ( )
| | check . Class ( " VclPtrInstance " ) . GlobalNamespace ( ) )
{
return false ;
2015-04-30 15:05:23 +02:00
}
2016-10-28 11:13:37 +02:00
return containsVclReferenceBaseSubclass ( qType . getTypePtr ( ) ) ;
2015-03-09 14:29:30 +02:00
}
2017-09-11 10:48:12 +02:00
bool containsVclReferenceBaseSubclass ( const clang : : Type * pType0 ) {
2015-03-13 16:26:50 +02:00
if ( ! pType0 )
return false ;
2017-09-11 10:48:12 +02:00
const clang : : Type * pType = pType0 - > getUnqualifiedDesugaredType ( ) ;
2015-03-13 16:26:50 +02:00
if ( ! pType )
return false ;
2015-03-09 14:29:30 +02:00
const CXXRecordDecl * pRecordDecl = pType - > getAsCXXRecordDecl ( ) ;
if ( pRecordDecl ) {
const ClassTemplateSpecializationDecl * pTemplate = dyn_cast < ClassTemplateSpecializationDecl > ( pRecordDecl ) ;
if ( pTemplate ) {
2016-12-07 07:27:24 +01:00
auto check = loplugin : : DeclCheck ( pTemplate ) ;
if ( check . Class ( " VclStatusListener " ) . GlobalNamespace ( ) ) {
2015-12-07 17:08:32 +01:00
return false ;
}
2016-12-07 07:27:24 +01:00
bool link = bool ( check . Class ( " Link " ) . GlobalNamespace ( ) ) ;
2015-03-09 14:29:30 +02:00
for ( unsigned i = 0 ; i < pTemplate - > getTemplateArgs ( ) . size ( ) ; + + i ) {
const TemplateArgument & rArg = pTemplate - > getTemplateArgs ( ) [ i ] ;
if ( rArg . getKind ( ) = = TemplateArgument : : ArgKind : : Type & &
2016-10-28 11:13:37 +02:00
containsVclReferenceBaseSubclass ( rArg . getAsType ( ) ) )
2015-03-09 14:29:30 +02:00
{
2015-04-30 15:06:21 +02:00
// OK for first template argument of tools/link.hxx Link
// to be a Window-derived pointer:
if ( ! link | | i ! = 0 ) {
return true ;
}
2015-03-09 14:29:30 +02:00
}
}
}
}
2015-03-13 16:26:50 +02:00
if ( pType - > isPointerType ( ) ) {
QualType pointeeType = pType - > getPointeeType ( ) ;
2016-10-28 11:13:37 +02:00
return containsVclReferenceBaseSubclass ( pointeeType ) ;
2015-03-16 12:17:44 +02:00
} else if ( pType - > isArrayType ( ) ) {
2017-09-11 10:48:12 +02:00
const clang : : ArrayType * pArrayType = dyn_cast < clang : : ArrayType > ( pType ) ;
2015-03-16 12:17:44 +02:00
QualType elementType = pArrayType - > getElementType ( ) ;
2016-10-28 11:13:37 +02:00
return containsVclReferenceBaseSubclass ( elementType ) ;
2015-03-13 16:26:50 +02:00
} else {
2016-10-28 11:13:37 +02:00
return isDerivedFromVclReferenceBase ( pRecordDecl ) ;
2015-03-13 16:26:50 +02:00
}
2015-01-08 13:30:36 +02:00
}
2015-01-26 08:43:33 +02:00
bool VCLWidgets : : VisitCXXDestructorDecl ( const CXXDestructorDecl * pCXXDestructorDecl )
{
if ( ignoreLocation ( pCXXDestructorDecl ) ) {
2015-01-08 14:09:13 +02:00
return true ;
}
2015-03-09 14:29:30 +02:00
if ( ! pCXXDestructorDecl - > isThisDeclarationADefinition ( ) ) {
2015-01-08 14:09:13 +02:00
return true ;
2015-01-26 08:43:33 +02:00
}
const CXXRecordDecl * pRecordDecl = pCXXDestructorDecl - > getParent ( ) ;
2016-05-18 12:25:54 +02:00
// ignore
2016-12-07 07:27:24 +01:00
if ( loplugin : : DeclCheck ( pRecordDecl ) . Class ( BASE_REF_COUNTED_CLASS )
. GlobalNamespace ( ) )
{
2015-01-26 13:10:46 +02:00
return true ;
}
2016-10-28 11:13:37 +02:00
// check if this class is derived from VclReferenceBase
if ( ! isDerivedFromVclReferenceBase ( pRecordDecl ) ) {
2015-01-08 14:09:13 +02:00
return true ;
}
2016-10-28 11:13:37 +02:00
// check if we have any VclPtr<> fields
2015-09-25 11:41:53 +01:00
bool bFoundVclPtrField = false ;
2015-05-05 14:16:08 +02:00
for ( auto fieldDecl = pRecordDecl - > field_begin ( ) ;
fieldDecl ! = pRecordDecl - > field_end ( ) ; + + fieldDecl )
{
2015-01-26 08:43:33 +02:00
const RecordType * pFieldRecordType = fieldDecl - > getType ( ) - > getAs < RecordType > ( ) ;
if ( pFieldRecordType ) {
2016-12-07 07:27:24 +01:00
if ( loplugin : : DeclCheck ( pFieldRecordType - > getDecl ( ) )
. Class ( " VclPtr " ) . GlobalNamespace ( ) )
{
2015-09-25 11:41:53 +01:00
bFoundVclPtrField = true ;
2015-01-26 08:43:33 +02:00
break ;
}
}
2015-01-08 14:09:13 +02:00
}
2016-10-28 11:13:37 +02:00
// check if there is a dispose() method
2015-09-25 11:41:53 +01:00
bool bFoundDispose = false ;
2015-05-05 14:16:08 +02:00
for ( auto methodDecl = pRecordDecl - > method_begin ( ) ;
methodDecl ! = pRecordDecl - > method_end ( ) ; + + methodDecl )
{
2016-12-07 07:27:24 +01:00
if ( methodDecl - > isInstance ( ) & & methodDecl - > param_size ( ) = = 0
& & loplugin : : DeclCheck ( * methodDecl ) . Function ( " dispose " ) )
{
2015-09-25 11:41:53 +01:00
bFoundDispose = true ;
2015-01-08 14:09:13 +02:00
break ;
}
}
2017-06-06 09:23:17 +02:00
const CompoundStmt * pCompoundStatement = dyn_cast_or_null < CompoundStmt > ( pCXXDestructorDecl - > getBody ( ) ) ;
2015-01-26 08:43:33 +02:00
// having an empty body and no dispose() method is fine
2017-06-06 09:23:17 +02:00
if ( ! bFoundVclPtrField & & ! bFoundDispose & & ( ! pCompoundStatement | | pCompoundStatement - > size ( ) = = 0 ) ) {
2015-01-26 08:43:33 +02:00
return true ;
}
2017-06-06 09:23:17 +02:00
if ( bFoundVclPtrField & & ( ! pCompoundStatement | | pCompoundStatement - > size ( ) = = 0 ) ) {
2015-01-08 14:09:13 +02:00
report (
DiagnosticsEngine : : Warning ,
2016-05-18 12:25:54 +02:00
BASE_REF_COUNTED_CLASS " subclass with VclPtr field must call disposeOnce() from its destructor " ,
2015-03-09 14:29:30 +02:00
pCXXDestructorDecl - > getLocStart ( ) )
< < pCXXDestructorDecl - > getSourceRange ( ) ;
2015-01-26 08:43:33 +02:00
return true ;
}
2017-06-26 16:37:20 +02:00
// Check that the destructor for a BASE_REF_COUNTED_CLASS subclass either
// only calls disposeOnce() or, if !bFoundVclPtrField, does nothing at all:
2015-09-25 11:41:53 +01:00
bool bOk = false ;
2015-05-07 11:18:25 +02:00
if ( pCompoundStatement ) {
bool bFoundDisposeOnce = false ;
int nNumExtraStatements = 0 ;
2016-06-28 16:25:55 +02:00
for ( auto i = pCompoundStatement - > body_begin ( ) ;
i ! = pCompoundStatement - > body_end ( ) ; + + i )
2015-05-07 11:18:25 +02:00
{
2017-06-26 16:37:20 +02:00
//TODO: The below erroneously also skips past entire statements like
//
// assert(true), ...;
//
auto skip = false ;
for ( auto loc = ( * i ) - > getLocStart ( ) ;
compiler . getSourceManager ( ) . isMacroBodyExpansion ( loc ) ;
loc = compiler . getSourceManager ( ) . getImmediateMacroCallerLoc (
loc ) )
{
auto const name = Lexer : : getImmediateMacroName (
loc , compiler . getSourceManager ( ) , compiler . getLangOpts ( ) ) ;
if ( name = = " SAL_DEBUG " | | name = = " assert " ) {
skip = true ;
break ;
}
}
if ( skip ) {
continue ;
}
if ( auto const pCallExpr = dyn_cast < CXXMemberCallExpr > ( * i ) ) {
2015-05-07 11:18:25 +02:00
if ( const FunctionDecl * func = pCallExpr - > getDirectCallee ( ) ) {
if ( func - > getNumParams ( ) = = 0 & & func - > getIdentifier ( ) ! = NULL
& & ( func - > getName ( ) = = " disposeOnce " ) ) {
bFoundDisposeOnce = true ;
2017-06-26 16:37:20 +02:00
continue ;
2015-05-07 11:18:25 +02:00
}
2015-03-10 09:07:06 +02:00
}
}
2017-06-26 16:37:20 +02:00
nNumExtraStatements + + ;
2015-01-26 08:43:33 +02:00
}
2017-06-26 16:37:20 +02:00
bOk = ( bFoundDisposeOnce | | ! bFoundVclPtrField )
& & nNumExtraStatements = = 0 ;
2015-01-26 08:43:33 +02:00
}
2015-09-25 11:41:53 +01:00
if ( ! bOk ) {
2015-03-19 13:53:55 +02:00
SourceLocation spellingLocation = compiler . getSourceManager ( ) . getSpellingLoc (
pCXXDestructorDecl - > getLocStart ( ) ) ;
StringRef filename = compiler . getSourceManager ( ) . getFilename ( spellingLocation ) ;
2017-05-18 09:56:01 +02:00
if ( ! ( loplugin : : hasPathnamePrefix ( filename , SRCDIR " /vcl/source/window/window.cxx " ) )
& & ! ( loplugin : : hasPathnamePrefix ( filename , SRCDIR " /vcl/source/gdi/virdev.cxx " ) )
& & ! ( loplugin : : hasPathnamePrefix ( filename , SRCDIR " /vcl/qa/cppunit/lifecycle.cxx " ) ) )
2015-03-19 13:53:55 +02:00
{
report (
DiagnosticsEngine : : Warning ,
2016-05-18 12:25:54 +02:00
BASE_REF_COUNTED_CLASS " subclass should have nothing in its destructor but a call to disposeOnce() " ,
2015-03-19 13:53:55 +02:00
pCXXDestructorDecl - > getLocStart ( ) )
< < pCXXDestructorDecl - > getSourceRange ( ) ;
}
2015-01-08 14:09:13 +02:00
}
return true ;
}
2016-11-10 12:53:02 +02:00
bool VCLWidgets : : VisitBinaryOperator ( const BinaryOperator * binaryOperator )
{
if ( ignoreLocation ( binaryOperator ) ) {
return true ;
}
if ( ! binaryOperator - > isAssignmentOp ( ) ) {
return true ;
}
2016-11-25 16:23:17 +02:00
SourceLocation spellingLocation = compiler . getSourceManager ( ) . getSpellingLoc (
binaryOperator - > getLocStart ( ) ) ;
checkAssignmentForVclPtrToRawConversion ( spellingLocation , binaryOperator - > getLHS ( ) - > getType ( ) . getTypePtr ( ) , binaryOperator - > getRHS ( ) ) ;
2016-11-10 12:53:02 +02:00
return true ;
}
// Look for places where we are accidentally assigning a returned-by-value VclPtr<T> to a T*, which generally
// ends up in a use-after-free.
2017-09-11 10:48:12 +02:00
void VCLWidgets : : checkAssignmentForVclPtrToRawConversion ( const SourceLocation & spellingLocation , const clang : : Type * lhsType , const Expr * rhs )
2016-11-10 12:53:02 +02:00
{
2017-09-11 10:48:12 +02:00
if ( ! lhsType | | ! isa < clang : : PointerType > ( lhsType ) ) {
2016-11-10 12:53:02 +02:00
return ;
}
if ( ! rhs ) {
return ;
}
2016-11-25 16:23:17 +02:00
StringRef filename = compiler . getSourceManager ( ) . getFilename ( spellingLocation ) ;
2017-05-18 09:56:01 +02:00
if ( loplugin : : isSamePathname ( filename , SRCDIR " /include/rtl/ref.hxx " ) ) {
2016-11-25 16:23:17 +02:00
return ;
2016-11-10 12:53:02 +02:00
}
const CXXRecordDecl * pointeeClass = lhsType - > getPointeeType ( ) - > getAsCXXRecordDecl ( ) ;
if ( ! isDerivedFromVclReferenceBase ( pointeeClass ) ) {
return ;
}
2016-11-25 16:23:17 +02:00
// if we have T* on the LHS and VclPtr<T> on the RHS, we expect to see either
// an ImplicitCastExpr
// or a ExprWithCleanups and then an ImplicitCastExpr
if ( auto implicitCastExpr = dyn_cast < ImplicitCastExpr > ( rhs ) ) {
if ( implicitCastExpr - > getCastKind ( ) ! = CK_UserDefinedConversion ) {
return ;
}
rhs = rhs - > IgnoreCasts ( ) ;
} else if ( auto exprWithCleanups = dyn_cast < ExprWithCleanups > ( rhs ) ) {
if ( auto implicitCastExpr = dyn_cast < ImplicitCastExpr > ( exprWithCleanups - > getSubExpr ( ) ) ) {
if ( implicitCastExpr - > getCastKind ( ) ! = CK_UserDefinedConversion ) {
return ;
}
rhs = exprWithCleanups - > IgnoreCasts ( ) ;
} else {
return ;
}
} else {
return ;
}
if ( isa < CXXNullPtrLiteralExpr > ( rhs ) ) {
2016-11-10 12:53:02 +02:00
return ;
}
2016-11-25 16:23:17 +02:00
if ( isa < CXXThisExpr > ( rhs ) ) {
2016-11-10 12:53:02 +02:00
return ;
}
2016-11-25 16:23:17 +02:00
// ignore assignments from a member field to a local variable, to avoid unnecessary refcounting traffic
if ( auto callExpr = dyn_cast < CXXMemberCallExpr > ( rhs ) ) {
if ( auto calleeMemberExpr = dyn_cast < MemberExpr > ( callExpr - > getCallee ( ) ) ) {
if ( ( calleeMemberExpr = dyn_cast < MemberExpr > ( calleeMemberExpr - > getBase ( ) - > IgnoreImpCasts ( ) ) ) ) {
if ( isa < FieldDecl > ( calleeMemberExpr - > getMemberDecl ( ) ) ) {
return ;
}
}
}
}
// ignore assignments from a local variable to a local variable, to avoid unnecessary refcounting traffic
if ( auto callExpr = dyn_cast < CXXMemberCallExpr > ( rhs ) ) {
if ( auto calleeMemberExpr = dyn_cast < MemberExpr > ( callExpr - > getCallee ( ) ) ) {
if ( auto declRefExpr = dyn_cast < DeclRefExpr > ( calleeMemberExpr - > getBase ( ) - > IgnoreImpCasts ( ) ) ) {
if ( isa < VarDecl > ( declRefExpr - > getDecl ( ) ) ) {
return ;
}
}
}
}
if ( auto declRefExpr = dyn_cast < DeclRefExpr > ( rhs - > IgnoreImpCasts ( ) ) ) {
if ( isa < VarDecl > ( declRefExpr - > getDecl ( ) ) ) {
return ;
}
}
2016-11-10 12:53:02 +02:00
report (
DiagnosticsEngine : : Warning ,
2016-11-25 16:23:17 +02:00
" assigning a returned-by-value VclPtr<T> to a T* variable is dodgy, should be assigned to a VclPtr. If you know that the RHS does not return a newly created T, then add a '.get()' to the RHS " ,
2016-11-10 12:53:02 +02:00
rhs - > getSourceRange ( ) . getBegin ( ) )
< < rhs - > getSourceRange ( ) ;
}
2015-01-08 14:09:13 +02:00
2015-02-11 14:42:23 +02:00
bool VCLWidgets : : VisitVarDecl ( const VarDecl * pVarDecl ) {
if ( ignoreLocation ( pVarDecl ) ) {
return true ;
}
2016-10-28 11:13:37 +02:00
if ( isa < ParmVarDecl > ( pVarDecl ) ) {
2015-02-11 14:42:23 +02:00
return true ;
}
2016-11-25 16:23:17 +02:00
SourceLocation spellingLocation = compiler . getSourceManager ( ) . getSpellingLoc (
pVarDecl - > getLocStart ( ) ) ;
2016-11-10 12:53:02 +02:00
if ( pVarDecl - > getInit ( ) ) {
2016-11-25 16:23:17 +02:00
checkAssignmentForVclPtrToRawConversion ( spellingLocation , pVarDecl - > getType ( ) . getTypePtr ( ) , pVarDecl - > getInit ( ) ) ;
2016-11-10 12:53:02 +02:00
}
2016-11-25 16:23:17 +02:00
StringRef aFileName = compiler . getSourceManager ( ) . getFilename ( spellingLocation ) ;
2017-05-18 09:56:01 +02:00
if ( loplugin : : isSamePathname ( aFileName , SRCDIR " /include/vcl/vclptr.hxx " ) )
2016-10-28 11:13:37 +02:00
return true ;
2017-05-18 09:56:01 +02:00
if ( loplugin : : isSamePathname ( aFileName , SRCDIR " /vcl/source/window/layout.cxx " ) )
2016-10-28 11:13:37 +02:00
return true ;
// whitelist the valid things that can contain pointers.
// It is containing stuff like std::unique_ptr we get worried
if ( pVarDecl - > getType ( ) - > isArrayType ( ) ) {
2015-05-12 13:30:32 +02:00
return true ;
}
2016-10-28 11:13:37 +02:00
auto tc = loplugin : : TypeCheck ( pVarDecl - > getType ( ) ) ;
if ( tc . Pointer ( )
| | tc . Class ( " map " ) . StdNamespace ( )
| | tc . Class ( " multimap " ) . StdNamespace ( )
| | tc . Class ( " vector " ) . StdNamespace ( )
| | tc . Class ( " list " ) . StdNamespace ( )
| | tc . Class ( " mem_fun1_t " ) . StdNamespace ( )
// registration template thing, doesn't actually allocate anything we need to care about
2017-06-23 21:25:26 +02:00
| | tc . Class ( " OMultiInstanceAutoRegistration " ) . Namespace ( " compmodule " ) . GlobalNamespace ( ) )
2016-10-28 11:13:37 +02:00
{
2015-05-12 13:30:32 +02:00
return true ;
}
2016-10-28 11:13:37 +02:00
// Apparently I should be doing some kind of lookup for a partial specialisations of std::iterator_traits<T> to see if an
// object is an iterator, but that sounds like too much work
2016-10-31 21:01:18 +01:00
auto t = pVarDecl - > getType ( ) . getDesugaredType ( compiler . getASTContext ( ) ) ;
std : : string s = t . getAsString ( ) ;
if ( s . find ( " iterator " ) ! = std : : string : : npos
| | loplugin : : TypeCheck ( t ) . Class ( " __wrap_iter " ) . StdNamespace ( ) )
{
2015-05-12 13:30:32 +02:00
return true ;
}
2016-10-28 11:13:37 +02:00
// std::pair seems to show up in whacky ways in clang's AST. Sometimes it's a class, sometimes it's a typedef, and sometimes
// its an ElaboratedType (whatever that is)
if ( s . find ( " pair " ) ! = std : : string : : npos ) {
return true ;
}
if ( containsVclReferenceBaseSubclass ( pVarDecl - > getType ( ) ) ) {
2015-05-12 13:30:32 +02:00
report (
DiagnosticsEngine : : Warning ,
2016-10-28 11:13:37 +02:00
BASE_REF_COUNTED_CLASS " subclass %0 should be wrapped in VclPtr " ,
2015-05-12 13:30:32 +02:00
pVarDecl - > getLocation ( ) )
2016-10-28 11:13:37 +02:00
< < pVarDecl - > getType ( ) < < pVarDecl - > getSourceRange ( ) ;
return true ;
2015-03-17 15:13:34 +02:00
}
2015-02-11 14:42:23 +02:00
return true ;
}
2014-07-21 08:27:11 +02:00
bool VCLWidgets : : VisitFieldDecl ( const FieldDecl * fieldDecl ) {
if ( ignoreLocation ( fieldDecl ) ) {
return true ;
}
2016-10-28 11:13:37 +02:00
StringRef aFileName = compiler . getSourceManager ( ) . getFilename ( compiler . getSourceManager ( ) . getSpellingLoc ( fieldDecl - > getLocStart ( ) ) ) ;
2017-05-18 09:56:01 +02:00
if ( loplugin : : isSamePathname ( aFileName , SRCDIR " /include/vcl/vclptr.hxx " ) )
2016-10-28 11:13:37 +02:00
return true ;
2017-05-18 09:56:01 +02:00
if ( loplugin : : isSamePathname ( aFileName , SRCDIR " /include/rtl/ref.hxx " ) )
2016-10-28 11:13:37 +02:00
return true ;
2017-05-18 09:56:01 +02:00
if ( loplugin : : isSamePathname ( aFileName , SRCDIR " /include/o3tl/enumarray.hxx " ) )
2016-10-28 11:13:37 +02:00
return true ;
2017-05-18 09:56:01 +02:00
if ( loplugin : : isSamePathname ( aFileName , SRCDIR " /vcl/source/window/layout.cxx " ) )
2016-10-28 11:13:37 +02:00
return true ;
2014-07-21 08:27:11 +02:00
if ( fieldDecl - > isBitField ( ) ) {
return true ;
}
2015-04-28 22:36:48 +02:00
const CXXRecordDecl * pParentRecordDecl = isa < RecordDecl > ( fieldDecl - > getDeclContext ( ) ) ? dyn_cast < CXXRecordDecl > ( fieldDecl - > getParent ( ) ) : nullptr ;
2016-12-07 07:27:24 +01:00
if ( loplugin : : DeclCheck ( pParentRecordDecl ) . Class ( " VclPtr " )
. GlobalNamespace ( ) )
{
2016-10-28 11:13:37 +02:00
return true ;
}
if ( containsVclReferenceBaseSubclass ( fieldDecl - > getType ( ) ) ) {
2015-04-17 09:59:18 +02:00
// have to ignore this for now, nasty reverse dependency from tools->vcl
2016-12-07 07:27:24 +01:00
auto check = loplugin : : DeclCheck ( pParentRecordDecl ) ;
2017-04-27 10:03:03 +02:00
if ( ! ( check . Struct ( " ImplErrorContext " ) . GlobalNamespace ( )
2016-12-07 07:27:24 +01:00
| | check . Class ( " ScHFEditPage " ) . GlobalNamespace ( ) ) )
{
2015-04-17 09:59:18 +02:00
report (
DiagnosticsEngine : : Warning ,
2016-05-18 12:25:54 +02:00
BASE_REF_COUNTED_CLASS " subclass %0 declared as a pointer member, should be wrapped in VclPtr " ,
2015-04-17 09:59:18 +02:00
fieldDecl - > getLocation ( ) )
2015-11-05 09:30:42 +01:00
< < fieldDecl - > getType ( ) < < fieldDecl - > getSourceRange ( ) ;
2016-10-28 11:13:37 +02:00
if ( auto parent = dyn_cast < ClassTemplateSpecializationDecl > ( fieldDecl - > getParent ( ) ) ) {
report (
DiagnosticsEngine : : Note ,
" template field here " ,
parent - > getPointOfInstantiation ( ) ) ;
}
2015-04-17 09:59:18 +02:00
return true ;
2016-12-07 07:27:24 +01:00
}
2015-01-08 13:30:36 +02:00
}
2014-07-21 08:27:11 +02:00
const RecordType * recordType = fieldDecl - > getType ( ) - > getAs < RecordType > ( ) ;
if ( recordType = = nullptr ) {
return true ;
}
const CXXRecordDecl * recordDecl = dyn_cast < CXXRecordDecl > ( recordType - > getDecl ( ) ) ;
if ( recordDecl = = nullptr ) {
return true ;
}
2015-01-26 08:43:33 +02:00
2016-10-28 11:13:37 +02:00
// check if this field is derived fromVclReferenceBase
if ( isDerivedFromVclReferenceBase ( recordDecl ) ) {
2015-01-26 08:43:33 +02:00
report (
DiagnosticsEngine : : Warning ,
2016-05-18 12:25:54 +02:00
BASE_REF_COUNTED_CLASS " subclass allocated as a class member, should be allocated via VclPtr " ,
2015-01-26 08:43:33 +02:00
fieldDecl - > getLocation ( ) )
< < fieldDecl - > getSourceRange ( ) ;
}
// If this field is a VclPtr field, then the class MUST have a dispose method
2016-10-28 11:13:37 +02:00
if ( pParentRecordDecl & & isDerivedFromVclReferenceBase ( pParentRecordDecl )
2016-12-07 07:27:24 +01:00
& & loplugin : : DeclCheck ( recordDecl ) . Class ( " VclPtr " ) . GlobalNamespace ( ) )
2015-01-26 13:10:46 +02:00
{
2015-09-25 11:41:53 +01:00
bool bFoundDispose = false ;
2015-05-05 14:16:08 +02:00
for ( auto methodDecl = pParentRecordDecl - > method_begin ( ) ;
methodDecl ! = pParentRecordDecl - > method_end ( ) ; + + methodDecl )
{
2016-12-07 07:27:24 +01:00
if ( methodDecl - > isInstance ( ) & & methodDecl - > param_size ( ) = = 0
& & loplugin : : DeclCheck ( * methodDecl ) . Function ( " dispose " ) )
{
2015-09-25 11:41:53 +01:00
bFoundDispose = true ;
2015-01-26 08:43:33 +02:00
break ;
}
}
2015-09-25 11:41:53 +01:00
if ( ! bFoundDispose ) {
2015-01-26 13:10:46 +02:00
report (
DiagnosticsEngine : : Warning ,
2016-05-18 12:25:54 +02:00
BASE_REF_COUNTED_CLASS " subclass with a VclPtr field MUST override dispose() (and call its superclass dispose() as the last thing it does) " ,
2015-01-26 13:10:46 +02:00
fieldDecl - > getLocation ( ) )
< < fieldDecl - > getSourceRange ( ) ;
2015-01-26 08:43:33 +02:00
}
2015-03-17 08:55:36 +02:00
if ( ! pParentRecordDecl - > hasUserDeclaredDestructor ( ) ) {
report (
DiagnosticsEngine : : Warning ,
2016-05-18 12:25:54 +02:00
BASE_REF_COUNTED_CLASS " subclass with a VclPtr field MUST have a user-provided destructor (that calls disposeOnce()) " ,
2015-03-17 08:55:36 +02:00
fieldDecl - > getLocation ( ) )
< < fieldDecl - > getSourceRange ( ) ;
}
2014-07-21 08:27:11 +02:00
}
return true ;
}
2015-01-09 11:33:52 +02:00
bool VCLWidgets : : VisitParmVarDecl ( ParmVarDecl const * pvDecl )
{
2015-01-07 10:40:53 +02:00
if ( ignoreLocation ( pvDecl ) ) {
return true ;
}
2015-01-09 11:33:52 +02:00
// ignore the stuff in the VclPtr template class
const CXXMethodDecl * pMethodDecl = dyn_cast < CXXMethodDecl > ( pvDecl - > getDeclContext ( ) ) ;
2016-12-07 07:27:24 +01:00
if ( loplugin : : DeclCheck ( pMethodDecl ) . MemberFunction ( ) . Class ( " VclPtr " )
. GlobalNamespace ( ) )
{
2015-01-07 10:40:53 +02:00
return true ;
}
2015-03-09 14:29:30 +02:00
// we exclude this method in VclBuilder because it's so useful to have it like this
2016-12-07 07:27:24 +01:00
auto check = loplugin : : DeclCheck ( pMethodDecl ) . Function ( " get " ) ;
if ( check . Class ( " VclBuilder " ) . GlobalNamespace ( )
| | check . Class ( " VclBuilderContainer " ) . GlobalNamespace ( ) )
2015-03-09 14:29:30 +02:00
{
return true ;
}
2015-01-07 10:40:53 +02:00
return true ;
}
2014-07-21 08:27:11 +02:00
2016-06-30 14:09:31 +02:00
static void findDisposeAndClearStatements ( std : : set < const FieldDecl * > & aVclPtrFields , const Stmt * pStmt )
{
if ( ! pStmt )
return ;
if ( isa < CompoundStmt > ( pStmt ) ) {
const CompoundStmt * pCompoundStatement = dyn_cast < CompoundStmt > ( pStmt ) ;
2016-07-11 18:09:55 +02:00
for ( auto i = pCompoundStatement - > body_begin ( ) ;
i ! = pCompoundStatement - > body_end ( ) ; + + i )
{
findDisposeAndClearStatements ( aVclPtrFields , * i ) ;
2016-06-30 14:09:31 +02:00
}
return ;
}
if ( isa < ForStmt > ( pStmt ) ) {
findDisposeAndClearStatements ( aVclPtrFields , dyn_cast < ForStmt > ( pStmt ) - > getBody ( ) ) ;
return ;
}
if ( isa < IfStmt > ( pStmt ) ) {
findDisposeAndClearStatements ( aVclPtrFields , dyn_cast < IfStmt > ( pStmt ) - > getThen ( ) ) ;
findDisposeAndClearStatements ( aVclPtrFields , dyn_cast < IfStmt > ( pStmt ) - > getElse ( ) ) ;
return ;
}
if ( ! isa < CallExpr > ( pStmt ) ) return ;
const CallExpr * pCallExpr = dyn_cast < CallExpr > ( pStmt ) ;
if ( ! pCallExpr - > getDirectCallee ( ) ) return ;
if ( ! isa < CXXMethodDecl > ( pCallExpr - > getDirectCallee ( ) ) ) return ;
2016-12-07 07:27:24 +01:00
auto check = loplugin : : DeclCheck (
dyn_cast < CXXMethodDecl > ( pCallExpr - > getDirectCallee ( ) ) ) ;
if ( ! ( check . Function ( " disposeAndClear " ) | | check . Function ( " clear " ) ) )
2016-06-30 14:09:31 +02:00
return ;
if ( ! pCallExpr - > getCallee ( ) ) return ;
if ( ! isa < MemberExpr > ( pCallExpr - > getCallee ( ) ) ) return ;
const MemberExpr * pCalleeMemberExpr = dyn_cast < MemberExpr > ( pCallExpr - > getCallee ( ) ) ;
if ( ! pCalleeMemberExpr - > getBase ( ) ) return ;
2017-09-28 18:31:53 +02:00
const MemberExpr * pCalleeMemberExprBase = dyn_cast < MemberExpr > ( pCalleeMemberExpr - > getBase ( ) - > IgnoreImpCasts ( ) ) ;
if ( pCalleeMemberExprBase = = nullptr ) return ;
2016-06-30 14:09:31 +02:00
const FieldDecl * xxx = dyn_cast_or_null < FieldDecl > ( pCalleeMemberExprBase - > getMemberDecl ( ) ) ;
if ( xxx )
aVclPtrFields . erase ( xxx ) ;
}
2015-01-08 13:30:36 +02:00
bool VCLWidgets : : VisitFunctionDecl ( const FunctionDecl * functionDecl )
{
if ( ignoreLocation ( functionDecl ) ) {
return true ;
}
2015-01-09 11:33:52 +02:00
// ignore the stuff in the VclPtr template class
2016-12-07 07:27:24 +01:00
if ( loplugin : : DeclCheck ( functionDecl ) . MemberFunction ( ) . Class ( " VclPtr " )
. GlobalNamespace ( ) )
{
2015-01-09 11:33:52 +02:00
return true ;
}
2016-05-18 12:25:54 +02:00
// ignore the BASE_REF_COUNTED_CLASS::dispose() method
2016-12-07 07:27:24 +01:00
if ( loplugin : : DeclCheck ( functionDecl ) . Function ( " dispose " )
. Class ( BASE_REF_COUNTED_CLASS ) . GlobalNamespace ( ) )
{
2015-01-15 15:48:23 +02:00
return true ;
}
2016-12-07 07:27:24 +01:00
const CXXMethodDecl * pMethodDecl = dyn_cast < CXXMethodDecl > ( functionDecl ) ;
2016-10-28 11:13:37 +02:00
if ( functionDecl - > hasBody ( ) & & pMethodDecl & & isDerivedFromVclReferenceBase ( pMethodDecl - > getParent ( ) ) ) {
2015-01-15 15:48:23 +02:00
// check the last thing that the dispose() method does, is to call into the superclass dispose method
2016-12-07 07:27:24 +01:00
if ( loplugin : : DeclCheck ( functionDecl ) . Function ( " dispose " ) ) {
2015-01-15 15:48:23 +02:00
if ( ! isDisposeCallingSuperclassDispose ( pMethodDecl ) ) {
report (
DiagnosticsEngine : : Warning ,
2016-05-18 12:25:54 +02:00
BASE_REF_COUNTED_CLASS " subclass dispose() function MUST call dispose() of its superclass as the last thing it does " ,
2015-03-09 14:29:30 +02:00
functionDecl - > getLocStart ( ) )
< < functionDecl - > getSourceRange ( ) ;
2015-01-15 15:48:23 +02:00
}
}
}
2015-03-09 14:29:30 +02:00
2016-06-30 14:09:31 +02:00
// check dispose method to make sure we are actually disposing all of the VclPtr fields
// FIXME this is not exhaustive. We should enable shouldVisitTemplateInstantiations and look deeper inside type declarations
if ( pMethodDecl & & pMethodDecl - > isInstance ( ) & & pMethodDecl - > getBody ( )
& & pMethodDecl - > param_size ( ) = = 0
2016-12-07 07:27:24 +01:00
& & loplugin : : DeclCheck ( functionDecl ) . Function ( " dispose " )
2016-10-28 11:13:37 +02:00
& & isDerivedFromVclReferenceBase ( pMethodDecl - > getParent ( ) ) )
2016-06-30 14:09:31 +02:00
{
2016-12-07 07:27:24 +01:00
auto check = loplugin : : DeclCheck ( functionDecl ) . MemberFunction ( ) ;
if ( check . Class ( " VirtualDevice " ) . GlobalNamespace ( )
| | check . Class ( " Breadcrumb " ) . GlobalNamespace ( ) )
{
2016-06-30 14:09:31 +02:00
return true ;
2016-12-07 07:27:24 +01:00
}
2016-06-30 14:09:31 +02:00
std : : set < const FieldDecl * > aVclPtrFields ;
2016-07-11 18:09:55 +02:00
for ( auto i = pMethodDecl - > getParent ( ) - > field_begin ( ) ;
i ! = pMethodDecl - > getParent ( ) - > field_end ( ) ; + + i )
{
auto const type = loplugin : : TypeCheck ( ( * i ) - > getType ( ) ) ;
2016-06-30 14:09:31 +02:00
if ( type . Class ( " VclPtr " ) . GlobalNamespace ( ) ) {
2016-07-11 18:09:55 +02:00
aVclPtrFields . insert ( * i ) ;
2016-06-30 14:09:31 +02:00
} else if ( type . Class ( " vector " ) . StdNamespace ( )
| | type . Class ( " map " ) . StdNamespace ( )
| | type . Class ( " list " ) . StdNamespace ( )
| | type . Class ( " set " ) . StdNamespace ( ) )
{
2016-07-11 18:09:55 +02:00
const RecordType * recordType = dyn_cast_or_null < RecordType > ( ( * i ) - > getType ( ) - > getUnqualifiedDesugaredType ( ) ) ;
2016-06-30 14:09:31 +02:00
if ( recordType ) {
auto d = dyn_cast < ClassTemplateSpecializationDecl > ( recordType - > getDecl ( ) ) ;
if ( d & & d - > getTemplateArgs ( ) . size ( ) > 0 ) {
auto const type = loplugin : : TypeCheck ( d - > getTemplateArgs ( ) [ 0 ] . getAsType ( ) ) ;
if ( type . Class ( " VclPtr " ) . GlobalNamespace ( ) ) {
2016-07-11 18:09:55 +02:00
aVclPtrFields . insert ( * i ) ;
2016-06-30 14:09:31 +02:00
}
}
}
}
}
if ( ! aVclPtrFields . empty ( ) ) {
findDisposeAndClearStatements ( aVclPtrFields , pMethodDecl - > getBody ( ) ) ;
if ( ! aVclPtrFields . empty ( ) ) {
//pMethodDecl->dump();
std : : string aMessage = BASE_REF_COUNTED_CLASS " subclass dispose() method does not call disposeAndClear() or clear() on the following field(s): " ;
for ( auto s : aVclPtrFields )
aMessage + = " , " + s - > getNameAsString ( ) ;
report (
DiagnosticsEngine : : Warning ,
aMessage ,
functionDecl - > getLocStart ( ) )
< < functionDecl - > getSourceRange ( ) ;
}
}
}
2015-01-08 13:30:36 +02:00
return true ;
}
2015-03-09 14:29:30 +02:00
bool VCLWidgets : : VisitCXXDeleteExpr ( const CXXDeleteExpr * pCXXDeleteExpr )
{
if ( ignoreLocation ( pCXXDeleteExpr ) ) {
return true ;
}
2015-03-18 08:33:14 +02:00
const CXXRecordDecl * pPointee = pCXXDeleteExpr - > getArgument ( ) - > getType ( ) - > getPointeeCXXRecordDecl ( ) ;
2016-10-28 11:13:37 +02:00
if ( pPointee & & isDerivedFromVclReferenceBase ( pPointee ) ) {
2015-03-19 13:53:55 +02:00
SourceLocation spellingLocation = compiler . getSourceManager ( ) . getSpellingLoc (
pCXXDeleteExpr - > getLocStart ( ) ) ;
StringRef filename = compiler . getSourceManager ( ) . getFilename ( spellingLocation ) ;
2017-05-18 09:56:01 +02:00
if ( ! ( loplugin : : hasPathnamePrefix ( filename , SRCDIR " /include/vcl/vclreferencebase.hxx " ) ) )
2015-03-19 13:53:55 +02:00
{
report (
DiagnosticsEngine : : Warning ,
2016-05-18 12:25:54 +02:00
" calling delete on instance of " BASE_REF_COUNTED_CLASS " subclass, must rather call disposeAndClear() " ,
2015-03-19 13:53:55 +02:00
pCXXDeleteExpr - > getLocStart ( ) )
< < pCXXDeleteExpr - > getSourceRange ( ) ;
}
2015-03-18 08:33:14 +02:00
}
2015-03-09 14:29:30 +02:00
const ImplicitCastExpr * pImplicitCastExpr = dyn_cast < ImplicitCastExpr > ( pCXXDeleteExpr - > getArgument ( ) ) ;
if ( ! pImplicitCastExpr ) {
return true ;
}
if ( pImplicitCastExpr - > getCastKind ( ) ! = CK_UserDefinedConversion ) {
return true ;
}
report (
DiagnosticsEngine : : Warning ,
" calling delete on instance of VclPtr, must rather call disposeAndClear() " ,
pCXXDeleteExpr - > getLocStart ( ) )
< < pCXXDeleteExpr - > getSourceRange ( ) ;
return true ;
}
2015-01-15 15:48:23 +02:00
/**
The AST looks like :
` - CXXMemberCallExpr 0xb06d8b0 ' void '
` - MemberExpr 0xb06d868 ' < bound member function type > ' - > dispose 0x9d34880
` - ImplicitCastExpr 0xb06d8d8 ' class SfxTabPage * ' < UncheckedDerivedToBase ( SfxTabPage ) >
` - CXXThisExpr 0xb06d850 ' class SfxAcceleratorConfigPage * ' this
*/
bool VCLWidgets : : isDisposeCallingSuperclassDispose ( const CXXMethodDecl * pMethodDecl )
{
const CompoundStmt * pCompoundStatement = dyn_cast < CompoundStmt > ( pMethodDecl - > getBody ( ) ) ;
if ( ! pCompoundStatement ) return false ;
2015-03-09 14:29:30 +02:00
if ( pCompoundStatement - > size ( ) = = 0 ) return false ;
2015-01-15 15:48:23 +02:00
// find the last statement
const CXXMemberCallExpr * pCallExpr = dyn_cast < CXXMemberCallExpr > ( * pCompoundStatement - > body_rbegin ( ) ) ;
if ( ! pCallExpr ) return false ;
const MemberExpr * pMemberExpr = dyn_cast < MemberExpr > ( pCallExpr - > getCallee ( ) ) ;
if ( ! pMemberExpr ) return false ;
2016-12-07 07:27:24 +01:00
if ( ! loplugin : : DeclCheck ( pMemberExpr - > getMemberDecl ( ) ) . Function ( " dispose " ) ) return false ;
2015-01-15 15:48:23 +02:00
const CXXMethodDecl * pDirectCallee = dyn_cast < CXXMethodDecl > ( pCallExpr - > getDirectCallee ( ) ) ;
if ( ! pDirectCallee ) return false ;
/* Not working yet. Partially because sometimes the superclass does not a dispose() method, so it gets passed up the chain.
Need complex checking for that case .
if ( pDirectCallee - > getParent ( ) - > getTypeForDecl ( ) ! = ( * pMethodDecl - > getParent ( ) - > bases_begin ( ) ) . getType ( ) . getTypePtr ( ) ) {
report (
DiagnosticsEngine : : Warning ,
" dispose() method calling wrong baseclass, calling " + pDirectCallee - > getParent ( ) - > getQualifiedNameAsString ( ) +
" should be calling " + ( * pMethodDecl - > getParent ( ) - > bases_begin ( ) ) . getType ( ) . getAsString ( ) ,
pCallExpr - > getLocStart ( ) )
< < pCallExpr - > getSourceRange ( ) ;
return false ;
} */
return true ;
}
2017-09-11 10:48:12 +02:00
bool containsVclPtr ( const clang : : Type * pType0 ) ;
2015-05-18 10:08:00 +02:00
bool containsVclPtr ( const QualType & qType ) {
2016-12-07 07:27:24 +01:00
auto check = loplugin : : TypeCheck ( qType ) ;
if ( check . Class ( " ScopedVclPtr " ) . GlobalNamespace ( )
| | check . Class ( " ScopedVclPtrInstance " ) . GlobalNamespace ( )
| | check . Class ( " VclPtr " ) . GlobalNamespace ( )
| | check . Class ( " VclPtrInstance " ) . GlobalNamespace ( ) )
{
return true ;
2015-05-18 10:08:00 +02:00
}
return containsVclPtr ( qType . getTypePtr ( ) ) ;
}
2017-09-11 10:48:12 +02:00
bool containsVclPtr ( const clang : : Type * pType0 ) {
2015-05-18 10:08:00 +02:00
if ( ! pType0 )
return false ;
2017-09-11 10:48:12 +02:00
const clang : : Type * pType = pType0 - > getUnqualifiedDesugaredType ( ) ;
2015-05-18 10:08:00 +02:00
if ( ! pType )
return false ;
if ( pType - > isPointerType ( ) ) {
return false ;
} else if ( pType - > isArrayType ( ) ) {
2017-09-11 10:48:12 +02:00
const clang : : ArrayType * pArrayType = dyn_cast < clang : : ArrayType > ( pType ) ;
2015-05-18 10:08:00 +02:00
QualType elementType = pArrayType - > getElementType ( ) ;
return containsVclPtr ( elementType ) ;
} else {
const CXXRecordDecl * pRecordDecl = pType - > getAsCXXRecordDecl ( ) ;
if ( pRecordDecl )
{
2016-12-07 07:27:24 +01:00
auto check = loplugin : : DeclCheck ( pRecordDecl ) ;
if ( check . Class ( " ScopedVclPtr " ) . GlobalNamespace ( )
| | check . Class ( " ScopedVclPtrInstance " ) . GlobalNamespace ( )
| | check . Class ( " VclPtr " ) . GlobalNamespace ( )
| | check . Class ( " VclPtrInstance " ) . GlobalNamespace ( ) )
2015-05-18 10:08:00 +02:00
{
return true ;
}
for ( auto fieldDecl = pRecordDecl - > field_begin ( ) ;
fieldDecl ! = pRecordDecl - > field_end ( ) ; + + fieldDecl )
{
const RecordType * pFieldRecordType = fieldDecl - > getType ( ) - > getAs < RecordType > ( ) ;
if ( pFieldRecordType & & containsVclPtr ( pFieldRecordType ) ) {
return true ;
}
}
for ( auto baseSpecifier = pRecordDecl - > bases_begin ( ) ;
baseSpecifier ! = pRecordDecl - > bases_end ( ) ; + + baseSpecifier )
{
const RecordType * pFieldRecordType = baseSpecifier - > getType ( ) - > getAs < RecordType > ( ) ;
if ( pFieldRecordType & & containsVclPtr ( pFieldRecordType ) ) {
return true ;
}
}
}
}
return false ;
}
bool VCLWidgets : : VisitCallExpr ( const CallExpr * pCallExpr )
{
if ( ignoreLocation ( pCallExpr ) ) {
return true ;
}
FunctionDecl const * fdecl = pCallExpr - > getDirectCallee ( ) ;
if ( fdecl = = nullptr ) {
return true ;
}
std : : string qname { fdecl - > getQualifiedNameAsString ( ) } ;
if ( qname . find ( " memcpy " ) = = std : : string : : npos
& & qname . find ( " bcopy " ) = = std : : string : : npos
& & qname . find ( " memmove " ) = = std : : string : : npos
& & qname . find ( " rtl_copy " ) = = std : : string : : npos ) {
return true ;
}
mbCheckingMemcpy = true ;
Stmt * pStmt = const_cast < Stmt * > ( static_cast < const Stmt * > ( pCallExpr - > getArg ( 0 ) ) ) ;
TraverseStmt ( pStmt ) ;
mbCheckingMemcpy = false ;
return true ;
}
bool VCLWidgets : : VisitDeclRefExpr ( const DeclRefExpr * pDeclRefExpr )
{
if ( ! mbCheckingMemcpy ) {
return true ;
}
if ( ignoreLocation ( pDeclRefExpr ) ) {
return true ;
}
QualType pType = pDeclRefExpr - > getDecl ( ) - > getType ( ) ;
if ( pType - > isPointerType ( ) ) {
pType = pType - > getPointeeType ( ) ;
}
if ( ! containsVclPtr ( pType ) ) {
return true ;
}
report (
DiagnosticsEngine : : Warning ,
" Calling memcpy on a type which contains a VclPtr " ,
pDeclRefExpr - > getExprLoc ( ) ) ;
return true ;
}
2015-01-15 15:48:23 +02:00
2015-06-08 10:09:34 +01:00
bool VCLWidgets : : VisitCXXConstructExpr ( const CXXConstructExpr * constructExpr )
{
if ( ignoreLocation ( constructExpr ) ) {
return true ;
}
if ( constructExpr - > getConstructionKind ( ) ! = CXXConstructExpr : : CK_Complete ) {
return true ;
}
const CXXConstructorDecl * pConstructorDecl = constructExpr - > getConstructor ( ) ;
const CXXRecordDecl * recordDecl = pConstructorDecl - > getParent ( ) ;
2016-10-28 11:13:37 +02:00
if ( isDerivedFromVclReferenceBase ( recordDecl ) ) {
2016-12-09 20:00:26 +01:00
StringRef aFileName = compiler . getSourceManager ( ) . getFilename ( compiler . getSourceManager ( ) . getSpellingLoc ( constructExpr - > getLocStart ( ) ) ) ;
2017-05-18 09:56:01 +02:00
if ( ! loplugin : : isSamePathname ( aFileName , SRCDIR " /include/vcl/vclptr.hxx " ) ) {
2016-12-09 20:00:26 +01:00
report (
DiagnosticsEngine : : Warning ,
" Calling constructor of a VclReferenceBase-derived type directly; all such creation should go via VclPtr<>::Create " ,
constructExpr - > getExprLoc ( ) ) ;
}
2015-06-08 10:09:34 +01:00
}
return true ;
}
2015-01-08 13:30:36 +02:00
2014-07-21 08:27:11 +02:00
loplugin : : Plugin : : Registration < VCLWidgets > X ( " vclwidgets " ) ;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */