1mod attribute;
2mod attribute_ref;
3mod callback;
4mod class;
5mod r#const;
6mod delegate;
7mod r#enum;
8mod field;
9mod file;
10mod r#fn;
11pub(crate) mod guid;
12mod index;
13mod interface;
14mod item;
15mod method;
16mod module;
17mod param;
18mod r#struct;
19mod typedef;
20mod union;
21
22use super::*;
23use attribute::*;
24use callback::*;
25use class::*;
26use r#const::*;
27use delegate::*;
28use r#enum::*;
29use field::*;
30use file::*;
31use r#fn::*;
32use index::*;
33use interface::*;
34use item::*;
35use method::*;
36use module::*;
37use r#struct::*;
38use typedef::*;
39use union::*;
40use windows_metadata as metadata;
41
42fn fixed_signed_value(value: i64) -> metadata::Value {
43 i32::try_from(value)
44 .map(metadata::Value::I32)
45 .unwrap_or(metadata::Value::I64(value))
46}
47
48fn fixed_unsigned_value(value: u64) -> metadata::Value {
49 u32::try_from(value)
50 .map(metadata::Value::U32)
51 .unwrap_or(metadata::Value::U64(value))
52}
53
54#[derive(Default)]
55pub struct Reader {
57 input: Vec<PathBuf>,
58 input_text: Vec<String>,
59 reference: Vec<PathBuf>,
60 reference_default: bool,
61 reference_bytes: Vec<Vec<u8>>,
62 output: PathBuf,
63}
64
65impl Reader {
66 pub fn new() -> Self {
68 Self::default()
69 }
70
71 pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
73 self.input.push(input.as_ref().to_path_buf());
74 self
75 }
76
77 pub fn input_text(&mut self, input: &str) -> &mut Self {
79 self.input_text.push(input.to_string());
80 self
81 }
82
83 pub fn input_texts<I, S>(&mut self, inputs: I) -> &mut Self
85 where
86 I: IntoIterator<Item = S>,
87 S: AsRef<str>,
88 {
89 for input in inputs {
90 self.input_text(input.as_ref());
91 }
92 self
93 }
94
95 pub fn reference(&mut self, input: impl AsRef<Path>) -> &mut Self {
97 self.reference.push(input.as_ref().to_path_buf());
98 self
99 }
100
101 pub fn references<I, S>(&mut self, inputs: I) -> &mut Self
103 where
104 I: IntoIterator<Item = S>,
105 S: AsRef<Path>,
106 {
107 for input in inputs {
108 self.reference(input);
109 }
110 self
111 }
112
113 pub fn reference_bytes(&mut self, input: &[u8]) -> &mut Self {
115 self.reference_bytes.push(input.to_vec());
116 self
117 }
118
119 pub fn reference_byte_sets<I, B>(&mut self, inputs: I) -> &mut Self
121 where
122 I: IntoIterator<Item = B>,
123 B: AsRef<[u8]>,
124 {
125 for input in inputs {
126 self.reference_bytes(input.as_ref());
127 }
128 self
129 }
130
131 pub fn reference_default(&mut self) -> &mut Self {
133 self.reference_default = true;
134 self
135 }
136
137 pub fn inputs<I, S>(&mut self, inputs: I) -> &mut Self
139 where
140 I: IntoIterator<Item = S>,
141 S: AsRef<Path>,
142 {
143 for input in inputs {
144 self.input(input);
145 }
146 self
147 }
148
149 pub fn output(&mut self, output: impl AsRef<Path>) -> &mut Self {
151 self.output = output.as_ref().to_path_buf();
152 self
153 }
154
155 pub fn write(&self) -> Result<(), Error> {
157 if self.output.as_os_str().is_empty() {
158 return Err(Error::new("output is required", "", 0, 0));
159 }
160
161 let rdl_paths = expand_input_files(&self.input, "rdl")?;
162 let reference_paths = expand_input_files(&self.reference, "winmd")?;
163
164 let input = expand_rdl_files(&rdl_paths, &self.input_text)?;
165
166 let mut index = Index::new();
167
168 for file in &input {
169 for item in &file.items {
170 index.insert(file, "", item);
171 }
172 }
173
174 let mut reference = vec![];
175
176 for file_name in &reference_paths {
177 let source = file_name.to_string_lossy();
178 reference.push(
179 metadata::reader::File::read(file_name)
180 .ok_or_else(|| Error::new("invalid reference", &source, 0, 0))?,
181 );
182 }
183
184 if self.reference_default {
185 reference.extend(
186 [windows_default::WINRT, windows_default::WIN32]
187 .into_iter()
188 .map(|bytes| metadata::reader::File::new(bytes.to_vec()).unwrap()),
189 );
190 }
191
192 for bytes in &self.reference_bytes {
193 reference.push(
194 metadata::reader::File::new(bytes.clone())
195 .ok_or_else(|| Error::new("invalid reference", "<memory>", 0, 0))?,
196 );
197 }
198
199 let reference = metadata::reader::Index::new(reference);
200 validate_use_declarations(&input, &index, &reference)?;
201
202 let assembly_name = self
203 .output
204 .file_stem()
205 .and_then(|file_name| file_name.to_str())
206 .ok_or_else(|| Error::new("invalid output", &self.output.to_string_lossy(), 0, 0))?;
207
208 let mut output = metadata::writer::File::new(assembly_name);
209 output.set_reference(reference);
210
211 for (namespace, members) in &index.namespaces {
212 for variants in members.types.values() {
213 for (file, item) in variants {
214 let name = item.to_string();
215 let encoder = &mut Encoder {
216 output: &mut output,
217 index: &index,
218 file,
219 namespace,
220 name: &name,
221 generics: vec![],
222 };
223 match item {
224 Item::Attribute(ty) => encoder.encode_attribute(ty),
225 Item::Callback(ty) => encoder.encode_callback(ty),
226 Item::Class(ty) => encoder.encode_class(ty),
227 Item::Const(ty) => encoder.encode_const(ty),
228 Item::Delegate(ty) => encoder.encode_delegate(ty),
229 Item::Enum(ty) => encoder.encode_enum(ty),
230 Item::Fn(ty) => encoder.encode_fn(ty),
231 Item::Interface(ty) => encoder.encode_interface(ty),
232 Item::Struct(ty) => encoder.encode_struct(ty),
233 Item::Typedef(ty) => encoder.encode_typedef(ty),
234 Item::Union(ty) => encoder.encode_union(ty),
235 Item::Module(_) => unreachable!(
236 "Module items are expanded during indexing and never encoded directly"
237 ),
238 }?;
239 }
240 }
241
242 if !members.functions.is_empty() || !members.constants.is_empty() {
243 let class =
244 metadata::writer::TypeDefOrRef::TypeRef(output.TypeRef("System", "Object"));
245
246 output.TypeDef(
247 namespace,
248 "Apis",
249 class,
250 metadata::TypeAttributes::Public | metadata::TypeAttributes::Sealed,
251 );
252
253 for (name, variants) in &members.functions {
254 for (file, item) in variants {
255 let Item::Fn(ty) = item else {
256 unreachable!("functions index only contains Item::Fn")
257 };
258 Encoder {
259 output: &mut output,
260 index: &index,
261 file,
262 namespace,
263 name,
264 generics: vec![],
265 }
266 .encode_fn(ty)?;
267 }
268 }
269
270 for (name, variants) in &members.constants {
271 for (file, item) in variants {
272 let Item::Const(ty) = item else {
273 unreachable!("constants index only contains Item::Const")
274 };
275 Encoder {
276 output: &mut output,
277 index: &index,
278 file,
279 namespace,
280 name,
281 generics: vec![],
282 }
283 .encode_const(ty)?;
284 }
285 }
286 }
287 }
288
289 std::fs::write(&self.output, output.into_stream())
290 .map_err(|error| Error::new(&error.to_string(), &self.output.to_string_lossy(), 0, 0))
291 }
292}
293
294pub(crate) fn item_names(path: impl AsRef<Path>, namespace: &str) -> Result<Vec<String>, Error> {
296 let path = path.as_ref().to_path_buf();
297 let input = expand_rdl_files(std::slice::from_ref(&path), &[])?;
298 let mut index = Index::new();
299 for file in &input {
300 for item in &file.items {
301 index.insert(file, "", item);
302 }
303 }
304 let mut names = vec![];
305 if let Some(ns) = index.namespaces.get(namespace) {
306 names.extend(ns.types.keys().cloned());
307 names.extend(ns.functions.keys().cloned());
308 names.extend(ns.constants.keys().cloned());
309 }
310 Ok(names)
311}
312
313fn preprocess_rdl(contents: &str) -> std::borrow::Cow<'_, str> {
315 let needs_in = contents.contains("#[in]");
316 let needs_doc = contents.contains("//!");
317 if !needs_in && !needs_doc {
318 return std::borrow::Cow::Borrowed(contents);
319 }
320 let mut result = contents.to_string();
321 if needs_in {
322 result = result.replace("#[in]", "#[r#in]");
323 }
324 if needs_doc {
325 result = result.replace("//!", "//");
326 }
327 std::borrow::Cow::Owned(result)
328}
329
330fn expand_rdl_files(paths: &[PathBuf], input_text: &[String]) -> Result<Vec<File>, Error> {
331 let mut input = vec![];
332
333 for path in paths {
334 let source = path.to_string_lossy();
335 let Ok(contents) = std::fs::read_to_string(path) else {
336 return Err(Error::new("failed to read binary file", &source, 0, 0));
337 };
338
339 let contents = preprocess_rdl(&contents);
340 let mut file = syn::parse_str::<File>(&contents).map_err(|error| {
341 let start = error.span().start();
342 Error::new(&error.to_string(), &source, start.line, start.column)
343 })?;
344
345 file.source = source.replace('\\', "/");
346 input.push(file);
347 }
348
349 for contents in input_text {
350 let contents = preprocess_rdl(contents);
351 let mut file = syn::parse_str::<File>(&contents).map_err(|error| {
352 let start = error.span().start();
353 Error::new(&error.to_string(), ".rdl", start.line, start.column)
354 })?;
355
356 file.source = ".rdl".to_string();
357 input.push(file);
358 }
359
360 for file in &mut input {
361 for item in &mut file.items {
362 resolve_winrt(item, &file.source, None)?;
363 }
364 }
365
366 Ok(input)
367}
368
369fn resolve_winrt(item: &mut Item, source_file: &str, parent: Option<bool>) -> Result<(), Error> {
370 match item {
371 Item::Enum(item) => {
372 item.winrt = read_winrt_expected(source_file, &item.token, &item.attrs, parent)?;
373 }
374 Item::Interface(item) => {
375 item.winrt = read_winrt_expected(source_file, &item.token, &item.attrs, parent)?;
376 }
377 Item::Struct(item) => {
378 item.winrt = read_winrt_expected(source_file, &item.span, &item.attrs, parent)?;
379 }
380 Item::Attribute(item) => {
381 item.winrt = read_winrt_expected(source_file, &item.token, &item.attrs, parent)?;
382 }
383 Item::Module(item) => {
384 let parent = read_winrt(source_file, &item.token, &item.attrs, parent)?;
385
386 for child in &mut item.items {
387 resolve_winrt(child, source_file, parent)?;
388 }
389 }
390 _ => {}
391 }
392
393 Ok(())
394}
395
396fn read_winrt_expected<S: Spanned>(
397 source_file: &str,
398 span: &S,
399 attrs: &[syn::Attribute],
400 parent: Option<bool>,
401) -> Result<bool, Error> {
402 if let Some(winrt) = read_winrt(source_file, span, attrs, parent)? {
403 Ok(winrt)
404 } else {
405 let start = span.span().start();
406
407 Err(Error::new(
408 "`winrt` or `win32` attribute required",
409 source_file,
410 start.line,
411 start.column,
412 ))
413 }
414}
415
416fn read_winrt<S: Spanned>(
417 source_file: &str,
418 span: &S,
419 attrs: &[syn::Attribute],
420 parent: Option<bool>,
421) -> Result<Option<bool>, Error> {
422 let mut winrt = false;
423 let mut win32 = false;
424
425 for attr in attrs {
426 if attr.path().is_ident("winrt") {
427 winrt = true;
428 } else if attr.path().is_ident("win32") {
429 win32 = true;
430 }
431 }
432
433 if winrt && win32 {
434 let start = span.span().start();
435
436 return Err(Error::new(
437 "`winrt` and `win32` attributes are mutually exclusive",
438 source_file,
439 start.line,
440 start.column,
441 ));
442 } else if !winrt
443 && !win32
444 && let Some(parent) = parent
445 {
446 if parent {
447 winrt = true;
448 } else {
449 win32 = true;
450 }
451 }
452
453 if winrt {
454 Ok(Some(true))
455 } else if win32 {
456 Ok(Some(false))
457 } else {
458 Ok(None)
459 }
460}
461
462fn validate_use_declarations(
463 input: &[File],
464 index: &Index,
465 reference: &metadata::reader::Index,
466) -> Result<(), Error> {
467 for file in input {
468 for use_item in &file.uses {
469 if let Some(ns) = glob_use_namespace(use_item)
470 && !index.namespaces.contains_key(&ns)
471 && !reference.contains_namespace(&ns)
472 {
473 let start = use_item.span().start();
474 return Err(Error::new(
475 "use namespace not found",
476 &file.source,
477 start.line,
478 start.column,
479 ));
480 }
481 }
482 }
483 Ok(())
484}
485
486pub(super) fn parse_guid_u128(lit: &syn::LitInt) -> Result<u128, ()> {
489 let s: String = lit
490 .token()
491 .to_string()
492 .chars()
493 .filter(|&c| c != '_')
494 .collect();
495 if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
496 u128::from_str_radix(hex, 16).map_err(|_| ())
497 } else {
498 s.parse::<u128>().map_err(|_| ())
499 }
500}
501
502struct Encoder<'a> {
503 output: &'a mut metadata::writer::File,
504 index: &'a Index<'a>,
505 file: &'a File,
506 namespace: &'a str,
507 name: &'a str,
508 generics: Vec<String>,
509}
510
511impl Encoder<'_> {
512 fn error<S: Spanned>(&self, spanned: S, message: &str) -> Error {
513 let start = spanned.span().start();
514
515 Error::new(message, &self.file.source, start.line, start.column)
516 }
517
518 fn err<T, S: Spanned>(&self, spanned: S, message: &str) -> Result<T, Error> {
519 Err(self.error(spanned, message))
520 }
521
522 fn read_packed(&self, attrs: &[syn::Attribute]) -> Result<Option<u16>, Error> {
523 for attr in attrs {
524 if !attr.path().is_ident("packed") {
525 continue;
526 }
527
528 let Ok(size_literal) = attr.parse_args::<syn::LitInt>() else {
529 return self.err(attr, "`packed` attribute requires an integer argument");
530 };
531
532 let Ok(size) = size_literal.base10_parse::<u16>() else {
533 return self.err(attr, "`packed` size must be a valid u16");
534 };
535
536 return Ok(Some(size));
537 }
538
539 Ok(None)
540 }
541
542 fn read_align(&self, attrs: &[syn::Attribute]) -> Result<Option<u16>, Error> {
544 for attr in attrs {
545 if !attr.path().is_ident("align") {
546 continue;
547 }
548
549 let Ok(size_literal) = attr.parse_args::<syn::LitInt>() else {
550 return self.err(attr, "`align` attribute requires an integer argument");
551 };
552
553 let Ok(size) = size_literal.base10_parse::<u16>() else {
554 return self.err(attr, "`align` size must be a valid u16");
555 };
556
557 return Ok(Some(size));
558 }
559
560 Ok(None)
561 }
562
563 fn read_arch(&self, attrs: &[syn::Attribute]) -> Result<Option<i32>, Error> {
564 for attr in attrs {
565 if !attr.path().is_ident("arch") {
566 continue;
567 }
568
569 let expr = attr.parse_args::<syn::Expr>().map_err(|_| {
570 self.error(
571 attr,
572 "`arch` attribute requires architecture arguments (e.g. `#[arch(X86)]`)",
573 )
574 })?;
575
576 let bits = parse_arch_bitmask(&expr).ok_or_else(|| {
577 self.error(
578 attr,
579 "invalid `arch` value; expected `X86`, `X64`, `Arm64`, or a `|`-combination",
580 )
581 })?;
582
583 return Ok(Some(bits));
584 }
585
586 Ok(None)
587 }
588
589 fn encode_type(&self, ty: &syn::Type) -> Result<metadata::Type, Error> {
590 match ty {
591 syn::Type::Path(ty) => self.encode_type_path(ty),
592 syn::Type::Ptr(ty) => self.encode_type_ptr(ty),
593 syn::Type::Reference(ty) => self.encode_type_reference(ty),
594 syn::Type::Slice(ty) => self.encode_type_slice(ty),
595 syn::Type::Array(ty) => self.encode_type_array(ty),
596 rest => self.err(rest, "type not supported"),
597 }
598 }
599
600 fn encode_type_in_attr_ns(
602 &self,
603 attr_ns: &str,
604 ty: &syn::Type,
605 ) -> Result<metadata::Type, Error> {
606 if attr_ns == self.namespace {
607 return self.encode_type(ty);
608 }
609
610 if let syn::Type::Path(type_path) = ty
611 && type_path.qself.is_none()
612 && type_path.path.leading_colon.is_none()
613 {
614 let segs: Vec<String> = type_path
615 .path
616 .segments
617 .iter()
618 .map(|s| s.ident.to_string())
619 .collect();
620
621 if !segs.is_empty() && !segs.iter().any(|s| s == "super") {
622 let name = segs.last().unwrap();
623 let candidate_ns = if segs.len() == 1 {
624 attr_ns.to_string()
625 } else {
626 format!("{}.{}", attr_ns, segs[..segs.len() - 1].join("."))
627 };
628
629 if self.index.contains(&candidate_ns, name)
630 || self
631 .output
632 .reference()
633 .is_some_and(|r| r.contains(&candidate_ns, name))
634 {
635 let tn = metadata::TypeName {
636 namespace: candidate_ns.clone(),
637 name: name.clone(),
638 generics: vec![],
639 };
640 return Ok(if self.type_is_value(&candidate_ns, name) {
641 metadata::Type::ValueName(tn)
642 } else {
643 metadata::Type::ClassName(tn)
644 });
645 }
646 }
647 }
648
649 self.encode_type(ty)
650 }
651
652 fn type_is_value(&self, namespace: &str, name: &str) -> bool {
653 self.index.is_value_type(namespace, name)
654 || self
655 .output
656 .reference()
657 .and_then(|r| r.get(namespace, name).next())
658 .is_some_and(|def| {
659 matches!(
660 def.category(),
661 metadata::reader::TypeCategory::Struct
662 | metadata::reader::TypeCategory::Enum
663 )
664 })
665 }
666
667 fn encode_type_slice(&self, ty: &syn::TypeSlice) -> Result<metadata::Type, Error> {
668 Ok(metadata::Type::Array(Box::new(self.encode_type(&ty.elem)?)))
669 }
670
671 fn encode_type_array(&self, ty: &syn::TypeArray) -> Result<metadata::Type, Error> {
672 Ok(metadata::Type::ArrayFixed(
673 Box::new(self.encode_type(&ty.elem)?),
674 self.encode_lit_int::<usize>(&ty.len)?,
675 ))
676 }
677
678 fn encode_value(
679 &self,
680 ty: &metadata::Type,
681 value: &syn::Expr,
682 ) -> Result<metadata::Value, Error> {
683 if matches!(ty, metadata::Type::ISize | metadata::Type::USize)
684 && let Some(value) = self.encode_fixed_width_value(value)?
685 {
686 return Ok(value);
687 }
688
689 let value = match ty {
690 metadata::Type::I8 => metadata::Value::I8(self.encode_lit_sint(value, 8)? as i8),
691 metadata::Type::U8 => metadata::Value::U8(self.encode_lit_uint(value, 8)? as u8),
692 metadata::Type::I16 => metadata::Value::I16(self.encode_lit_sint(value, 16)? as i16),
693 metadata::Type::U16 => metadata::Value::U16(self.encode_lit_uint(value, 16)? as u16),
694 metadata::Type::I32 => metadata::Value::I32(self.encode_lit_sint(value, 32)? as i32),
695 metadata::Type::U32 => metadata::Value::U32(self.encode_lit_uint(value, 32)? as u32),
696 metadata::Type::I64 => metadata::Value::I64(self.encode_lit_sint(value, 64)?),
697 metadata::Type::U64 => metadata::Value::U64(self.encode_lit_uint(value, 64)?),
698 metadata::Type::F32 => metadata::Value::F32(self.encode_neg_lit_float::<f32>(value)?),
699 metadata::Type::F64 => metadata::Value::F64(self.encode_neg_lit_float::<f64>(value)?),
700 metadata::Type::String => metadata::Value::Utf16(self.encode_lit_string(value)?),
701 metadata::Type::ISize => fixed_signed_value(self.encode_lit_sint(value, 64)?),
702 metadata::Type::USize => fixed_unsigned_value(self.encode_lit_uint(value, 64)?),
703 metadata::Type::PtrMut(_, _) | metadata::Type::PtrConst(_, _) => {
704 let v = self.encode_neg_lit_int::<i64>(value)?;
705 if let Ok(v) = i32::try_from(v) {
706 metadata::Value::I32(v)
707 } else {
708 metadata::Value::I64(v)
709 }
710 }
711 metadata::Type::ValueName(tn) | metadata::Type::ClassName(tn) => {
712 let underlying = self
713 .output
714 .reference()
715 .and_then(|r| r.get(&tn.namespace, &tn.name).next())
716 .and_then(|def| def.underlying_type())
717 .or_else(|| self.rdl_underlying_type(&tn.namespace, &tn.name));
718
719 match underlying {
720 Some(underlying) => return self.encode_value(&underlying, value),
721 None => {
722 return self.err(value, &format!("constant type not supported: {ty:?}"));
723 }
724 }
725 }
726 rest => return self.err(value, &format!("constant type not supported: {rest:?}")),
727 };
728
729 Ok(value)
730 }
731
732 fn encode_fixed_width_value(
733 &self,
734 value: &syn::Expr,
735 ) -> Result<Option<metadata::Value>, Error> {
736 let int = match value {
737 syn::Expr::Lit(syn::ExprLit {
738 lit: syn::Lit::Int(int),
739 ..
740 }) => int,
741 syn::Expr::Unary(syn::ExprUnary { expr, .. }) => {
742 let syn::Expr::Lit(syn::ExprLit {
743 lit: syn::Lit::Int(int),
744 ..
745 }) = expr.as_ref()
746 else {
747 return Ok(None);
748 };
749 int
750 }
751 _ => return Ok(None),
752 };
753
754 let value = match int.suffix() {
755 "i32" => metadata::Value::I32(self.encode_lit_sint(value, 32)? as i32),
756 "u32" => metadata::Value::U32(self.encode_lit_uint(value, 32)? as u32),
757 "i64" => metadata::Value::I64(self.encode_lit_sint(value, 64)?),
758 "u64" => metadata::Value::U64(self.encode_lit_uint(value, 64)?),
759 _ => return Ok(None),
760 };
761 Ok(Some(value))
762 }
763
764 fn rdl_underlying_type(&self, namespace: &str, name: &str) -> Option<metadata::Type> {
765 let item = self.index.get(namespace, name).next()?;
766
767 match item {
768 Item::Typedef(t) => self.encode_underlying(&t.ty, namespace),
769 Item::Enum(e) => {
770 let repr = e.attrs.iter().find(|a| a.path().is_ident("repr"))?;
772 let path = repr.parse_args::<syn::Path>().ok()?;
773 self.encode_path(&path).ok()
774 }
775 Item::Struct(s) => {
776 let mut fields = s.fields.iter();
777
778 if let Some(field) = fields.next()
779 && fields.next().is_none()
780 && let FieldType::Type(ty) = &field.ty
781 {
782 return self.encode_underlying(ty, namespace);
783 }
784
785 None
786 }
787 _ => None,
788 }
789 }
790
791 fn encode_underlying(&self, ty: &syn::Type, namespace: &str) -> Option<metadata::Type> {
794 match ty {
795 syn::Type::Ptr(ptr) => {
797 let pointee = self
798 .encode_underlying(&ptr.elem, namespace)
799 .unwrap_or(metadata::Type::Void);
800 Some(if ptr.mutability.is_some() {
801 metadata::Type::PtrMut(Box::new(pointee), 1)
802 } else {
803 metadata::Type::PtrConst(Box::new(pointee), 1)
804 })
805 }
806 syn::Type::Path(tp)
807 if tp.qself.is_none()
808 && tp.path.segments.len() == 1
809 && matches!(tp.path.segments[0].arguments, syn::PathArguments::None) =>
810 {
811 if let Ok(resolved) = self.encode_type(ty)
812 && !matches!(
813 resolved,
814 metadata::Type::ValueName(_) | metadata::Type::ClassName(_)
815 )
816 {
817 return Some(resolved);
818 }
819 let ident = tp.path.segments[0].ident.unraw_to_string();
820 Some(metadata::Type::value_named(namespace, &ident))
821 }
822 _ => self.encode_type(ty).ok(),
823 }
824 }
825
826 fn encode_neg_lit_int<T>(&self, expr: &syn::Expr) -> Result<T, Error>
827 where
828 T: std::str::FromStr + TryFrom<i128>,
829 T::Err: std::fmt::Display,
830 {
831 let value = match expr {
832 syn::Expr::Lit(syn::ExprLit {
833 lit: syn::Lit::Int(int),
834 ..
835 }) => int.base10_parse().ok(),
836 syn::Expr::Unary(syn::ExprUnary {
837 op: syn::UnOp::Neg(_),
838 expr,
839 ..
840 }) => match expr.as_ref() {
841 syn::Expr::Lit(syn::ExprLit {
842 lit: syn::Lit::Int(int),
843 ..
844 }) => int
845 .base10_parse::<u64>()
846 .ok()
847 .and_then(|v| T::try_from(-(v as i128)).ok()),
848 _ => None,
849 },
850 _ => None,
851 };
852
853 value.ok_or_else(|| self.error(expr, "value not valid"))
854 }
855
856 fn encode_lit_int<T>(&self, expr: &syn::Expr) -> Result<T, Error>
857 where
858 T: std::str::FromStr,
859 T::Err: std::fmt::Display,
860 {
861 let value = match expr {
862 syn::Expr::Lit(syn::ExprLit {
863 lit: syn::Lit::Int(int),
864 ..
865 }) => int.base10_parse().ok(),
866
867 _ => None,
868 };
869
870 value.ok_or_else(|| self.error(expr, "value not valid"))
871 }
872
873 fn encode_lit_uint(&self, expr: &syn::Expr, bits: u32) -> Result<u64, Error> {
875 let mask: u128 = if bits >= 128 {
876 u128::MAX
877 } else {
878 (1u128 << bits) - 1
879 };
880 let value = match expr {
881 syn::Expr::Lit(syn::ExprLit {
882 lit: syn::Lit::Int(int),
883 ..
884 }) => int.base10_parse::<u64>().ok(),
885 syn::Expr::Unary(syn::ExprUnary {
886 op: syn::UnOp::Neg(_),
887 expr,
888 ..
889 }) => match expr.as_ref() {
890 syn::Expr::Lit(syn::ExprLit {
891 lit: syn::Lit::Int(int),
892 ..
893 }) => int
894 .base10_parse::<u64>()
895 .ok()
896 .map(|v| ((v as i128).wrapping_neg() as u128 & mask) as u64),
897 _ => None,
898 },
899 _ => None,
900 };
901
902 value.ok_or_else(|| self.error(expr, "value not valid"))
903 }
904
905 fn encode_lit_sint(&self, expr: &syn::Expr, bits: u32) -> Result<i64, Error> {
907 let raw: Option<u64> = match expr {
908 syn::Expr::Lit(syn::ExprLit {
909 lit: syn::Lit::Int(int),
910 ..
911 }) => int.base10_parse::<u64>().ok(),
912 syn::Expr::Unary(syn::ExprUnary {
913 op: syn::UnOp::Neg(_),
914 expr,
915 ..
916 }) => match expr.as_ref() {
917 syn::Expr::Lit(syn::ExprLit {
918 lit: syn::Lit::Int(int),
919 ..
920 }) => int
921 .base10_parse::<u64>()
922 .ok()
923 .map(|v| (v as i128).wrapping_neg() as u64),
924 _ => None,
925 },
926 _ => None,
927 };
928
929 let raw = raw.ok_or_else(|| self.error(expr, "value not valid"))?;
930
931 if bits >= 64 {
932 Ok(raw as i64)
933 } else {
934 let mask = (1u64 << bits) - 1;
935 let masked = raw & mask;
936 let sign_bit = 1u64 << (bits - 1);
937 Ok(if masked & sign_bit != 0 {
938 (masked | !mask) as i64
939 } else {
940 masked as i64
941 })
942 }
943 }
944
945 fn encode_neg_lit_float<T>(&self, expr: &syn::Expr) -> Result<T, Error>
946 where
947 T: std::str::FromStr + std::ops::Neg<Output = T>,
948 T::Err: std::fmt::Display,
949 {
950 let value = match expr {
951 syn::Expr::Lit(syn::ExprLit {
952 lit: syn::Lit::Float(float),
953 ..
954 }) => float.base10_parse().ok(),
955 syn::Expr::Unary(syn::ExprUnary {
956 op: syn::UnOp::Neg(_),
957 expr,
958 ..
959 }) => match expr.as_ref() {
960 syn::Expr::Lit(syn::ExprLit {
961 lit: syn::Lit::Float(float),
962 ..
963 }) => float.base10_parse().ok().map(|value: T| -value),
964 _ => None,
965 },
966 _ => None,
967 };
968
969 value.ok_or_else(|| self.error(expr, "value not valid"))
970 }
971
972 fn encode_lit_string(&self, expr: &syn::Expr) -> Result<String, Error> {
973 let value = match expr {
974 syn::Expr::Lit(syn::ExprLit {
975 lit: syn::Lit::Str(string),
976 ..
977 }) => Some(string.value()),
978 _ => None,
979 };
980
981 value.ok_or_else(|| self.error(expr, "value not valid"))
982 }
983
984 fn encode_type_reference(&self, ty: &syn::TypeReference) -> Result<metadata::Type, Error> {
985 let is_mut = ty.mutability.is_some();
986 let ty = self.encode_type(&ty.elem)?;
987
988 let ty = if is_mut {
989 metadata::Type::RefMut(Box::new(ty))
990 } else {
991 metadata::Type::RefConst(Box::new(ty))
992 };
993
994 Ok(ty)
995 }
996
997 fn encode_type_ptr(&self, ty: &syn::TypePtr) -> Result<metadata::Type, Error> {
998 let is_mut = ty.mutability.is_some();
999 let encoded = self.encode_type(&ty.elem)?;
1000
1001 let ty = match encoded {
1002 metadata::Type::PtrMut(inner, pointers) if is_mut => {
1003 metadata::Type::PtrMut(inner, pointers + 1)
1004 }
1005 metadata::Type::PtrConst(inner, pointers) if !is_mut => {
1006 metadata::Type::PtrConst(inner, pointers + 1)
1007 }
1008 metadata::Type::PtrMut(..) | metadata::Type::PtrConst(..) => {
1009 return self.err(
1010 ty.elem.as_ref(),
1011 "mixed `*mut` and `*const` pointer chains are not representable",
1012 );
1013 }
1014 _ => {
1015 if is_mut {
1016 metadata::Type::PtrMut(Box::new(encoded), 1)
1017 } else {
1018 metadata::Type::PtrConst(Box::new(encoded), 1)
1019 }
1020 }
1021 };
1022
1023 Ok(ty)
1024 }
1025
1026 fn encode_type_path(&self, ty: &syn::TypePath) -> Result<metadata::Type, Error> {
1027 self.encode_path(&ty.path)
1028 }
1029
1030 fn encode_path(&self, ty: &syn::Path) -> Result<metadata::Type, Error> {
1031 let mut path = vec![];
1032
1033 for segment in &ty.segments {
1034 if segment.ident == "super" {
1035 if path.is_empty() {
1036 for part in self.namespace.split('.') {
1037 path.push(part.to_string());
1038 }
1039 }
1040
1041 if path.pop().is_none() {
1042 return self.err(ty, "too many leading `super` keywords");
1043 }
1044 } else {
1045 path.push(segment.ident.to_string());
1046 }
1047 }
1048
1049 let mut generics = vec![];
1050
1051 if let Some(last) = ty.segments.last()
1052 && let syn::PathArguments::AngleBracketed(arguments) = &last.arguments
1053 {
1054 for argument in &arguments.args {
1055 if let syn::GenericArgument::Type(ty) = argument {
1056 generics.push(self.encode_type(ty)?);
1057 }
1058 }
1059 }
1060
1061 if path.len() == 1 {
1062 if let Some(number) = self.generics.iter().position(|generic| *generic == path[0]) {
1063 return Ok(metadata::Type::Generic(
1064 path[0].clone(),
1065 number.try_into().unwrap(),
1066 ));
1067 }
1068
1069 match path[0].as_str() {
1070 "bool" => return Ok(metadata::Type::Bool),
1071 "i8" => return Ok(metadata::Type::I8),
1072 "u8" => return Ok(metadata::Type::U8),
1073 "i16" => return Ok(metadata::Type::I16),
1074 "u16" => return Ok(metadata::Type::U16),
1075 "i32" => return Ok(metadata::Type::I32),
1076 "u32" => return Ok(metadata::Type::U32),
1077 "i64" => return Ok(metadata::Type::I64),
1078 "u64" => return Ok(metadata::Type::U64),
1079 "f32" => return Ok(metadata::Type::F32),
1080 "f64" => return Ok(metadata::Type::F64),
1081 "isize" => return Ok(metadata::Type::ISize),
1082 "usize" => return Ok(metadata::Type::USize),
1083
1084 "void" => return Ok(metadata::Type::Void),
1085 "String" => return Ok(metadata::Type::String),
1086 "Object" => return Ok(metadata::Type::Object),
1087 "Char16" => return Ok(metadata::Type::Char),
1088
1089 _ => {}
1090 }
1091 }
1092
1093 let (name, namespace) = path.split_last().unwrap();
1094
1095 let namespace = if namespace.is_empty() {
1096 self.namespace.to_string()
1097 } else {
1098 namespace.join(".")
1099 };
1100
1101 let make_type = |namespace: &str| -> Option<metadata::Type> {
1102 if self.index.contains(namespace, name)
1103 || self
1104 .output
1105 .reference()
1106 .is_some_and(|r| r.contains(namespace, name))
1107 {
1108 let tn = metadata::TypeName {
1109 namespace: namespace.to_string(),
1110 name: name.clone(),
1111 generics: generics.clone(),
1112 };
1113 if self.type_is_value(namespace, name) {
1114 Some(metadata::Type::ValueName(tn))
1115 } else {
1116 Some(metadata::Type::ClassName(tn))
1117 }
1118 } else {
1119 None
1120 }
1121 };
1122
1123 if let Some(ty) = make_type(&namespace) {
1124 return Ok(ty);
1125 }
1126
1127 let namespace = format!("{}.{}", self.namespace, namespace);
1128
1129 if let Some(ty) = make_type(&namespace) {
1130 return Ok(ty);
1131 }
1132
1133 for use_item in &self.file.uses {
1134 if let Some(ns) = glob_use_namespace(use_item)
1135 && let Some(ty) = make_type(&ns)
1136 {
1137 return Ok(ty);
1138 }
1139 }
1140
1141 if ty.segments.len() == 1 {
1143 match name.as_str() {
1144 "Type" => return Ok(metadata::Type::class_named("System", "Type")),
1145 "GUID" => return Ok(metadata::Type::value_named("System", "Guid")),
1146 "HRESULT" => {
1147 return Ok(metadata::Type::value_named("Windows.Foundation", "HResult"));
1148 }
1149 _ => {}
1150 }
1151 }
1152
1153 Err(self.error(ty, "type not found"))
1154 }
1155
1156 fn encode_return_type(&self, ty: &syn::ReturnType) -> Result<metadata::Type, Error> {
1157 match ty {
1158 syn::ReturnType::Type(_, ty) => self.encode_type(ty),
1159 _ => Ok(metadata::Type::Void),
1160 }
1161 }
1162
1163 fn validate_type_is_winrt<S: Spanned + quote::ToTokens>(
1165 &self,
1166 span: &S,
1167 ty: &metadata::Type,
1168 ) -> Result<(), Error> {
1169 match ty {
1170 metadata::Type::ValueName(tn) | metadata::Type::ClassName(tn) => {
1171 for generic_ty in &tn.generics {
1172 self.validate_type_is_winrt(span, generic_ty)?;
1173 }
1174
1175 if let Some(is_winrt) = self.index.is_winrt(&tn.namespace, &tn.name) {
1176 if !is_winrt {
1177 return self.err(span, "WinRT types cannot refer to non-WinRT types");
1178 }
1179 } else if let Some(reference) = self.output.reference()
1180 && let Some(def) = reference.get(&tn.namespace, &tn.name).next()
1181 && !def
1182 .flags()
1183 .contains(metadata::TypeAttributes::WindowsRuntime)
1184 {
1185 return self.err(span, "WinRT types cannot refer to non-WinRT types");
1186 }
1187 }
1188 metadata::Type::PtrMut(inner, _) | metadata::Type::PtrConst(inner, _) => {
1189 self.validate_type_is_winrt(span, inner)?;
1190 }
1191 metadata::Type::RefMut(inner) | metadata::Type::RefConst(inner) => {
1192 self.validate_type_is_winrt(span, inner)?;
1193 }
1194 metadata::Type::Array(inner) | metadata::Type::ArrayFixed(inner, _) => {
1195 self.validate_type_is_winrt(span, inner)?;
1196 }
1197 _ => {}
1198 }
1199
1200 Ok(())
1201 }
1202}
1203
1204pub(crate) fn parse_arch_bitmask(expr: &syn::Expr) -> Option<i32> {
1206 match expr {
1207 syn::Expr::Path(p)
1208 if p.qself.is_none()
1209 && p.path.leading_colon.is_none()
1210 && p.path.segments.len() == 1 =>
1211 {
1212 arch_name_to_bits(&p.path.segments[0].ident.to_string())
1213 }
1214 syn::Expr::Binary(syn::ExprBinary {
1215 left,
1216 op: syn::BinOp::BitOr(_),
1217 right,
1218 ..
1219 }) => {
1220 let l = parse_arch_bitmask(left)?;
1221 let r = parse_arch_bitmask(right)?;
1222 Some(l | r)
1223 }
1224 _ => None,
1225 }
1226}
1227
1228fn arch_name_to_bits(name: &str) -> Option<i32> {
1229 match name {
1230 "X86" => Some(1),
1231 "X64" => Some(2),
1232 "Arm64" => Some(4),
1233 _ => None,
1234 }
1235}
1236
1237pub(crate) fn make_sig(
1238 fn_token: syn::Token![fn],
1239 ident: syn::Ident,
1240 generics: syn::Generics,
1241 paren_token: syn::token::Paren,
1242 inputs: syn::punctuated::Punctuated<syn::FnArg, syn::Token![,]>,
1243 variadic: Option<syn::Variadic>,
1244 output: syn::ReturnType,
1245) -> syn::Signature {
1246 syn::Signature {
1247 constness: None,
1248 asyncness: None,
1249 unsafety: None,
1250 abi: None,
1251 fn_token,
1252 ident,
1253 generics,
1254 paren_token,
1255 inputs,
1256 variadic,
1257 output,
1258 }
1259}
1260
1261pub(crate) fn parse_fn_inputs(
1262 content: &syn::parse::ParseBuffer,
1263) -> syn::Result<(
1264 syn::punctuated::Punctuated<syn::FnArg, syn::Token![,]>,
1265 Option<syn::Variadic>,
1266)> {
1267 let mut args = syn::punctuated::Punctuated::new();
1268 let mut variadic = None;
1269
1270 while !content.is_empty() {
1271 let fork = content.fork();
1272 let _ = fork.call(syn::Attribute::parse_outer);
1273 if fork.peek(syn::Token![...]) {
1274 let attrs = content.call(syn::Attribute::parse_outer)?;
1275 let dots: syn::Token![...] = content.parse()?;
1276 variadic = Some(syn::Variadic {
1277 attrs,
1278 pat: None,
1279 dots,
1280 comma: if content.is_empty() {
1281 None
1282 } else {
1283 Some(content.parse()?)
1284 },
1285 });
1286 break;
1287 }
1288
1289 let arg: syn::FnArg = content.parse()?;
1290 args.push_value(arg);
1291
1292 if content.is_empty() {
1293 break;
1294 }
1295
1296 let comma: syn::Token![,] = content.parse()?;
1297 args.push_punct(comma);
1298 }
1299
1300 Ok((args, variadic))
1301}
1302
1303pub(crate) fn parse_return_type_with_attrs(
1305 input: syn::parse::ParseStream,
1306) -> syn::Result<(syn::ReturnType, Vec<syn::Attribute>)> {
1307 if input.peek(syn::Token![->]) {
1308 let arrow = input.parse::<syn::Token![->]>()?;
1309 let return_attrs = input.call(syn::Attribute::parse_outer)?;
1310 let ty: syn::Type = input.parse()?;
1311 Ok((syn::ReturnType::Type(arrow, Box::new(ty)), return_attrs))
1312 } else {
1313 Ok((syn::ReturnType::Default, vec![]))
1314 }
1315}
1316
1317fn glob_use_namespace(use_item: &syn::ItemUse) -> Option<String> {
1318 fn extract(tree: &syn::UseTree, parts: &mut Vec<String>) -> bool {
1319 match tree {
1320 syn::UseTree::Path(p) => {
1321 parts.push(p.ident.to_string());
1322 extract(&p.tree, parts)
1323 }
1324 syn::UseTree::Glob(_) => true,
1325 _ => false,
1326 }
1327 }
1328 let mut parts = vec![];
1329 if extract(&use_item.tree, &mut parts) && !parts.is_empty() {
1330 Some(parts.join("."))
1331 } else {
1332 None
1333 }
1334}
1335
1336trait IdentMethods {
1337 fn unraw_to_string(&self) -> String;
1338}
1339
1340impl IdentMethods for syn::Ident {
1341 fn unraw_to_string(&self) -> String {
1342 use syn::ext::IdentExt;
1343 self.unraw().to_string()
1344 }
1345}
1346
1347#[test]
1348fn use_glob_resolves_type() {
1349 let output = std::env::temp_dir().join("windows_rdl_use_glob_resolves_type.winmd");
1350
1351 reader()
1352 .input_text(
1353 r#"
1354use Other::*;
1355
1356#[winrt]
1357mod Test {
1358 struct Thing {
1359 a: Point,
1360 }
1361}
1362
1363#[winrt]
1364mod Other {
1365 struct Point {
1366 x: i32,
1367 y: i32,
1368 }
1369}
1370 "#,
1371 )
1372 .output(&output)
1373 .write()
1374 .unwrap();
1375}