1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use std::io::Write;
6
7use crate::bindgen::config::Layout;
8use crate::bindgen::declarationtyperesolver::DeclarationType;
9use crate::bindgen::ir::{ConstExpr, Function, GenericArgument, Type};
10use crate::bindgen::writer::{ListType, SourceWriter};
11use crate::bindgen::{Config, Language};
12
13// This code is for translating Rust types into C declarations.
14// See Section 6.7, Declarations, in the C standard for background.
15// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
16
17enum CDeclarator {
18 Ptr {
19 is_const: bool,
20 is_nullable: bool,
21 is_ref: bool,
22 },
23 Array(String),
24 Func {
25 args: Vec<(Option<String>, CDecl)>,
26 layout: Layout,
27 never_return: bool,
28 },
29}
30
31impl CDeclarator {
32 fn is_ptr(&self) -> bool {
33 matches!(self, CDeclarator::Ptr { .. } | CDeclarator::Func { .. })
34 }
35}
36
37struct CDecl {
38 type_qualifers: String,
39 type_name: String,
40 type_generic_args: Vec<GenericArgument>,
41 declarators: Vec<CDeclarator>,
42 type_ctype: Option<DeclarationType>,
43 deprecated: Option<String>,
44}
45
46impl CDecl {
47 fn new() -> CDecl {
48 CDecl {
49 type_qualifers: String::new(),
50 type_name: String::new(),
51 type_generic_args: Vec::new(),
52 declarators: Vec::new(),
53 type_ctype: None,
54 deprecated: None,
55 }
56 }
57
58 fn from_type(t: &Type, config: &Config) -> CDecl {
59 let mut cdecl = CDecl::new();
60 cdecl.build_type(t, false, config);
61 cdecl
62 }
63
64 fn from_func_arg(t: &Type, array_length: Option<&str>, config: &Config) -> CDecl {
65 let mut cdecl = CDecl::new();
66 let length = match array_length {
67 Some(l) => l,
68 None => return CDecl::from_type(t, config),
69 };
70 let (ty, is_const) = match t {
71 Type::Ptr { ty, is_const, .. } => (ty, is_const),
72 _ => unreachable!(
73 "Should never have an array length for a non pointer type {:?}",
74 t
75 ),
76 };
77 let ptr_as_array = Type::Array(ty.clone(), ConstExpr::Value(length.to_string()));
78 cdecl.build_type(&ptr_as_array, *is_const, config);
79 cdecl
80 }
81
82 fn from_func(f: &Function, layout: Layout, config: &Config) -> CDecl {
83 let mut cdecl = CDecl::new();
84 cdecl.build_func(f, layout, config);
85 cdecl
86 }
87
88 fn build_func(&mut self, f: &Function, layout: Layout, config: &Config) {
89 let args = f
90 .args
91 .iter()
92 .map(|arg| {
93 (
94 arg.name.clone(),
95 CDecl::from_func_arg(&arg.ty, arg.array_length.as_deref(), config),
96 )
97 })
98 .collect();
99 self.declarators.push(CDeclarator::Func {
100 args,
101 layout,
102 never_return: f.never_return,
103 });
104 self.deprecated = f.annotations.deprecated.clone();
105 self.build_type(&f.ret, false, config);
106 }
107
108 fn build_type(&mut self, t: &Type, is_const: bool, config: &Config) {
109 match t {
110 Type::Path(ref generic) => {
111 if is_const {
112 assert!(
113 self.type_qualifers.is_empty(),
114 "error generating cdecl for {:?}",
115 t
116 );
117 self.type_qualifers = "const".to_owned();
118 }
119
120 assert!(
121 self.type_name.is_empty(),
122 "error generating cdecl for {:?}",
123 t
124 );
125 self.type_name = generic.export_name().to_owned();
126 assert!(
127 self.type_generic_args.is_empty(),
128 "error generating cdecl for {:?}",
129 t
130 );
131 self.type_generic_args = generic.generics().to_owned();
132 self.type_ctype = generic.ctype().cloned();
133 }
134 Type::Primitive(ref p) => {
135 if is_const {
136 assert!(
137 self.type_qualifers.is_empty(),
138 "error generating cdecl for {:?}",
139 t
140 );
141 self.type_qualifers = "const".to_owned();
142 }
143
144 assert!(
145 self.type_name.is_empty(),
146 "error generating cdecl for {:?}",
147 t
148 );
149 self.type_name = p.to_repr_c(config).to_string();
150 }
151 Type::Ptr {
152 ref ty,
153 is_nullable,
154 is_const: ptr_is_const,
155 is_ref,
156 } => {
157 self.declarators.push(CDeclarator::Ptr {
158 is_const,
159 is_nullable: *is_nullable,
160 is_ref: *is_ref,
161 });
162 self.build_type(ty, *ptr_is_const, config);
163 }
164 Type::Array(ref t, ref constant) => {
165 let len = constant.as_str().to_owned();
166 self.declarators.push(CDeclarator::Array(len));
167 self.build_type(t, is_const, config);
168 }
169 Type::FuncPtr {
170 ref ret,
171 ref args,
172 is_nullable: _,
173 never_return,
174 } => {
175 let args = args
176 .iter()
177 .map(|(ref name, ref ty)| (name.clone(), CDecl::from_type(ty, config)))
178 .collect();
179 self.declarators.push(CDeclarator::Ptr {
180 is_const: false,
181 is_nullable: true,
182 is_ref: false,
183 });
184 self.declarators.push(CDeclarator::Func {
185 args,
186 layout: config.function.args.clone(),
187 never_return: *never_return,
188 });
189 self.build_type(ret, false, config);
190 }
191 }
192 }
193
194 fn write<F: Write>(&self, out: &mut SourceWriter<F>, ident: Option<&str>, config: &Config) {
195 // Write the type-specifier and type-qualifier first
196 if !self.type_qualifers.is_empty() {
197 write!(out, "{} ", self.type_qualifers);
198 }
199
200 if config.language != Language::Cython {
201 if let Some(ref ctype) = self.type_ctype {
202 write!(out, "{} ", ctype.to_str());
203 }
204 }
205
206 write!(out, "{}", self.type_name);
207
208 if !self.type_generic_args.is_empty() {
209 out.write("<");
210 out.write_horizontal_source_list(&self.type_generic_args, ListType::Join(", "));
211 out.write(">");
212 }
213
214 // When we have an identifier, put a space between the type and the declarators
215 if ident.is_some() {
216 out.write(" ");
217 }
218
219 // Write the left part of declarators before the identifier
220 let mut iter_rev = self.declarators.iter().rev().peekable();
221
222 #[allow(clippy::while_let_on_iterator)]
223 while let Some(declarator) = iter_rev.next() {
224 let next_is_pointer = iter_rev.peek().map_or(false, |x| x.is_ptr());
225
226 match *declarator {
227 CDeclarator::Ptr {
228 is_const,
229 is_nullable,
230 is_ref,
231 } => {
232 out.write(if is_ref { "&" } else { "*" });
233 if is_const {
234 out.write("const ");
235 }
236 if !is_nullable && !is_ref && config.language != Language::Cython {
237 if let Some(attr) = &config.pointer.non_null_attribute {
238 write!(out, "{} ", attr);
239 }
240 }
241 }
242 CDeclarator::Array(..) => {
243 if next_is_pointer {
244 out.write("(");
245 }
246 }
247 CDeclarator::Func { .. } => {
248 if next_is_pointer {
249 out.write("(");
250 }
251 }
252 }
253 }
254
255 // Write the identifier
256 if let Some(ident) = ident {
257 write!(out, "{}", ident);
258 }
259
260 // Write the right part of declarators after the identifier
261 let mut iter = self.declarators.iter();
262 let mut last_was_pointer = false;
263
264 #[allow(clippy::while_let_on_iterator)]
265 while let Some(declarator) = iter.next() {
266 match *declarator {
267 CDeclarator::Ptr { .. } => {
268 last_was_pointer = true;
269 }
270 CDeclarator::Array(ref constant) => {
271 if last_was_pointer {
272 out.write(")");
273 }
274 write!(out, "[{}]", constant);
275
276 last_was_pointer = false;
277 }
278 CDeclarator::Func {
279 ref args,
280 ref layout,
281 never_return,
282 } => {
283 if last_was_pointer {
284 out.write(")");
285 }
286
287 out.write("(");
288 if args.is_empty() && config.language == Language::C {
289 out.write("void");
290 }
291
292 fn write_vertical<F: Write>(
293 out: &mut SourceWriter<F>,
294 config: &Config,
295 args: &[(Option<String>, CDecl)],
296 ) {
297 let align_length = out.line_length_for_align();
298 out.push_set_spaces(align_length);
299 for (i, (arg_ident, arg_ty)) in args.iter().enumerate() {
300 if i != 0 {
301 out.write(",");
302 out.new_line();
303 }
304
305 // Convert &Option<String> to Option<&str>
306 let arg_ident = arg_ident.as_ref().map(|x| x.as_ref());
307
308 arg_ty.write(out, arg_ident, config);
309 }
310 out.pop_tab();
311 }
312
313 fn write_horizontal<F: Write>(
314 out: &mut SourceWriter<F>,
315 config: &Config,
316 args: &[(Option<String>, CDecl)],
317 ) {
318 for (i, (arg_ident, arg_ty)) in args.iter().enumerate() {
319 if i != 0 {
320 out.write(", ");
321 }
322
323 // Convert &Option<String> to Option<&str>
324 let arg_ident = arg_ident.as_ref().map(|x| x.as_ref());
325
326 arg_ty.write(out, arg_ident, config);
327 }
328 }
329
330 match layout {
331 Layout::Vertical => write_vertical(out, config, args),
332 Layout::Horizontal => write_horizontal(out, config, args),
333 Layout::Auto => {
334 if !out.try_write(
335 |out| write_horizontal(out, config, args),
336 config.line_length,
337 ) {
338 write_vertical(out, config, args)
339 }
340 }
341 }
342 out.write(")");
343
344 if never_return && config.language != Language::Cython {
345 if let Some(ref no_return_attr) = config.function.no_return {
346 out.write_fmt(format_args!(" {}", no_return_attr));
347 }
348 }
349
350 last_was_pointer = true;
351 }
352 }
353 }
354 }
355}
356
357pub fn write_func<F: Write>(
358 out: &mut SourceWriter<F>,
359 f: &Function,
360 layout: Layout,
361 config: &Config,
362) {
363 CDecl::from_func(f, layout, config).write(out, ident:Some(f.path().name()), config);
364}
365
366pub fn write_field<F: Write>(out: &mut SourceWriter<F>, t: &Type, ident: &str, config: &Config) {
367 CDecl::from_type(t, config).write(out, ident:Some(ident), config);
368}
369
370pub fn write_type<F: Write>(out: &mut SourceWriter<F>, t: &Type, config: &Config) {
371 CDecl::from_type(t, config).write(out, ident:None, config);
372}
373