1mod 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
85pub const DECLARATION_MACRO_NAMES: &[&str] = declarations::DECLARATION_MACROS;
88
89pub mod codes {
91 pub const FILE: &str = "FRN9001";
93 pub const FORM: &str = "FRN9010";
95 pub const POSITION: &str = "FRN9011";
97 pub const LOCAL_MACRO: &str = "FRN9012";
99 pub const HAND_WRITTEN: &str = "FRN9013";
101 pub const UNRESOLVED: &str = "FRN9020";
103 pub const REGISTRATION: &str = "FRN9021";
105 pub const DUPLICATE: &str = "FRN9022";
107 pub const OWNER: &str = "FRN9023";
109 pub const PUBLIC_PATH: &str = "FRN9024";
111 pub const UNREGISTERED: &str = "FRN9025";
113 pub const ASSEMBLY: &str = "FRN9030";
115}
116
117#[derive(Clone, Debug)]
119pub struct ScanOptions {
120 pub crate_name: String,
122 pub root: PathBuf,
124 pub assembly_name: Option<String>,
126 pub extern_crates: Vec<String>,
129 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 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 Error,
154 Warning,
156 Note,
158}
159
160#[derive(Clone, Debug, PartialEq, Eq)]
162pub struct Diagnostic {
163 pub severity: Severity,
164 pub code: &'static str,
166 pub file: PathBuf,
167 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#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
186pub struct InvocationCounts {
187 pub read: usize,
189 pub failed: usize,
191 pub in_macro_definitions: usize,
193 pub unread: usize,
196 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#[derive(Clone, Debug)]
216pub struct ScannedFile {
217 pub path: PathBuf,
218 pub module: String,
220 pub invocations: BTreeMap<String, InvocationCounts>,
222}
223
224#[derive(Clone, Debug, PartialEq, Eq)]
227pub struct InherentFunction {
228 pub owner: String,
231 pub name: String,
232 pub visibility: String,
234 pub receiver: bool,
236 pub parameters: Vec<String>,
238 pub return_type: Option<String>,
239 pub declared_by: Option<String>,
241 pub module: String,
243 pub file: PathBuf,
244 pub line: usize,
245}
246
247#[derive(Clone, Debug, Default, PartialEq, Eq)]
249pub struct Statistics {
250 pub files: usize,
251 pub modules: usize,
252 pub invocations: BTreeMap<String, InvocationCounts>,
254 pub expanded: BTreeMap<String, usize>,
256 pub classes: usize,
257 pub static_types: usize,
258 pub markup_types: usize,
259 pub enums: usize,
260 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 pub unknown_registrations: usize,
273 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 pub callables: usize,
284 pub callable_paths: usize,
285 pub callable_paths_resolved: usize,
286 pub call_forms: crate::call_forms::CallFormStatistics,
289 pub enum_members: usize,
290 pub enum_members_without_value: usize,
291 pub type_texts: usize,
293 pub type_texts_unresolved: usize,
294 pub unresolved_paths: BTreeMap<String, usize>,
296 pub rust_paths_listed: usize,
299 pub rust_paths_agreeing: usize,
300}
301
302pub 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 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 pub fn diagnostics_of(&self, severity: Severity) -> impl Iterator<Item = &Diagnostic> {
325 self.diagnostics.iter().filter(move |diagnostic| diagnostic.severity == severity)
326 }
327
328 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
424fn 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
442pub 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 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
483fn 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
515struct Builder {
517 source: Source,
518 model: AssemblyModel,
519 index: BTreeMap<String, usize>,
521 reported: BTreeSet<(usize, usize, String)>,
523 statistics: Statistics,
524}
525
526impl Builder {
527 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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(®istration.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 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 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 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) = ®istered.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 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 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 fn aliases_and_handles(&mut self) {
1036 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 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 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 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 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 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 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 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 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 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 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 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}