@@ -159,6 +159,11 @@ export class Assembler implements Emitter {
159159 *
160160 * Will not invoke the function with any 'undefined's; an error will already have been emitted in
161161 * that case anyway.
162+ *
163+ * @param fqn FQN of the current type (the type that has a dependency on baseTypes)
164+ * @param baseTypes Array of type references to be looked up
165+ * @param referencingNode Node to report a diagnostic on if we fail to look up a t ype
166+ * @param cb Callback to be invoked with the Types corresponding to the TypeReferences in baseTypes
162167 */
163168 // tslint:disable-next-line:max-line-length
164169 private _deferUntilTypesAvailable ( fqn : string , baseTypes : spec . NamedTypeReference [ ] , referencingNode : ts . Node , cb : ( ...xs : spec . Type [ ] ) => void ) {
@@ -585,9 +590,64 @@ export class Assembler implements Emitter {
585590 jsiiType . initializer = { initializer : true } ;
586591 }
587592
593+ this . _verifyNoStaticMixing ( jsiiType , type . symbol . valueDeclaration ) ;
594+
588595 return _sortMembers ( jsiiType ) ;
589596 }
590597
598+ /**
599+ * Check that this class doesn't declare any members that are of different staticness in itself or any of its bases
600+ */
601+ private _verifyNoStaticMixing ( klass : spec . ClassType , decl : ts . Declaration ) {
602+ function stat ( s ?: boolean ) {
603+ return s ? 'static' : 'non-static' ;
604+ }
605+
606+ // Check class itself--may have two methods/props with the same name, so check the arrays
607+ const statics = new Set ( ( klass . methods || [ ] ) . concat ( klass . properties || [ ] ) . filter ( x => x . static ) . map ( x => x . name ) ) ;
608+ const nonStatics = new Set ( ( klass . methods || [ ] ) . concat ( klass . properties || [ ] ) . filter ( x => ! x . static ) . map ( x => x . name ) ) ;
609+ // Intersect
610+ for ( const member of intersect ( statics , nonStatics ) ) {
611+ this . _diagnostic ( decl , ts . DiagnosticCategory . Error ,
612+ `member '${ member } ' of class '${ klass . name } ' cannot be declared both statically and non-statically` ) ;
613+ }
614+
615+ // Check against base classes. They will not contain duplicate member names so we can load
616+ // the members into a map.
617+ const classMembers = typeMembers ( klass ) ;
618+ this . _withBaseClass ( klass , decl , ( base , recurse ) => {
619+ for ( const [ name , baseMember ] of Object . entries ( typeMembers ( base ) ) ) {
620+ const member = classMembers [ name ] ;
621+ if ( ! member ) { continue ; }
622+
623+ if ( ! ! baseMember . static !== ! ! member . static ) {
624+ this . _diagnostic ( decl , ts . DiagnosticCategory . Error ,
625+ // tslint:disable-next-line:max-line-length
626+ `${ stat ( member . static ) } member '${ name } ' of class '${ klass . name } ' conflicts with ${ stat ( baseMember . static ) } member in ancestor '${ base . name } '` ) ;
627+ }
628+ }
629+
630+ recurse ( ) ;
631+ } ) ;
632+ }
633+
634+ /**
635+ * Wrapper around _deferUntilTypesAvailable, invoke the callback with the given classes' base type
636+ *
637+ * Does nothing if the given class doesn't have a base class.
638+ *
639+ * The second argument will be a `recurse` function for easy recursion up the inheritance tree
640+ * (no messing around with binding 'self' and 'this' and doing multiple calls to _withBaseClass.)
641+ */
642+ private _withBaseClass ( klass : spec . ClassType , decl : ts . Declaration , cb : ( base : spec . ClassType , recurse : ( ) => void ) => void ) {
643+ if ( klass . base ) {
644+ this . _deferUntilTypesAvailable ( klass . fqn , [ klass . base ] , decl , ( base ) => {
645+ if ( ! spec . isClassType ( base ) ) { throw new Error ( 'Oh no' ) ; }
646+ cb ( base , ( ) => this . _withBaseClass ( base , decl , cb ) ) ;
647+ } ) ;
648+ }
649+ }
650+
591651 /**
592652 * @returns true if this member is internal and should be omitted from the type manifest
593653 */
@@ -791,13 +851,13 @@ export class Assembler implements Emitter {
791851
792852 // Check that no interface declares a member that's already declared
793853 // in a base type (not allowed in C#).
794- const memberNames = interfaceMemberNames ( jsiiType ) ;
854+ const names = memberNames ( jsiiType ) ;
795855 const checkNoIntersection = ( ...bases : spec . Type [ ] ) => {
796856 for ( const base of bases ) {
797857 if ( ! spec . isInterfaceType ( base ) ) { continue ; }
798858
799- const baseMembers = interfaceMemberNames ( base ) ;
800- for ( const memberName of memberNames ) {
859+ const baseMembers = memberNames ( base ) ;
860+ for ( const memberName of names ) {
801861 if ( baseMembers . includes ( memberName ) ) {
802862 this . _diagnostic ( type . symbol . declarations [ 0 ] ,
803863 ts . DiagnosticCategory . Error ,
@@ -1390,14 +1450,21 @@ function intersection<T>(xs: Set<T>, ys: Set<T>): Set<T> {
13901450 *
13911451 * Returns empty string for a non-interface type.
13921452 */
1393- function interfaceMemberNames ( jsiiType : spec . InterfaceType ) : string [ ] {
1394- const ret = new Array < string > ( ) ;
1395- if ( jsiiType . methods ) {
1396- ret . push ( ...jsiiType . methods . map ( m => m . name ) . filter ( x => x !== undefined ) as string [ ] ) ;
1453+ function memberNames ( jsiiType : spec . InterfaceType | spec . ClassType ) : string [ ] {
1454+ return Object . keys ( typeMembers ( jsiiType ) ) . filter ( n => n !== '' ) ;
1455+ }
1456+
1457+ function typeMembers ( jsiiType : spec . InterfaceType | spec . ClassType ) : { [ key : string ] : spec . Property | spec . Method } {
1458+ const ret : { [ key : string ] : spec . Property | spec . Method } = { } ;
1459+
1460+ for ( const prop of jsiiType . properties || [ ] ) {
1461+ ret [ prop . name ] = prop ;
13971462 }
1398- if ( jsiiType . properties ) {
1399- ret . push ( ...jsiiType . properties . map ( m => m . name ) ) ;
1463+
1464+ for ( const method of jsiiType . methods || [ ] ) {
1465+ ret [ method . name || '' ] = method ;
14001466 }
1467+
14011468 return ret ;
14021469}
14031470
@@ -1415,3 +1482,11 @@ function getConstructor(type: ts.Type): ts.Symbol | undefined {
14151482 return type . symbol . members
14161483 && type . symbol . members . get ( ts . InternalSymbolName . Constructor ) ;
14171484}
1485+
1486+ function * intersect < T > ( xs : Set < T > , ys : Set < T > ) {
1487+ for ( const x of xs ) {
1488+ if ( ys . has ( x ) ) {
1489+ yield x ;
1490+ }
1491+ }
1492+ }
0 commit comments