@@ -30,6 +30,7 @@ struct InterfaceGenerator<'a> {
30
30
unique_names : HashSet < String > ,
31
31
types_in_interface : Vec < Type > ,
32
32
package_name : & ' a str ,
33
+ version : Option < Version > ,
33
34
}
34
35
35
36
#[ derive( Clone ) ]
@@ -147,7 +148,7 @@ impl Generator {
147
148
name : PackageName {
148
149
namespace,
149
150
name : package_name. clone ( ) ,
150
- version,
151
+ version : version . clone ( ) ,
151
152
} ,
152
153
file : File :: default ( ) ,
153
154
sources : SourceMap :: new ( ) ,
@@ -195,7 +196,8 @@ impl Generator {
195
196
let world_name =
196
197
file. gen_unique_package_name ( u, & mut package_names, DefinitionKind :: World ) ?;
197
198
log:: debug!( "new world `{world_name}` in {i}" ) ;
198
- let world = self . gen_world ( u, & world_name, file, & package_name) ?;
199
+ let world =
200
+ self . gen_world ( u, & world_name, file, & package_name, version. clone ( ) ) ?;
199
201
file. items . push ( world) ;
200
202
201
203
// Insert the world at the package and file level, asserting
@@ -227,7 +229,7 @@ impl Generator {
227
229
let id = self . next_interface_id ;
228
230
self . next_interface_id += 1 ;
229
231
let ( src, types) =
230
- self . gen_interface ( u, Some ( & name) , file, & package_name, None ) ?;
232
+ self . gen_interface ( u, Some ( & name) , file, & package_name, None , None ) ?;
231
233
file. items . push ( src) ;
232
234
if types. is_empty ( ) {
233
235
continue ;
@@ -352,8 +354,9 @@ impl Generator {
352
354
name : & str ,
353
355
file : & mut File ,
354
356
package_name : & str ,
357
+ version : Option < Version > ,
355
358
) -> Result < String > {
356
- InterfaceGenerator :: new ( self , file, package_name) . gen_world ( u, name)
359
+ InterfaceGenerator :: new ( self , file, package_name, version ) . gen_world ( u, name)
357
360
}
358
361
359
362
fn gen_interface (
@@ -363,8 +366,9 @@ impl Generator {
363
366
file : & mut File ,
364
367
package_name : & str ,
365
368
world_name : Option < & str > ,
369
+ version : Option < Version > ,
366
370
) -> Result < ( String , Vec < Type > ) > {
367
- let mut generator = InterfaceGenerator :: new ( self , file, package_name) ;
371
+ let mut generator = InterfaceGenerator :: new ( self , file, package_name, version ) ;
368
372
let ret = generator. gen_interface ( u, name, world_name) ?;
369
373
Ok ( ( ret, generator. types_in_interface ) )
370
374
}
@@ -495,6 +499,7 @@ impl<'a> InterfaceGenerator<'a> {
495
499
generator : & ' a mut Generator ,
496
500
file : & ' a mut File ,
497
501
package_name : & ' a str ,
502
+ version : Option < Version > ,
498
503
) -> InterfaceGenerator < ' a > {
499
504
InterfaceGenerator {
500
505
generator,
@@ -504,6 +509,48 @@ impl<'a> InterfaceGenerator<'a> {
504
509
// ABI always using a linear memory named `memory`.
505
510
unique_names : HashSet :: from_iter ( [ "memory" . to_string ( ) ] ) ,
506
511
package_name : package_name,
512
+ version,
513
+ }
514
+ }
515
+
516
+ // Generate a feature gate annotation (@since, @unstable, or @deprecated)
517
+ // If version is provided, ensures the annotation is compatible with the version
518
+ fn gen_feature_annotation ( & self , u : & mut Unstructured < ' _ > ) -> Result < Option < String > > {
519
+ if u. arbitrary ( ) ? {
520
+ return Ok ( None ) ;
521
+ }
522
+
523
+ let feature_names = [ "active" , "inactive" ] ;
524
+ #[ derive( Arbitrary ) ]
525
+ enum AnnotationType {
526
+ Since ,
527
+ Unstable ,
528
+ Deprecated ,
529
+ }
530
+
531
+ match self . version {
532
+ None => {
533
+ // No package version available
534
+ return Ok ( None ) ;
535
+ }
536
+ Some ( _) => match u. arbitrary ( ) ? {
537
+ AnnotationType :: Since => {
538
+ let v = gen_version_less_than ( u, & self . version ) ?;
539
+ Ok ( Some ( format ! ( "@since(version = {v})" ) ) )
540
+ }
541
+ AnnotationType :: Unstable => {
542
+ let feature = u. choose ( & feature_names) ?;
543
+ Ok ( Some ( format ! ( "@unstable(feature = {feature})" ) ) )
544
+ }
545
+ AnnotationType :: Deprecated => {
546
+ let depreciation_version = gen_version_less_than ( u, & self . version ) ?;
547
+ let since_version =
548
+ gen_version_less_than ( u, & Some ( depreciation_version. clone ( ) ) ) ?;
549
+ Ok ( Some ( format ! (
550
+ "@deprecated(version = {depreciation_version})\n @since(version = {since_version})" ,
551
+ ) ) )
552
+ }
553
+ } ,
507
554
}
508
555
}
509
556
@@ -514,6 +561,12 @@ impl<'a> InterfaceGenerator<'a> {
514
561
world_name : Option < & str > ,
515
562
) -> Result < String > {
516
563
let mut ret = String :: new ( ) ;
564
+
565
+ if let Some ( annotation) = self . gen_feature_annotation ( u) ? {
566
+ ret. push_str ( & annotation) ;
567
+ ret. push_str ( "\n " ) ;
568
+ }
569
+
517
570
ret. push_str ( "interface " ) ;
518
571
if let Some ( name) = name {
519
572
ret. push_str ( "%" ) ;
@@ -576,7 +629,7 @@ impl<'a> InterfaceGenerator<'a> {
576
629
ret. push_str ( world_name) ;
577
630
ret. push_str ( " {\n " ) ;
578
631
579
- #[ derive( Arbitrary , Copy , Clone ) ]
632
+ #[ derive( Arbitrary , Copy , Clone , Debug ) ]
580
633
enum Direction {
581
634
Import ,
582
635
Export ,
@@ -615,6 +668,12 @@ impl<'a> InterfaceGenerator<'a> {
615
668
} ;
616
669
617
670
let mut part = String :: new ( ) ;
671
+
672
+ if let Some ( annotation) = self . gen_feature_annotation ( u) ? {
673
+ part. push_str ( & annotation) ;
674
+ part. push_str ( "\n " ) ;
675
+ }
676
+
618
677
if let Some ( dir) = direction {
619
678
part. push_str ( match dir {
620
679
Direction :: Import => "import " ,
@@ -686,15 +745,14 @@ impl<'a> InterfaceGenerator<'a> {
686
745
}
687
746
ItemKind :: AnonInterface ( _) => {
688
747
let iface =
689
- InterfaceGenerator :: new ( self . generator , self . file , self . package_name )
748
+ InterfaceGenerator :: new ( self . generator , self . file , self . package_name , None )
690
749
. gen_interface ( u, None , Some ( world_name) ) ?;
691
750
part. push_str ( & iface) ;
692
751
}
693
752
694
753
ItemKind :: Type => {
695
754
let name = name. unwrap ( ) ;
696
755
let ( ty, typedef) = self . gen_typedef ( u, & name) ?;
697
- assert ! ( part. is_empty( ) ) ;
698
756
part = typedef;
699
757
let is_resource = ty. is_resource ;
700
758
self . types_in_interface . push ( ty) ;
@@ -803,20 +861,41 @@ impl<'a> InterfaceGenerator<'a> {
803
861
Item :: Constructor if has_constructor => { }
804
862
Item :: Constructor => {
805
863
has_constructor = true ;
806
- let mut part = format ! ( "constructor" ) ;
864
+ let mut part = String :: new ( ) ;
865
+
866
+ if let Some ( annotation) = self . gen_feature_annotation ( u) ? {
867
+ part. push_str ( & annotation) ;
868
+ part. push_str ( "\n " ) ;
869
+ }
870
+
871
+ part. push_str ( "constructor" ) ;
807
872
self . gen_params ( u, & mut part, false ) ?;
808
873
part. push_str ( ";" ) ;
809
874
parts. push ( part) ;
810
875
}
811
876
Item :: Static => {
812
- let mut part = format ! ( "%" ) ;
877
+ let mut part = String :: new ( ) ;
878
+
879
+ if let Some ( annotation) = self . gen_feature_annotation ( u) ? {
880
+ part. push_str ( & annotation) ;
881
+ part. push_str ( "\n " ) ;
882
+ }
883
+
884
+ part. push_str ( "%" ) ;
813
885
part. push_str ( & gen_unique_name ( u, & mut names) ?) ;
814
886
part. push_str ( ": static " ) ;
815
887
self . gen_func_sig ( u, & mut part, false ) ?;
816
888
parts. push ( part) ;
817
889
}
818
890
Item :: Method => {
819
- let mut part = format ! ( "%" ) ;
891
+ let mut part = String :: new ( ) ;
892
+
893
+ if let Some ( annotation) = self . gen_feature_annotation ( u) ? {
894
+ part. push_str ( & annotation) ;
895
+ part. push_str ( "\n " ) ;
896
+ }
897
+
898
+ part. push_str ( "%" ) ;
820
899
part. push_str ( & gen_unique_name ( u, & mut names) ?) ;
821
900
part. push_str ( ": " ) ;
822
901
self . gen_func_sig ( u, & mut part, true ) ?;
@@ -840,6 +919,11 @@ impl<'a> InterfaceGenerator<'a> {
840
919
part : & mut String ,
841
920
world_name : Option < & str > ,
842
921
) -> Result < bool > {
922
+ if let Some ( annotation) = self . gen_feature_annotation ( u) ? {
923
+ part. push_str ( & annotation) ;
924
+ part. push_str ( "\n " ) ;
925
+ }
926
+
843
927
let mut path = String :: new ( ) ;
844
928
let ( _name, _id, types) =
845
929
match self . generator . gen_interface_path ( u, self . file , & mut path) ? {
@@ -893,6 +977,12 @@ impl<'a> InterfaceGenerator<'a> {
893
977
894
978
let mut fuel = self . generator . config . max_type_size ;
895
979
let mut ret = String :: new ( ) ;
980
+
981
+ if let Some ( annotation) = self . gen_feature_annotation ( u) ? {
982
+ ret. push_str ( & annotation) ;
983
+ ret. push_str ( "\n " ) ;
984
+ }
985
+
896
986
let mut is_resource = false ;
897
987
match u. arbitrary ( ) ? {
898
988
Kind :: Record => {
@@ -1148,7 +1238,14 @@ impl<'a> InterfaceGenerator<'a> {
1148
1238
}
1149
1239
1150
1240
fn gen_func ( & mut self , u : & mut Unstructured < ' _ > ) -> Result < String > {
1151
- let mut ret = "%" . to_string ( ) ;
1241
+ let mut ret = String :: new ( ) ;
1242
+
1243
+ if let Some ( annotation) = self . gen_feature_annotation ( u) ? {
1244
+ ret. push_str ( & annotation) ;
1245
+ ret. push_str ( "\n " ) ;
1246
+ }
1247
+
1248
+ ret. push_str ( "%" ) ;
1152
1249
ret. push_str ( & self . gen_unique_name ( u) ?) ;
1153
1250
ret. push_str ( ": " ) ;
1154
1251
self . gen_func_sig ( u, & mut ret, false ) ?;
@@ -1351,20 +1448,45 @@ impl File {
1351
1448
}
1352
1449
}
1353
1450
1354
- fn gen_version ( u : & mut Unstructured < ' _ > ) -> Result < Version > {
1355
- Ok ( Version {
1356
- major : u. int_in_range ( 0 ..=10 ) ?,
1357
- minor : u. int_in_range ( 0 ..=10 ) ?,
1358
- patch : u. int_in_range ( 0 ..=10 ) ?,
1359
- pre : if u. arbitrary ( ) ? {
1451
+ fn gen_version_less_than (
1452
+ u : & mut Unstructured < ' _ > ,
1453
+ existing_version : & Option < Version > ,
1454
+ ) -> Result < Version > {
1455
+ const MAX_VERSION_RANGE : u64 = 10 ;
1456
+ let ( major, minor, patch) = match existing_version {
1457
+ Some ( v) => ( v. major , v. minor , v. patch ) ,
1458
+ None => ( MAX_VERSION_RANGE , MAX_VERSION_RANGE , MAX_VERSION_RANGE ) ,
1459
+ } ;
1460
+
1461
+ let new_version = Version {
1462
+ major : u. int_in_range ( 0 ..=major) ?,
1463
+ minor : u. int_in_range ( 0 ..=minor) ?,
1464
+ patch : u. int_in_range ( 0 ..=patch) ?,
1465
+ pre : if ( u. arbitrary ( ) ? && existing_version. is_none ( ) )
1466
+ || existing_version. as_ref ( ) . is_some_and ( |x| !x. pre . is_empty ( ) )
1467
+ {
1360
1468
semver:: Prerelease :: new ( "alpha.0" ) . unwrap ( )
1361
1469
} else {
1362
1470
semver:: Prerelease :: EMPTY
1363
1471
} ,
1364
- build : if u. arbitrary ( ) ? {
1472
+ build : if ( u. arbitrary ( ) ? && existing_version. is_none ( ) )
1473
+ || existing_version
1474
+ . as_ref ( )
1475
+ . is_some_and ( |x| !x. build . is_empty ( ) )
1476
+ {
1365
1477
semver:: BuildMetadata :: new ( "1.2.0" ) . unwrap ( )
1366
1478
} else {
1367
1479
semver:: BuildMetadata :: EMPTY
1368
1480
} ,
1369
- } )
1481
+ } ;
1482
+
1483
+ if let Some ( v) = existing_version {
1484
+ assert ! ( & new_version <= v, "{} <= {}" , & new_version, v) ;
1485
+ }
1486
+
1487
+ Ok ( new_version)
1488
+ }
1489
+
1490
+ fn gen_version ( u : & mut Unstructured < ' _ > ) -> Result < Version > {
1491
+ gen_version_less_than ( u, & None )
1370
1492
}
0 commit comments