Skip to content

Commit 0f69069

Browse files
committed
ctest-next: Improve type qualifier support
Replace `constness` with a `Qual` struct to handle all type qualifiers. Additionally make formatting a bit better for unnamed variables.
1 parent ccec325 commit 0f69069

File tree

1 file changed

+134
-15
lines changed

1 file changed

+134
-15
lines changed

ctest-next/src/cdecl.rs

Lines changed: 134 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ pub(crate) enum Constness {
1010
Const,
1111
Mut,
1212
}
13+
14+
#[cfg_attr(not(test), expect(unused_imports))]
1315
use 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)]
141183
mod 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

Comments
 (0)