@@ -190,7 +190,7 @@ export class Assembler implements Emitter {
190
190
*
191
191
* @returns the de-referenced type, if it was found, otherwise ``undefined``.
192
192
*/
193
- private _dereference ( ref : spec . NamedTypeReference , referencingNode : ts . Node ) : spec . Type | undefined {
193
+ private _dereference ( ref : spec . NamedTypeReference , referencingNode : ts . Node | null ) : spec . Type | undefined {
194
194
const [ assm , ] = ref . fqn . split ( '.' ) ;
195
195
let type ;
196
196
if ( assm === this . projectInfo . name ) {
@@ -615,11 +615,13 @@ export class Assembler implements Emitter {
615
615
this . _diagnostic ( declaration , ts . DiagnosticCategory . Error , `Unable to compute signature for ${ type . fqn } #${ symbol . name } ` ) ;
616
616
return ;
617
617
}
618
+ const parameters = await Promise . all ( signature . getParameters ( ) . map ( p => this . _toParameter ( p ) ) ) ;
619
+
618
620
const returnType = signature . getReturnType ( ) ;
619
621
const method : spec . Method = {
620
622
abstract : _isAbstract ( symbol , type ) ,
621
623
name : symbol . name ,
622
- parameters : await Promise . all ( signature . getParameters ( ) . map ( p => this . _toParameter ( p ) ) ) ,
624
+ parameters,
623
625
protected : _isProtected ( symbol ) ,
624
626
returns : _isVoid ( returnType ) ? undefined : await this . _typeReference ( returnType , declaration ) ,
625
627
static : _isStatic ( symbol ) ,
@@ -628,6 +630,29 @@ export class Assembler implements Emitter {
628
630
629
631
this . _visitDocumentation ( symbol , method ) ;
630
632
633
+ // If the last parameter is a datatype, verify that it does not share any field names with
634
+ // other function arguments, so that it can be turned into keyword arguments by jsii frontends
635
+ // that support such.
636
+ const lastParamTypeRef = apply ( last ( parameters ) , x => x . type ) ;
637
+ const lastParamSymbol = last ( signature . getParameters ( ) ) ;
638
+ if ( lastParamTypeRef && spec . isNamedTypeReference ( lastParamTypeRef ) ) {
639
+ this . _deferUntilTypesAvailable ( symbol . name , [ lastParamTypeRef ] , lastParamSymbol ! . declarations [ 0 ] , ( lastParamType ) => {
640
+ if ( ! spec . isInterfaceType ( lastParamType ) || ! lastParamType . datatype ) { return ; }
641
+
642
+ // Liftable datatype, make sure no parameter names match any of the properties in the datatype
643
+ const propNames = this . allProperties ( lastParamType ) ;
644
+ const paramNames = new Set ( parameters . slice ( 0 , parameters . length - 1 ) . map ( x => x . name ) ) ;
645
+ const sharedNames = intersection ( propNames , paramNames ) ;
646
+
647
+ if ( sharedNames . size > 0 ) {
648
+ this . _diagnostic (
649
+ declaration ,
650
+ ts . DiagnosticCategory . Error ,
651
+ `Name occurs in both function arguments and in datatype properties, rename one: ${ Array . from ( sharedNames ) . join ( ', ' ) } ` ) ;
652
+ }
653
+ } ) ;
654
+ }
655
+
631
656
type . methods = type . methods || [ ] ;
632
657
type . methods . push ( method ) ;
633
658
}
@@ -867,6 +892,31 @@ export class Assembler implements Emitter {
867
892
def . dependedFqns = def . dependedFqns . filter ( fqns . has . bind ( fqns ) ) ;
868
893
}
869
894
}
895
+
896
+ /**
897
+ * Return the set of all (inherited) properties of an interface
898
+ */
899
+ private allProperties ( root : spec . InterfaceType ) : Set < string > {
900
+ const self = this ;
901
+
902
+ const ret = new Set < string > ( ) ;
903
+ recurse ( root ) ;
904
+ return ret ;
905
+
906
+ function recurse ( int : spec . InterfaceType ) {
907
+ for ( const property of int . properties || [ ] ) {
908
+ ret . add ( property . name ) ;
909
+ }
910
+
911
+ for ( const baseRef of int . interfaces || [ ] ) {
912
+ const base = self . _dereference ( baseRef , null ) ;
913
+ if ( ! base ) { throw new Error ( 'Impossible to have unresolvable base in allProperties()' ) ; }
914
+ if ( ! spec . isInterfaceType ( base ) ) { throw new Error ( 'Impossible to have non-interface base in allProperties()' ) ; }
915
+
916
+ recurse ( base ) ;
917
+ }
918
+ }
919
+ }
870
920
}
871
921
872
922
/**
@@ -1024,6 +1074,33 @@ interface DeferredRecord {
1024
1074
cb : ( ) => void ;
1025
1075
}
1026
1076
1077
+ /**
1078
+ * Return the last element from a list
1079
+ */
1080
+ function last < T > ( xs : T [ ] ) : T | undefined {
1081
+ return xs . length > 0 ? xs [ xs . length - 1 ] : undefined ;
1082
+ }
1083
+
1084
+ /**
1085
+ * Apply a function to a value if it's not equal to undefined
1086
+ */
1087
+ function apply < T , U > ( x : T | undefined , fn : ( x : T ) => U | undefined ) : U | undefined {
1088
+ return x !== undefined ? fn ( x ) : undefined ;
1089
+ }
1090
+
1091
+ /**
1092
+ * Return the intersection of two sets
1093
+ */
1094
+ function intersection < T > ( xs : Set < T > , ys : Set < T > ) : Set < T > {
1095
+ const ret = new Set < T > ( ) ;
1096
+ for ( const x of xs ) {
1097
+ if ( ys . has ( x ) ) {
1098
+ ret . add ( x ) ;
1099
+ }
1100
+ }
1101
+ return ret ;
1102
+ }
1103
+
1027
1104
/**
1028
1105
* Whether or not the given name is conventionally an interface name
1029
1106
*
@@ -1032,4 +1109,4 @@ interface DeferredRecord {
1032
1109
*/
1033
1110
function isInterfaceName ( name : string ) {
1034
1111
return name . length >= 2 && name . charAt ( 0 ) === 'I' && name . charAt ( 1 ) . toUpperCase ( ) === name . charAt ( 1 ) ;
1035
- }
1112
+ }
0 commit comments