@ -7,9 +7,9 @@ namespace CppSharp.Passes
/// <summary>
/// <summary>
/// Checks for classes that should be bound as static classes.
/// Checks for classes that should be bound as static classes.
/// </summary>
/// </summary>
public class CheckStaticClass : TranslationUnitPass
public class CheckStaticClassPass : TranslationUnitPass
{
{
public CheckStaticClass ( )
public CheckStaticClassPass ( )
= > VisitOptions . ResetFlags ( VisitFlags . ClassMethods ) ;
= > VisitOptions . ResetFlags ( VisitFlags . ClassMethods ) ;
public override bool VisitDeclaration ( Declaration decl )
public override bool VisitDeclaration ( Declaration decl )
@ -32,8 +32,7 @@ namespace CppSharp.Passes
if ( decl . Access ! = AccessSpecifier . Protected )
if ( decl . Access ! = AccessSpecifier . Protected )
return false ;
return false ;
var @class = decl . Namespace as Class ;
return decl . Namespace is Class { IsStatic : true } ;
return @class ! = null & & @class . IsStatic ;
}
}
static void SetDeclarationAccessToPrivate ( Declaration decl )
static void SetDeclarationAccessToPrivate ( Declaration decl )
@ -42,7 +41,7 @@ namespace CppSharp.Passes
// as an internal in the final C# wrapper.
// as an internal in the final C# wrapper.
decl . Access = AccessSpecifier . Private ;
decl . Access = AccessSpecifier . Private ;
// We need to explicity set the generation else the
// We need to explicitl y set the generation else the
// now private declaration will get filtered out later.
// now private declaration will get filtered out later.
decl . GenerationKind = GenerationKind . Generate ;
decl . GenerationKind = GenerationKind . Generate ;
}
}
@ -51,53 +50,90 @@ namespace CppSharp.Passes
{
{
var returnType = function . ReturnType . Type . Desugar ( ) ;
var returnType = function . ReturnType . Type . Desugar ( ) ;
var tag = returnType as TagType ;
if ( returnType is not TagType tag )
if ( tag = = null )
returnType . IsPointerTo ( out tag ) ;
returnType . IsPointerTo ( out tag ) ;
if ( tag = = null )
var decl = tag ? . Declaration ;
if ( decl is not Class )
return false ;
return false ;
var @class = ( Class ) function . Namespace ;
var @class = ( Class ) function . Namespace ;
var decl = tag . Declaration ;
return @class . QualifiedOriginalName = = decl . QualifiedOriginalName ;
}
if ( ! ( decl is Class ) )
static bool AcceptsClassInstance ( Function function )
return false ;
{
return function . Parameters . Any ( param = >
{
var paramType = param . Type . Desugar ( ) ;
return @class . QualifiedOriginalName = = decl . QualifiedOriginalName ;
if ( paramType is not TagType tag )
paramType . IsPointerTo ( out tag ) ;
var decl = tag ? . Declaration ;
if ( decl is not Class )
return false ;
var @class = ( Class ) function . Namespace ;
return @class . QualifiedOriginalName = = decl . QualifiedOriginalName ;
}
) ;
}
}
public override bool VisitClassDecl ( Class @class )
public override bool VisitClassDecl ( Class @class )
{
{
// If the class has any non-private constructors then it cannot
// If the class is to be used as an opaque type, then it cannot be
// be bound as a static class and we bail out early.
// bound as static.
if ( @class . Constructors . Any ( m = >
if ( @class . IsOpaque )
! ( m . IsCopyConstructor | | m . IsMoveConstructor )
return false ;
& & m . Access ! = AccessSpecifier . Private ) )
if ( @class . IsDependent )
return false ;
// Polymorphic classes are currently not supported.
// TODO: We could support this if the base class is also static, since it's composition then.
if ( @class . IsPolymorphic )
return false ;
// Make sure we have at least one accessible static method or field
if ( ! @class . Methods . Any ( m = > m . Kind = = CXXMethodKind . Normal & & m . Access ! = AccessSpecifier . Private & & m . IsStatic )
& & @class . Variables . All ( v = > v . Access = = AccessSpecifier . Private ) )
return false ;
return false ;
// Check for any non-static fields or methods, in which case we
// Check for any non-static fields or methods, in which case we
// assume the class is not meant to be static.
// assume the class is not meant to be static.
// Note: Static fields are represented as variables in the AST.
// Note: Static fields are represented as variables in the AST.
if ( @class . Fields . Any ( ) | |
if ( @class . Fields . Count ! = 0 )
@class . Methods . Any ( m = > m . Kind = = CXXMethodKind . Normal
& & ! m . IsStatic ) )
return false ;
return false ;
// Check for any static function that return a pointer to the class.
if ( @class . Methods . Any ( m = > m . Kind = = CXXMethodKind . Normal & & ! m . IsStatic ) )
// If one exists, we assume it's a factory function and the class is
// not meant to be static. It's a simple heuristic but it should be
// good enough for the time being.
if ( @class . Functions . Any ( m = > ! m . IsOperator & & ReturnsClassInstance ( m ) ) | |
@class . Methods . Any ( m = > ! m . IsOperator & & ReturnsClassInstance ( m ) ) )
return false ;
return false ;
// If the class is to be used as an opaque type, then it cannot be
if ( @class . Constructors . Any ( m = >
// bound as static.
{
if ( @class . IsOpaque )
// Implicit constructors are not user-defined, so assume this was unintentional.
if ( m . IsImplicit )
return false ;
// Ignore deleted constructors.
if ( m . IsDeleted )
return false ;
// If the class has a copy or move constructor, it cannot be static.
if ( m . IsCopyConstructor | | m . IsMoveConstructor )
return true ;
// If the class has any (user defined) non-private constructors then it cannot be static
return m . Access ! = AccessSpecifier . Private ;
} ) )
{
return false ;
return false ;
}
if ( @class . IsDependent )
// Check for any static function that accepts/returns a pointer to the class.
// If one exists, we assume it's not meant to be static
if ( @class . Functions . Any ( m = > ! m . IsOperator & & ( ReturnsClassInstance ( m ) | | AcceptsClassInstance ( m ) ) ) | |
@class . Methods . Any ( m = > ! m . IsOperator & & ! m . IsConstructor & & ( ReturnsClassInstance ( m ) | | AcceptsClassInstance ( m ) ) ) )
return false ;
return false ;
// TODO: We should take C++ friends into account here, they might allow
// TODO: We should take C++ friends into account here, they might allow