i_slint_compiler/passes/
unique_id.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use crate::diagnostics::BuildDiagnostics;
5use crate::langtype::ElementType;
6use crate::object_tree::*;
7use smol_str::{format_smolstr, SmolStr, ToSmolStr};
8use std::collections::HashMap;
9use std::rc::Rc;
10
11/// This pass make sure that the id of the elements are unique
12///
13/// It currently does so by adding a number to the existing id
14pub fn assign_unique_id(doc: &Document) {
15    let mut count = 0;
16    doc.visit_all_used_components(|component| {
17        if !component.is_global() {
18            assign_unique_id_in_component(component, &mut count)
19        }
20    });
21    rename_globals(doc, count);
22}
23
24fn assign_unique_id_in_component(component: &Rc<Component>, count: &mut u32) {
25    recurse_elem_including_sub_components(component, &(), &mut |elem, _| {
26        *count += 1;
27        let mut elem_mut = elem.borrow_mut();
28        let old_id = if !elem_mut.id.is_empty() {
29            elem_mut.id.clone()
30        } else {
31            elem_mut.base_type.to_smolstr().to_ascii_lowercase().into()
32        };
33        elem_mut.id = format_smolstr!("{}-{}", old_id, count);
34
35        let enclosing = elem_mut.enclosing_component.upgrade().unwrap();
36        if Rc::ptr_eq(elem, &enclosing.root_element) {
37            for o in enclosing.optimized_elements.borrow().iter() {
38                *count += 1;
39                let mut elem_mut = o.borrow_mut();
40                elem_mut.id = format_smolstr!("optimized-{}-{}", elem_mut.id, count);
41            }
42        }
43    });
44}
45
46/// Give globals unique name
47fn rename_globals(doc: &Document, mut count: u32) {
48    for g in &doc.used_types.borrow().globals {
49        count += 1;
50        let mut root = g.root_element.borrow_mut();
51        if matches!(&root.base_type, ElementType::Builtin(_)) {
52            // builtin global keeps its name
53            root.id.clone_from(&g.id);
54        } else if let Some(s) = g.exported_global_names.borrow().first() {
55            root.id = s.to_smolstr();
56        } else if g.from_library.get() {
57            root.id = format_smolstr!("{}", g.id);
58        } else {
59            root.id = format_smolstr!("{}-{}", g.id, count);
60        }
61    }
62}
63
64/// Checks that all ids in the Component are unique
65pub fn check_unique_id(doc: &Document, diag: &mut BuildDiagnostics) {
66    for component in &doc.inner_components {
67        check_unique_id_in_component(component, diag);
68    }
69}
70
71fn check_unique_id_in_component(component: &Rc<Component>, diag: &mut BuildDiagnostics) {
72    struct SeenId {
73        element: ElementRc,
74        error_reported: bool,
75    }
76    let mut seen_ids: HashMap<SmolStr, SeenId> = HashMap::new();
77
78    recurse_elem(&component.root_element, &(), &mut |elem, _| {
79        let elem_bor = elem.borrow();
80        let id = &elem_bor.id;
81        if !id.is_empty() {
82            if let Some(other_loc) = seen_ids.get_mut(id) {
83                debug_assert!(!Rc::ptr_eq(&other_loc.element, elem));
84                let message = format!("duplicated element id '{id}'");
85                if !other_loc.error_reported {
86                    diag.push_error(message.clone(), &*other_loc.element.borrow());
87                    other_loc.error_reported = true;
88                }
89                diag.push_error(message, &*elem_bor);
90            } else {
91                seen_ids
92                    .insert(id.clone(), SeenId { element: elem.clone(), error_reported: false });
93            }
94        }
95    })
96}