Skip to main content

windows_rdl/reader/
mod.rs

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)]
55/// Builder that compiles RDL files into `.winmd` metadata.
56pub 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    /// Creates a new builder with default options.
67    pub fn new() -> Self {
68        Self::default()
69    }
70
71    /// Adds an input `.rdl` file or directory.
72    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    /// Adds inline RDL source text to compile instead of a file on disk.
78    pub fn input_text(&mut self, input: &str) -> &mut Self {
79        self.input_text.push(input.to_string());
80        self
81    }
82
83    /// Adds inline RDL source texts to compile instead of files on disk.
84    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    /// Adds a `.winmd` reference file or directory.
96    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    /// Adds multiple `.winmd` reference files or directories.
102    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    /// Adds a `.winmd` reference from memory.
114    pub fn reference_bytes(&mut self, input: &[u8]) -> &mut Self {
115        self.reference_bytes.push(input.to_vec());
116        self
117    }
118
119    /// Adds `.winmd` references from memory.
120    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    /// Adds the default Windows metadata references.
132    pub fn reference_default(&mut self) -> &mut Self {
133        self.reference_default = true;
134        self
135    }
136
137    /// Adds multiple input `.rdl` files or directories.
138    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    /// Sets the output `.winmd` file path.
150    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    /// Compiles the inputs and writes the `.winmd` to the configured output.
156    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
294/// Parses one `.rdl` file and returns the items it defines under `namespace`.
295pub(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
313/// Rewrites RDL tokens that would otherwise confuse `syn`.
314fn 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
486/// Parses a `syn::LitInt` as a `u128`, supporting both `0x...` hex and decimal literals.
487/// Underscore separators (e.g. `0x005023ca_72b1_11d3_9fc4_00c04f79a0a3`) are accepted.
488pub(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    /// Reads forced over-alignment from `#[align(N)]`.
543    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    /// Resolves unqualified attribute-argument type names in the attribute's namespace first.
601    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                // Enum-typed constants encode against the enum's `#[repr(iN)]` type.
771                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    /// Resolves a typedef's bare sibling names in the typedef namespace, not the constant
792    /// namespace.
793    fn encode_underlying(&self, ty: &syn::Type, namespace: &str) -> Option<metadata::Type> {
794        match ty {
795            // MAKEINTRESOURCE-style pointer constants only need the pointer kind.
796            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    /// Accepts C unsigned sentinels spelled as negated casts by masking to the target width.
874    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    /// Reinterprets signed constants from their C bit pattern, including overflowing HRESULTs.
906    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        // Fall back to core aliases only when the scrape did not define its own type.
1142        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    /// Rejects references from WinRT types to non-WinRT named types.
1164    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
1204/// Parses a `#[arch(...)]` expression into the X86/X64/Arm64 bitmask.
1205pub(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
1303/// Parses `-> #[attr]* Type`, keeping return-value attributes with the return type.
1304pub(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}