@@ -10,6 +10,8 @@ pub(crate) enum Constness {
1010 Const ,
1111 Mut ,
1212}
13+
14+ #[ cfg_attr( not( test) , expect( unused_imports) ) ]
1315use Constness :: { Const , Mut } ;
1416
1517/// A basic representation of C's types.
@@ -19,13 +21,15 @@ pub(crate) enum CTy {
1921 /// `int`, `struct foo`, etc. There is only ever one basic type per decl.
2022 Named {
2123 name : BoxStr ,
22- constness : Constness ,
24+ qual : Qual ,
2325 } ,
2426 Ptr {
2527 ty : Box < Self > ,
26- constness : Constness ,
28+ qual : Qual ,
2729 } ,
2830 Array {
31+ // C99 also supports type qualifiers in arrays, e.g. `[const volatile restrict]`. MSVC does
32+ // not though, so we ignore these for now.
2933 ty : Box < Self > ,
3034 len : Option < BoxStr > ,
3135 } ,
@@ -98,17 +102,20 @@ fn cdecl_impl(cty: &CTy, s: &mut String, prev: Option<&CTy>) -> Result<(), Inval
98102 cty. check_ret_ty ( ) ?;
99103 cty. parens_if_needed ( s, prev) ;
100104 match cty {
101- CTy :: Named { name, constness } => {
102- let sp = if s. is_empty ( ) { "" } else { " " } ;
103- let c = if * constness == Const { "const " } else { "" } ;
104- let to_insert = format ! ( "{c}{name}{sp}" ) ;
105+ CTy :: Named { name, qual } => {
106+ assert ! ( !qual. restrict, "restrict is not allowed for named types" ) ;
107+ let mut to_insert = String :: new ( ) ;
108+ qual. write_to ( & mut to_insert) ;
109+ space_if ( !to_insert. is_empty ( ) && !name. is_empty ( ) , & mut to_insert) ;
110+ to_insert. push_str ( name) ;
111+ space_if ( !to_insert. is_empty ( ) && !s. is_empty ( ) , & mut to_insert) ;
105112 s. insert_str ( 0 , & to_insert) ;
106113 }
107- CTy :: Ptr { ty, constness } => {
108- match constness {
109- Const => s . insert_str ( 0 , "*const " ) ,
110- Mut => s . insert ( 0 , '*' ) ,
111- }
114+ CTy :: Ptr { ty, qual } => {
115+ let mut to_insert = "*" . to_owned ( ) ;
116+ qual . write_to ( & mut to_insert ) ;
117+ space_if ( to_insert . len ( ) > 1 && !s . is_empty ( ) , & mut to_insert ) ;
118+ s . insert_str ( 0 , & to_insert ) ;
112119 cdecl_impl ( ty, s, Some ( cty) ) ?;
113120 }
114121 CTy :: Array { ty, len } => {
@@ -136,6 +143,41 @@ fn cdecl_impl(cty: &CTy, s: &mut String, prev: Option<&CTy>) -> Result<(), Inval
136143 Ok ( ( ) )
137144}
138145
146+ /// Keyword qualifiers.
147+ #[ derive( Clone , Copy , Debug ) ]
148+ pub ( crate ) struct Qual {
149+ // C11 also supports _Atomic, but it doesn't really come up for `ctest`.
150+ pub constness : Constness ,
151+ pub volatile : bool ,
152+ pub restrict : bool ,
153+ }
154+
155+ impl Qual {
156+ fn write_to ( self , s : & mut String ) {
157+ let mut need_sp = false ;
158+ if self . constness == Const {
159+ s. push_str ( "const" ) ;
160+ need_sp = true ;
161+ }
162+ if self . volatile {
163+ space_if ( need_sp, s) ;
164+ s. push_str ( "volatile" ) ;
165+ need_sp = true ;
166+ }
167+ if self . restrict {
168+ space_if ( need_sp, s) ;
169+ s. push_str ( "restrict" ) ;
170+ }
171+ }
172+ }
173+
174+ // We do this a surprising number of times.
175+ fn space_if ( yes : bool , s : & mut String ) {
176+ if yes {
177+ s. push ( ' ' ) ;
178+ }
179+ }
180+
139181/// Checked with <https://cdecl.org/>.
140182#[ cfg( test) ]
141183mod tests {
@@ -149,6 +191,17 @@ mod tests {
149191
150192 /* Helpful constructors */
151193
194+ const RESTRICT : Qual = Qual {
195+ constness : Mut ,
196+ volatile : false ,
197+ restrict : true ,
198+ } ;
199+ const VOLATILE : Qual = Qual {
200+ constness : Mut ,
201+ volatile : true ,
202+ restrict : false ,
203+ } ;
204+
152205 fn mut_int ( ) -> CTy {
153206 named ( "int" , Mut )
154207 }
@@ -160,14 +213,36 @@ mod tests {
160213 fn named ( name : & str , constness : Constness ) -> CTy {
161214 CTy :: Named {
162215 name : name. into ( ) ,
163- constness,
216+ qual : Qual {
217+ constness,
218+ volatile : false ,
219+ restrict : false ,
220+ } ,
221+ }
222+ }
223+
224+ fn named_qual ( name : & str , qual : Qual ) -> CTy {
225+ CTy :: Named {
226+ name : name. into ( ) ,
227+ qual,
164228 }
165229 }
166230
167231 fn ptr ( inner : CTy , constness : Constness ) -> CTy {
232+ ptr_qual (
233+ inner,
234+ Qual {
235+ constness,
236+ volatile : false ,
237+ restrict : false ,
238+ } ,
239+ )
240+ }
241+
242+ fn ptr_qual ( inner : CTy , qual : Qual ) -> CTy {
168243 CTy :: Ptr {
169244 ty : Box :: new ( inner) ,
170- constness ,
245+ qual ,
171246 }
172247 }
173248
@@ -217,6 +292,19 @@ mod tests {
217292 & ptr ( ptr ( const_int ( ) , Const ) , Const ) ,
218293 "const int *const *const foo" ,
219294 ) ;
295+ assert_decl ( & ptr_qual ( mut_int ( ) , RESTRICT ) , "int *restrict foo" ) ;
296+ assert_decl ( & ptr_qual ( mut_int ( ) , VOLATILE ) , "int *volatile foo" ) ;
297+ assert_decl (
298+ & ptr_qual (
299+ mut_int ( ) ,
300+ Qual {
301+ constness : Const ,
302+ volatile : true ,
303+ restrict : true ,
304+ } ,
305+ ) ,
306+ "int *const volatile restrict foo" ,
307+ ) ;
220308 }
221309
222310 #[ test]
@@ -251,6 +339,10 @@ mod tests {
251339 & func ( vec ! [ const_int( ) , mut_int( ) ] , mut_int ( ) ) ,
252340 "int foo(const int, int)" ,
253341 ) ;
342+ assert_decl (
343+ & func ( vec ! [ ] , named_qual ( "int" , VOLATILE ) ) ,
344+ "volatile int foo()" ,
345+ ) ;
254346 }
255347
256348 #[ test]
@@ -310,13 +402,40 @@ mod tests {
310402 // Function args are usually unnamed
311403 assert_eq ! ( cdecl( & mut_int( ) , String :: new( ) ) . unwrap( ) , "int" ) ;
312404 assert_eq ! (
313- cdecl( & ptr( array( mut_int( ) , None ) , Mut ) , String :: new( ) ) . unwrap( ) ,
314- "int (*)[]"
405+ cdecl( & array( mut_int( ) , None ) , String :: new( ) ) . unwrap( ) ,
406+ "int []"
407+ ) ;
408+ assert_eq ! (
409+ cdecl( & array( const_int( ) , None ) , String :: new( ) ) . unwrap( ) ,
410+ "const int []"
315411 ) ;
316412 assert_eq ! (
317413 cdecl( & array( ptr( mut_int( ) , Mut ) , None ) , String :: new( ) ) . unwrap( ) ,
318414 "int *[]"
319415 ) ;
416+ assert_eq ! (
417+ cdecl( & ptr( array( mut_int( ) , None ) , Mut ) , String :: new( ) ) . unwrap( ) ,
418+ "int (*)[]"
419+ ) ;
420+ assert_eq ! (
421+ cdecl( & ptr( array( mut_int( ) , None ) , Const ) , String :: new( ) ) . unwrap( ) ,
422+ "int (*const)[]"
423+ ) ;
424+ assert_eq ! (
425+ cdecl(
426+ & ptr_qual(
427+ mut_int( ) ,
428+ Qual {
429+ constness: Const ,
430+ volatile: true ,
431+ restrict: true ,
432+ } ,
433+ ) ,
434+ String :: new( ) ,
435+ )
436+ . unwrap( ) ,
437+ "int *const volatile restrict" ,
438+ ) ;
320439 }
321440
322441 #[ test]
0 commit comments