Skip to main content

ferroui_build_scan/scanner/
mod.rs

1//! The source scanner: reads the sources of a crate and fills the build-time
2//! type model from its declarations (docs/porting/xaml.md, 9.5.1 and 9.5.2).
3//!
4//! # What is read
5//!
6//! The files of the crate, from its root along the `mod` declarations
7//! (`#[path]` included; modules and items under `#[cfg(test)]` are left
8//! out), and in them:
9//!
10//! | Declaration | Gives |
11//! |---|---|
12//! | `ferro_class!(X: Base ..)` | a class, its base class, its Rust path |
13//! | `ferro_static_type!(X)` | the owner type of attached properties |
14//! | `ferro_properties! { impl X .. { .. } }`, `ferro_property!` | the registered properties: accessor, kind and value type from the type of the accessor; name, owner and host from the registration in its body; added owners |
15//! | `ferro_class_info!(X { new, interfaces, markup })` | the default constructor, the interfaces and the markup metadata of a class |
16//! | `ferro_markup_type!`, `ferro_markup_enum!` | the other types, with handles, members and values |
17//! | `const NAMESPACES`, `static _: MarkupAssembly` | the namespace table and the assembly |
18//! | `const TYPES: &[&TypeInfo]` | the classes the crate registers: a class that is not in the list is marked ([`TypeModel::unregistered`]) |
19//! | `MarkupType::register_handle::<H>(..)`, `ValueTypes::register_cast::<A, B>(..)` in a function | one more handle of a type with markup metadata ([`AssemblyModel::handles`]); the casts between Rust types ([`AssemblyModel::casts`]) |
20//! | `ValueTypes::register_nullable::<T>`, `register_reference`, `register_object`, `register_element_ref`, `register_interface`, `register_upcast` with their types stated: called or handed on in a function, in a closure that is the value of a constant or a static, in the body of a declaration, in the expansion of a macro a function invokes | what the crate registers with the untyped value conversions ([`AssemblyModel::value_types`]); the calls the text has and the model does not are counted ([`AssemblyModel::unread_value_types`]) |
21//! | `generics: [(Type<A>, "crate::Type<::path::A>")]` of `ferro_rust_paths!` | the path the crate states for an instantiation of a generic type ([`TypeModel::stated_path`]) |
22//! | `struct`, `enum`, `trait`, `type`, `use`, `mod`, `bitflags!` | the names of each module, for the resolution of paths; the values of enumerations; the type aliases ([`AssemblyModel::aliases`]) |
23//! | `impl X { fn .., const .. }` | the functions of each type ([`Scan::functions`]); the constants an enumeration or a set of flags names as members |
24//! | `ferro_rust_paths!` | the public paths the crate states, compared with the ones the scanner finds |
25//!
26//! Macros are not expanded, except the macros the crate itself defines with
27//! a single form of identifiers and types (`ferro_transition_class!`): an
28//! invocation of one, as an item or among the members of an `impl` block, is
29//! expanded by substitution and the declarations it writes are read.
30//!
31//! # What is not guessed
32//!
33//! A declaration whose form a reader does not know is a diagnostic with the
34//! file and the line, and is not in the model. A path of a type that the
35//! `use` items and the modules of the crate do not resolve stays as written
36//! and is listed in [`RustType::unresolved`]. The name of a property whose
37//! accessor adds an owner to a property of another crate is nothing: the
38//! model of that crate has it.
39//!
40//! # Seams for the next stages
41//!
42//! - [`MemberModel::call`](crate::model::MemberModel::call) and
43//!   [`AccessorModel::call`](crate::model::AccessorModel::call) are chosen at
44//!   the end of the scan ([`crate::call_forms`], 9.5.3), from the resolved path
45//!   of the callable, [`Scan::functions`] and the functions the models of the
46//!   dependencies list.
47//! - Without the models of the crates the crate is built on, paths into them
48//!   are absolute as the file spells them. With them
49//!   ([`ScanOptions::dependencies`]) a path into such a crate is the path of
50//!   the declaring module, a name behind a glob import of such a crate is
51//!   resolved, and an owner added to a property of such a crate has the name
52//!   of the property.
53//! - [`Scan::normalise`] resolves any further type text against the modules
54//!   of the scan (the signatures of the handlers of an `x:Class` type).
55
56mod constants;
57mod declarations;
58mod modules;
59mod source;
60mod tokens;
61
62#[cfg(test)]
63mod tests;
64
65use std::collections::{BTreeMap, BTreeSet};
66use std::fmt;
67use std::path::PathBuf;
68
69use proc_macro2::TokenTree;
70
71use crate::model::{
72    AccessorModel, AliasModel, AssemblyModel, AttributeModel, AttributeValueModel, CallableModel, CastModel, EnumMemberModel, ExportModel, GenericModel,
73    HandleModel, MacroModel, MemberModel, ParameterModel, PropertyModel, RegisteredKind, RegisteredModel, RegistrationModel, RustType, TypeKind, TypeModel,
74    ValueTypeModel, XmlnsDefinitionModel, XmlnsPrefixModel,
75};
76use crate::model_set::ModelSet;
77use declarations::{
78    first_line, property_type, read_registration, Accessor, Declaration, EnumMember, MarkupBody, RawAccessor, RawAttribute, RawMember, RawParameter,
79    RawProperty, RawValue,
80};
81use modules::{Modules, Target};
82use source::{Located, Site, Source, Text};
83use tokens::{pieces, plain_path, text_of, text_of_pieces, tokens_of, Tokens};
84
85/// The names of the declaration macros: the macros whose invocations are counted
86/// ([`ScannedFile::invocations`]) and read.
87pub const DECLARATION_MACRO_NAMES: &[&str] = declarations::DECLARATION_MACROS;
88
89/// The codes of the diagnostics of the scanner (the range `FRN9xxx` of xaml.md 9.6.5).
90pub mod codes {
91    /// A file cannot be read, is not Rust the reader parses, or is not found.
92    pub const FILE: &str = "FRN9001";
93    /// A declaration has a form the scanner does not know.
94    pub const FORM: &str = "FRN9010";
95    /// A declaration macro is invoked where the scanner does not read.
96    pub const POSITION: &str = "FRN9011";
97    /// A macro of the crate declares through a declaration macro and is not expanded.
98    pub const LOCAL_MACRO: &str = "FRN9012";
99    /// A runtime type or markup metadata is implemented by hand.
100    pub const HAND_WRITTEN: &str = "FRN9013";
101    /// A path of a type is not resolved.
102    pub const UNRESOLVED: &str = "FRN9020";
103    /// The accessor of a registered property is not read, or its registration is not.
104    pub const REGISTRATION: &str = "FRN9021";
105    /// Two declarations state the same thing.
106    pub const DUPLICATE: &str = "FRN9022";
107    /// A declaration names a type the scanner has no declaration of.
108    pub const OWNER: &str = "FRN9023";
109    /// The public path the crate states for a type is not the one the scanner finds.
110    pub const PUBLIC_PATH: &str = "FRN9024";
111    /// A class the crate declares is not in its list of registered classes.
112    pub const UNREGISTERED: &str = "FRN9025";
113    /// The assembly, the namespace table or the list of registered classes is not read.
114    pub const ASSEMBLY: &str = "FRN9030";
115}
116
117/// What to scan.
118#[derive(Clone, Debug)]
119pub struct ScanOptions {
120    /// The name of the crate, as Rust paths spell it (`ferroui_controls`).
121    pub crate_name: String,
122    /// The root file of the crate (`lib.rs`).
123    pub root: PathBuf,
124    /// The assembly name, for a crate that has no `MarkupAssembly`.
125    pub assembly_name: Option<String>,
126    /// The names of the crates the crate is built on. They are needed only where a file
127    /// imports a glob of another crate; elsewhere the `use` items tell.
128    pub extern_crates: Vec<String>,
129    /// The models of the crates the crate is built on (the ones it depends on directly
130    /// and theirs), as their scans wrote them. With them the scan resolves what lives in
131    /// those crates: the names behind a glob import of one of their modules, the declaring
132    /// module of every type of theirs the crate names (the type texts of the model are
133    /// then canonical, [`ModelSet::canonical`]), and the names of their properties the
134    /// crate adds owners to.
135    pub dependencies: Vec<AssemblyModel>,
136}
137
138impl ScanOptions {
139    pub fn new(crate_name: &str, root: impl Into<PathBuf>) -> Self {
140        Self { crate_name: crate_name.to_string(), root: root.into(), assembly_name: None, extern_crates: Vec::new(), dependencies: Vec::new() }
141    }
142
143    /// The scan with the models of the crates the crate is built on.
144    pub fn with_dependencies(mut self, dependencies: Vec<AssemblyModel>) -> Self {
145        self.dependencies = dependencies;
146        self
147    }
148}
149
150#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
151pub enum Severity {
152    /// Something of the crate is not in the model.
153    Error,
154    /// Something of the model is incomplete.
155    Warning,
156    /// Something the reader of the model should know.
157    Note,
158}
159
160/// A diagnostic of the scanner, with the place in the Rust source.
161#[derive(Clone, Debug, PartialEq, Eq)]
162pub struct Diagnostic {
163    pub severity: Severity,
164    /// One of [`codes`].
165    pub code: &'static str,
166    pub file: PathBuf,
167    /// The line, from 1; 0 when the diagnostic is about the file.
168    pub line: usize,
169    pub message: String,
170}
171
172impl fmt::Display for Diagnostic {
173    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
174        let severity = match self.severity {
175            Severity::Error => "error",
176            Severity::Warning => "warning",
177            Severity::Note => "note",
178        };
179        write!(formatter, "{}({}): {severity} {}: {}", self.file.display(), self.line, self.code, self.message)
180    }
181}
182
183/// How the invocations of one declaration macro in a file were met. Their sum is the
184/// number of invocations written in the file.
185#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
186pub struct InvocationCounts {
187    /// Read into the model.
188    pub read: usize,
189    /// In a form the scanner does not know (a diagnostic says which).
190    pub failed: usize,
191    /// Inside a `macro_rules!` definition.
192    pub in_macro_definitions: usize,
193    /// Where the scanner does not read: inside a function, another item or the arguments
194    /// of another macro (a diagnostic says where).
195    pub unread: usize,
196    /// Inside an item under `#[cfg(test)]`.
197    pub in_test_code: usize,
198}
199
200impl InvocationCounts {
201    pub fn total(&self) -> usize {
202        self.read + self.failed + self.in_macro_definitions + self.unread + self.in_test_code
203    }
204
205    fn add(&mut self, other: &InvocationCounts) {
206        self.read += other.read;
207        self.failed += other.failed;
208        self.in_macro_definitions += other.in_macro_definitions;
209        self.unread += other.unread;
210        self.in_test_code += other.in_test_code;
211    }
212}
213
214/// A file the scanner read.
215#[derive(Clone, Debug)]
216pub struct ScannedFile {
217    pub path: PathBuf,
218    /// The module the file is (`ferroui_controls::border`).
219    pub module: String,
220    /// The invocations of the declaration macros in the file, by the name of the macro.
221    pub invocations: BTreeMap<String, InvocationCounts>,
222}
223
224/// A function of an inherent `impl` block of the crate, as written: what the choice of a
225/// call form (9.5.3) and the handlers of an `x:Class` type (9.4.4) are checked against.
226#[derive(Clone, Debug, PartialEq, Eq)]
227pub struct InherentFunction {
228    /// The Rust path of the type (`::ferroui_controls::border::Border`), or its name as
229    /// written when the scanner did not resolve it.
230    pub owner: String,
231    pub name: String,
232    /// The visibility as written; empty when private.
233    pub visibility: String,
234    /// Whether the function takes `self`.
235    pub receiver: bool,
236    /// The types of the parameters after `self`, as written.
237    pub parameters: Vec<String>,
238    pub return_type: Option<String>,
239    /// The macro that declares the function (`ferro_routed_event`), when one does.
240    pub declared_by: Option<String>,
241    /// The module of the `impl` block.
242    pub module: String,
243    pub file: PathBuf,
244    pub line: usize,
245}
246
247/// What a scan found, in numbers.
248#[derive(Clone, Debug, Default, PartialEq, Eq)]
249pub struct Statistics {
250    pub files: usize,
251    pub modules: usize,
252    /// The invocations of each declaration macro, over all files.
253    pub invocations: BTreeMap<String, InvocationCounts>,
254    /// The declarations read from expansions of macros of the crate, by macro.
255    pub expanded: BTreeMap<String, usize>,
256    pub classes: usize,
257    pub static_types: usize,
258    pub markup_types: usize,
259    pub enums: usize,
260    /// The classes and static types that are not in the list of registered classes of
261    /// the crate.
262    pub unregistered: usize,
263    pub types_without_namespace: usize,
264    pub types_without_public_path: usize,
265    pub registered: usize,
266    pub styled: usize,
267    pub direct: usize,
268    pub attached: usize,
269    pub added_owners: usize,
270    pub aliases: usize,
271    /// Accessors whose body states no registration the scanner reads.
272    pub unknown_registrations: usize,
273    /// Registered properties whose name is not in the model.
274    pub registered_without_name: usize,
275    pub constructors: usize,
276    pub properties: usize,
277    pub indexers: usize,
278    pub methods: usize,
279    pub fields: usize,
280    pub events: usize,
281    /// The callables of the members, and how many are paths, and of those how many are
282    /// resolved.
283    pub callables: usize,
284    pub callable_paths: usize,
285    pub callable_paths_resolved: usize,
286    /// The call forms chosen for the callables (9.5.3): every callable but `new:` of a
287    /// class and the source of an added owner, which are form A by their declaration.
288    pub call_forms: crate::call_forms::CallFormStatistics,
289    pub enum_members: usize,
290    pub enum_members_without_value: usize,
291    /// The type texts of the model, and how many have an unresolved path.
292    pub type_texts: usize,
293    pub type_texts_unresolved: usize,
294    /// The unresolved paths, as written, with the number of type texts each is in.
295    pub unresolved_paths: BTreeMap<String, usize>,
296    /// The entries of `ferro_rust_paths!`, and how many are the public path the scanner
297    /// finds for the type.
298    pub rust_paths_listed: usize,
299    pub rust_paths_agreeing: usize,
300}
301
302/// The result of a scan.
303pub struct Scan {
304    pub model: AssemblyModel,
305    pub diagnostics: Vec<Diagnostic>,
306    pub files: Vec<ScannedFile>,
307    pub functions: Vec<InherentFunction>,
308    pub statistics: Statistics,
309    modules: Modules,
310}
311
312impl Scan {
313    /// The type text `text`, written in the module `module` of the scanned crate
314    /// (`ferroui_controls::border`), normalised as the type texts of the model are.
315    /// Nothing when the module is not one of the scan or the text is not tokens.
316    pub fn normalise(&self, module: &str, text: &str) -> Option<RustType> {
317        let module = self.modules.find_module(module)?;
318        let tokens = tokens_of(text.parse().ok()?);
319        let (text, unresolved) = normalise(&self.modules, module, &tokens, None, &[]);
320        Some(RustType { text, unresolved })
321    }
322
323    /// The diagnostics of one severity.
324    pub fn diagnostics_of(&self, severity: Severity) -> impl Iterator<Item = &Diagnostic> {
325        self.diagnostics.iter().filter(move |diagnostic| diagnostic.severity == severity)
326    }
327
328    /// The numbers of the scan as text, one per line: what a reader judges the coverage by.
329    pub fn summary(&self) -> String {
330        let statistics = &self.statistics;
331        let mut lines = vec![
332            format!("crate {} (assembly `{}`): {} files, {} modules", self.model.crate_name, self.model.name, statistics.files, statistics.modules),
333            format!(
334                "types: {} ({} classes, {} static types, {} markup types, {} enumerations); {} without a namespace, {} without a public path",
335                self.model.types.len(),
336                statistics.classes,
337                statistics.static_types,
338                statistics.markup_types,
339                statistics.enums,
340                statistics.types_without_namespace,
341                statistics.types_without_public_path
342            ),
343            format!(
344                "registered properties: {} ({} styled, {} direct, {} attached); {} added owners, {} aliases, {} registrations not read, {} without a name",
345                statistics.registered,
346                statistics.styled,
347                statistics.direct,
348                statistics.attached,
349                statistics.added_owners,
350                statistics.aliases,
351                statistics.unknown_registrations,
352                statistics.registered_without_name
353            ),
354            format!(
355                "members: {} constructors, {} plain properties, {} indexers, {} methods, {} fields, {} events",
356                statistics.constructors, statistics.properties, statistics.indexers, statistics.methods, statistics.fields, statistics.events
357            ),
358            format!(
359                "callables: {} ({} paths, {} of them resolved; the others are closures)",
360                statistics.callables, statistics.callable_paths, statistics.callable_paths_resolved
361            ),
362            statistics.call_forms.summary(),
363            format!("enumeration members: {} ({} without a value)", statistics.enum_members, statistics.enum_members_without_value),
364            format!(
365                "registration: {} classes are not in the list of registered classes; {} handles registered for types with markup metadata; {} casts; {} type aliases",
366                statistics.unregistered,
367                self.model.handles.len(),
368                self.model.casts.len(),
369                self.model.aliases.len()
370            ),
371            format!(
372                "value types: {} registrations read ({}); not read: {}",
373                self.model.value_types.len(),
374                crate::model::VALUE_REGISTRATIONS
375                    .iter()
376                    .filter(|(registration, _)| *registration != "cast")
377                    .map(|(registration, _)| format!("{} {registration}", self.model.value_types.iter().filter(|read| read.registration == *registration).count()))
378                    .collect::<Vec<_>>()
379                    .join(", "),
380                match self.model.unread_value_types.is_empty() {
381                    true => "none".to_string(),
382                    false => self.model.unread_value_types.iter().map(|(registration, count)| format!("{count} {registration}")).collect::<Vec<_>>().join(", "),
383                }
384            ),
385            format!(
386                "type texts: {} ({} with an unresolved path, {} distinct unresolved paths)",
387                statistics.type_texts,
388                statistics.type_texts_unresolved,
389                statistics.unresolved_paths.len()
390            ),
391            format!("public paths stated by the crate (ferro_rust_paths!): {} ({} are the path the scanner finds)", statistics.rust_paths_listed, statistics.rust_paths_agreeing),
392            format!("inherent functions: {}", self.functions.len()),
393        ];
394        for (name, counts) in &statistics.invocations {
395            lines.push(format!(
396                "{name}!: {} invocations ({} read, {} not read: form, {} not read: position, {} in macro definitions, {} in test code)",
397                counts.total(),
398                counts.read,
399                counts.failed,
400                counts.unread,
401                counts.in_macro_definitions,
402                counts.in_test_code
403            ));
404        }
405        for (name, count) in &statistics.expanded {
406            lines.push(format!("{name}! (a macro of the crate): {count} declarations read from its expansions"));
407        }
408        let mut by_code: BTreeMap<(Severity, &str), usize> = BTreeMap::new();
409        for diagnostic in &self.diagnostics {
410            *by_code.entry((diagnostic.severity, diagnostic.code)).or_default() += 1;
411        }
412        for ((severity, code), count) in by_code {
413            lines.push(format!("diagnostics: {count} {severity:?} {code}"));
414        }
415        let mut unresolved: Vec<(&String, &usize)> = statistics.unresolved_paths.iter().collect();
416        unresolved.sort_by(|left, right| right.1.cmp(left.1).then(left.0.cmp(right.0)));
417        for (path, count) in unresolved.into_iter().take(40) {
418            lines.push(format!("unresolved: `{path}` in {count} type texts"));
419        }
420        lines.join("\n")
421    }
422}
423
424/// The path of the function a closure without parameters dereferences the result of:
425/// `|| *Type::function()`, as it is or in parentheses. Nothing for any other expression.
426fn dereferenced_call(tokens: &[TokenTree]) -> Option<Vec<String>> {
427    let is = |token: Option<&TokenTree>, character: char| matches!(token, Some(TokenTree::Punct(punct)) if punct.as_char() == character);
428    match tokens {
429        [TokenTree::Group(group)] if group.delimiter() == proc_macro2::Delimiter::Parenthesis => {
430            dereferenced_call(&group.stream().into_iter().collect::<Vec<_>>())
431        }
432        [.., TokenTree::Group(arguments)] if tokens.len() > 4 && arguments.delimiter() == proc_macro2::Delimiter::Parenthesis && arguments.stream().is_empty() => {
433            if !(is(tokens.first(), '|') && is(tokens.get(1), '|') && is(tokens.get(2), '*')) {
434                return None;
435            }
436            plain_path(&tokens[3..tokens.len() - 1])
437        }
438        _ => None,
439    }
440}
441
442/// Scans the crate `options` describes. The scan does not fail: what it cannot read is in
443/// the diagnostics of the result.
444pub fn scan_crate(options: &ScanOptions) -> Scan {
445    let mut source = Source::read(options);
446    let declarations = std::mem::take(&mut source.declarations);
447    let mut builder = Builder {
448        source,
449        model: AssemblyModel::new(options.assembly_name.as_deref().unwrap_or(""), &options.crate_name),
450        index: BTreeMap::new(),
451        reported: BTreeSet::new(),
452        statistics: Statistics::default(),
453    };
454    builder.assembly(options);
455    // The types the crate declares come first: the other declarations name them.
456    for declaration in &declarations {
457        match &declaration.value {
458            Declaration::Class { name, base, .. } => builder.class(name, Some(base), TypeKind::Class, &declaration.site),
459            Declaration::StaticType { name } => builder.class(name, None, TypeKind::Static, &declaration.site),
460            _ => {}
461        }
462    }
463    builder.hand_written();
464    builder.class_lists();
465    for declaration in &declarations {
466        match &declaration.value {
467            Declaration::MarkupType { kind, type_, is_dyn, name, body } => builder.markup_type(*kind, type_, *is_dyn, name.as_deref(), body, &declaration.site),
468            Declaration::MarkupEnum { name, flags, members, body } => builder.markup_enum(name, *flags, members, body, &declaration.site),
469            _ => {}
470        }
471    }
472    for declaration in &declarations {
473        match &declaration.value {
474            Declaration::ClassInfo { name, new, interfaces, markup } => builder.class_info(name, new.as_deref(), interfaces, markup.as_ref(), &declaration.site),
475            Declaration::Properties { owner, accessors, function_of } => builder.properties(owner, accessors, function_of.as_deref(), &declaration.site),
476            _ => {}
477        }
478    }
479    builder.link_properties();
480    builder.finish(&options.dependencies)
481}
482
483/// The text of the type `tokens` written in `module`: every path the modules resolve made
484/// absolute, the others as written and listed. `self_type` is what `Self` stands for;
485/// `imports` are the names the function the type is written in imports for itself.
486fn normalise(modules: &Modules, module: usize, tokens: &[TokenTree], self_type: Option<&str>, imports: &[(String, Vec<String>)]) -> (String, Vec<String>) {
487    let mut unresolved: Vec<String> = Vec::new();
488    let text = text_of_pieces(&pieces(tokens, &mut |segments: &[String]| {
489        let written = segments.join("::");
490        if let (Some(self_type), Some("Self")) = (self_type, segments.first().map(String::as_str)) {
491            let mut text = self_type.to_string();
492            for segment in &segments[1..] {
493                text.push_str("::");
494                text.push_str(segment);
495            }
496            return text;
497        }
498        let imported: Option<Vec<String>> = imports
499            .iter()
500            .find(|(name, _)| Some(name) == segments.first())
501            .map(|(_, path)| path.iter().chain(&segments[1..]).cloned().collect());
502        match modules.resolve(module, imported.as_deref().unwrap_or(segments)) {
503            Some(target) => modules.absolute(&target).unwrap_or(written),
504            None => {
505                if !unresolved.contains(&written) {
506                    unresolved.push(written.clone());
507                }
508                written
509            }
510        }
511    }));
512    (text, unresolved)
513}
514
515/// Builds the model from what the files state.
516struct Builder {
517    source: Source,
518    model: AssemblyModel,
519    /// The types of the model by the text of their Rust path.
520    index: BTreeMap<String, usize>,
521    /// The unresolved paths already reported, by file, line and path.
522    reported: BTreeSet<(usize, usize, String)>,
523    statistics: Statistics,
524}
525
526impl Builder {
527    /// The site of `tokens` inside the declaration at `site`: their own line, unless the
528    /// declaration is read from an expansion.
529    fn site_of(&self, tokens: &[TokenTree], site: &Site) -> Site {
530        if site.expanded {
531            site.clone()
532        } else {
533            Site { line: first_line(tokens, site.line), ..site.clone() }
534        }
535    }
536
537    fn rust_type(&mut self, tokens: &[TokenTree], site: &Site, self_type: Option<&str>) -> RustType {
538        self.rust_type_in(tokens, site, self_type, &[])
539    }
540
541    /// The type `tokens` written in a function with the imports `imports`.
542    fn rust_type_in(&mut self, tokens: &[TokenTree], site: &Site, self_type: Option<&str>, imports: &[(String, Vec<String>)]) -> RustType {
543        let (text, unresolved) = normalise(&self.source.modules, site.module, tokens, self_type, imports);
544        self.statistics.type_texts += 1;
545        if !unresolved.is_empty() {
546            self.statistics.type_texts_unresolved += 1;
547            let site = self.site_of(tokens, site);
548            for path in &unresolved {
549                *self.statistics.unresolved_paths.entry(path.clone()).or_default() += 1;
550                if self.reported.insert((site.file, site.line, path.clone())) {
551                    let message = format!("the path `{path}` of the type `{text}` is not resolved by the `use` items and the modules of the crate: it stays as written");
552                    self.source.diagnostic(Severity::Warning, codes::UNRESOLVED, &site, message);
553                }
554            }
555        }
556        RustType { text, unresolved }
557    }
558
559    fn rust_types(&mut self, types: &[Tokens], site: &Site, self_type: Option<&str>) -> Vec<RustType> {
560        types.iter().map(|tokens| self.rust_type(tokens, site, self_type)).collect()
561    }
562
563    /// The absolute text of a path of an expression, when its head is resolved.
564    fn resolve_path(&self, module: usize, segments: &[String], self_type: Option<&str>) -> Option<String> {
565        if let (Some(self_type), Some("Self")) = (self_type, segments.first().map(String::as_str)) {
566            return Some(std::iter::once(self_type.to_string()).chain(segments[1..].iter().cloned()).collect::<Vec<_>>().join("::"));
567        }
568        let target = self.source.modules.resolve(module, segments)?;
569        self.source.modules.absolute(&target)
570    }
571
572    fn callable(&mut self, tokens: &[TokenTree], site: &Site, self_type: Option<&str>) -> CallableModel {
573        self.statistics.callables += 1;
574        match plain_path(tokens) {
575            Some(segments) => {
576                let resolved = self.resolve_path(site.module, &segments, self_type);
577                self.statistics.callable_paths += 1;
578                if resolved.is_some() {
579                    self.statistics.callable_paths_resolved += 1;
580                }
581                CallableModel { path: Some(text_of(tokens)), resolved, dereferenced: None }
582            }
583            None => {
584                let dereferenced = dereferenced_call(tokens).and_then(|segments| self.resolve_path(site.module, &segments, self_type));
585                CallableModel { path: None, resolved: None, dereferenced }
586            }
587        }
588    }
589
590    /// The type of the model the name `name` stands for in the module of `site`.
591    fn find_declared(&self, site: &Site, name: &str) -> Option<usize> {
592        let target = self.source.modules.resolve(site.module, &[name.to_string()])?;
593        match &target {
594            Target::Item { rest, .. } if rest.is_empty() => self.index.get(&self.source.modules.absolute(&target)?).copied(),
595            _ => None,
596        }
597    }
598
599    /// Adds a type to the model. A second type with the Rust type of a known one is added
600    /// and reported; the declarations that name the type go to the first.
601    fn add_type(&mut self, type_: TypeModel, site: &Site) {
602        let key = type_.rust_path.text.clone();
603        if self.index.contains_key(&key) {
604            let message = format!("`{key}` is declared twice: both declarations are in the model, and the declarations that name the type go to the first");
605            self.source.diagnostic(Severity::Warning, codes::DUPLICATE, site, message);
606        } else {
607            self.index.insert(key, self.model.types.len());
608        }
609        self.model.types.push(type_);
610    }
611
612    /// A class (`ferro_class!`) or a static type (`ferro_static_type!`): declared in the
613    /// module of the invocation.
614    fn class(&mut self, name: &str, base: Option<&Tokens>, kind: TypeKind, site: &Site) {
615        let module = self.source.modules.module_path(site.module);
616        let mut type_ = TypeModel::new(name, kind, RustType::resolved(&format!("::{module}::{name}")), &module);
617        type_.object_model = true;
618        type_.cfg = site.cfg.clone();
619        type_.base = base.map(|base| self.rust_type(base, site, None));
620        self.add_type(type_, site);
621    }
622
623    /// The types whose runtime type is implemented by hand: a class when the type
624    /// implements `ObjectType`, else a static type. Their base is not read.
625    fn hand_written(&mut self) {
626        let hand_written = std::mem::take(&mut self.source.hand_written);
627        for type_ in &hand_written {
628            let module = self.source.modules.module_path(type_.site.module);
629            let key = format!("::{module}::{}", type_.name);
630            let kind = if type_.class { TypeKind::Class } else { TypeKind::Static };
631            match self.index.get(&key) {
632                Some(index) => {
633                    self.model.types[*index].object_model = true;
634                    if type_.class {
635                        self.model.types[*index].kind = TypeKind::Class;
636                    }
637                }
638                None => {
639                    let mut model = TypeModel::new(&type_.name, kind, RustType::resolved(&key), &module);
640                    model.object_model = true;
641                    model.cfg = type_.site.cfg.clone();
642                    self.add_type(model, &type_.site);
643                    let message = format!("the runtime type of `{}` is implemented by hand (`impl StaticType`): the model has the type, and not its base", type_.name);
644                    self.source.diagnostic(Severity::Note, codes::HAND_WRITTEN, &type_.site, message);
645                }
646            }
647        }
648    }
649
650    /// Marks the classes and static types that are not in the lists of registered classes
651    /// of the crate. A crate without such a list, or with a list the scanner did not
652    /// read, has no marked type: nothing says which of its types it registers.
653    fn class_lists(&mut self) {
654        let lists = std::mem::take(&mut self.source.class_lists);
655        let (Some(first), true) = (lists.first(), lists.iter().all(|list| list.value.is_some())) else { return };
656        let mut listed: BTreeSet<usize> = BTreeSet::new();
657        for Located { site, value } in &lists {
658            for entry in value.iter().flatten() {
659                let path = plain_path(entry).and_then(|segments| self.resolve_path(site.module, &segments, None));
660                match path.and_then(|path| self.index.get(&path).copied()) {
661                    Some(index) => {
662                        listed.insert(index);
663                    }
664                    None => {
665                        let message = format!("the list of registered classes names `{}`, which is no class or static type of the crate the scanner read", text_of(entry));
666                        self.source.diagnostic(Severity::Warning, codes::OWNER, site, message);
667                    }
668                }
669            }
670        }
671        for index in 0..self.model.types.len() {
672            if self.model.types[index].object_model && !listed.contains(&index) {
673                self.model.types[index].unregistered = true;
674                let message = format!(
675                    "`{}` is declared and is not in the list of registered classes: the type is not known by its name or by its handle until an instance of it is created",
676                    self.model.types[index].rust_path.text
677                );
678                self.source.diagnostic(Severity::Note, codes::UNREGISTERED, &first.site, message);
679            }
680        }
681    }
682
683    fn markup_type(&mut self, kind: TypeKind, type_: &Tokens, is_dyn: bool, name: Option<&str>, body: &MarkupBody, site: &Site) {
684        // The metadata of a static type that owns attached properties belongs to its
685        // runtime type (`type_info: X`).
686        if let Some(type_info) = &body.type_info {
687            if let Some(index) = plain_path(type_info).filter(|path| path.len() == 1).and_then(|path| self.find_declared(site, &path[0])) {
688                let self_type = self.model.types[index].rust_path.text.clone();
689                self.apply_markup(index, body, site, &self_type);
690                return;
691            }
692        }
693        let mut rust_path = self.rust_type(type_, site, None);
694        if is_dyn {
695            rust_path.text = format!("dyn {}", rust_path.text);
696        }
697        let name = name.map(str::to_string).unwrap_or_else(|| text_of(type_));
698        let module = self.source.modules.module_path(site.module);
699        let self_type = rust_path.text.clone();
700        let mut model = TypeModel::new(&name, kind, rust_path, &module);
701        model.cfg = site.cfg.clone();
702        let index = self.model.types.len();
703        self.add_type(model, site);
704        self.apply_markup(index, body, site, &self_type);
705    }
706
707    fn markup_enum(&mut self, name: &str, flags: bool, members: &[EnumMember], body: &MarkupBody, site: &Site) {
708        let segments = [name.to_string()];
709        let target = self.source.modules.resolve(site.module, &segments);
710        // The name is read again as tokens, which have no line of the file: the line of a
711        // diagnostic is the one of the declaration.
712        let rust_path = self.rust_type(&tokens_of(name.parse().unwrap_or_default()), &Site { expanded: true, ..site.clone() }, None);
713        let declared = match &target {
714            Some(Target::Item { module, name, rest }) if rest.is_empty() => Some((*module, name.clone())),
715            _ => None,
716        };
717        let module = self.source.modules.module_path(site.module);
718        let mut model = TypeModel::new(name, TypeKind::Enum, rust_path, &module);
719        model.cfg = site.cfg.clone();
720        model.is_flags = flags;
721        for member in members {
722            let mut value = None;
723            let mut rust_value = None;
724            let mut rust_variant = None;
725            if flags {
726                let tokens = member.value.clone().unwrap_or_default();
727                rust_value = Some(text_of(&tokens));
728                // `Type::CONSTANT` of the `bitflags!` type the enumeration is: a constant of
729                // the `bitflags!` invocation or an associated constant of the type.
730                if let (Some(path), Some(declared)) = (plain_path(&tokens), &declared) {
731                    let same_type = path.len() == 2 && (path[0] == "Self" || self.source.modules.resolve(site.module, &path[..1]) == target);
732                    if same_type {
733                        value = self.source.member_value(declared, &path[1], 0);
734                    }
735                }
736            } else {
737                let variant = member.variant.clone().unwrap_or_else(|| member.name.clone());
738                // A variant, or an associated constant of the enumeration that names one
739                // (`pub const Enter: Key = Key::Return;`).
740                if let Some(declared) = &declared {
741                    value = self.source.member_value(declared, &variant, 0);
742                }
743                rust_variant = Some(variant);
744            }
745            model.enum_members.push(EnumMemberModel { name: member.name.clone(), rust_variant, rust_value, value });
746        }
747        let self_type = model.rust_path.text.clone();
748        let index = self.model.types.len();
749        self.add_type(model, site);
750        self.apply_markup(index, body, site, &self_type);
751    }
752
753    fn class_info(&mut self, name: &str, new: Option<&[TokenTree]>, interfaces: &[Tokens], markup: Option<&MarkupBody>, site: &Site) {
754        let Some(index) = self.find_declared(site, name) else {
755            let message = format!("`ferro_class_info!` of `{name}`: no class `{name}` is read (its `ferro_class!` is not in a file the scanner read, or the name is not resolved here): the declaration is not in the model");
756            self.source.diagnostic(Severity::Error, codes::OWNER, site, message);
757            return;
758        };
759        let self_type = self.model.types[index].rust_path.text.clone();
760        if let Some(new) = new {
761            let callable = self.callable(new, site, Some(&self_type));
762            self.model.types[index].default_constructor = Some(callable);
763        }
764        let interfaces = self.rust_types(interfaces, site, Some(&self_type));
765        self.model.types[index].interfaces.extend(interfaces);
766        if let Some(markup) = markup {
767            self.model.types[index].class_markup = true;
768            self.apply_markup(index, markup, site, &self_type);
769        }
770    }
771
772    /// Adds the parts of markup metadata to the type `index`.
773    fn apply_markup(&mut self, index: usize, body: &MarkupBody, site: &Site, self_type: &str) {
774        let self_type = Some(self_type);
775        let handles = self.rust_types(&body.handles, site, self_type);
776        let interfaces = self.rust_types(&body.interfaces, site, self_type);
777        let this = body.this.as_ref().map(|tokens| self.rust_type(tokens, site, self_type));
778        let base = body.base.as_ref().map(|tokens| self.rust_type(tokens, site, self_type));
779        let type_info = body.type_info.as_ref().map(|tokens| self.rust_type(tokens, site, self_type));
780        let notify = body.notify_property_changed.as_ref().map(|tokens| self.rust_type(tokens, site, self_type));
781        let generic = body.generic.as_ref().map(|(definition, arguments)| GenericModel { definition: definition.clone(), arguments: self.rust_types(arguments, site, self_type) });
782        let parse = body.parse.as_ref().map(|tokens| self.callable(tokens, site, self_type));
783        let constructors: Vec<MemberModel> =
784            body.constructors.iter().enumerate().map(|(position, raw)| self.member(raw, site, self_type, Some(format!("__markup_new_{position}")))).collect();
785        let methods: Vec<MemberModel> = body
786            .methods
787            .iter()
788            .enumerate()
789            .map(|(position, raw)| self.member(raw, site, self_type, Some(format!("__markup_{}_{position}", raw.name))))
790            .collect();
791        let fields: Vec<MemberModel> = body.fields.iter().map(|raw| self.member(raw, site, self_type, Some(format!("__markup_field_{}", raw.name)))).collect();
792        let events: Vec<MemberModel> = body.events.iter().map(|raw| self.member(raw, site, self_type, None)).collect();
793        let properties: Vec<PropertyModel> = body.properties.iter().map(|raw| self.property(raw, site, self_type, Some("__markup_"))).collect();
794        let static_properties: Vec<PropertyModel> = body.static_properties.iter().map(|raw| self.property(raw, site, self_type, Some("__markup_static_"))).collect();
795        let indexers: Vec<PropertyModel> = body.indexers.iter().map(|raw| self.property(raw, site, self_type, None)).collect();
796        let attributes = self.attributes(&body.attributes, site, self_type);
797        let property_attributes: Vec<(String, Vec<AttributeModel>)> =
798            body.property_attributes.iter().map(|(name, attributes)| (name.clone(), self.attributes(attributes, site, self_type))).collect();
799
800        let type_ = &mut self.model.types[index];
801        if body.namespace.is_some() {
802            type_.explicit_namespace = body.namespace.clone();
803        }
804        type_.handles.extend(handles);
805        type_.interfaces.extend(interfaces);
806        type_.this = this.or(type_.this.take());
807        // The base of a class of the object model is its base class; `base:` is for the others.
808        type_.base = type_.base.take().or(base);
809        type_.type_info = type_info.or(type_.type_info.take());
810        type_.notify_property_changed = notify.or(type_.notify_property_changed.take());
811        type_.generic = generic.or(type_.generic.take());
812        type_.parse = parse.or(type_.parse.take());
813        if body.content.is_some() {
814            type_.content_property = body.content.clone();
815        }
816        type_.constructors.extend(constructors);
817        type_.methods.extend(methods);
818        type_.fields.extend(fields);
819        type_.events.extend(events);
820        type_.properties.extend(properties);
821        type_.static_properties.extend(static_properties);
822        type_.indexers.extend(indexers);
823        type_.attributes.extend(attributes);
824        type_.property_attributes.extend(property_attributes);
825    }
826
827    fn member(&mut self, raw: &RawMember, site: &Site, self_type: Option<&str>, typed_function: Option<String>) -> MemberModel {
828        MemberModel {
829            name: raw.name.clone(),
830            parameters: raw.parameters.iter().map(|parameter| self.parameter(parameter, site, self_type)).collect(),
831            return_type: raw.return_type.as_ref().map(|tokens| self.rust_type(tokens, site, self_type)),
832            is_static: raw.is_static,
833            fallible: raw.fallible,
834            attributes: self.attributes(&raw.attributes, site, self_type),
835            callable: self.callable(&raw.callable, site, self_type),
836            typed_function,
837            call: None,
838        }
839    }
840
841    fn parameter(&mut self, raw: &RawParameter, site: &Site, self_type: Option<&str>) -> ParameterModel {
842        ParameterModel { name: raw.name.clone(), type_: self.rust_type(&raw.type_, site, self_type), attributes: self.attributes(&raw.attributes, site, self_type) }
843    }
844
845    /// `prefix` is the start of the names of the typed functions of the accessors
846    /// (`__markup_` for `__markup_get_Name`); nothing for an indexer, which has none.
847    fn property(&mut self, raw: &RawProperty, site: &Site, self_type: Option<&str>, prefix: Option<&str>) -> PropertyModel {
848        let accessor = |builder: &mut Self, raw_accessor: &Option<RawAccessor>, which: &str| {
849            raw_accessor.as_ref().map(|raw_accessor| AccessorModel {
850                fallible: raw_accessor.fallible,
851                callable: builder.callable(&raw_accessor.callable, site, self_type),
852                typed_function: prefix.map(|prefix| format!("{prefix}{which}_{}", raw.name)),
853                call: None,
854            })
855        };
856        let getter = accessor(self, &raw.getter, "get");
857        let setter = accessor(self, &raw.setter, "set");
858        PropertyModel {
859            name: raw.name.clone(),
860            parameters: raw.parameters.iter().map(|parameter| self.parameter(parameter, site, self_type)).collect(),
861            value_type: self.rust_type(&raw.type_, site, self_type),
862            getter,
863            setter,
864            attributes: self.attributes(&raw.attributes, site, self_type),
865        }
866    }
867
868    fn attributes(&mut self, raw: &[RawAttribute], site: &Site, self_type: Option<&str>) -> Vec<AttributeModel> {
869        raw.iter()
870            .map(|attribute| AttributeModel {
871                name: attribute.name.clone(),
872                arguments: attribute.arguments.iter().map(|value| self.value(value, site, self_type)).collect(),
873                properties: attribute.properties.iter().map(|(name, value)| (name.clone(), self.value(value, site, self_type))).collect(),
874            })
875            .collect()
876    }
877
878    fn value(&mut self, raw: &RawValue, site: &Site, self_type: Option<&str>) -> AttributeValueModel {
879        match raw {
880            RawValue::Null => AttributeValueModel::Null,
881            RawValue::Bool(value) => AttributeValueModel::Bool(*value),
882            RawValue::Int(value) => AttributeValueModel::Int(*value),
883            RawValue::Float(text) => AttributeValueModel::Float(text.clone()),
884            RawValue::Str(text) => AttributeValueModel::Str(text.clone()),
885            RawValue::Type(tokens) => AttributeValueModel::Type(self.rust_type(tokens, site, self_type)),
886            RawValue::Array(items) => AttributeValueModel::Array(items.iter().map(|item| self.value(item, site, self_type)).collect()),
887        }
888    }
889
890    /// The accessors of the registered properties of `owner`. `function_of` is the name of
891    /// the type whose `impl` block has them, when it is not the owner.
892    fn properties(&mut self, owner: &str, accessors: &[Accessor], function_of: Option<&str>, site: &Site) {
893        let Some(index) = self.find_declared(site, owner) else {
894            let message = format!("the properties of `{owner}`: no class or static type `{owner}` is read (no `ferro_class!` or `ferro_static_type!` of it in a file the scanner read): the properties are not in the model");
895            self.source.diagnostic(Severity::Error, codes::OWNER, site, message);
896            return;
897        };
898        let self_type = self.model.types[index].rust_path.text.clone();
899        let function_of = function_of.map(|name| self.resolve_path(site.module, &[name.to_string()], None).unwrap_or_else(|| name.to_string()));
900        for accessor in accessors {
901            let site = if site.expanded { site.clone() } else { Site { line: accessor.line, ..site.clone() } };
902            let Some((kind, value_type)) = property_type(&accessor.return_type) else {
903                let message = format!(
904                    "the accessor `{}` returns `{}`, which is not a property definition (`StyledProperty<T>`, `AttachedProperty<T>`, `DirectProperty<Owner, T>`): it is not in the model",
905                    accessor.name,
906                    text_of(&accessor.return_type)
907                );
908                self.source.diagnostic(Severity::Warning, codes::REGISTRATION, &site, message);
909                continue;
910            };
911            let value_type = self.rust_type(&value_type, &site, Some(&self_type));
912            let registration = read_registration(&accessor.body);
913            let mut model = RegisteredModel {
914                name: registration.name.clone(),
915                kind,
916                value_type,
917                owner: None,
918                host: None,
919                accessor: accessor.name.clone(),
920                function_of: function_of.clone(),
921                visibility: accessor.visibility.clone(),
922                registration: registration.kind,
923                source: None,
924                assign_binding: registration.assign_binding,
925                inherits: registration.inherits,
926                read_only: kind == RegisteredKind::Direct && registration.read_only,
927                added_owners: Vec::new(),
928            };
929            match registration.kind {
930                RegistrationModel::Declared => {
931                    // `register::<Owner, T>`, `register_attached::<Owner, Host, T>`.
932                    model.owner = registration.type_arguments.first().map(|tokens| self.rust_type(tokens, &site, Some(&self_type)));
933                    if kind == RegisteredKind::Attached && registration.type_arguments.len() == 3 {
934                        model.host = Some(self.rust_type(&registration.type_arguments[1], &site, Some(&self_type)));
935                    }
936                    if model.name.is_none() {
937                        let message = format!("the accessor `{}` registers a property whose name is not a string literal: the name is not in the model", accessor.name);
938                        self.source.diagnostic(Severity::Warning, codes::REGISTRATION, &site, message);
939                    }
940                }
941                RegistrationModel::AddedOwner | RegistrationModel::Alias => {
942                    model.source = registration.source.as_ref().map(|tokens| self.callable(tokens, &site, Some(&self_type)));
943                    // `.add_owner::<Owner>(..)`.
944                    model.owner = registration.added_owner.as_ref().map(|tokens| self.rust_type(tokens, &site, Some(&self_type)));
945                }
946                RegistrationModel::Unknown => {
947                    let message = format!(
948                        "the body of the accessor `{}` states no registration the scanner reads (`FerroProperty::register*::<..>(\"Name\", ..)`, `Other::accessor().add_owner*::<..>(..)`, `Other::accessor()`): the name of the property is not in the model",
949                        accessor.name
950                    );
951                    self.source.diagnostic(Severity::Warning, codes::REGISTRATION, &site, message);
952                }
953            }
954            self.model.types[index].registered.push(model);
955        }
956    }
957
958    /// Gives an added owner and an alias the name of the property its source declares,
959    /// and the declaration its added owners, where the source is in the scanned crate.
960    fn link_properties(&mut self) {
961        let mut names: Vec<(usize, usize, Option<String>)> = Vec::new();
962        let mut owners: Vec<(usize, usize, String)> = Vec::new();
963        // The accessors that are functions of another type than the one they are listed
964        // under, by their path.
965        let mut functions: BTreeMap<String, (usize, usize)> = BTreeMap::new();
966        for (type_index, type_) in self.model.types.iter().enumerate() {
967            for (position, registered) in type_.registered.iter().enumerate() {
968                if let Some(function_of) = &registered.function_of {
969                    functions.entry(format!("{function_of}::{}", registered.accessor)).or_insert((type_index, position));
970                }
971            }
972        }
973        for (type_index, type_) in self.model.types.iter().enumerate() {
974            for (position, registered) in type_.registered.iter().enumerate() {
975                if !matches!(registered.registration, RegistrationModel::AddedOwner | RegistrationModel::Alias) {
976                    continue;
977                }
978                let mut current = registered;
979                for _ in 0..8 {
980                    let Some((source_type, source_position)) = current.source.as_ref().and_then(|source| source.resolved.as_deref()).and_then(|path| self.locate(path, &functions)) else {
981                        break;
982                    };
983                    current = &self.model.types[source_type].registered[source_position];
984                    if current.registration == RegistrationModel::Declared {
985                        names.push((type_index, position, current.name.clone()));
986                        if registered.registration == RegistrationModel::AddedOwner {
987                            owners.push((source_type, source_position, type_.rust_path.text.clone()));
988                        }
989                        break;
990                    }
991                }
992            }
993        }
994        for (type_index, position, name) in names {
995            self.model.types[type_index].registered[position].name = name;
996        }
997        for (type_index, position, owner) in owners {
998            let added = &mut self.model.types[type_index].registered[position].added_owners;
999            if !added.contains(&owner) {
1000                added.push(owner);
1001            }
1002        }
1003    }
1004
1005    /// The type and the position of the accessor with the absolute path `path`
1006    /// (`::crate::decorator::Decorator::child_property`): the path of the type the
1007    /// accessor is a function of, which `functions` has for the accessors listed under
1008    /// another type.
1009    fn locate(&self, path: &str, functions: &BTreeMap<String, (usize, usize)>) -> Option<(usize, usize)> {
1010        if let Some(found) = functions.get(path) {
1011            return Some(*found);
1012        }
1013        let (type_path, accessor) = path.rsplit_once("::")?;
1014        let type_index = *self.index.get(type_path)?;
1015        let position = self.model.types[type_index].registered.iter().position(|registered| registered.accessor == accessor && registered.function_of.is_none())?;
1016        Some((type_index, position))
1017    }
1018
1019    /// The macros the crate exports that declare through a declaration macro, with their
1020    /// rules: what a crate built on this one expands their invocations with. A name the
1021    /// crate defines more than once is left out, as the crate itself does not expand it.
1022    fn exported_macros(&mut self) {
1023        let text = |tokens: &[proc_macro2::TokenTree]| tokens.iter().cloned().collect::<proc_macro2::TokenStream>().to_string();
1024        for (name, definitions) in &self.source.local_macros {
1025            let [definition] = definitions.as_slice() else { continue };
1026            if !definition.exported || !definition.declaring || !definition.site.cfg.is_empty() || definition.rules.is_empty() {
1027                continue;
1028            }
1029            let rules = definition.rules.iter().map(|(matcher, transcriber)| (text(matcher), text(transcriber))).collect();
1030            self.model.macros.push(MacroModel { name: name.clone(), rules });
1031        }
1032    }
1033
1034    /// The type aliases, the registered handles and the registered casts of the crate.
1035    fn aliases_and_handles(&mut self) {
1036        // An alias under a `cfg` condition, or declared twice, is left a name: which of
1037        // its declarations holds is decided by the build.
1038        let aliases = std::mem::take(&mut self.source.aliases);
1039        let mut declared: BTreeMap<String, usize> = BTreeMap::new();
1040        let paths: Vec<String> = aliases.iter().map(|alias| format!("::{}::{}", self.source.modules.module_path(alias.site.module), alias.value.0)).collect();
1041        for path in &paths {
1042            *declared.entry(path.clone()).or_default() += 1;
1043        }
1044        for (alias, path) in aliases.iter().zip(paths) {
1045            if !alias.site.cfg.is_empty() || declared.get(&path) != Some(&1) {
1046                continue;
1047            }
1048            // Its type is no type text of a declaration: it is not counted and not reported,
1049            // and an alias whose type is not resolved is left a name.
1050            let (text, unresolved) = normalise(&self.source.modules, alias.site.module, &alias.value.1, None, &[]);
1051            if unresolved.is_empty() {
1052                self.model.aliases.push(AliasModel { path, target: RustType { text, unresolved } });
1053            }
1054        }
1055        let handles = std::mem::take(&mut self.source.handles);
1056        for Located { site, value } in &handles {
1057            let handle = self.rust_type_in(&value.handle, site, None, &value.imports);
1058            let type_ = self.rust_type_in(&value.type_, site, None, &value.imports);
1059            self.model.handles.push(HandleModel { handle, type_ });
1060        }
1061        let casts = std::mem::take(&mut self.source.casts);
1062        for Located { site, value } in &casts {
1063            let from = self.rust_type_in(&value.from, site, None, &value.imports);
1064            let to = self.rust_type_in(&value.to, site, None, &value.imports);
1065            self.model.casts.push(CastModel { from, to });
1066        }
1067        let value_types = std::mem::take(&mut self.source.value_types);
1068        for Located { site, value } in &value_types {
1069            let types = value.types.iter().map(|type_| self.rust_type_in(type_, site, None, &value.imports)).collect();
1070            self.model.value_types.push(ValueTypeModel { registration: value.registration.clone(), types });
1071        }
1072        // A call the functions did not give the model: in the text and not read, or read
1073        // with a type the scanner did not resolve (a type parameter of the function).
1074        let mut unread: BTreeMap<String, i64> = BTreeMap::new();
1075        for (registration, (written, read)) in &self.source.value_calls {
1076            if written > read {
1077                *unread.entry(registration.clone()).or_default() += (*written - *read) as i64;
1078            }
1079        }
1080        let unresolved_casts = self.model.casts.iter().filter(|cast| !cast.from.is_resolved() || !cast.to.is_resolved()).count();
1081        if unresolved_casts > 0 {
1082            *unread.entry("cast".to_string()).or_default() += unresolved_casts as i64;
1083        }
1084        for registration in self.model.value_types.iter().filter(|registration| registration.types.iter().any(|type_| !type_.is_resolved())) {
1085            *unread.entry(registration.registration.clone()).or_default() += 1;
1086        }
1087        self.model.unread_value_types = unread.into_iter().collect();
1088    }
1089
1090    /// The value of a text of the assembly or of the namespace table: a literal, a text
1091    /// constant of the crate, or one of the constants of the base crate an assembly names.
1092    fn text_value(&self, text: &Text, module: usize) -> Result<String, String> {
1093        match text {
1094            Text::Literal(text) => Ok(text.clone()),
1095            Text::Other(written) => Err(written.clone()),
1096            Text::Path(segments) => {
1097                let written = segments.join("::");
1098                let value = match self.source.modules.resolve(module, segments) {
1099                    Some(Target::Item { module, name, rest }) if rest.is_empty() => self.source.constants.get(&(module, name)).cloned(),
1100                    Some(Target::Item { name, rest, .. }) if rest.len() == 1 => self.source.associated_constants.get(&(name, rest[0].clone())).cloned(),
1101                    Some(Target::External(segments)) if segments.first().map(String::as_str) == Some("ferroui_base") => {
1102                        let last: Vec<&str> = segments.iter().rev().take(2).map(String::as_str).collect();
1103                        match last.as_slice() {
1104                            ["FERRO_XML_NAMESPACE", ..] => Some(crate::FERRO_XML_NAMESPACE.to_string()),
1105                            ["CREATE_SOURCE_INFO", "MarkupAssembly"] => Some(crate::CREATE_SOURCE_INFO.to_string()),
1106                            ["USE_COMPILED_BINDINGS_BY_DEFAULT", "MarkupAssembly"] => {
1107                                Some(crate::USE_COMPILED_BINDINGS_BY_DEFAULT.to_string())
1108                            }
1109                            _ => None,
1110                        }
1111                    }
1112                    _ => None,
1113                };
1114                value.ok_or(written)
1115            }
1116        }
1117    }
1118
1119    /// The value of a text that may be absent; nothing when it is absent or not read.
1120    fn optional_text(&self, text: &Option<Text>, module: usize) -> Option<String> {
1121        text.as_ref().and_then(|text| self.text_value(text, module).ok())
1122    }
1123
1124    fn text_pairs(&mut self, pairs: &[(Text, Text)], site: &Site, what: &str) -> Vec<(String, String)> {
1125        let mut values = Vec::new();
1126        for (first, second) in pairs {
1127            match (self.text_value(first, site.module), self.text_value(second, site.module)) {
1128                (Ok(first), Ok(second)) => values.push((first, second)),
1129                (Err(written), _) | (_, Err(written)) => {
1130                    let message = format!("`{written}` in {what} is not a string literal or a text constant the scanner reads: the entry is left out");
1131                    self.source.diagnostic(Severity::Error, codes::ASSEMBLY, site, message);
1132                }
1133            }
1134        }
1135        values
1136    }
1137
1138    /// The assembly and the namespace table of the crate.
1139    fn assembly(&mut self, options: &ScanOptions) {
1140        let namespaces = std::mem::take(&mut self.source.namespaces);
1141        for Located { site, value } in &namespaces {
1142            let pairs = self.text_pairs(value, site, "the namespace table");
1143            self.model.namespaces.extend(pairs);
1144        }
1145        let assemblies = std::mem::take(&mut self.source.assemblies);
1146        let own = assemblies
1147            .iter()
1148            .find(|assembly| self.optional_text(&assembly.crate_name, assembly.site.module).as_deref() == Some(options.crate_name.as_str()))
1149            .or(if assemblies.len() == 1 { assemblies.first() } else { None });
1150        let Some(assembly) = own else {
1151            if let Some(other) = assemblies.first() {
1152                let message = format!("none of the {} `MarkupAssembly` values of the crate names the crate `{}`: the assembly is not read", assemblies.len(), options.crate_name);
1153                self.source.diagnostic(Severity::Error, codes::ASSEMBLY, &other.site, message);
1154            }
1155            return;
1156        };
1157        match self.optional_text(&assembly.name, assembly.site.module) {
1158            Some(name) => self.model.name = name,
1159            None => {
1160                let message = "the name of the `MarkupAssembly` is not a string literal or a text constant the scanner reads".to_string();
1161                self.source.diagnostic(Severity::Error, codes::ASSEMBLY, &assembly.site, message);
1162            }
1163        }
1164        let definitions = self.text_pairs(&assembly.xmlns_definitions, &assembly.site, "the xmlns definitions of the assembly");
1165        self.model.xmlns_definitions = definitions.into_iter().map(|(xml_namespace, namespace)| XmlnsDefinitionModel { xml_namespace, namespace }).collect();
1166        let prefixes = self.text_pairs(&assembly.xmlns_prefixes, &assembly.site, "the xmlns prefixes of the assembly");
1167        self.model.xmlns_prefixes = prefixes.into_iter().map(|(xml_namespace, prefix)| XmlnsPrefixModel { xml_namespace, prefix }).collect();
1168        self.model.metadata = self.text_pairs(&assembly.metadata, &assembly.site, "the metadata of the assembly");
1169    }
1170
1171    /// What the models of the crates the crate is built on give the model: every type
1172    /// text and every resolved callable is written with the paths of the declaring
1173    /// modules, and an owner added to (or an alias of) a property of one of those crates
1174    /// has the name of the property.
1175    fn link_dependencies(&mut self, dependencies: &[AssemblyModel]) {
1176        if dependencies.is_empty() {
1177            return;
1178        }
1179        let set = ModelSet::new(dependencies.to_vec());
1180        let mut canonical = |rust_type: &mut RustType| {
1181            if rust_type.text.contains("::") {
1182                rust_type.text = set.canonical(&rust_type.text);
1183            }
1184        };
1185        self.model.visit_types_mut(&mut canonical);
1186        for type_ in &mut self.model.types {
1187            type_.visit_types_mut(&mut canonical);
1188            type_.visit_callables_mut(&mut |callable: &mut CallableModel| {
1189                for path in [&mut callable.resolved, &mut callable.dereferenced] {
1190                    let canonical = path.as_deref().map(|path| set.canonical_path(path));
1191                    if canonical.is_some() {
1192                        *path = canonical;
1193                    }
1194                }
1195            });
1196        }
1197        // The names: an accessor is followed to the declaration of its property through
1198        // the accessors of this crate and of the other crates alike.
1199        let mut models = dependencies.to_vec();
1200        models.push(self.model.clone());
1201        let own = models.len() - 1;
1202        let set = ModelSet::new(models);
1203        let mut names: Vec<(usize, usize, String)> = Vec::new();
1204        for (type_index, type_) in set.models()[own].types.iter().enumerate() {
1205            for (position, registered) in type_.registered.iter().enumerate() {
1206                if registered.name.is_some() {
1207                    continue;
1208                }
1209                if let Some(name) = set.name_of(registered) {
1210                    names.push((type_index, position, name.to_string()));
1211                }
1212            }
1213        }
1214        for (type_index, position, name) in names {
1215            self.model.types[type_index].registered[position].name = Some(name);
1216        }
1217    }
1218
1219    /// The namespaces and the public paths of the types, the functions, the numbers.
1220    fn finish(mut self, dependencies: &[AssemblyModel]) -> Scan {
1221        let public = self.source.modules.public_paths();
1222        for index in 0..self.model.types.len() {
1223            let namespace = match &self.model.types[index].explicit_namespace {
1224                Some(namespace) => namespace.clone(),
1225                None => self.model.namespace_of_module(&self.model.types[index].module).to_string(),
1226            };
1227            let type_ = &mut self.model.types[index];
1228            type_.namespace = namespace;
1229            let path = type_.rust_path.text.strip_prefix("dyn ").unwrap_or(&type_.rust_path.text);
1230            type_.public_path = public.get(path).cloned();
1231        }
1232        // The paths the crate states for the instantiations of generic types it declares
1233        // metadata for.
1234        let generic_paths = std::mem::take(&mut self.source.generic_paths);
1235        for Located { site, value: (type_, path, contract) } in &generic_paths {
1236            let (text, _) = normalise(&self.source.modules, site.module, type_, None, &[]);
1237            let text = if *contract { format!("dyn {text}") } else { text };
1238            if let Some(declared) = self.model.types.iter_mut().find(|declared| declared.rust_path.text == text) {
1239                declared.stated_path = Some(format!("::{}", path.trim_start_matches("::")));
1240            }
1241        }
1242        self.model.exports = self.source.modules.export_table().into_iter().map(|(path, declared)| ExportModel { path, declared }).collect();
1243        self.aliases_and_handles();
1244        self.exported_macros();
1245        self.link_dependencies(dependencies);
1246
1247        // The public paths the crate states for the emitter, against the ones found here.
1248        let rust_paths = std::mem::take(&mut self.source.rust_paths);
1249        for Located { site, value } in &rust_paths {
1250            self.statistics.rust_paths_listed += 1;
1251            let stated = format!("::{}", std::iter::once(self.model.crate_name.as_str()).chain(value.iter().skip(1).map(String::as_str)).collect::<Vec<_>>().join("::"));
1252            let declared = self.source.modules.resolve(site.module, value).and_then(|target| self.source.modules.absolute(&target));
1253            let found = declared.as_ref().and_then(|declared| public.get(declared));
1254            if found == Some(&stated) {
1255                self.statistics.rust_paths_agreeing += 1;
1256            } else {
1257                let message = match found {
1258                    Some(found) => format!("`ferro_rust_paths!` states the public path `{stated}`; the scanner finds `{found}`"),
1259                    None => format!("`ferro_rust_paths!` states the public path `{stated}`; the scanner finds no public path of that item"),
1260                };
1261                self.source.diagnostic(Severity::Note, codes::PUBLIC_PATH, site, message);
1262            }
1263        }
1264
1265        let functions = std::mem::take(&mut self.source.functions);
1266        let functions: Vec<InherentFunction> = functions
1267            .into_iter()
1268            .map(|function| InherentFunction {
1269                owner: self.resolve_path(function.site.module, &[function.owner.clone()], None).unwrap_or(function.owner),
1270                name: function.name,
1271                visibility: function.visibility,
1272                receiver: function.receiver,
1273                parameters: function.parameters,
1274                return_type: function.return_type,
1275                declared_by: function.declared_by,
1276                module: self.source.modules.module_path(function.site.module),
1277                file: self.source.files[function.site.file].path.clone(),
1278                line: function.site.line,
1279            })
1280            .collect();
1281
1282        // The call form of every callable (9.5.3), and the public functions of the crate
1283        // for the crates built on it.
1284        let call_forms = crate::call_forms::choose(&mut self.model, &functions, &public, dependencies);
1285
1286        let mut statistics = self.statistics;
1287        statistics.call_forms = call_forms;
1288        statistics.files = self.source.files.len();
1289        statistics.modules = self.source.modules.modules.len();
1290        statistics.expanded = std::mem::take(&mut self.source.expanded);
1291        for file in &self.source.files {
1292            for (name, counts) in &file.invocations {
1293                statistics.invocations.entry(name.clone()).or_default().add(counts);
1294            }
1295        }
1296        for type_ in &self.model.types {
1297            let object_model = type_.handles.is_empty() && type_.rust_path.text == format!("::{}::{}", type_.module, type_.name);
1298            match type_.kind {
1299                TypeKind::Enum => statistics.enums += 1,
1300                TypeKind::Class if object_model => statistics.classes += 1,
1301                TypeKind::Static if object_model => statistics.static_types += 1,
1302                _ => statistics.markup_types += 1,
1303            }
1304            if type_.unregistered {
1305                statistics.unregistered += 1;
1306            }
1307            if type_.namespace.is_empty() {
1308                statistics.types_without_namespace += 1;
1309            }
1310            if type_.public_path.is_none() {
1311                statistics.types_without_public_path += 1;
1312            }
1313            statistics.constructors += type_.constructors.len();
1314            statistics.properties += type_.properties.len() + type_.static_properties.len();
1315            statistics.indexers += type_.indexers.len();
1316            statistics.methods += type_.methods.len();
1317            statistics.fields += type_.fields.len();
1318            statistics.events += type_.events.len();
1319            statistics.enum_members += type_.enum_members.len();
1320            statistics.enum_members_without_value += type_.enum_members.iter().filter(|member| member.value.is_none()).count();
1321            for registered in &type_.registered {
1322                statistics.registered += 1;
1323                match registered.kind {
1324                    RegisteredKind::Styled => statistics.styled += 1,
1325                    RegisteredKind::Direct => statistics.direct += 1,
1326                    RegisteredKind::Attached => statistics.attached += 1,
1327                }
1328                match registered.registration {
1329                    RegistrationModel::Declared => {}
1330                    RegistrationModel::AddedOwner => statistics.added_owners += 1,
1331                    RegistrationModel::Alias => statistics.aliases += 1,
1332                    RegistrationModel::Unknown => statistics.unknown_registrations += 1,
1333                }
1334                if registered.name.is_none() {
1335                    statistics.registered_without_name += 1;
1336                }
1337            }
1338        }
1339        let mut diagnostics = self.source.diagnostics;
1340        diagnostics.sort_by(|left, right| (&left.file, left.line, left.severity).cmp(&(&right.file, right.line, right.severity)));
1341        Scan { model: self.model, diagnostics, files: self.source.files, functions, statistics, modules: self.source.modules }
1342    }
1343}