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 syn::ext::IdentExt;
8
9use crate::bindgen::config::{Config, Language, LayoutConfig};
10use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
11use crate::bindgen::dependencies::Dependencies;
12use crate::bindgen::ir::{
13 AnnotationSet, Cfg, ConditionWrite, Constant, DeprecatedNoteKind, Documentation, Field,
14 GenericArgument, GenericParams, Item, ItemContainer, Path, Repr, ReprAlign, ReprStyle,
15 ToCondition, Type, Typedef,
16};
17use crate::bindgen::library::Library;
18use crate::bindgen::mangle;
19use crate::bindgen::monomorph::Monomorphs;
20use crate::bindgen::rename::{IdentifierType, RenameRule};
21use crate::bindgen::reserved;
22use crate::bindgen::utilities::IterHelpers;
23use crate::bindgen::writer::{ListType, Source, SourceWriter};
24
25#[derive(Debug, Clone)]
26pub struct Struct {
27 pub path: Path,
28 pub export_name: String,
29 pub generic_params: GenericParams,
30 pub fields: Vec<Field>,
31 /// Whether there's a tag field on the body of this struct. When this is
32 /// true, is_enum_variant_body is also guaranteed to be true.
33 pub has_tag_field: bool,
34 /// Whether this is an enum variant body.
35 pub is_enum_variant_body: bool,
36 pub alignment: Option<ReprAlign>,
37 pub is_transparent: bool,
38 pub cfg: Option<Cfg>,
39 pub annotations: AnnotationSet,
40 pub documentation: Documentation,
41 pub associated_constants: Vec<Constant>,
42}
43
44impl Struct {
45 /// Whether this struct can derive operator== / operator!=.
46 pub fn can_derive_eq(&self) -> bool {
47 !self.fields.is_empty() && self.fields.iter().all(|x| x.ty.can_cmp_eq())
48 }
49
50 pub fn add_associated_constant(&mut self, c: Constant) {
51 self.associated_constants.push(c);
52 }
53
54 pub fn load(
55 layout_config: &LayoutConfig,
56 item: &syn::ItemStruct,
57 mod_cfg: Option<&Cfg>,
58 ) -> Result<Self, String> {
59 let repr = Repr::load(&item.attrs)?;
60 let is_transparent = match repr.style {
61 ReprStyle::C => false,
62 ReprStyle::Transparent => true,
63 _ => {
64 return Err("Struct is not marked #[repr(C)] or #[repr(transparent)].".to_owned());
65 }
66 };
67
68 let path = Path::new(item.ident.unraw().to_string());
69
70 // Ensure we can safely represent the struct given the configuration.
71 if let Some(align) = repr.align {
72 layout_config.ensure_safe_to_represent(&align)?;
73 }
74
75 let fields = match item.fields {
76 syn::Fields::Unit => Vec::new(),
77 syn::Fields::Named(ref fields) => fields
78 .named
79 .iter()
80 .try_skip_map(|field| Field::load(field, &path))?,
81 syn::Fields::Unnamed(ref fields) => {
82 let mut out = Vec::new();
83 let mut current = 0;
84 for field in fields.unnamed.iter() {
85 if let Some(mut ty) = Type::load(&field.ty)? {
86 ty.replace_self_with(&path);
87 out.push(Field {
88 name: format!("{}", current),
89 ty,
90 cfg: Cfg::load(&field.attrs),
91 annotations: AnnotationSet::load(&field.attrs)?,
92 documentation: Documentation::load(&field.attrs),
93 });
94 current += 1;
95 }
96 }
97 out
98 }
99 };
100
101 let has_tag_field = false;
102 let is_enum_variant_body = false;
103
104 Ok(Struct::new(
105 path,
106 GenericParams::load(&item.generics)?,
107 fields,
108 has_tag_field,
109 is_enum_variant_body,
110 repr.align,
111 is_transparent,
112 Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
113 AnnotationSet::load(&item.attrs)?,
114 Documentation::load(&item.attrs),
115 ))
116 }
117
118 #[allow(clippy::too_many_arguments)]
119 pub fn new(
120 path: Path,
121 generic_params: GenericParams,
122 fields: Vec<Field>,
123 has_tag_field: bool,
124 is_enum_variant_body: bool,
125 alignment: Option<ReprAlign>,
126 is_transparent: bool,
127 cfg: Option<Cfg>,
128 annotations: AnnotationSet,
129 documentation: Documentation,
130 ) -> Self {
131 let export_name = path.name().to_owned();
132 Self {
133 path,
134 export_name,
135 generic_params,
136 fields,
137 has_tag_field,
138 is_enum_variant_body,
139 alignment,
140 is_transparent,
141 cfg,
142 annotations,
143 documentation,
144 associated_constants: vec![],
145 }
146 }
147
148 pub fn simplify_standard_types(&mut self, config: &Config) {
149 for field in &mut self.fields {
150 field.ty.simplify_standard_types(config);
151 }
152 }
153
154 pub fn is_generic(&self) -> bool {
155 self.generic_params.len() > 0
156 }
157
158 pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
159 // Generic structs can instantiate monomorphs only once they've been
160 // instantiated. See `instantiate_monomorph` for more details.
161 if self.is_generic() {
162 return;
163 }
164
165 for field in &self.fields {
166 field.ty.add_monomorphs(library, out);
167 }
168 }
169
170 pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
171 for field in &mut self.fields {
172 field.ty.mangle_paths(monomorphs);
173 }
174 }
175
176 pub fn specialize(
177 &self,
178 generic_values: &[GenericArgument],
179 mappings: &[(&Path, &GenericArgument)],
180 config: &Config,
181 ) -> Self {
182 let mangled_path = mangle::mangle_path(&self.path, generic_values, &config.export.mangle);
183 Struct::new(
184 mangled_path,
185 GenericParams::default(),
186 self.fields
187 .iter()
188 .map(|field| Field {
189 name: field.name.clone(),
190 ty: field.ty.specialize(mappings),
191 cfg: field.cfg.clone(),
192 annotations: field.annotations.clone(),
193 documentation: field.documentation.clone(),
194 })
195 .collect(),
196 self.has_tag_field,
197 self.is_enum_variant_body,
198 self.alignment,
199 self.is_transparent,
200 self.cfg.clone(),
201 self.annotations.clone(),
202 self.documentation.clone(),
203 )
204 }
205
206 fn emit_bitflags_binop<F: Write>(
207 &self,
208 constexpr_prefix: &str,
209 operator: char,
210 other: &str,
211 out: &mut SourceWriter<F>,
212 ) {
213 let bits = &self.fields[0].name;
214 out.new_line();
215 write!(
216 out,
217 "{}{} operator{}(const {}& {}) const",
218 constexpr_prefix,
219 self.export_name(),
220 operator,
221 self.export_name(),
222 other
223 );
224 out.open_brace();
225 write!(
226 out,
227 "return {} {{ static_cast<decltype({bits})>(this->{bits} {operator} {other}.{bits}) }};",
228 self.export_name()
229 );
230 out.close_brace(false);
231
232 out.new_line();
233 write!(
234 out,
235 "{}& operator{}=(const {}& {})",
236 self.export_name(),
237 operator,
238 self.export_name(),
239 other
240 );
241 out.open_brace();
242 write!(out, "*this = (*this {} {});", operator, other);
243 out.new_line();
244 write!(out, "return *this;");
245 out.close_brace(false);
246 }
247}
248
249impl Item for Struct {
250 fn path(&self) -> &Path {
251 &self.path
252 }
253
254 fn export_name(&self) -> &str {
255 &self.export_name
256 }
257
258 fn cfg(&self) -> Option<&Cfg> {
259 self.cfg.as_ref()
260 }
261
262 fn annotations(&self) -> &AnnotationSet {
263 &self.annotations
264 }
265
266 fn annotations_mut(&mut self) -> &mut AnnotationSet {
267 &mut self.annotations
268 }
269
270 fn container(&self) -> ItemContainer {
271 ItemContainer::Struct(self.clone())
272 }
273
274 fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
275 if self.is_transparent {
276 resolver.add_none(&self.path);
277 } else {
278 resolver.add_struct(&self.path);
279 }
280 }
281
282 fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
283 for field in &mut self.fields {
284 field.ty.resolve_declaration_types(resolver);
285 }
286 }
287
288 fn rename_for_config(&mut self, config: &Config) {
289 // Rename the name of the struct
290 if !(self.has_tag_field && config.language == Language::Cxx) {
291 config.export.rename(&mut self.export_name);
292 }
293
294 // Rename the types used in fields
295 {
296 let fields = self.fields.iter_mut().skip(self.has_tag_field as usize);
297 for field in fields {
298 field.ty.rename_for_config(config, &self.generic_params);
299 }
300 }
301
302 // Apply renaming rules to fields in the following order
303 // 1. `cbindgen::field-names` annotation
304 // 2. `cbindgen::rename-all` annotation
305 // 3. config struct rename rule
306 // If the struct is a tuple struct and we have not renamed the
307 // fields, then prefix each of them with an underscore.
308 // If any field is a reserved keyword, then postfix it with an
309 // underscore.
310
311 // Scope for mutable borrow of fields
312 {
313 let names = self.fields.iter_mut().map(|field| &mut field.name);
314
315 let field_rules = self
316 .annotations
317 .parse_atom::<RenameRule>("rename-all")
318 .unwrap_or(config.structure.rename_fields);
319
320 if let Some(o) = self.annotations.list("field-names") {
321 for (dest, src) in names.zip(o) {
322 *dest = src;
323 }
324 } else if let Some(r) = field_rules.not_none() {
325 for name in names {
326 *name = r.apply(name, IdentifierType::StructMember).into_owned();
327 }
328 } else {
329 // If we don't have any rules for a tuple struct, prefix them with
330 // an underscore so it still compiles.
331 for name in names {
332 if name.starts_with(|c: char| c.is_ascii_digit()) {
333 name.insert(0, '_');
334 }
335 }
336 }
337 }
338
339 for field in &mut self.fields {
340 reserved::escape(&mut field.name);
341 }
342
343 for c in self.associated_constants.iter_mut() {
344 c.rename_for_config(config);
345 }
346 }
347
348 fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
349 let mut fields = self.fields.iter();
350
351 // If there is a tag field, skip it
352 if self.has_tag_field {
353 fields.next();
354 }
355
356 for field in fields {
357 field
358 .ty
359 .add_dependencies_ignoring_generics(&self.generic_params, library, out);
360 }
361
362 for c in &self.associated_constants {
363 c.add_dependencies(library, out);
364 }
365 }
366
367 fn instantiate_monomorph(
368 &self,
369 generic_values: &[GenericArgument],
370 library: &Library,
371 out: &mut Monomorphs,
372 ) {
373 let mappings = self.generic_params.call(self.path.name(), generic_values);
374 let monomorph = self.specialize(generic_values, &mappings, library.get_config());
375 out.insert_struct(library, self, monomorph, generic_values.to_owned());
376 }
377}
378
379impl Source for Struct {
380 fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
381 if self.is_transparent {
382 let typedef = Typedef {
383 path: self.path.clone(),
384 export_name: self.export_name.to_owned(),
385 generic_params: self.generic_params.clone(),
386 aliased: self.fields[0].ty.clone(),
387 cfg: self.cfg.clone(),
388 annotations: self.annotations.clone(),
389 documentation: self.documentation.clone(),
390 };
391 typedef.write(config, out);
392 for constant in &self.associated_constants {
393 out.new_line();
394 constant.write(config, out, Some(self));
395 }
396 return;
397 }
398
399 let condition = self.cfg.to_condition(config);
400 condition.write_before(config, out);
401
402 self.documentation.write(config, out);
403
404 if !self.is_enum_variant_body {
405 self.generic_params.write(config, out);
406 }
407
408 // The following results in
409 // C++ or C with Tag as style:
410 // struct Name {
411 // C with Type only style:
412 // typedef struct {
413 // C with Both as style:
414 // typedef struct Name {
415 match config.language {
416 Language::C if config.style.generate_typedef() => out.write("typedef "),
417 Language::C | Language::Cxx => {}
418 Language::Cython => out.write(config.style.cython_def()),
419 }
420
421 // Cython extern declarations don't manage layouts, layouts are defined entierly by the
422 // corresponding C code. So this `packed` is only for documentation, and missing
423 // `aligned(n)` is also not a problem.
424 if config.language == Language::Cython {
425 if let Some(align) = self.alignment {
426 match align {
427 ReprAlign::Packed => out.write("packed "),
428 ReprAlign::Align(_) => {} // Not supported
429 }
430 }
431 }
432
433 out.write("struct");
434
435 if config.language != Language::Cython {
436 if let Some(align) = self.alignment {
437 match align {
438 ReprAlign::Packed => {
439 if let Some(ref anno) = config.layout.packed {
440 write!(out, " {}", anno);
441 }
442 }
443 ReprAlign::Align(n) => {
444 if let Some(ref anno) = config.layout.aligned_n {
445 write!(out, " {}({})", anno, n);
446 }
447 }
448 }
449 }
450 }
451
452 if self.annotations.must_use(config) {
453 if let Some(ref anno) = config.structure.must_use {
454 write!(out, " {}", anno);
455 }
456 }
457 if let Some(note) = self
458 .annotations
459 .deprecated_note(config, DeprecatedNoteKind::Struct)
460 {
461 write!(out, " {}", note);
462 }
463
464 if config.language != Language::C || config.style.generate_tag() {
465 write!(out, " {}", self.export_name());
466 }
467
468 out.open_brace();
469
470 // Emit the pre_body section, if relevant
471 if let Some(body) = config.export.pre_body(&self.path) {
472 out.write_raw_block(body);
473 out.new_line();
474 }
475
476 out.write_vertical_source_list(&self.fields, ListType::Cap(";"));
477 if config.language == Language::Cython && self.fields.is_empty() {
478 out.write("pass");
479 }
480
481 if config.language == Language::Cxx {
482 let mut wrote_start_newline = false;
483
484 if config.structure.derive_constructor(&self.annotations) && !self.fields.is_empty() {
485 if !wrote_start_newline {
486 wrote_start_newline = true;
487 out.new_line();
488 }
489
490 out.new_line();
491
492 let arg_renamer = |name: &str| {
493 config
494 .function
495 .rename_args
496 .apply(name, IdentifierType::FunctionArg)
497 .into_owned()
498 };
499 write!(out, "{}(", self.export_name());
500 let vec: Vec<_> = self
501 .fields
502 .iter()
503 .map(|field| {
504 Field::from_name_and_type(
505 // const-ref args to constructor
506 format!("const& {}", arg_renamer(&field.name)),
507 field.ty.clone(),
508 )
509 })
510 .collect();
511 out.write_vertical_source_list(&vec[..], ListType::Join(","));
512 write!(out, ")");
513 out.new_line();
514 write!(out, " : ");
515 let vec: Vec<_> = self
516 .fields
517 .iter()
518 .map(|field| format!("{}({})", field.name, arg_renamer(&field.name)))
519 .collect();
520 out.write_vertical_source_list(&vec[..], ListType::Join(","));
521 out.new_line();
522 write!(out, "{{}}");
523 out.new_line();
524 }
525
526 let other = config
527 .function
528 .rename_args
529 .apply("other", IdentifierType::FunctionArg);
530
531 if self
532 .annotations
533 .bool("internal-derive-bitflags")
534 .unwrap_or(false)
535 {
536 assert_eq!(self.fields.len(), 1);
537 let bits = &self.fields[0].name;
538 if !wrote_start_newline {
539 wrote_start_newline = true;
540 out.new_line();
541 }
542 let constexpr_prefix = if config.constant.allow_constexpr {
543 "constexpr "
544 } else {
545 ""
546 };
547
548 out.new_line();
549 write!(out, "{}explicit operator bool() const", constexpr_prefix);
550 out.open_brace();
551 write!(out, "return !!{bits};");
552 out.close_brace(false);
553
554 out.new_line();
555 write!(
556 out,
557 "{}{} operator~() const",
558 constexpr_prefix,
559 self.export_name()
560 );
561 out.open_brace();
562 write!(
563 out,
564 "return {} {{ static_cast<decltype({bits})>(~{bits}) }};",
565 self.export_name()
566 );
567 out.close_brace(false);
568 self.emit_bitflags_binop(constexpr_prefix, '|', &other, out);
569 self.emit_bitflags_binop(constexpr_prefix, '&', &other, out);
570 self.emit_bitflags_binop(constexpr_prefix, '^', &other, out);
571 }
572
573 // Generate a serializer function that allows dumping this struct
574 // to an std::ostream. It's defined as a friend function inside the
575 // struct definition, and doesn't need the `inline` keyword even
576 // though it's implemented right in the generated header file.
577 if config.structure.derive_ostream(&self.annotations) {
578 if !wrote_start_newline {
579 wrote_start_newline = true;
580 out.new_line();
581 }
582
583 out.new_line();
584 let stream = config
585 .function
586 .rename_args
587 .apply("stream", IdentifierType::FunctionArg);
588 let instance = config
589 .function
590 .rename_args
591 .apply("instance", IdentifierType::FunctionArg);
592 write!(
593 out,
594 "friend std::ostream& operator<<(std::ostream& {}, const {}& {})",
595 stream,
596 self.export_name(),
597 instance,
598 );
599 out.open_brace();
600 write!(out, "return {} << \"{{ \"", stream);
601 let vec: Vec<_> = self
602 .fields
603 .iter()
604 .map(|x| format!(" << \"{}=\" << {}.{}", x.name, instance, x.name))
605 .collect();
606 out.write_vertical_source_list(&vec[..], ListType::Join(" << \", \""));
607 out.write(" << \" }\";");
608 out.close_brace(false);
609 }
610
611 let skip_fields = self.has_tag_field as usize;
612
613 macro_rules! emit_op {
614 ($op_name:expr, $op:expr, $conjuc:expr) => {{
615 if !wrote_start_newline {
616 #[allow(unused_assignments)]
617 {
618 wrote_start_newline = true;
619 }
620 out.new_line();
621 }
622
623 out.new_line();
624
625 if let Some(Some(attrs)) =
626 self.annotations.atom(concat!($op_name, "-attributes"))
627 {
628 write!(out, "{} ", attrs);
629 }
630
631 write!(
632 out,
633 "bool operator{}(const {}& {}) const",
634 $op,
635 self.export_name(),
636 other
637 );
638 out.open_brace();
639 out.write("return ");
640 let vec: Vec<_> = self
641 .fields
642 .iter()
643 .skip(skip_fields)
644 .map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name))
645 .collect();
646 out.write_vertical_source_list(
647 &vec[..],
648 ListType::Join(&format!(" {}", $conjuc)),
649 );
650 out.write(";");
651 out.close_brace(false);
652 }};
653 }
654
655 if config.structure.derive_eq(&self.annotations) && self.can_derive_eq() {
656 emit_op!("eq", "==", "&&");
657 }
658 if config.structure.derive_neq(&self.annotations) && self.can_derive_eq() {
659 emit_op!("neq", "!=", "||");
660 }
661 if config.structure.derive_lt(&self.annotations)
662 && self.fields.len() == 1
663 && self.fields[0].ty.can_cmp_order()
664 {
665 emit_op!("lt", "<", "&&");
666 }
667 if config.structure.derive_lte(&self.annotations)
668 && self.fields.len() == 1
669 && self.fields[0].ty.can_cmp_order()
670 {
671 emit_op!("lte", "<=", "&&");
672 }
673 if config.structure.derive_gt(&self.annotations)
674 && self.fields.len() == 1
675 && self.fields[0].ty.can_cmp_order()
676 {
677 emit_op!("gt", ">", "&&");
678 }
679 if config.structure.derive_gte(&self.annotations)
680 && self.fields.len() == 1
681 && self.fields[0].ty.can_cmp_order()
682 {
683 emit_op!("gte", ">=", "&&");
684 }
685 }
686
687 // Emit the post_body section, if relevant
688 if let Some(body) = config.export.post_body(&self.path) {
689 out.new_line();
690 out.write_raw_block(body);
691 }
692
693 if config.language == Language::Cxx
694 && config.structure.associated_constants_in_body
695 && config.constant.allow_static_const
696 {
697 for constant in &self.associated_constants {
698 out.new_line();
699 constant.write_declaration(config, out, self);
700 }
701 }
702
703 if config.language == Language::C && config.style.generate_typedef() {
704 out.close_brace(false);
705 write!(out, " {};", self.export_name());
706 } else {
707 out.close_brace(true);
708 }
709
710 for constant in &self.associated_constants {
711 out.new_line();
712 constant.write(config, out, Some(self));
713 }
714
715 condition.write_after(config, out);
716 }
717}
718