@@ -7,9 +7,9 @@ namespace CppSharp.Passes
77 /// <summary>
88 /// Checks for classes that should be bound as static classes.
99 /// </summary>
10- public class CheckStaticClass : TranslationUnitPass
10+ public class CheckStaticClassPass : TranslationUnitPass
1111 {
12- public CheckStaticClass ( )
12+ public CheckStaticClassPass ( )
1313 => VisitOptions . ResetFlags ( VisitFlags . ClassMethods ) ;
1414
1515 public override bool VisitDeclaration ( Declaration decl )
@@ -32,8 +32,7 @@ static bool IsProtectedClassMember(Declaration decl)
3232 if ( decl . Access != AccessSpecifier . Protected )
3333 return false ;
3434
35- var @class = decl . Namespace as Class ;
36- return @class != null && @class . IsStatic ;
35+ return decl . Namespace is Class { IsStatic : true } ;
3736 }
3837
3938 static void SetDeclarationAccessToPrivate ( Declaration decl )
@@ -42,7 +41,7 @@ static void SetDeclarationAccessToPrivate(Declaration decl)
4241 // as an internal in the final C# wrapper.
4342 decl . Access = AccessSpecifier . Private ;
4443
45- // We need to explicity set the generation else the
44+ // We need to explicitly set the generation else the
4645 // now private declaration will get filtered out later.
4746 decl . GenerationKind = GenerationKind . Generate ;
4847 }
@@ -51,53 +50,90 @@ static bool ReturnsClassInstance(Function function)
5150 {
5251 var returnType = function . ReturnType . Type . Desugar ( ) ;
5352
54- var tag = returnType as TagType ;
55- if ( tag == null )
53+ if ( returnType is not TagType tag )
5654 returnType . IsPointerTo ( out tag ) ;
5755
58- if ( tag == null )
56+ var decl = tag ? . Declaration ;
57+ if ( decl is not Class )
5958 return false ;
6059
6160 var @class = ( Class ) function . Namespace ;
62- var decl = tag . Declaration ;
61+ return @class . QualifiedOriginalName == decl . QualifiedOriginalName ;
62+ }
6363
64- if ( ! ( decl is Class ) )
65- return false ;
64+ static bool AcceptsClassInstance ( Function function )
65+ {
66+ return function . Parameters . Any ( param =>
67+ {
68+ var paramType = param . Type . Desugar ( ) ;
6669
67- return @class . QualifiedOriginalName == decl . QualifiedOriginalName ;
70+ if ( paramType is not TagType tag )
71+ paramType . IsPointerTo ( out tag ) ;
72+
73+ var decl = tag ? . Declaration ;
74+ if ( decl is not Class )
75+ return false ;
76+
77+ var @class = ( Class ) function . Namespace ;
78+ return @class . QualifiedOriginalName == decl . QualifiedOriginalName ;
79+ }
80+ ) ;
6881 }
6982
7083 public override bool VisitClassDecl ( Class @class )
7184 {
72- // If the class has any non-private constructors then it cannot
73- // be bound as a static class and we bail out early.
74- if ( @class . Constructors . Any ( m =>
75- ! ( m . IsCopyConstructor || m . IsMoveConstructor )
76- && m . Access != AccessSpecifier . Private ) )
85+ // If the class is to be used as an opaque type, then it cannot be
86+ // bound as static.
87+ if ( @class . IsOpaque )
88+ return false ;
89+
90+ if ( @class . IsDependent )
91+ return false ;
92+
93+ // Polymorphic classes are currently not supported.
94+ // TODO: We could support this if the base class is also static, since it's composition then.
95+ if ( @class . IsPolymorphic )
96+ return false ;
97+
98+ // Make sure we have at least one accessible static method or field
99+ if ( ! @class . Methods . Any ( m => m . Kind == CXXMethodKind . Normal && m . Access != AccessSpecifier . Private && m . IsStatic )
100+ && @class . Variables . All ( v => v . Access == AccessSpecifier . Private ) )
77101 return false ;
78102
79103 // Check for any non-static fields or methods, in which case we
80104 // assume the class is not meant to be static.
81105 // Note: Static fields are represented as variables in the AST.
82- if ( @class . Fields . Any ( ) ||
83- @class . Methods . Any ( m => m . Kind == CXXMethodKind . Normal
84- && ! m . IsStatic ) )
106+ if ( @class . Fields . Count != 0 )
85107 return false ;
86108
87- // Check for any static function that return a pointer to the class.
88- // If one exists, we assume it's a factory function and the class is
89- // not meant to be static. It's a simple heuristic but it should be
90- // good enough for the time being.
91- if ( @class . Functions . Any ( m => ! m . IsOperator && ReturnsClassInstance ( m ) ) ||
92- @class . Methods . Any ( m => ! m . IsOperator && ReturnsClassInstance ( m ) ) )
109+ if ( @class . Methods . Any ( m => m . Kind == CXXMethodKind . Normal && ! m . IsStatic ) )
93110 return false ;
94111
95- // If the class is to be used as an opaque type, then it cannot be
96- // bound as static.
97- if ( @class . IsOpaque )
112+ if ( @class . Constructors . Any ( m =>
113+ {
114+ // Implicit constructors are not user-defined, so assume this was unintentional.
115+ if ( m . IsImplicit )
116+ return false ;
117+
118+ // Ignore deleted constructors.
119+ if ( m . IsDeleted )
120+ return false ;
121+
122+ // If the class has a copy or move constructor, it cannot be static.
123+ if ( m . IsCopyConstructor || m . IsMoveConstructor )
124+ return true ;
125+
126+ // If the class has any (user defined) non-private constructors then it cannot be static
127+ return m . Access != AccessSpecifier . Private ;
128+ } ) )
129+ {
98130 return false ;
131+ }
99132
100- if ( @class . IsDependent )
133+ // Check for any static function that accepts/returns a pointer to the class.
134+ // If one exists, we assume it's not meant to be static
135+ if ( @class . Functions . Any ( m => ! m . IsOperator && ( ReturnsClassInstance ( m ) || AcceptsClassInstance ( m ) ) ) ||
136+ @class . Methods . Any ( m => ! m . IsOperator && ! m . IsConstructor && ( ReturnsClassInstance ( m ) || AcceptsClassInstance ( m ) ) ) )
101137 return false ;
102138
103139 // TODO: We should take C++ friends into account here, they might allow
0 commit comments