@@ -79,12 +79,92 @@ impl fmt::Display for InvalidRustTarget {
7979 }
8080}
8181
82+ /// This macro defines the Rust editions supported by bindgen.
83+ macro_rules! define_rust_editions {
84+ ( $( $variant: ident( $value: literal) => $minor: literal, ) * ) => {
85+ #[ derive( Clone , Copy , Debug , PartialEq , Eq , Hash , PartialOrd , Ord ) ]
86+ pub enum RustEdition {
87+ $(
88+ #[ doc = concat!( "The " , stringify!( $value) , " edition of Rust." ) ]
89+ $variant,
90+ ) *
91+ }
92+
93+ impl FromStr for RustEdition {
94+ type Err = InvalidRustEdition ;
95+
96+ fn from_str( s: & str ) -> Result <Self , Self :: Err > {
97+ match s {
98+ $( stringify!( $value) => Ok ( Self :: $variant) , ) *
99+ _ => Err ( InvalidRustEdition ( s. to_owned( ) ) ) ,
100+ }
101+ }
102+ }
103+
104+ impl fmt:: Display for RustEdition {
105+ fn fmt( & self , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
106+ match self {
107+ $( Self :: $variant => stringify!( $value) . fmt( f) , ) *
108+ }
109+ }
110+ }
111+
112+ impl RustEdition {
113+ pub ( crate ) const ALL : [ Self ; [ $( $value, ) * ] . len( ) ] = [ $( Self :: $variant, ) * ] ;
114+
115+ pub ( crate ) fn is_available( self , target: RustTarget ) -> bool {
116+ let Some ( minor) = target. minor( ) else {
117+ return true ;
118+ } ;
119+
120+ match self {
121+ $( Self :: $variant => $minor <= minor, ) *
122+ }
123+ }
124+ }
125+ }
126+ }
127+
128+ #[ derive( Debug ) ]
129+ pub struct InvalidRustEdition ( String ) ;
130+
131+ impl fmt:: Display for InvalidRustEdition {
132+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
133+ write ! ( f, "\" {}\" is not a valid Rust edition" , self . 0 )
134+ }
135+ }
136+
137+ impl std:: error:: Error for InvalidRustEdition { }
138+
139+ define_rust_editions ! {
140+ Edition2018 ( 2018 ) => 31 ,
141+ Edition2021 ( 2021 ) => 56 ,
142+ }
143+
144+ impl RustTarget {
145+ /// Returns the latest edition supported by this target.
146+ pub ( crate ) fn latest_edition ( self ) -> RustEdition {
147+ RustEdition :: ALL
148+ . iter ( )
149+ . rev ( )
150+ . find ( |edition| edition. is_available ( self ) )
151+ . copied ( )
152+ . expect ( "bindgen should always support at least one edition" )
153+ }
154+ }
155+
156+ impl Default for RustEdition {
157+ fn default ( ) -> Self {
158+ RustTarget :: default ( ) . latest_edition ( )
159+ }
160+ }
161+
82162/// This macro defines the [`RustTarget`] and [`RustFeatures`] types.
83163macro_rules! define_rust_targets {
84164 (
85- Nightly => { $( $nightly_feature: ident $( : #$issue: literal) ?) ,* $( , ) ?} $( , ) ?
165+ Nightly => { $( $nightly_feature: ident $( ( $nightly_edition : literal ) ) * $ ( : #$issue: literal) ?) ,* $( , ) ?} $( , ) ?
86166 $(
87- $variant: ident( $minor: literal) => { $( $feature: ident $( : #$pull: literal) ?) ,* $( , ) ?} ,
167+ $variant: ident( $minor: literal) => { $( $feature: ident $( ( $edition : literal ) ) * $ ( : #$pull: literal) ?) ,* $( , ) ?} ,
88168 ) *
89169 $( , ) ?
90170 ) => {
@@ -128,23 +208,35 @@ macro_rules! define_rust_targets {
128208 $( pub ( crate ) $nightly_feature: bool , ) *
129209 }
130210
131- impl From <RustTarget > for RustFeatures {
132- fn from( target: RustTarget ) -> Self {
133- if target == RustTarget :: Nightly {
134- return Self {
135- $( $( $feature: true , ) * ) *
136- $( $nightly_feature: true , ) *
137- } ;
138- }
139-
211+ impl RustFeatures {
212+ /// Compute the features that must be enabled in a specific Rust target with a specific edition.
213+ pub ( crate ) fn new( target: RustTarget , edition: RustEdition ) -> Self {
140214 let mut features = Self {
141215 $( $( $feature: false , ) * ) *
142216 $( $nightly_feature: false , ) *
143217 } ;
144218
145- $( if target. is_compatible( & RustTarget :: $variant) {
146- $( features. $feature = true ; ) *
147- } ) *
219+ if target. is_compatible( & RustTarget :: nightly( ) ) {
220+ $(
221+ let editions: & [ RustEdition ] = & [ $( stringify!( $nightly_edition) . parse:: <RustEdition >( ) . ok( ) . expect( "invalid edition" ) , ) * ] ;
222+
223+ if editions. is_empty( ) || editions. contains( & edition) {
224+ features. $nightly_feature = true ;
225+ }
226+ ) *
227+ }
228+
229+ $(
230+ if target. is_compatible( & RustTarget :: $variant) {
231+ $(
232+ let editions: & [ RustEdition ] = & [ $( stringify!( $edition) . parse:: <RustEdition >( ) . ok( ) . expect( "invalid edition" ) , ) * ] ;
233+
234+ if editions. is_empty( ) || editions. contains( & edition) {
235+ features. $feature = true ;
236+ }
237+ ) *
238+ }
239+ ) *
148240
149241 features
150242 }
@@ -163,7 +255,7 @@ define_rust_targets! {
163255 } ,
164256 Stable_1_77 ( 77 ) => {
165257 offset_of: #106655 ,
166- literal_cstr: #117472 ,
258+ literal_cstr( 2021 ) : #117472 ,
167259 } ,
168260 Stable_1_73 ( 73 ) => { thiscall_abi: #42202 } ,
169261 Stable_1_71 ( 71 ) => { c_unwind_abi: #106075 } ,
@@ -296,9 +388,17 @@ impl FromStr for RustTarget {
296388 }
297389}
298390
391+ impl RustFeatures {
392+ /// Compute the features that must be enabled in a specific Rust target with the latest edition
393+ /// available in that target.
394+ pub ( crate ) fn new_with_latest_edition ( target : RustTarget ) -> Self {
395+ Self :: new ( target, target. latest_edition ( ) )
396+ }
397+ }
398+
299399impl Default for RustFeatures {
300400 fn default ( ) -> Self {
301- RustTarget :: default ( ) . into ( )
401+ Self :: new_with_latest_edition ( RustTarget :: default ( ) )
302402 }
303403}
304404
@@ -308,24 +408,39 @@ mod test {
308408
309409 #[ test]
310410 fn target_features ( ) {
311- let features = RustFeatures :: from ( RustTarget :: Stable_1_71 ) ;
411+ let features =
412+ RustFeatures :: new_with_latest_edition ( RustTarget :: Stable_1_71 ) ;
312413 assert ! (
313414 features. c_unwind_abi &&
314415 features. abi_efiapi &&
315416 !features. thiscall_abi
316417 ) ;
317- let f_nightly = RustFeatures :: from ( RustTarget :: Nightly ) ;
418+
419+ let features = RustFeatures :: new (
420+ RustTarget :: Stable_1_77 ,
421+ RustEdition :: Edition2018 ,
422+ ) ;
423+ assert ! ( !features. literal_cstr) ;
424+
425+ let features =
426+ RustFeatures :: new_with_latest_edition ( RustTarget :: Stable_1_77 ) ;
427+ assert ! ( features. literal_cstr) ;
428+
429+ let f_nightly =
430+ RustFeatures :: new_with_latest_edition ( RustTarget :: Nightly ) ;
318431 assert ! (
319- f_nightly. maybe_uninit &&
320- f_nightly. thiscall_abi &&
321- f_nightly. vectorcall_abi
432+ f_nightly. vectorcall_abi &&
433+ f_nightly. ptr_metadata &&
434+ f_nightly. layout_for_ptr
322435 ) ;
323436 }
324437
325438 fn test_target ( input : & str , expected : RustTarget ) {
326439 // Two targets are equivalent if they enable the same set of features
327- let expected = RustFeatures :: from ( expected) ;
328- let found = RustFeatures :: from ( input. parse :: < RustTarget > ( ) . unwrap ( ) ) ;
440+ let expected = RustFeatures :: new_with_latest_edition ( expected) ;
441+ let found = RustFeatures :: new_with_latest_edition (
442+ input. parse :: < RustTarget > ( ) . unwrap ( ) ,
443+ ) ;
329444 assert_eq ! (
330445 expected,
331446 found,
0 commit comments