i_slint_compiler/
object_tree.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
4/*!
5 This module contains the intermediate representation of the code in the form of an object tree
6*/
7
8// cSpell: ignore qualname
9
10use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
11use crate::expression_tree::{self, BindingExpression, Callable, Expression, Unit};
12use crate::langtype::{
13    BuiltinElement, BuiltinPropertyDefault, Enumeration, EnumerationValue, Function, NativeClass,
14    Struct, Type,
15};
16use crate::langtype::{ElementType, PropertyLookupResult};
17use crate::layout::{LayoutConstraints, Orientation};
18use crate::namedreference::NamedReference;
19use crate::parser;
20use crate::parser::{syntax_nodes, SyntaxKind, SyntaxNode};
21use crate::typeloader::{ImportKind, ImportedTypes};
22use crate::typeregister::TypeRegister;
23use itertools::Either;
24use smol_str::{format_smolstr, SmolStr, ToSmolStr};
25use std::cell::{Cell, OnceCell, RefCell};
26use std::collections::btree_map::Entry;
27use std::collections::{BTreeMap, HashMap, HashSet};
28use std::fmt::Display;
29use std::path::PathBuf;
30use std::rc::{Rc, Weak};
31
32macro_rules! unwrap_or_continue {
33    ($e:expr ; $diag:expr) => {
34        match $e {
35            Some(x) => x,
36            None => {
37                debug_assert!($diag.has_errors()); // error should have been reported at parsing time
38                continue;
39            }
40        }
41    };
42}
43
44/// The full document (a complete file)
45#[derive(Default)]
46pub struct Document {
47    pub node: Option<syntax_nodes::Document>,
48    pub inner_components: Vec<Rc<Component>>,
49    pub inner_types: Vec<Type>,
50    pub local_registry: TypeRegister,
51    /// A list of paths to .ttf/.ttc files that are supposed to be registered on
52    /// startup for custom font use.
53    pub custom_fonts: Vec<(SmolStr, crate::parser::SyntaxToken)>,
54    pub exports: Exports,
55    pub imports: Vec<ImportedTypes>,
56
57    /// Map of resources that should be embedded in the generated code, indexed by their absolute path on
58    /// disk on the build system
59    pub embedded_file_resources:
60        RefCell<BTreeMap<SmolStr, crate::embedded_resources::EmbeddedResources>>,
61
62    #[cfg(feature = "bundle-translations")]
63    pub translation_builder: Option<crate::translations::TranslationsBuilder>,
64
65    /// The list of used extra types used recursively.
66    pub used_types: RefCell<UsedSubTypes>,
67
68    /// The popup_menu_impl
69    pub popup_menu_impl: Option<Rc<Component>>,
70}
71
72impl Document {
73    pub fn from_node(
74        node: syntax_nodes::Document,
75        imports: Vec<ImportedTypes>,
76        reexports: Exports,
77        diag: &mut BuildDiagnostics,
78        parent_registry: &Rc<RefCell<TypeRegister>>,
79    ) -> Self {
80        debug_assert_eq!(node.kind(), SyntaxKind::Document);
81
82        let mut local_registry = TypeRegister::new(parent_registry);
83        let mut inner_components = vec![];
84        let mut inner_types = vec![];
85
86        let mut process_component =
87            |n: syntax_nodes::Component,
88             diag: &mut BuildDiagnostics,
89             local_registry: &mut TypeRegister| {
90                let compo = Component::from_node(n, diag, local_registry);
91                if !local_registry.add(compo.clone()) {
92                    diag.push_warning(format!("Component '{}' is replacing a previously defined component with the same name", compo.id), &syntax_nodes::Component::from(compo.node.clone().unwrap()).DeclaredIdentifier());
93                }
94                inner_components.push(compo);
95            };
96        let process_struct = |n: syntax_nodes::StructDeclaration,
97                              diag: &mut BuildDiagnostics,
98                              local_registry: &mut TypeRegister,
99                              inner_types: &mut Vec<Type>| {
100            let rust_attributes = n.AtRustAttr().map(|child| vec![child.text().to_smolstr()]);
101            let ty = type_struct_from_node(
102                n.ObjectType(),
103                diag,
104                local_registry,
105                rust_attributes,
106                parser::identifier_text(&n.DeclaredIdentifier()),
107            );
108            assert!(matches!(ty, Type::Struct(_)));
109            if !local_registry.insert_type(ty.clone()) {
110                diag.push_warning(
111                    format!(
112                        "Struct '{ty}' is replacing a previously defined type with the same name"
113                    ),
114                    &n.DeclaredIdentifier(),
115                );
116            }
117            inner_types.push(ty);
118        };
119        let process_enum = |n: syntax_nodes::EnumDeclaration,
120                            diag: &mut BuildDiagnostics,
121                            local_registry: &mut TypeRegister,
122                            inner_types: &mut Vec<Type>| {
123            let Some(name) = parser::identifier_text(&n.DeclaredIdentifier()) else {
124                assert!(diag.has_errors());
125                return;
126            };
127            let mut existing_names = HashSet::new();
128            let values = n
129                .EnumValue()
130                .filter_map(|v| {
131                    let value = parser::identifier_text(&v)?;
132                    if value == name {
133                        diag.push_error(
134                            format!("Enum '{value}' can't have a value with the same name"),
135                            &v,
136                        );
137                        None
138                    } else if !existing_names.insert(crate::generator::to_pascal_case(&value)) {
139                        diag.push_error(format!("Duplicated enum value '{value}'"), &v);
140                        None
141                    } else {
142                        Some(value)
143                    }
144                })
145                .collect();
146            let en =
147                Enumeration { name: name.clone(), values, default_value: 0, node: Some(n.clone()) };
148            if en.values.is_empty() {
149                diag.push_error("Enums must have at least one value".into(), &n);
150            }
151
152            let ty = Type::Enumeration(Rc::new(en));
153            if !local_registry.insert_type_with_name(ty.clone(), name.clone()) {
154                diag.push_warning(
155                    format!(
156                        "Enum '{name}' is replacing a previously defined type with the same name"
157                    ),
158                    &n.DeclaredIdentifier(),
159                );
160            }
161            inner_types.push(ty);
162        };
163
164        for n in node.children() {
165            match n.kind() {
166                SyntaxKind::Component => process_component(n.into(), diag, &mut local_registry),
167                SyntaxKind::StructDeclaration => {
168                    process_struct(n.into(), diag, &mut local_registry, &mut inner_types)
169                }
170                SyntaxKind::EnumDeclaration => {
171                    process_enum(n.into(), diag, &mut local_registry, &mut inner_types)
172                }
173                SyntaxKind::ExportsList => {
174                    for n in n.children() {
175                        match n.kind() {
176                            SyntaxKind::Component => {
177                                process_component(n.into(), diag, &mut local_registry)
178                            }
179                            SyntaxKind::StructDeclaration => process_struct(
180                                n.into(),
181                                diag,
182                                &mut local_registry,
183                                &mut inner_types,
184                            ),
185                            SyntaxKind::EnumDeclaration => {
186                                process_enum(n.into(), diag, &mut local_registry, &mut inner_types)
187                            }
188                            _ => {}
189                        }
190                    }
191                }
192                _ => {}
193            };
194        }
195        let mut exports = Exports::from_node(&node, &inner_components, &local_registry, diag);
196        exports.add_reexports(reexports, diag);
197
198        let custom_fonts = imports
199            .iter()
200            .filter(|import| matches!(import.import_kind, ImportKind::FileImport))
201            .filter_map(|import| {
202                if import.file.ends_with(".ttc")
203                    || import.file.ends_with(".ttf")
204                    || import.file.ends_with(".otf")
205                {
206                    let token_path = import.import_uri_token.source_file.path();
207                    let import_file_path = PathBuf::from(import.file.clone());
208                    let import_file_path = crate::pathutils::join(token_path, &import_file_path)
209                        .unwrap_or(import_file_path);
210
211                    // Assume remote urls are valid, we need to load them at run-time (which we currently don't). For
212                    // local paths we should try to verify the existence and let the developer know ASAP.
213                    if crate::pathutils::is_url(&import_file_path)
214                        || crate::fileaccess::load_file(std::path::Path::new(&import_file_path))
215                            .is_some()
216                    {
217                        Some((import_file_path.to_string_lossy().into(), import.import_uri_token.clone()))
218                    } else {
219                        diag.push_error(
220                            format!("File \"{}\" not found", import.file),
221                            &import.import_uri_token,
222                        );
223                        None
224                    }
225                } else if import.file.ends_with(".slint") {
226                    diag.push_error("Import names are missing. Please specify which types you would like to import".into(), &import.import_uri_token.parent());
227                    None
228                } else {
229                    diag.push_error(
230                        format!("Unsupported foreign import \"{}\"", import.file),
231                        &import.import_uri_token,
232                    );
233                    None
234                }
235            })
236            .collect();
237
238        for local_compo in &inner_components {
239            if exports
240                .components_or_types
241                .iter()
242                .filter_map(|(_, exported_compo_or_type)| exported_compo_or_type.as_ref().left())
243                .any(|exported_compo| Rc::ptr_eq(exported_compo, local_compo))
244            {
245                continue;
246            }
247            // Don't warn about these for now - detecting their use can only be done after the resolve_expressions
248            // pass.
249            if local_compo.is_global() {
250                continue;
251            }
252            if !local_compo.used.get() {
253                diag.push_warning(
254                    "Component is neither used nor exported".into(),
255                    &local_compo.node.as_ref().map(|n| n.to_source_location()),
256                )
257            }
258        }
259
260        Document {
261            node: Some(node),
262            inner_components,
263            inner_types,
264            local_registry,
265            custom_fonts,
266            imports,
267            exports,
268            embedded_file_resources: Default::default(),
269            #[cfg(feature = "bundle-translations")]
270            translation_builder: None,
271            used_types: Default::default(),
272            popup_menu_impl: None,
273        }
274    }
275
276    pub fn exported_roots(&self) -> impl DoubleEndedIterator<Item = Rc<Component>> + '_ {
277        self.exports.iter().filter_map(|e| e.1.as_ref().left()).filter(|c| !c.is_global()).cloned()
278    }
279
280    /// This is the component that is going to be instantiated by the interpreter
281    pub fn last_exported_component(&self) -> Option<Rc<Component>> {
282        self.exports
283            .iter()
284            .filter_map(|e| Some((&e.0.name_ident, e.1.as_ref().left()?)))
285            .filter(|(_, c)| !c.is_global())
286            .max_by_key(|(n, _)| n.text_range().end())
287            .map(|(_, c)| c.clone())
288    }
289
290    /// visit all root and used component (including globals)
291    pub fn visit_all_used_components(&self, mut v: impl FnMut(&Rc<Component>)) {
292        let used_types = self.used_types.borrow();
293        for c in &used_types.sub_components {
294            v(c);
295        }
296        for c in self.exported_roots() {
297            v(&c);
298        }
299        for c in &used_types.globals {
300            v(c);
301        }
302        if let Some(c) = &self.popup_menu_impl {
303            v(c);
304        }
305    }
306}
307
308#[derive(Debug, Clone)]
309pub struct PopupWindow {
310    pub component: Rc<Component>,
311    pub x: NamedReference,
312    pub y: NamedReference,
313    pub close_policy: EnumerationValue,
314    pub parent_element: ElementRc,
315}
316
317#[derive(Debug, Clone)]
318pub struct Timer {
319    pub interval: NamedReference,
320    pub triggered: NamedReference,
321    pub running: NamedReference,
322    pub element: ElementWeak,
323}
324
325#[derive(Clone, Debug)]
326pub struct ChildrenInsertionPoint {
327    pub parent: ElementRc,
328    pub insertion_index: usize,
329    pub node: syntax_nodes::ChildrenPlaceholder,
330}
331
332/// Used sub types for a root component
333#[derive(Debug, Default)]
334pub struct UsedSubTypes {
335    /// All the globals used by the component and its children.
336    pub globals: Vec<Rc<Component>>,
337    /// All the structs and enums used by the component and its children.
338    pub structs_and_enums: Vec<Type>,
339    /// All the sub components use by this components and its children,
340    /// and the amount of time it is used
341    pub sub_components: Vec<Rc<Component>>,
342}
343
344#[derive(Debug, Default, Clone)]
345pub struct InitCode {
346    // Code from init callbacks collected from elements
347    pub constructor_code: Vec<Expression>,
348    /// Code to set the initial focus via forward-focus on the Window
349    pub focus_setting_code: Vec<Expression>,
350    /// Code to register embedded fonts.
351    pub font_registration_code: Vec<Expression>,
352
353    /// Code inserted from inlined components, ordered by offset of the place where it was inlined from. This way
354    /// we can preserve the order across multiple inlining passes.
355    pub inlined_init_code: BTreeMap<usize, Expression>,
356}
357
358impl InitCode {
359    pub fn iter(&self) -> impl Iterator<Item = &Expression> {
360        self.font_registration_code
361            .iter()
362            .chain(self.focus_setting_code.iter())
363            .chain(self.constructor_code.iter())
364            .chain(self.inlined_init_code.values())
365    }
366    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Expression> {
367        self.font_registration_code
368            .iter_mut()
369            .chain(self.focus_setting_code.iter_mut())
370            .chain(self.constructor_code.iter_mut())
371            .chain(self.inlined_init_code.values_mut())
372    }
373}
374
375/// A component is a type in the language which can be instantiated,
376/// Or is materialized for repeated expression.
377#[derive(Default, Debug)]
378pub struct Component {
379    pub node: Option<syntax_nodes::Component>,
380    pub id: SmolStr,
381    pub root_element: ElementRc,
382
383    /// The parent element within the parent component if this component represents a repeated element
384    pub parent_element: Weak<RefCell<Element>>,
385
386    /// List of elements that are not attached to the root anymore because they have been
387    /// optimized away, but their properties may still be in use
388    pub optimized_elements: RefCell<Vec<ElementRc>>,
389
390    /// The layout constraints of the root item
391    pub root_constraints: RefCell<LayoutConstraints>,
392
393    /// When creating this component and inserting "children", append them to the children of
394    /// the element pointer to by this field.
395    pub child_insertion_point: RefCell<Option<ChildrenInsertionPoint>>,
396
397    pub init_code: RefCell<InitCode>,
398
399    pub popup_windows: RefCell<Vec<PopupWindow>>,
400    pub timers: RefCell<Vec<Timer>>,
401    pub menu_item_tree: RefCell<Vec<Rc<Component>>>,
402
403    /// This component actually inherits PopupWindow (although that has been changed to a Window by the lower_popups pass)
404    pub inherits_popup_window: Cell<bool>,
405
406    /// The names under which this component should be accessible
407    /// if it is a global singleton and exported.
408    pub exported_global_names: RefCell<Vec<ExportedName>>,
409
410    /// True if this component is used as a sub-component by at least one other component.
411    pub used: Cell<bool>,
412
413    /// The list of properties (name and type) declared as private in the component.
414    /// This is used to issue better error in the generated code if the property is used.
415    pub private_properties: RefCell<Vec<(SmolStr, Type)>>,
416}
417
418impl Component {
419    pub fn from_node(
420        node: syntax_nodes::Component,
421        diag: &mut BuildDiagnostics,
422        tr: &TypeRegister,
423    ) -> Rc<Self> {
424        let mut child_insertion_point = None;
425        let is_legacy_syntax = node.child_token(SyntaxKind::ColonEqual).is_some();
426        let c = Component {
427            node: Some(node.clone()),
428            id: parser::identifier_text(&node.DeclaredIdentifier()).unwrap_or_default(),
429            root_element: Element::from_node(
430                node.Element(),
431                "root".into(),
432                if node.child_text(SyntaxKind::Identifier).is_some_and(|t| t == "global") {
433                    ElementType::Global
434                } else {
435                    ElementType::Error
436                },
437                &mut child_insertion_point,
438                is_legacy_syntax,
439                diag,
440                tr,
441            ),
442            child_insertion_point: RefCell::new(child_insertion_point),
443            ..Default::default()
444        };
445        let c = Rc::new(c);
446        let weak = Rc::downgrade(&c);
447        recurse_elem(&c.root_element, &(), &mut |e, _| {
448            e.borrow_mut().enclosing_component = weak.clone();
449            if let Some(qualified_id) =
450                e.borrow_mut().debug.first_mut().and_then(|x| x.qualified_id.as_mut())
451            {
452                *qualified_id = format_smolstr!("{}::{}", c.id, qualified_id);
453            }
454        });
455        c
456    }
457
458    /// This component is a global component introduced with the "global" keyword
459    pub fn is_global(&self) -> bool {
460        match &self.root_element.borrow().base_type {
461            ElementType::Global => true,
462            ElementType::Builtin(c) => c.is_global,
463            _ => false,
464        }
465    }
466
467    /// Returns the names of aliases to global singletons, exactly as
468    /// specified in the .slint markup (not normalized).
469    pub fn global_aliases(&self) -> Vec<SmolStr> {
470        self.exported_global_names
471            .borrow()
472            .iter()
473            .filter(|name| name.as_str() != self.root_element.borrow().id)
474            .map(|name| name.original_name())
475            .collect()
476    }
477
478    // Number of repeaters in this component, including sub-components
479    pub fn repeater_count(&self) -> u32 {
480        let mut count = 0;
481        recurse_elem(&self.root_element, &(), &mut |element, _| {
482            let element = element.borrow();
483            if let Some(sub_component) = element.sub_component() {
484                count += sub_component.repeater_count();
485            } else if element.repeated.is_some() {
486                count += 1;
487            }
488        });
489        count
490    }
491}
492
493#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
494pub enum PropertyVisibility {
495    #[default]
496    Private,
497    Input,
498    Output,
499    InOut,
500    /// for builtin properties that must be known at compile time and cannot be changed at runtime
501    Constexpr,
502    /// For builtin properties that are meant to just be bindings but cannot be read or written
503    /// (eg, Path's `commands`)
504    Fake,
505    /// For functions, not properties
506    Public,
507    Protected,
508}
509
510impl Display for PropertyVisibility {
511    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
512        match self {
513            PropertyVisibility::Private => f.write_str("private"),
514            PropertyVisibility::Input => f.write_str("input"),
515            PropertyVisibility::Output => f.write_str("output"),
516            PropertyVisibility::InOut => f.write_str("input output"),
517            PropertyVisibility::Constexpr => f.write_str("constexpr"),
518            PropertyVisibility::Public => f.write_str("public"),
519            PropertyVisibility::Protected => f.write_str("protected"),
520            PropertyVisibility::Fake => f.write_str("fake"),
521        }
522    }
523}
524
525#[derive(Clone, Debug, Default)]
526pub struct PropertyDeclaration {
527    pub property_type: Type,
528    pub node: Option<SyntaxNode>,
529    /// Tells if getter and setter will be added to expose in the native language API
530    pub expose_in_public_api: bool,
531    /// Public API property exposed as an alias: it shouldn't be generated but instead forward to the alias.
532    pub is_alias: Option<NamedReference>,
533    pub visibility: PropertyVisibility,
534    /// For function or callback: whether it is declared as `pure` (None for private function for which this has to be deduced)
535    pub pure: Option<bool>,
536}
537
538impl PropertyDeclaration {
539    // For diagnostics: return a node pointing to the type
540    pub fn type_node(&self) -> Option<SyntaxNode> {
541        let node = self.node.as_ref()?;
542        if let Some(x) = syntax_nodes::PropertyDeclaration::new(node.clone()) {
543            Some(x.Type().map_or_else(|| x.into(), |x| x.into()))
544        } else {
545            node.clone().into()
546        }
547    }
548}
549
550impl From<Type> for PropertyDeclaration {
551    fn from(ty: Type) -> Self {
552        PropertyDeclaration { property_type: ty, ..Self::default() }
553    }
554}
555
556#[derive(Debug, Clone, Copy, PartialEq)]
557pub enum TransitionDirection {
558    In,
559    Out,
560    InOut,
561}
562
563#[derive(Debug, Clone)]
564pub struct TransitionPropertyAnimation {
565    /// The state id as computed in lower_state
566    pub state_id: i32,
567    /// The direction of the transition
568    pub direction: TransitionDirection,
569    /// The content of the `animation` object
570    pub animation: ElementRc,
571}
572
573impl TransitionPropertyAnimation {
574    /// Return an expression which returns a boolean which is true if the transition is active.
575    /// The state argument is an expression referencing the state property of type StateInfo
576    pub fn condition(&self, state: Expression) -> Expression {
577        match self.direction {
578            TransitionDirection::In => Expression::BinaryExpression {
579                lhs: Box::new(Expression::StructFieldAccess {
580                    base: Box::new(state),
581                    name: "current-state".into(),
582                }),
583                rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
584                op: '=',
585            },
586            TransitionDirection::Out => Expression::BinaryExpression {
587                lhs: Box::new(Expression::StructFieldAccess {
588                    base: Box::new(state),
589                    name: "previous-state".into(),
590                }),
591                rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
592                op: '=',
593            },
594            TransitionDirection::InOut => Expression::BinaryExpression {
595                lhs: Box::new(Expression::BinaryExpression {
596                    lhs: Box::new(Expression::StructFieldAccess {
597                        base: Box::new(state.clone()),
598                        name: "current-state".into(),
599                    }),
600                    rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
601                    op: '=',
602                }),
603                rhs: Box::new(Expression::BinaryExpression {
604                    lhs: Box::new(Expression::StructFieldAccess {
605                        base: Box::new(state),
606                        name: "previous-state".into(),
607                    }),
608                    rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
609                    op: '=',
610                }),
611                op: '|',
612            },
613        }
614    }
615}
616
617#[derive(Debug)]
618pub enum PropertyAnimation {
619    Static(ElementRc),
620    Transition { state_ref: Expression, animations: Vec<TransitionPropertyAnimation> },
621}
622
623impl Clone for PropertyAnimation {
624    fn clone(&self) -> Self {
625        fn deep_clone(e: &ElementRc) -> ElementRc {
626            let e = e.borrow();
627            debug_assert!(e.children.is_empty());
628            debug_assert!(e.property_declarations.is_empty());
629            debug_assert!(e.states.is_empty() && e.transitions.is_empty());
630            Rc::new(RefCell::new(Element {
631                id: e.id.clone(),
632                base_type: e.base_type.clone(),
633                bindings: e.bindings.clone(),
634                property_analysis: e.property_analysis.clone(),
635                enclosing_component: e.enclosing_component.clone(),
636                repeated: None,
637                debug: e.debug.clone(),
638                ..Default::default()
639            }))
640        }
641        match self {
642            PropertyAnimation::Static(e) => PropertyAnimation::Static(deep_clone(e)),
643            PropertyAnimation::Transition { state_ref, animations } => {
644                PropertyAnimation::Transition {
645                    state_ref: state_ref.clone(),
646                    animations: animations
647                        .iter()
648                        .map(|t| TransitionPropertyAnimation {
649                            state_id: t.state_id,
650                            direction: t.direction,
651                            animation: deep_clone(&t.animation),
652                        })
653                        .collect(),
654                }
655            }
656        }
657    }
658}
659
660/// Map the accessibility property (eg "accessible-role", "accessible-label") to its named reference
661#[derive(Default, Clone)]
662pub struct AccessibilityProps(pub BTreeMap<String, NamedReference>);
663
664#[derive(Clone, Debug)]
665pub struct GeometryProps {
666    pub x: NamedReference,
667    pub y: NamedReference,
668    pub width: NamedReference,
669    pub height: NamedReference,
670}
671
672impl GeometryProps {
673    pub fn new(element: &ElementRc) -> Self {
674        Self {
675            x: NamedReference::new(element, SmolStr::new_static("x")),
676            y: NamedReference::new(element, SmolStr::new_static("y")),
677            width: NamedReference::new(element, SmolStr::new_static("width")),
678            height: NamedReference::new(element, SmolStr::new_static("height")),
679        }
680    }
681}
682
683pub type BindingsMap = BTreeMap<SmolStr, RefCell<BindingExpression>>;
684
685#[derive(Clone)]
686pub struct ElementDebugInfo {
687    // The id qualified with the enclosing component name. Given `foo := Bar {}` this is `EnclosingComponent::foo`
688    pub qualified_id: Option<SmolStr>,
689    pub type_name: String,
690    // Hold an id for each element that is unique during this build, based on the source file and
691    // the offset of the `LBrace` token.
692    //
693    // This helps to cross-reference the element in the different build stages the LSP has to deal with.
694    pub element_hash: u64,
695    pub node: syntax_nodes::Element,
696    // Field to indicate whether this element was a layout that had
697    // been lowered into a rectangle in the lower_layouts pass.
698    pub layout: Option<crate::layout::Layout>,
699    /// Set to true if the ElementDebugInfo following this one in the debug vector
700    /// in Element::debug is the last one and the next entry belongs to an other element.
701    /// This can happen as a result of rectangle optimization, for example.
702    pub element_boundary: bool,
703}
704
705impl ElementDebugInfo {
706    // Returns a comma separate string that encodes the element type name (`Rectangle`, `MyButton`, etc.)
707    // and the qualified id (`SurroundingComponent::my-id`).
708    fn encoded_element_info(&self) -> String {
709        let mut info = self.type_name.clone();
710        info.push(',');
711        if let Some(id) = self.qualified_id.as_ref() {
712            info.push_str(id);
713        }
714        info
715    }
716}
717
718/// An Element is an instantiation of a Component
719#[derive(Default)]
720pub struct Element {
721    /// The id as named in the original .slint file.
722    ///
723    /// Note that it can only be used for lookup before inlining.
724    /// After inlining there can be duplicated id in the component.
725    /// The id are then re-assigned unique id in the assign_id pass
726    pub id: SmolStr,
727    //pub base: QualifiedTypeName,
728    pub base_type: ElementType,
729    /// Currently contains also the callbacks. FIXME: should that be changed?
730    pub bindings: BindingsMap,
731    pub change_callbacks: BTreeMap<SmolStr, RefCell<Vec<Expression>>>,
732    pub property_analysis: RefCell<HashMap<SmolStr, PropertyAnalysis>>,
733
734    pub children: Vec<ElementRc>,
735    /// The component which contains this element.
736    pub enclosing_component: Weak<Component>,
737
738    pub property_declarations: BTreeMap<SmolStr, PropertyDeclaration>,
739
740    /// Main owner for a reference to a property.
741    pub named_references: crate::namedreference::NamedReferenceContainer,
742
743    /// This element is part of a `for <xxx> in <model>`:
744    pub repeated: Option<RepeatedElementInfo>,
745    /// This element is a placeholder to embed an Component at
746    pub is_component_placeholder: bool,
747
748    pub states: Vec<State>,
749    pub transitions: Vec<Transition>,
750
751    /// true when this item's geometry is handled by a layout
752    pub child_of_layout: bool,
753    /// The property pointing to the layout info. `(horizontal, vertical)`
754    pub layout_info_prop: Option<(NamedReference, NamedReference)>,
755    /// Whether we have `preferred-{width,height}: 100%`
756    pub default_fill_parent: (bool, bool),
757
758    pub accessibility_props: AccessibilityProps,
759
760    /// Reference to the property.
761    /// This is always initialized from the element constructor, but is Option because it references itself
762    pub geometry_props: Option<GeometryProps>,
763
764    /// true if this Element is the fake Flickable viewport
765    pub is_flickable_viewport: bool,
766
767    /// true if this Element may have a popup as child meaning it cannot be optimized
768    /// because the popup references it.
769    pub has_popup_child: bool,
770
771    /// This is the component-local index of this item in the item tree array.
772    /// It is generated after the last pass and before the generators run.
773    pub item_index: OnceCell<u32>,
774    /// the index of the first children in the tree, set with item_index
775    pub item_index_of_first_children: OnceCell<u32>,
776
777    /// True when this element is in a component was declared with the `:=` symbol instead of the `component` keyword
778    pub is_legacy_syntax: bool,
779
780    /// How many times the element was inlined
781    pub inline_depth: i32,
782
783    /// Debug information about this element.
784    ///
785    /// There can be several in case of inlining or optimization (child merged into their parent).
786    ///
787    /// The order in the list is first the parent, and then the removed children.
788    pub debug: Vec<ElementDebugInfo>,
789}
790
791impl Spanned for Element {
792    fn span(&self) -> crate::diagnostics::Span {
793        self.debug.first().map(|n| n.node.span()).unwrap_or_default()
794    }
795
796    fn source_file(&self) -> Option<&crate::diagnostics::SourceFile> {
797        self.debug.first().map(|n| &n.node.source_file)
798    }
799}
800
801impl core::fmt::Debug for Element {
802    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
803        pretty_print(f, self, 0)
804    }
805}
806
807pub fn pretty_print(
808    f: &mut impl std::fmt::Write,
809    e: &Element,
810    indentation: usize,
811) -> std::fmt::Result {
812    if let Some(repeated) = &e.repeated {
813        write!(f, "for {}[{}] in ", repeated.model_data_id, repeated.index_id)?;
814        expression_tree::pretty_print(f, &repeated.model)?;
815        write!(f, ":")?;
816        if let ElementType::Component(base) = &e.base_type {
817            write!(f, "(base) ")?;
818            if base.parent_element.upgrade().is_some() {
819                pretty_print(f, &base.root_element.borrow(), indentation)?;
820                return Ok(());
821            }
822        }
823    }
824    if e.is_component_placeholder {
825        write!(f, "/* Component Placeholder */ ")?;
826    }
827    writeln!(f, "{} := {} {{  /* {} */", e.id, e.base_type, e.element_infos())?;
828    let mut indentation = indentation + 1;
829    macro_rules! indent {
830        () => {
831            for _ in 0..indentation {
832                write!(f, "   ")?
833            }
834        };
835    }
836    for (name, ty) in &e.property_declarations {
837        indent!();
838        if let Some(alias) = &ty.is_alias {
839            writeln!(f, "alias<{}> {} <=> {:?};", ty.property_type, name, alias)?
840        } else {
841            writeln!(f, "property<{}> {};", ty.property_type, name)?
842        }
843    }
844    for (name, expr) in &e.bindings {
845        let expr = expr.borrow();
846        indent!();
847        write!(f, "{name}: ")?;
848        expression_tree::pretty_print(f, &expr.expression)?;
849        if expr.analysis.as_ref().is_some_and(|a| a.is_const) {
850            write!(f, "/*const*/")?;
851        }
852        writeln!(f, ";")?;
853        //writeln!(f, "; /*{}*/", expr.priority)?;
854        if let Some(anim) = &expr.animation {
855            indent!();
856            writeln!(f, "animate {name} {anim:?}")?;
857        }
858        for nr in &expr.two_way_bindings {
859            indent!();
860            writeln!(f, "{name} <=> {nr:?};")?;
861        }
862    }
863    for (name, ch) in &e.change_callbacks {
864        for ex in &*ch.borrow() {
865            indent!();
866            write!(f, "changed {name} => ")?;
867            expression_tree::pretty_print(f, ex)?;
868            writeln!(f)?;
869        }
870    }
871    if !e.states.is_empty() {
872        indent!();
873        writeln!(f, "states {:?}", e.states)?;
874    }
875    if !e.transitions.is_empty() {
876        indent!();
877        writeln!(f, "transitions {:?} ", e.transitions)?;
878    }
879    for c in &e.children {
880        indent!();
881        pretty_print(f, &c.borrow(), indentation)?
882    }
883    if let Some(g) = &e.geometry_props {
884        indent!();
885        writeln!(f, "geometry {g:?} ")?;
886    }
887
888    /*if let Type::Component(base) = &e.base_type {
889        pretty_print(f, &c.borrow(), indentation)?
890    }*/
891    indentation -= 1;
892    indent!();
893    writeln!(f, "}}")
894}
895
896#[derive(Clone, Default, Debug)]
897pub struct PropertyAnalysis {
898    /// true if somewhere in the code, there is an expression that changes this property with an assignment
899    pub is_set: bool,
900
901    /// True if this property might be set from a different component.
902    pub is_set_externally: bool,
903
904    /// true if somewhere in the code, an expression is reading this property
905    /// Note: currently this is only set in the binding analysis pass
906    pub is_read: bool,
907
908    /// true if this property is read from another component
909    pub is_read_externally: bool,
910
911    /// True if the property is linked to another property that is read only. That property becomes read-only
912    pub is_linked_to_read_only: bool,
913
914    /// True if this property is linked to another property
915    pub is_linked: bool,
916}
917
918impl PropertyAnalysis {
919    /// Merge analysis from base element for inlining
920    ///
921    /// Contrary to `merge`, we don't keep the external uses because
922    /// they should come from us
923    pub fn merge_with_base(&mut self, other: &PropertyAnalysis) {
924        self.is_set |= other.is_set;
925        self.is_read |= other.is_read;
926    }
927
928    /// Merge the analysis
929    pub fn merge(&mut self, other: &PropertyAnalysis) {
930        self.is_set |= other.is_set;
931        self.is_read |= other.is_read;
932        self.is_read_externally |= other.is_read_externally;
933        self.is_set_externally |= other.is_set_externally;
934    }
935
936    /// Return true if it is read or set or used in any way
937    pub fn is_used(&self) -> bool {
938        self.is_read || self.is_read_externally || self.is_set || self.is_set_externally
939    }
940}
941
942#[derive(Debug, Clone)]
943pub struct ListViewInfo {
944    pub viewport_y: NamedReference,
945    pub viewport_height: NamedReference,
946    pub viewport_width: NamedReference,
947    /// The ListView's inner visible height (not counting eventual scrollbar)
948    pub listview_height: NamedReference,
949    /// The ListView's inner visible width (not counting eventual scrollbar)
950    pub listview_width: NamedReference,
951}
952
953#[derive(Debug, Clone)]
954/// If the parent element is a repeated element, this has information about the models
955pub struct RepeatedElementInfo {
956    pub model: Expression,
957    pub model_data_id: SmolStr,
958    pub index_id: SmolStr,
959    /// A conditional element is just a for whose model is a boolean expression
960    ///
961    /// When this is true, the model is of type boolean instead of Model
962    pub is_conditional_element: bool,
963    /// When the for is the delegate of a ListView
964    pub is_listview: Option<ListViewInfo>,
965}
966
967pub type ElementRc = Rc<RefCell<Element>>;
968pub type ElementWeak = Weak<RefCell<Element>>;
969
970impl Element {
971    pub fn make_rc(self) -> ElementRc {
972        let r = ElementRc::new(RefCell::new(self));
973        let g = GeometryProps::new(&r);
974        r.borrow_mut().geometry_props = Some(g);
975        r
976    }
977
978    pub fn from_node(
979        node: syntax_nodes::Element,
980        id: SmolStr,
981        parent_type: ElementType,
982        component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
983        is_legacy_syntax: bool,
984        diag: &mut BuildDiagnostics,
985        tr: &TypeRegister,
986    ) -> ElementRc {
987        let base_type = if let Some(base_node) = node.QualifiedName() {
988            let base = QualifiedTypeName::from_node(base_node.clone());
989            let base_string = base.to_smolstr();
990            match parent_type.lookup_type_for_child_element(&base_string, tr) {
991                Ok(ElementType::Component(c)) if c.is_global() => {
992                    diag.push_error(
993                        "Cannot create an instance of a global component".into(),
994                        &base_node,
995                    );
996                    ElementType::Error
997                }
998                Ok(ty) => ty,
999                Err(err) => {
1000                    diag.push_error(err, &base_node);
1001                    ElementType::Error
1002                }
1003            }
1004        } else if parent_type == ElementType::Global {
1005            // This must be a global component it can only have properties and callback
1006            let mut error_on = |node: &dyn Spanned, what: &str| {
1007                diag.push_error(format!("A global component cannot have {what}"), node);
1008            };
1009            node.SubElement().for_each(|n| error_on(&n, "sub elements"));
1010            node.RepeatedElement().for_each(|n| error_on(&n, "sub elements"));
1011            if let Some(n) = node.ChildrenPlaceholder() {
1012                error_on(&n, "sub elements");
1013            }
1014            node.PropertyAnimation().for_each(|n| error_on(&n, "animations"));
1015            node.States().for_each(|n| error_on(&n, "states"));
1016            node.Transitions().for_each(|n| error_on(&n, "transitions"));
1017            node.CallbackDeclaration().for_each(|cb| {
1018                if parser::identifier_text(&cb.DeclaredIdentifier()).is_some_and(|s| s == "init") {
1019                    error_on(&cb, "an 'init' callback")
1020                }
1021            });
1022            node.CallbackConnection().for_each(|cb| {
1023                if parser::identifier_text(&cb).is_some_and(|s| s == "init") {
1024                    error_on(&cb, "an 'init' callback")
1025                }
1026            });
1027
1028            ElementType::Global
1029        } else if parent_type != ElementType::Error {
1030            // This should normally never happen because the parser does not allow for this
1031            assert!(diag.has_errors());
1032            return ElementRc::default();
1033        } else {
1034            tr.empty_type()
1035        };
1036        // This isn't truly qualified yet, the enclosing component is added at the end of Component::from_node
1037        let qualified_id = (!id.is_empty()).then(|| id.clone());
1038        if let ElementType::Component(c) = &base_type {
1039            c.used.set(true);
1040        }
1041        let type_name = base_type
1042            .type_name()
1043            .filter(|_| base_type != tr.empty_type())
1044            .unwrap_or_default()
1045            .to_string();
1046        let mut r = Element {
1047            id,
1048            base_type,
1049            debug: vec![ElementDebugInfo {
1050                qualified_id,
1051                element_hash: 0,
1052                type_name,
1053                node: node.clone(),
1054                layout: None,
1055                element_boundary: false,
1056            }],
1057            is_legacy_syntax,
1058            ..Default::default()
1059        };
1060
1061        for prop_decl in node.PropertyDeclaration() {
1062            let prop_type = prop_decl
1063                .Type()
1064                .map(|type_node| type_from_node(type_node, diag, tr))
1065                // Type::Void is used for two way bindings without type specified
1066                .unwrap_or(Type::InferredProperty);
1067
1068            let unresolved_prop_name =
1069                unwrap_or_continue!(parser::identifier_text(&prop_decl.DeclaredIdentifier()); diag);
1070            let PropertyLookupResult {
1071                resolved_name: prop_name,
1072                property_type: maybe_existing_prop_type,
1073                ..
1074            } = r.lookup_property(&unresolved_prop_name);
1075            match maybe_existing_prop_type {
1076                Type::Callback { .. } => {
1077                    diag.push_error(
1078                        format!("Cannot declare property '{prop_name}' when a callback with the same name exists"),
1079                        &prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(),
1080                    );
1081                    continue;
1082                }
1083                Type::Function { .. } => {
1084                    diag.push_error(
1085                        format!("Cannot declare property '{prop_name}' when a function with the same name exists"),
1086                        &prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(),
1087                    );
1088                    continue;
1089                }
1090                Type::Invalid => {} // Ok to proceed with a new declaration
1091                _ => {
1092                    diag.push_error(
1093                        format!("Cannot override property '{unresolved_prop_name}'"),
1094                        &prop_decl
1095                            .DeclaredIdentifier()
1096                            .child_token(SyntaxKind::Identifier)
1097                            .unwrap(),
1098                    );
1099                    continue;
1100                }
1101            }
1102
1103            let mut visibility = None;
1104            for token in prop_decl.children_with_tokens() {
1105                if token.kind() != SyntaxKind::Identifier {
1106                    continue;
1107                }
1108                match (token.as_token().unwrap().text(), visibility) {
1109                    ("in", None) => visibility = Some(PropertyVisibility::Input),
1110                    ("in", Some(_)) => diag.push_error("Extra 'in' keyword".into(), &token),
1111                    ("out", None) => visibility = Some(PropertyVisibility::Output),
1112                    ("out", Some(_)) => diag.push_error("Extra 'out' keyword".into(), &token),
1113                    ("in-out" | "in_out", None) => visibility = Some(PropertyVisibility::InOut),
1114                    ("in-out" | "in_out", Some(_)) => {
1115                        diag.push_error("Extra 'in-out' keyword".into(), &token)
1116                    }
1117                    ("private", None) => visibility = Some(PropertyVisibility::Private),
1118                    ("private", Some(_)) => {
1119                        diag.push_error("Extra 'private' keyword".into(), &token)
1120                    }
1121                    _ => (),
1122                }
1123            }
1124            let visibility = visibility.unwrap_or({
1125                if is_legacy_syntax {
1126                    PropertyVisibility::InOut
1127                } else {
1128                    PropertyVisibility::Private
1129                }
1130            });
1131
1132            r.property_declarations.insert(
1133                prop_name.clone().into(),
1134                PropertyDeclaration {
1135                    property_type: prop_type,
1136                    node: Some(prop_decl.clone().into()),
1137                    visibility,
1138                    ..Default::default()
1139                },
1140            );
1141
1142            if let Some(csn) = prop_decl.BindingExpression() {
1143                match r.bindings.entry(prop_name.clone().into()) {
1144                    Entry::Vacant(e) => {
1145                        e.insert(BindingExpression::new_uncompiled(csn.into()).into());
1146                    }
1147                    Entry::Occupied(_) => {
1148                        diag.push_error(
1149                            "Duplicated property binding".into(),
1150                            &prop_decl.DeclaredIdentifier(),
1151                        );
1152                    }
1153                }
1154            }
1155            if let Some(csn) = prop_decl.TwoWayBinding() {
1156                if r.bindings
1157                    .insert(prop_name.into(), BindingExpression::new_uncompiled(csn.into()).into())
1158                    .is_some()
1159                {
1160                    diag.push_error(
1161                        "Duplicated property binding".into(),
1162                        &prop_decl.DeclaredIdentifier(),
1163                    );
1164                }
1165            }
1166        }
1167
1168        r.parse_bindings(
1169            node.Binding().filter_map(|b| {
1170                Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into()))
1171            }),
1172            is_legacy_syntax,
1173            diag,
1174        );
1175        r.parse_bindings(
1176            node.TwoWayBinding()
1177                .filter_map(|b| Some((b.child_token(SyntaxKind::Identifier)?, b.into()))),
1178            is_legacy_syntax,
1179            diag,
1180        );
1181
1182        apply_default_type_properties(&mut r);
1183
1184        for sig_decl in node.CallbackDeclaration() {
1185            let name =
1186                unwrap_or_continue!(parser::identifier_text(&sig_decl.DeclaredIdentifier()); diag);
1187
1188            let pure = Some(
1189                sig_decl.child_token(SyntaxKind::Identifier).is_some_and(|t| t.text() == "pure"),
1190            );
1191
1192            let PropertyLookupResult {
1193                resolved_name: existing_name,
1194                property_type: maybe_existing_prop_type,
1195                ..
1196            } = r.lookup_property(&name);
1197            if !matches!(maybe_existing_prop_type, Type::Invalid) {
1198                if matches!(maybe_existing_prop_type, Type::Callback { .. }) {
1199                    if r.property_declarations.contains_key(&name) {
1200                        diag.push_error(
1201                            "Duplicated callback declaration".into(),
1202                            &sig_decl.DeclaredIdentifier(),
1203                        );
1204                    } else {
1205                        diag.push_error(
1206                            format!("Cannot override callback '{existing_name}'"),
1207                            &sig_decl.DeclaredIdentifier(),
1208                        )
1209                    }
1210                } else {
1211                    diag.push_error(
1212                        format!(
1213                            "Cannot declare callback '{existing_name}' when a {} with the same name exists",
1214                            if matches!(maybe_existing_prop_type, Type::Function { .. }) { "function" } else { "property" }
1215                        ),
1216                        &sig_decl.DeclaredIdentifier(),
1217                    );
1218                }
1219                continue;
1220            }
1221
1222            if let Some(csn) = sig_decl.TwoWayBinding() {
1223                r.bindings
1224                    .insert(name.clone(), BindingExpression::new_uncompiled(csn.into()).into());
1225                r.property_declarations.insert(
1226                    name,
1227                    PropertyDeclaration {
1228                        property_type: Type::InferredCallback,
1229                        node: Some(sig_decl.into()),
1230                        visibility: PropertyVisibility::InOut,
1231                        pure,
1232                        ..Default::default()
1233                    },
1234                );
1235                continue;
1236            }
1237
1238            let args = sig_decl
1239                .CallbackDeclarationParameter()
1240                .map(|p| type_from_node(p.Type(), diag, tr))
1241                .collect();
1242            let return_type = sig_decl
1243                .ReturnType()
1244                .map(|ret_ty| type_from_node(ret_ty.Type(), diag, tr))
1245                .unwrap_or(Type::Void);
1246            let arg_names = sig_decl
1247                .CallbackDeclarationParameter()
1248                .map(|a| {
1249                    a.DeclaredIdentifier()
1250                        .and_then(|x| parser::identifier_text(&x))
1251                        .unwrap_or_default()
1252                })
1253                .collect();
1254            r.property_declarations.insert(
1255                name,
1256                PropertyDeclaration {
1257                    property_type: Type::Callback(Rc::new(Function {
1258                        return_type,
1259                        args,
1260                        arg_names,
1261                    })),
1262                    node: Some(sig_decl.into()),
1263                    visibility: PropertyVisibility::InOut,
1264                    pure,
1265                    ..Default::default()
1266                },
1267            );
1268        }
1269
1270        for func in node.Function() {
1271            let name =
1272                unwrap_or_continue!(parser::identifier_text(&func.DeclaredIdentifier()); diag);
1273
1274            let PropertyLookupResult {
1275                resolved_name: existing_name,
1276                property_type: maybe_existing_prop_type,
1277                ..
1278            } = r.lookup_property(&name);
1279            if !matches!(maybe_existing_prop_type, Type::Invalid) {
1280                if matches!(maybe_existing_prop_type, Type::Callback { .. } | Type::Function { .. })
1281                {
1282                    diag.push_error(
1283                        format!("Cannot override '{existing_name}'"),
1284                        &func.DeclaredIdentifier(),
1285                    )
1286                } else {
1287                    diag.push_error(
1288                        format!("Cannot declare function '{existing_name}' when a property with the same name exists"),
1289                        &func.DeclaredIdentifier(),
1290                    );
1291                }
1292                continue;
1293            }
1294
1295            let mut args = vec![];
1296            let mut arg_names = vec![];
1297            for a in func.ArgumentDeclaration() {
1298                args.push(type_from_node(a.Type(), diag, tr));
1299                let name =
1300                    unwrap_or_continue!(parser::identifier_text(&a.DeclaredIdentifier()); diag);
1301                if arg_names.contains(&name) {
1302                    diag.push_error(
1303                        format!("Duplicated argument name '{name}'"),
1304                        &a.DeclaredIdentifier(),
1305                    );
1306                }
1307                arg_names.push(name);
1308            }
1309            let return_type = func
1310                .ReturnType()
1311                .map_or(Type::Void, |ret_ty| type_from_node(ret_ty.Type(), diag, tr));
1312            if r.bindings
1313                .insert(name.clone(), BindingExpression::new_uncompiled(func.clone().into()).into())
1314                .is_some()
1315            {
1316                assert!(diag.has_errors());
1317            }
1318
1319            let mut visibility = PropertyVisibility::Private;
1320            let mut pure = None;
1321            for token in func.children_with_tokens() {
1322                if token.kind() != SyntaxKind::Identifier {
1323                    continue;
1324                }
1325                match token.as_token().unwrap().text() {
1326                    "pure" => pure = Some(true),
1327                    "public" => {
1328                        visibility = PropertyVisibility::Public;
1329                        pure = pure.or(Some(false));
1330                    }
1331                    "protected" => {
1332                        visibility = PropertyVisibility::Protected;
1333                        pure = pure.or(Some(false));
1334                    }
1335                    _ => (),
1336                }
1337            }
1338
1339            r.property_declarations.insert(
1340                name,
1341                PropertyDeclaration {
1342                    property_type: Type::Function(Rc::new(Function {
1343                        return_type,
1344                        args,
1345                        arg_names,
1346                    })),
1347                    node: Some(func.into()),
1348                    visibility,
1349                    pure,
1350                    ..Default::default()
1351                },
1352            );
1353        }
1354
1355        for con_node in node.CallbackConnection() {
1356            let unresolved_name = unwrap_or_continue!(parser::identifier_text(&con_node); diag);
1357            let PropertyLookupResult { resolved_name, property_type, .. } =
1358                r.lookup_property(&unresolved_name);
1359            if let Type::Callback(callback) = &property_type {
1360                let num_arg = con_node.DeclaredIdentifier().count();
1361                if num_arg > callback.args.len() {
1362                    diag.push_error(
1363                        format!(
1364                            "'{}' only has {} arguments, but {} were provided",
1365                            unresolved_name,
1366                            callback.args.len(),
1367                            num_arg
1368                        ),
1369                        &con_node.child_token(SyntaxKind::Identifier).unwrap(),
1370                    );
1371                }
1372            } else if property_type == Type::InferredCallback {
1373                // argument matching will happen later
1374            } else {
1375                if r.base_type != ElementType::Error {
1376                    diag.push_error(
1377                        format!("'{}' is not a callback in {}", unresolved_name, r.base_type),
1378                        &con_node.child_token(SyntaxKind::Identifier).unwrap(),
1379                    );
1380                }
1381                continue;
1382            }
1383            match r.bindings.entry(resolved_name.into()) {
1384                Entry::Vacant(e) => {
1385                    e.insert(BindingExpression::new_uncompiled(con_node.clone().into()).into());
1386                }
1387                Entry::Occupied(_) => diag.push_error(
1388                    "Duplicated callback".into(),
1389                    &con_node.child_token(SyntaxKind::Identifier).unwrap(),
1390                ),
1391            }
1392        }
1393
1394        for anim in node.PropertyAnimation() {
1395            if let Some(star) = anim.child_token(SyntaxKind::Star) {
1396                diag.push_error(
1397                    "catch-all property is only allowed within transitions".into(),
1398                    &star,
1399                )
1400            };
1401            for prop_name_token in anim.QualifiedName() {
1402                match QualifiedTypeName::from_node(prop_name_token.clone()).members.as_slice() {
1403                    [unresolved_prop_name] => {
1404                        if r.base_type == ElementType::Error {
1405                            continue;
1406                        };
1407                        let lookup_result = r.lookup_property(unresolved_prop_name);
1408                        let valid_assign = lookup_result.is_valid_for_assignment();
1409                        if let Some(anim_element) = animation_element_from_node(
1410                            &anim,
1411                            &prop_name_token,
1412                            lookup_result.property_type,
1413                            diag,
1414                            tr,
1415                        ) {
1416                            if !valid_assign {
1417                                diag.push_error(
1418                                    format!(
1419                                        "Cannot animate {} property '{}'",
1420                                        lookup_result.property_visibility, unresolved_prop_name
1421                                    ),
1422                                    &prop_name_token,
1423                                );
1424                            }
1425
1426                            if unresolved_prop_name != lookup_result.resolved_name.as_ref() {
1427                                diag.push_property_deprecation_warning(
1428                                    unresolved_prop_name,
1429                                    &lookup_result.resolved_name,
1430                                    &prop_name_token,
1431                                );
1432                            }
1433
1434                            let expr_binding = r
1435                                .bindings
1436                                .entry(lookup_result.resolved_name.into())
1437                                .or_insert_with(|| {
1438                                    let mut r = BindingExpression::from(Expression::Invalid);
1439                                    r.priority = 1;
1440                                    r.span = Some(prop_name_token.to_source_location());
1441                                    r.into()
1442                                });
1443                            if expr_binding
1444                                .get_mut()
1445                                .animation
1446                                .replace(PropertyAnimation::Static(anim_element))
1447                                .is_some()
1448                            {
1449                                diag.push_error("Duplicated animation".into(), &prop_name_token)
1450                            }
1451                        }
1452                    }
1453                    _ => diag.push_error(
1454                        "Can only refer to property in the current element".into(),
1455                        &prop_name_token,
1456                    ),
1457                }
1458            }
1459        }
1460
1461        for ch in node.PropertyChangedCallback() {
1462            let Some(prop) = parser::identifier_text(&ch.DeclaredIdentifier()) else { continue };
1463            let lookup_result = r.lookup_property(&prop);
1464            if !lookup_result.is_valid() {
1465                if r.base_type != ElementType::Error {
1466                    diag.push_error(
1467                        format!("Property '{prop}' does not exist"),
1468                        &ch.DeclaredIdentifier(),
1469                    );
1470                }
1471            } else if !lookup_result.property_type.is_property_type() {
1472                let what = match lookup_result.property_type {
1473                    Type::Function { .. } => "a function",
1474                    Type::Callback { .. } => "a callback",
1475                    _ => "not a property",
1476                };
1477                diag.push_error(
1478                    format!(
1479                        "Change callback can only be set on properties, and '{prop}' is {what}"
1480                    ),
1481                    &ch.DeclaredIdentifier(),
1482                );
1483            } else if lookup_result.property_visibility == PropertyVisibility::Private
1484                && !lookup_result.is_local_to_component
1485            {
1486                diag.push_error(
1487                    format!("Change callback on a private property '{prop}'"),
1488                    &ch.DeclaredIdentifier(),
1489                );
1490            }
1491            let handler = Expression::Uncompiled(ch.clone().into());
1492            match r.change_callbacks.entry(prop) {
1493                Entry::Vacant(e) => {
1494                    e.insert(vec![handler].into());
1495                }
1496                Entry::Occupied(mut e) => {
1497                    diag.push_error(
1498                        format!("Duplicated change callback on '{}'", e.key()),
1499                        &ch.DeclaredIdentifier(),
1500                    );
1501                    e.get_mut().get_mut().push(handler);
1502                }
1503            }
1504        }
1505
1506        let mut children_placeholder = None;
1507        let r = r.make_rc();
1508
1509        for se in node.children() {
1510            if se.kind() == SyntaxKind::SubElement {
1511                let parent_type = r.borrow().base_type.clone();
1512                r.borrow_mut().children.push(Element::from_sub_element_node(
1513                    se.into(),
1514                    parent_type,
1515                    component_child_insertion_point,
1516                    is_legacy_syntax,
1517                    diag,
1518                    tr,
1519                ));
1520            } else if se.kind() == SyntaxKind::RepeatedElement {
1521                let mut sub_child_insertion_point = None;
1522                let rep = Element::from_repeated_node(
1523                    se.into(),
1524                    &r,
1525                    &mut sub_child_insertion_point,
1526                    is_legacy_syntax,
1527                    diag,
1528                    tr,
1529                );
1530                if let Some(ChildrenInsertionPoint { node: se, .. }) = sub_child_insertion_point {
1531                    diag.push_error(
1532                        "The @children placeholder cannot appear in a repeated element".into(),
1533                        &se,
1534                    )
1535                }
1536                r.borrow_mut().children.push(rep);
1537            } else if se.kind() == SyntaxKind::ConditionalElement {
1538                let mut sub_child_insertion_point = None;
1539                let rep = Element::from_conditional_node(
1540                    se.into(),
1541                    r.borrow().base_type.clone(),
1542                    &mut sub_child_insertion_point,
1543                    is_legacy_syntax,
1544                    diag,
1545                    tr,
1546                );
1547                if let Some(ChildrenInsertionPoint { node: se, .. }) = sub_child_insertion_point {
1548                    diag.push_error(
1549                        "The @children placeholder cannot appear in a conditional element".into(),
1550                        &se,
1551                    )
1552                }
1553                r.borrow_mut().children.push(rep);
1554            } else if se.kind() == SyntaxKind::ChildrenPlaceholder {
1555                if children_placeholder.is_some() {
1556                    diag.push_error(
1557                        "The @children placeholder can only appear once in an element".into(),
1558                        &se,
1559                    )
1560                } else {
1561                    children_placeholder = Some((se.clone().into(), r.borrow().children.len()));
1562                }
1563            }
1564        }
1565
1566        if let Some((children_placeholder, index)) = children_placeholder {
1567            if component_child_insertion_point.is_some() {
1568                diag.push_error(
1569                    "The @children placeholder can only appear once in an element hierarchy".into(),
1570                    &children_placeholder,
1571                )
1572            } else {
1573                *component_child_insertion_point = Some(ChildrenInsertionPoint {
1574                    parent: r.clone(),
1575                    insertion_index: index,
1576                    node: children_placeholder,
1577                });
1578            }
1579        }
1580
1581        for state in node.States().flat_map(|s| s.State()) {
1582            let s = State {
1583                id: parser::identifier_text(&state.DeclaredIdentifier()).unwrap_or_default(),
1584                condition: state.Expression().map(|e| Expression::Uncompiled(e.into())),
1585                property_changes: state
1586                    .StatePropertyChange()
1587                    .filter_map(|s| {
1588                        lookup_property_from_qualified_name_for_state(s.QualifiedName(), &r, diag)
1589                            .map(|(ne, ty)| {
1590                                if !ty.is_property_type() && !matches!(ty, Type::Invalid) {
1591                                    diag.push_error(
1592                                        format!("'{}' is not a property", **s.QualifiedName()),
1593                                        &s,
1594                                    );
1595                                }
1596                                (ne, Expression::Uncompiled(s.BindingExpression().into()), s)
1597                            })
1598                    })
1599                    .collect(),
1600            };
1601            for trs in state.Transition() {
1602                let mut t = Transition::from_node(trs, &r, tr, diag);
1603                t.state_id.clone_from(&s.id);
1604                r.borrow_mut().transitions.push(t);
1605            }
1606            r.borrow_mut().states.push(s);
1607        }
1608
1609        for ts in node.Transitions() {
1610            if !is_legacy_syntax {
1611                diag.push_error("'transitions' block are no longer supported. Use 'in {...}' and 'out {...}' directly in the state definition".into(), &ts);
1612            }
1613            for trs in ts.Transition() {
1614                let trans = Transition::from_node(trs, &r, tr, diag);
1615                r.borrow_mut().transitions.push(trans);
1616            }
1617        }
1618
1619        if r.borrow().base_type.to_smolstr() == "ListView" {
1620            let mut seen_for = false;
1621            for se in node.children() {
1622                if se.kind() == SyntaxKind::RepeatedElement && !seen_for {
1623                    seen_for = true;
1624                } else if matches!(
1625                    se.kind(),
1626                    SyntaxKind::SubElement
1627                        | SyntaxKind::ConditionalElement
1628                        | SyntaxKind::RepeatedElement
1629                        | SyntaxKind::ChildrenPlaceholder
1630                ) {
1631                    diag.push_error("A ListView can just have a single 'for' as children. Anything else is not supported".into(), &se)
1632                }
1633            }
1634        }
1635
1636        r
1637    }
1638
1639    fn from_sub_element_node(
1640        node: syntax_nodes::SubElement,
1641        parent_type: ElementType,
1642        component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1643        is_in_legacy_component: bool,
1644        diag: &mut BuildDiagnostics,
1645        tr: &TypeRegister,
1646    ) -> ElementRc {
1647        let mut id = parser::identifier_text(&node).unwrap_or_default();
1648        if matches!(id.as_ref(), "parent" | "self" | "root") {
1649            diag.push_error(
1650                format!("'{id}' is a reserved id"),
1651                &node.child_token(SyntaxKind::Identifier).unwrap(),
1652            );
1653            id = SmolStr::default();
1654        }
1655        Element::from_node(
1656            node.Element(),
1657            id,
1658            parent_type,
1659            component_child_insertion_point,
1660            is_in_legacy_component,
1661            diag,
1662            tr,
1663        )
1664    }
1665
1666    fn from_repeated_node(
1667        node: syntax_nodes::RepeatedElement,
1668        parent: &ElementRc,
1669        component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1670        is_in_legacy_component: bool,
1671        diag: &mut BuildDiagnostics,
1672        tr: &TypeRegister,
1673    ) -> ElementRc {
1674        let is_listview = if parent.borrow().base_type.to_string() == "ListView" {
1675            Some(ListViewInfo {
1676                viewport_y: NamedReference::new(parent, SmolStr::new_static("viewport-y")),
1677                viewport_height: NamedReference::new(
1678                    parent,
1679                    SmolStr::new_static("viewport-height"),
1680                ),
1681                viewport_width: NamedReference::new(parent, SmolStr::new_static("viewport-width")),
1682                listview_height: NamedReference::new(parent, SmolStr::new_static("visible-height")),
1683                listview_width: NamedReference::new(parent, SmolStr::new_static("visible-width")),
1684            })
1685        } else {
1686            None
1687        };
1688        let rei = RepeatedElementInfo {
1689            model: Expression::Uncompiled(node.Expression().into()),
1690            model_data_id: node
1691                .DeclaredIdentifier()
1692                .and_then(|n| parser::identifier_text(&n))
1693                .unwrap_or_default(),
1694            index_id: node
1695                .RepeatedIndex()
1696                .and_then(|r| parser::identifier_text(&r))
1697                .unwrap_or_default(),
1698            is_conditional_element: false,
1699            is_listview,
1700        };
1701        let e = Element::from_sub_element_node(
1702            node.SubElement(),
1703            parent.borrow().base_type.clone(),
1704            component_child_insertion_point,
1705            is_in_legacy_component,
1706            diag,
1707            tr,
1708        );
1709        e.borrow_mut().repeated = Some(rei);
1710        e
1711    }
1712
1713    fn from_conditional_node(
1714        node: syntax_nodes::ConditionalElement,
1715        parent_type: ElementType,
1716        component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1717        is_in_legacy_component: bool,
1718        diag: &mut BuildDiagnostics,
1719        tr: &TypeRegister,
1720    ) -> ElementRc {
1721        let rei = RepeatedElementInfo {
1722            model: Expression::Uncompiled(node.Expression().into()),
1723            model_data_id: SmolStr::default(),
1724            index_id: SmolStr::default(),
1725            is_conditional_element: true,
1726            is_listview: None,
1727        };
1728        let e = Element::from_sub_element_node(
1729            node.SubElement(),
1730            parent_type,
1731            component_child_insertion_point,
1732            is_in_legacy_component,
1733            diag,
1734            tr,
1735        );
1736        e.borrow_mut().repeated = Some(rei);
1737        e
1738    }
1739
1740    /// Return the type of a property in this element or its base, along with the final name, in case
1741    /// the provided name points towards a property alias. Type::Invalid is returned if the property does
1742    /// not exist.
1743    pub fn lookup_property<'a>(&self, name: &'a str) -> PropertyLookupResult<'a> {
1744        self.property_declarations.get(name).map_or_else(
1745            || {
1746                let mut r = self.base_type.lookup_property(name);
1747                r.is_in_direct_base = r.is_local_to_component;
1748                r.is_local_to_component = false;
1749                r
1750            },
1751            |p| PropertyLookupResult {
1752                resolved_name: name.into(),
1753                property_type: p.property_type.clone(),
1754                property_visibility: p.visibility,
1755                declared_pure: p.pure,
1756                is_local_to_component: true,
1757                is_in_direct_base: false,
1758                builtin_function: None,
1759            },
1760        )
1761    }
1762
1763    fn parse_bindings(
1764        &mut self,
1765        bindings: impl Iterator<Item = (crate::parser::SyntaxToken, SyntaxNode)>,
1766        is_in_legacy_component: bool,
1767        diag: &mut BuildDiagnostics,
1768    ) {
1769        for (name_token, b) in bindings {
1770            let unresolved_name = crate::parser::normalize_identifier(name_token.text());
1771            let lookup_result = self.lookup_property(&unresolved_name);
1772            if !lookup_result.property_type.is_property_type() {
1773                match lookup_result.property_type {
1774                        Type::Invalid => {
1775                            if self.base_type != ElementType::Error {
1776                                diag.push_error(if self.base_type.to_smolstr() == "Empty" {
1777                                    format!( "Unknown property {unresolved_name}")
1778                                } else {
1779                                    format!( "Unknown property {unresolved_name} in {}", self.base_type)
1780                                },
1781                                &name_token);
1782                            }
1783                        }
1784                        Type::Callback { .. } => {
1785                            diag.push_error(format!("'{unresolved_name}' is a callback. Use `=>` to connect"),
1786                            &name_token)
1787                        }
1788                        _ => diag.push_error(format!(
1789                            "Cannot assign to {} in {} because it does not have a valid property type",
1790                            unresolved_name, self.base_type,
1791                        ),
1792                        &name_token),
1793                    }
1794            } else if !lookup_result.is_local_to_component
1795                && (lookup_result.property_visibility == PropertyVisibility::Private
1796                    || lookup_result.property_visibility == PropertyVisibility::Output)
1797            {
1798                if is_in_legacy_component
1799                    && lookup_result.property_visibility == PropertyVisibility::Output
1800                {
1801                    diag.push_warning(
1802                        format!("Assigning to output property '{unresolved_name}' is deprecated"),
1803                        &name_token,
1804                    );
1805                } else {
1806                    diag.push_error(
1807                        format!(
1808                            "Cannot assign to {} property '{}'",
1809                            lookup_result.property_visibility, unresolved_name
1810                        ),
1811                        &name_token,
1812                    );
1813                }
1814            }
1815
1816            if *lookup_result.resolved_name != *unresolved_name {
1817                diag.push_property_deprecation_warning(
1818                    &unresolved_name,
1819                    &lookup_result.resolved_name,
1820                    &name_token,
1821                );
1822            }
1823
1824            match self.bindings.entry(lookup_result.resolved_name.into()) {
1825                Entry::Occupied(_) => {
1826                    diag.push_error("Duplicated property binding".into(), &name_token);
1827                }
1828                Entry::Vacant(entry) => {
1829                    entry.insert(BindingExpression::new_uncompiled(b).into());
1830                }
1831            };
1832        }
1833    }
1834
1835    pub fn native_class(&self) -> Option<Rc<NativeClass>> {
1836        let mut base_type = self.base_type.clone();
1837        loop {
1838            match &base_type {
1839                ElementType::Component(component) => {
1840                    base_type = component.root_element.clone().borrow().base_type.clone();
1841                }
1842                ElementType::Builtin(builtin) => break Some(builtin.native_class.clone()),
1843                ElementType::Native(native) => break Some(native.clone()),
1844                _ => break None,
1845            }
1846        }
1847    }
1848
1849    pub fn builtin_type(&self) -> Option<Rc<BuiltinElement>> {
1850        let mut base_type = self.base_type.clone();
1851        loop {
1852            match &base_type {
1853                ElementType::Component(component) => {
1854                    base_type = component.root_element.clone().borrow().base_type.clone();
1855                }
1856                ElementType::Builtin(builtin) => break Some(builtin.clone()),
1857                _ => break None,
1858            }
1859        }
1860    }
1861
1862    pub fn layout_info_prop(&self, orientation: Orientation) -> Option<&NamedReference> {
1863        self.layout_info_prop.as_ref().map(|prop| match orientation {
1864            Orientation::Horizontal => &prop.0,
1865            Orientation::Vertical => &prop.1,
1866        })
1867    }
1868
1869    /// Returns the element's name as specified in the markup, not normalized.
1870    pub fn original_name(&self) -> SmolStr {
1871        self.debug
1872            .first()
1873            .and_then(|n| n.node.child_token(parser::SyntaxKind::Identifier))
1874            .map(|n| n.to_smolstr())
1875            .unwrap_or_else(|| self.id.clone())
1876    }
1877
1878    /// Return true if the binding is set, either on this element or in a base
1879    ///
1880    /// If `need_explicit` is true, then only consider binding set in the code, not the ones set
1881    /// by the compiler later.
1882    pub fn is_binding_set(self: &Element, property_name: &str, need_explicit: bool) -> bool {
1883        if self.bindings.get(property_name).is_some_and(|b| {
1884            b.borrow().has_binding() && (!need_explicit || b.borrow().priority > 0)
1885        }) {
1886            true
1887        } else if let ElementType::Component(base) = &self.base_type {
1888            base.root_element.borrow().is_binding_set(property_name, need_explicit)
1889        } else {
1890            false
1891        }
1892    }
1893
1894    /// Set the property `property_name` of this Element only if it was not set.
1895    /// the `expression_fn` will only be called if it isn't set
1896    ///
1897    /// returns true if the binding was changed
1898    pub fn set_binding_if_not_set(
1899        &mut self,
1900        property_name: SmolStr,
1901        expression_fn: impl FnOnce() -> Expression,
1902    ) -> bool {
1903        if self.is_binding_set(&property_name, false) {
1904            return false;
1905        }
1906
1907        match self.bindings.entry(property_name) {
1908            Entry::Vacant(vacant_entry) => {
1909                let mut binding: BindingExpression = expression_fn().into();
1910                binding.priority = i32::MAX;
1911                vacant_entry.insert(binding.into());
1912            }
1913            Entry::Occupied(mut existing_entry) => {
1914                let mut binding: BindingExpression = expression_fn().into();
1915                binding.priority = i32::MAX;
1916                existing_entry.get_mut().get_mut().merge_with(&binding);
1917            }
1918        };
1919        true
1920    }
1921
1922    pub fn sub_component(&self) -> Option<&Rc<Component>> {
1923        if self.repeated.is_some() {
1924            None
1925        } else if let ElementType::Component(sub_component) = &self.base_type {
1926            Some(sub_component)
1927        } else {
1928            None
1929        }
1930    }
1931
1932    pub fn element_infos(&self) -> String {
1933        let mut debug_infos = self.debug.clone();
1934        let mut base = self.base_type.clone();
1935        while let ElementType::Component(b) = base {
1936            let elem = b.root_element.borrow();
1937            base = elem.base_type.clone();
1938            debug_infos.extend(elem.debug.iter().cloned());
1939        }
1940
1941        let (infos, _, _) = debug_infos.into_iter().fold(
1942            (String::new(), false, true),
1943            |(mut infos, elem_boundary, first), debug_info| {
1944                if elem_boundary {
1945                    infos.push('/');
1946                } else if !first {
1947                    infos.push(';');
1948                }
1949
1950                infos.push_str(&debug_info.encoded_element_info());
1951                (infos, debug_info.element_boundary, false)
1952            },
1953        );
1954        infos
1955    }
1956}
1957
1958/// Apply default property values defined in `builtins.slint` to the element.
1959fn apply_default_type_properties(element: &mut Element) {
1960    // Apply default property values on top:
1961    if let ElementType::Builtin(builtin_base) = &element.base_type {
1962        for (prop, info) in &builtin_base.properties {
1963            if let BuiltinPropertyDefault::Expr(expr) = &info.default_value {
1964                element.bindings.entry(prop.clone()).or_insert_with(|| {
1965                    let mut binding = BindingExpression::from(expr.clone());
1966                    binding.priority = i32::MAX;
1967                    RefCell::new(binding)
1968                });
1969            }
1970        }
1971    }
1972}
1973
1974/// Create a Type for this node
1975pub fn type_from_node(
1976    node: syntax_nodes::Type,
1977    diag: &mut BuildDiagnostics,
1978    tr: &TypeRegister,
1979) -> Type {
1980    if let Some(qualified_type_node) = node.QualifiedName() {
1981        let qualified_type = QualifiedTypeName::from_node(qualified_type_node.clone());
1982
1983        let prop_type = tr.lookup_qualified(&qualified_type.members);
1984
1985        if prop_type == Type::Invalid && tr.lookup_element(&qualified_type.to_smolstr()).is_err() {
1986            diag.push_error(format!("Unknown type '{qualified_type}'"), &qualified_type_node);
1987        } else if !prop_type.is_property_type() {
1988            diag.push_error(
1989                format!("'{qualified_type}' is not a valid type"),
1990                &qualified_type_node,
1991            );
1992        }
1993        prop_type
1994    } else if let Some(object_node) = node.ObjectType() {
1995        type_struct_from_node(object_node, diag, tr, None, None)
1996    } else if let Some(array_node) = node.ArrayType() {
1997        Type::Array(Rc::new(type_from_node(array_node.Type(), diag, tr)))
1998    } else {
1999        assert!(diag.has_errors());
2000        Type::Invalid
2001    }
2002}
2003
2004/// Create a [`Type::Struct`] from a [`syntax_nodes::ObjectType`]
2005pub fn type_struct_from_node(
2006    object_node: syntax_nodes::ObjectType,
2007    diag: &mut BuildDiagnostics,
2008    tr: &TypeRegister,
2009    rust_attributes: Option<Vec<SmolStr>>,
2010    name: Option<SmolStr>,
2011) -> Type {
2012    let fields = object_node
2013        .ObjectTypeMember()
2014        .map(|member| {
2015            (
2016                parser::identifier_text(&member).unwrap_or_default(),
2017                type_from_node(member.Type(), diag, tr),
2018            )
2019        })
2020        .collect();
2021    Type::Struct(Rc::new(Struct { fields, name, node: Some(object_node), rust_attributes }))
2022}
2023
2024fn animation_element_from_node(
2025    anim: &syntax_nodes::PropertyAnimation,
2026    prop_name: &syntax_nodes::QualifiedName,
2027    prop_type: Type,
2028    diag: &mut BuildDiagnostics,
2029    tr: &TypeRegister,
2030) -> Option<ElementRc> {
2031    let anim_type = tr.property_animation_type_for_property(prop_type);
2032    if !matches!(anim_type, ElementType::Builtin(..)) {
2033        diag.push_error(
2034            format!(
2035                "'{}' is not a property that can be animated",
2036                prop_name.text().to_string().trim()
2037            ),
2038            prop_name,
2039        );
2040        None
2041    } else {
2042        let mut anim_element =
2043            Element { id: "".into(), base_type: anim_type, ..Default::default() };
2044        anim_element.parse_bindings(
2045            anim.Binding().filter_map(|b| {
2046                Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into()))
2047            }),
2048            false,
2049            diag,
2050        );
2051
2052        apply_default_type_properties(&mut anim_element);
2053
2054        Some(Rc::new(RefCell::new(anim_element)))
2055    }
2056}
2057
2058#[derive(Default, Debug, Clone)]
2059pub struct QualifiedTypeName {
2060    pub members: Vec<SmolStr>,
2061}
2062
2063impl QualifiedTypeName {
2064    pub fn from_node(node: syntax_nodes::QualifiedName) -> Self {
2065        debug_assert_eq!(node.kind(), SyntaxKind::QualifiedName);
2066        let members = node
2067            .children_with_tokens()
2068            .filter(|n| n.kind() == SyntaxKind::Identifier)
2069            .filter_map(|x| x.as_token().map(|x| crate::parser::normalize_identifier(x.text())))
2070            .collect();
2071        Self { members }
2072    }
2073}
2074
2075impl Display for QualifiedTypeName {
2076    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2077        write!(f, "{}", self.members.join("."))
2078    }
2079}
2080
2081/// Return a NamedReference for a qualified name used in a state (or transition),
2082/// if the reference is invalid, there will be a diagnostic
2083fn lookup_property_from_qualified_name_for_state(
2084    node: syntax_nodes::QualifiedName,
2085    r: &ElementRc,
2086    diag: &mut BuildDiagnostics,
2087) -> Option<(NamedReference, Type)> {
2088    let qualname = QualifiedTypeName::from_node(node.clone());
2089    match qualname.members.as_slice() {
2090        [unresolved_prop_name] => {
2091            let lookup_result = r.borrow().lookup_property(unresolved_prop_name.as_ref());
2092            if !lookup_result.property_type.is_property_type() {
2093                diag.push_error(format!("'{qualname}' is not a valid property"), &node);
2094            } else if !lookup_result.is_valid_for_assignment() {
2095                diag.push_error(
2096                    format!(
2097                        "'{}' cannot be set in a state because it is {}",
2098                        qualname, lookup_result.property_visibility
2099                    ),
2100                    &node,
2101                );
2102            }
2103            Some((
2104                NamedReference::new(r, lookup_result.resolved_name.to_smolstr()),
2105                lookup_result.property_type,
2106            ))
2107        }
2108        [elem_id, unresolved_prop_name] => {
2109            if let Some(element) = find_element_by_id(r, elem_id.as_ref()) {
2110                let lookup_result = element.borrow().lookup_property(unresolved_prop_name.as_ref());
2111                if !lookup_result.is_valid() {
2112                    diag.push_error(
2113                        format!("'{unresolved_prop_name}' not found in '{elem_id}'"),
2114                        &node,
2115                    );
2116                } else if !lookup_result.is_valid_for_assignment() {
2117                    diag.push_error(
2118                        format!(
2119                            "'{}' cannot be set in a state because it is {}",
2120                            qualname, lookup_result.property_visibility
2121                        ),
2122                        &node,
2123                    );
2124                }
2125                Some((
2126                    NamedReference::new(&element, lookup_result.resolved_name.to_smolstr()),
2127                    lookup_result.property_type,
2128                ))
2129            } else {
2130                diag.push_error(format!("'{elem_id}' is not a valid element id"), &node);
2131                None
2132            }
2133        }
2134        _ => {
2135            diag.push_error(format!("'{qualname}' is not a valid property"), &node);
2136            None
2137        }
2138    }
2139}
2140
2141/// FIXME: this is duplicated the resolving pass. Also, we should use a hash table
2142fn find_element_by_id(e: &ElementRc, name: &str) -> Option<ElementRc> {
2143    if e.borrow().id == name {
2144        return Some(e.clone());
2145    }
2146    for x in &e.borrow().children {
2147        if x.borrow().repeated.is_some() {
2148            continue;
2149        }
2150        if let Some(x) = find_element_by_id(x, name) {
2151            return Some(x);
2152        }
2153    }
2154
2155    None
2156}
2157
2158/// Find the parent element to a given element.
2159/// (since there is no parent mapping we need to fo an exhaustive search)
2160pub fn find_parent_element(e: &ElementRc) -> Option<ElementRc> {
2161    fn recurse(base: &ElementRc, e: &ElementRc) -> Option<ElementRc> {
2162        for child in &base.borrow().children {
2163            if Rc::ptr_eq(child, e) {
2164                return Some(base.clone());
2165            }
2166            if let Some(x) = recurse(child, e) {
2167                return Some(x);
2168            }
2169        }
2170        None
2171    }
2172
2173    let root = e.borrow().enclosing_component.upgrade().unwrap().root_element.clone();
2174    if Rc::ptr_eq(&root, e) {
2175        return None;
2176    }
2177    recurse(&root, e)
2178}
2179
2180/// Call the visitor for each children of the element recursively, starting with the element itself
2181///
2182/// The state returned by the visitor is passed to the children
2183pub fn recurse_elem<State>(
2184    elem: &ElementRc,
2185    state: &State,
2186    vis: &mut impl FnMut(&ElementRc, &State) -> State,
2187) {
2188    let state = vis(elem, state);
2189    for sub in &elem.borrow().children {
2190        recurse_elem(sub, &state, vis);
2191    }
2192}
2193
2194/// Same as [`recurse_elem`] but include the elements from sub_components
2195pub fn recurse_elem_including_sub_components<State>(
2196    component: &Component,
2197    state: &State,
2198    vis: &mut impl FnMut(&ElementRc, &State) -> State,
2199) {
2200    recurse_elem(&component.root_element, state, &mut |elem, state| {
2201        debug_assert!(std::ptr::eq(
2202            component as *const Component,
2203            (&*elem.borrow().enclosing_component.upgrade().unwrap()) as *const Component
2204        ));
2205        if elem.borrow().repeated.is_some() {
2206            if let ElementType::Component(base) = &elem.borrow().base_type {
2207                if base.parent_element.upgrade().is_some() {
2208                    recurse_elem_including_sub_components(base, state, vis);
2209                }
2210            }
2211        }
2212        vis(elem, state)
2213    });
2214    component
2215        .popup_windows
2216        .borrow()
2217        .iter()
2218        .for_each(|p| recurse_elem_including_sub_components(&p.component, state, vis));
2219    component
2220        .menu_item_tree
2221        .borrow()
2222        .iter()
2223        .for_each(|c| recurse_elem_including_sub_components(c, state, vis));
2224}
2225
2226/// Same as recurse_elem, but will take the children from the element as to not keep the element borrow
2227pub fn recurse_elem_no_borrow<State>(
2228    elem: &ElementRc,
2229    state: &State,
2230    vis: &mut impl FnMut(&ElementRc, &State) -> State,
2231) {
2232    let state = vis(elem, state);
2233    let children = elem.borrow().children.clone();
2234    for sub in &children {
2235        recurse_elem_no_borrow(sub, &state, vis);
2236    }
2237}
2238
2239/// Same as [`recurse_elem`] but include the elements form sub_components
2240pub fn recurse_elem_including_sub_components_no_borrow<State>(
2241    component: &Component,
2242    state: &State,
2243    vis: &mut impl FnMut(&ElementRc, &State) -> State,
2244) {
2245    recurse_elem_no_borrow(&component.root_element, state, &mut |elem, state| {
2246        let base = if elem.borrow().repeated.is_some() {
2247            if let ElementType::Component(base) = &elem.borrow().base_type {
2248                if base.parent_element.upgrade().is_some() {
2249                    Some(base.clone())
2250                } else {
2251                    // The process_repeater_components pass was not run yet
2252                    None
2253                }
2254            } else {
2255                None
2256            }
2257        } else {
2258            None
2259        };
2260        if let Some(base) = base {
2261            recurse_elem_including_sub_components_no_borrow(&base, state, vis);
2262        }
2263        vis(elem, state)
2264    });
2265    component
2266        .popup_windows
2267        .borrow()
2268        .iter()
2269        .for_each(|p| recurse_elem_including_sub_components_no_borrow(&p.component, state, vis));
2270    component
2271        .menu_item_tree
2272        .borrow()
2273        .iter()
2274        .for_each(|c| recurse_elem_including_sub_components_no_borrow(c, state, vis));
2275}
2276
2277/// This visit the binding attached to this element, but does not recurse in children elements
2278/// Also does not recurse within the expressions.
2279///
2280/// This code will temporarily move the bindings or states member so it can call the visitor without
2281/// maintaining a borrow on the RefCell.
2282pub fn visit_element_expressions(
2283    elem: &ElementRc,
2284    mut vis: impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
2285) {
2286    fn visit_element_expressions_simple(
2287        elem: &ElementRc,
2288        vis: &mut impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
2289    ) {
2290        for (name, expr) in &elem.borrow().bindings {
2291            vis(&mut expr.borrow_mut(), Some(name.as_str()), &|| {
2292                elem.borrow().lookup_property(name).property_type
2293            });
2294
2295            match &mut expr.borrow_mut().animation {
2296                Some(PropertyAnimation::Static(e)) => visit_element_expressions_simple(e, vis),
2297                Some(PropertyAnimation::Transition { animations, state_ref }) => {
2298                    vis(state_ref, None, &|| Type::Int32);
2299                    for a in animations {
2300                        visit_element_expressions_simple(&a.animation, vis)
2301                    }
2302                }
2303                None => (),
2304            }
2305        }
2306    }
2307
2308    let repeated = elem
2309        .borrow_mut()
2310        .repeated
2311        .as_mut()
2312        .map(|r| (std::mem::take(&mut r.model), r.is_conditional_element));
2313    if let Some((mut model, is_cond)) = repeated {
2314        vis(&mut model, None, &|| if is_cond { Type::Bool } else { Type::Model });
2315        elem.borrow_mut().repeated.as_mut().unwrap().model = model;
2316    }
2317    visit_element_expressions_simple(elem, &mut vis);
2318
2319    for expr in elem.borrow().change_callbacks.values() {
2320        for expr in expr.borrow_mut().iter_mut() {
2321            vis(expr, Some("$change callback$"), &|| Type::Void);
2322        }
2323    }
2324
2325    let mut states = std::mem::take(&mut elem.borrow_mut().states);
2326    for s in &mut states {
2327        if let Some(cond) = s.condition.as_mut() {
2328            vis(cond, None, &|| Type::Bool)
2329        }
2330        for (ne, e, _) in &mut s.property_changes {
2331            vis(e, Some(ne.name()), &|| {
2332                ne.element().borrow().lookup_property(ne.name()).property_type
2333            });
2334        }
2335    }
2336    elem.borrow_mut().states = states;
2337
2338    let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions);
2339    for t in &mut transitions {
2340        for (_, _, a) in &mut t.property_animations {
2341            visit_element_expressions_simple(a, &mut vis);
2342        }
2343    }
2344    elem.borrow_mut().transitions = transitions;
2345
2346    let component = elem.borrow().enclosing_component.upgrade().unwrap();
2347    if Rc::ptr_eq(&component.root_element, elem) {
2348        for e in component.init_code.borrow_mut().iter_mut() {
2349            vis(e, None, &|| Type::Void);
2350        }
2351    }
2352}
2353
2354pub fn visit_named_references_in_expression(
2355    expr: &mut Expression,
2356    vis: &mut impl FnMut(&mut NamedReference),
2357) {
2358    expr.visit_mut(|sub| visit_named_references_in_expression(sub, vis));
2359    match expr {
2360        Expression::PropertyReference(r) => vis(r),
2361        Expression::FunctionCall {
2362            function: Callable::Callback(r) | Callable::Function(r),
2363            ..
2364        } => vis(r),
2365        Expression::LayoutCacheAccess { layout_cache_prop, .. } => vis(layout_cache_prop),
2366        Expression::SolveLayout(l, _) => l.visit_named_references(vis),
2367        Expression::ComputeLayoutInfo(l, _) => l.visit_named_references(vis),
2368        // This is not really a named reference, but the result is the same, it need to be updated
2369        // FIXME: this should probably be lowered into a PropertyReference
2370        Expression::RepeaterModelReference { element }
2371        | Expression::RepeaterIndexReference { element } => {
2372            // FIXME: this is questionable
2373            let mut nc =
2374                NamedReference::new(&element.upgrade().unwrap(), SmolStr::new_static("$model"));
2375            vis(&mut nc);
2376            debug_assert!(nc.element().borrow().repeated.is_some());
2377            *element = Rc::downgrade(&nc.element());
2378        }
2379        _ => {}
2380    }
2381}
2382
2383/// Visit all the named reference in an element
2384/// But does not recurse in sub-elements. (unlike [`visit_all_named_references`] which recurse)
2385pub fn visit_all_named_references_in_element(
2386    elem: &ElementRc,
2387    mut vis: impl FnMut(&mut NamedReference),
2388) {
2389    visit_element_expressions(elem, |expr, _, _| {
2390        visit_named_references_in_expression(expr, &mut vis)
2391    });
2392    let mut states = std::mem::take(&mut elem.borrow_mut().states);
2393    for s in &mut states {
2394        for (r, _, _) in &mut s.property_changes {
2395            vis(r);
2396        }
2397    }
2398    elem.borrow_mut().states = states;
2399    let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions);
2400    for t in &mut transitions {
2401        for (r, _, _) in &mut t.property_animations {
2402            vis(r)
2403        }
2404    }
2405    elem.borrow_mut().transitions = transitions;
2406    let mut repeated = std::mem::take(&mut elem.borrow_mut().repeated);
2407    if let Some(r) = &mut repeated {
2408        if let Some(lv) = &mut r.is_listview {
2409            vis(&mut lv.viewport_y);
2410            vis(&mut lv.viewport_height);
2411            vis(&mut lv.viewport_width);
2412            vis(&mut lv.listview_height);
2413            vis(&mut lv.listview_width);
2414        }
2415    }
2416    elem.borrow_mut().repeated = repeated;
2417    let mut layout_info_prop = std::mem::take(&mut elem.borrow_mut().layout_info_prop);
2418    layout_info_prop.as_mut().map(|(h, b)| (vis(h), vis(b)));
2419    elem.borrow_mut().layout_info_prop = layout_info_prop;
2420    let mut debug = std::mem::take(&mut elem.borrow_mut().debug);
2421    for d in debug.iter_mut() {
2422        if let Some(l) = d.layout.as_mut() {
2423            l.visit_named_references(&mut vis)
2424        }
2425    }
2426    elem.borrow_mut().debug = debug;
2427
2428    let mut accessibility_props = std::mem::take(&mut elem.borrow_mut().accessibility_props);
2429    accessibility_props.0.iter_mut().for_each(|(_, x)| vis(x));
2430    elem.borrow_mut().accessibility_props = accessibility_props;
2431
2432    let geometry_props = elem.borrow_mut().geometry_props.take();
2433    if let Some(mut geometry_props) = geometry_props {
2434        vis(&mut geometry_props.x);
2435        vis(&mut geometry_props.y);
2436        vis(&mut geometry_props.width);
2437        vis(&mut geometry_props.height);
2438        elem.borrow_mut().geometry_props = Some(geometry_props);
2439    }
2440
2441    // visit two way bindings
2442    for expr in elem.borrow().bindings.values() {
2443        for nr in &mut expr.borrow_mut().two_way_bindings {
2444            vis(nr);
2445        }
2446    }
2447
2448    let mut property_declarations = std::mem::take(&mut elem.borrow_mut().property_declarations);
2449    for pd in property_declarations.values_mut() {
2450        pd.is_alias.as_mut().map(&mut vis);
2451    }
2452    elem.borrow_mut().property_declarations = property_declarations;
2453}
2454
2455/// Visit all named reference in this component and sub component
2456pub fn visit_all_named_references(
2457    component: &Component,
2458    vis: &mut impl FnMut(&mut NamedReference),
2459) {
2460    recurse_elem_including_sub_components_no_borrow(
2461        component,
2462        &Weak::new(),
2463        &mut |elem, parent_compo| {
2464            visit_all_named_references_in_element(elem, |nr| vis(nr));
2465            let compo = elem.borrow().enclosing_component.clone();
2466            if !Weak::ptr_eq(parent_compo, &compo) {
2467                let compo = compo.upgrade().unwrap();
2468                compo.root_constraints.borrow_mut().visit_named_references(vis);
2469                compo.popup_windows.borrow_mut().iter_mut().for_each(|p| {
2470                    vis(&mut p.x);
2471                    vis(&mut p.y);
2472                });
2473                compo.timers.borrow_mut().iter_mut().for_each(|t| {
2474                    vis(&mut t.interval);
2475                    vis(&mut t.triggered);
2476                    vis(&mut t.running);
2477                });
2478                for o in compo.optimized_elements.borrow().iter() {
2479                    visit_element_expressions(o, |expr, _, _| {
2480                        visit_named_references_in_expression(expr, vis)
2481                    });
2482                }
2483            }
2484            compo
2485        },
2486    );
2487}
2488
2489/// Visit all expression in this component and sub components
2490///
2491/// Does not recurse in the expression itself
2492pub fn visit_all_expressions(
2493    component: &Component,
2494    mut vis: impl FnMut(&mut Expression, &dyn Fn() -> Type),
2495) {
2496    recurse_elem_including_sub_components(component, &Weak::new(), &mut |elem, parent_compo| {
2497        visit_element_expressions(elem, |expr, _, ty| vis(expr, ty));
2498        let compo = elem.borrow().enclosing_component.clone();
2499        if !Weak::ptr_eq(parent_compo, &compo) {
2500            let compo = compo.upgrade().unwrap();
2501            for o in compo.optimized_elements.borrow().iter() {
2502                visit_element_expressions(o, |expr, _, ty| vis(expr, ty));
2503            }
2504        }
2505        compo
2506    })
2507}
2508
2509#[derive(Debug, Clone)]
2510pub struct State {
2511    pub id: SmolStr,
2512    pub condition: Option<Expression>,
2513    pub property_changes: Vec<(NamedReference, Expression, syntax_nodes::StatePropertyChange)>,
2514}
2515
2516#[derive(Debug, Clone)]
2517pub struct Transition {
2518    pub direction: TransitionDirection,
2519    pub state_id: SmolStr,
2520    pub property_animations: Vec<(NamedReference, SourceLocation, ElementRc)>,
2521    pub node: syntax_nodes::Transition,
2522}
2523
2524impl Transition {
2525    fn from_node(
2526        trs: syntax_nodes::Transition,
2527        r: &ElementRc,
2528        tr: &TypeRegister,
2529        diag: &mut BuildDiagnostics,
2530    ) -> Transition {
2531        if let Some(star) = trs.child_token(SyntaxKind::Star) {
2532            diag.push_error("catch-all not yet implemented".into(), &star);
2533        };
2534        let direction_text = trs
2535            .first_child_or_token()
2536            .and_then(|t| t.as_token().map(|tok| tok.text().to_string()))
2537            .unwrap_or_default();
2538
2539        Transition {
2540            direction: match direction_text.as_str() {
2541                "in" => TransitionDirection::In,
2542                "out" => TransitionDirection::Out,
2543                "in-out" => TransitionDirection::InOut,
2544                "in_out" => TransitionDirection::InOut,
2545                _ => {
2546                    unreachable!("Unknown transition direction: '{}'", direction_text);
2547                }
2548            },
2549            state_id: trs
2550                .DeclaredIdentifier()
2551                .and_then(|x| parser::identifier_text(&x))
2552                .unwrap_or_default(),
2553            property_animations: trs
2554                .PropertyAnimation()
2555                .flat_map(|pa| pa.QualifiedName().map(move |qn| (pa.clone(), qn)))
2556                .filter_map(|(pa, qn)| {
2557                    lookup_property_from_qualified_name_for_state(qn.clone(), r, diag).and_then(
2558                        |(ne, prop_type)| {
2559                            animation_element_from_node(&pa, &qn, prop_type, diag, tr)
2560                                .map(|anim_element| (ne, qn.to_source_location(), anim_element))
2561                        },
2562                    )
2563                })
2564                .collect(),
2565            node: trs.clone(),
2566        }
2567    }
2568}
2569
2570#[derive(Clone, Debug, derive_more::Deref)]
2571pub struct ExportedName {
2572    #[deref]
2573    pub name: SmolStr, // normalized
2574    pub name_ident: SyntaxNode,
2575}
2576
2577impl ExportedName {
2578    pub fn original_name(&self) -> SmolStr {
2579        self.name_ident
2580            .child_token(parser::SyntaxKind::Identifier)
2581            .map(|n| n.to_smolstr())
2582            .unwrap_or_else(|| self.name.clone())
2583    }
2584
2585    pub fn from_export_specifier(
2586        export_specifier: &syntax_nodes::ExportSpecifier,
2587    ) -> (SmolStr, ExportedName) {
2588        let internal_name =
2589            parser::identifier_text(&export_specifier.ExportIdentifier()).unwrap_or_default();
2590
2591        let (name, name_ident): (SmolStr, SyntaxNode) = export_specifier
2592            .ExportName()
2593            .and_then(|ident| {
2594                parser::identifier_text(&ident).map(|text| (text, ident.clone().into()))
2595            })
2596            .unwrap_or_else(|| (internal_name.clone(), export_specifier.ExportIdentifier().into()));
2597        (internal_name, ExportedName { name, name_ident })
2598    }
2599}
2600
2601#[derive(Default, Debug, derive_more::Deref)]
2602pub struct Exports {
2603    #[deref]
2604    components_or_types: Vec<(ExportedName, Either<Rc<Component>, Type>)>,
2605}
2606
2607impl Exports {
2608    pub fn from_node(
2609        doc: &syntax_nodes::Document,
2610        inner_components: &[Rc<Component>],
2611        type_registry: &TypeRegister,
2612        diag: &mut BuildDiagnostics,
2613    ) -> Self {
2614        let resolve_export_to_inner_component_or_import =
2615            |internal_name: &str, internal_name_node: &dyn Spanned, diag: &mut BuildDiagnostics| {
2616                if let Ok(ElementType::Component(c)) = type_registry.lookup_element(internal_name) {
2617                    Some(Either::Left(c))
2618                } else if let ty @ Type::Struct { .. } | ty @ Type::Enumeration(_) =
2619                    type_registry.lookup(internal_name)
2620                {
2621                    Some(Either::Right(ty))
2622                } else if type_registry.lookup_element(internal_name).is_ok()
2623                    || type_registry.lookup(internal_name) != Type::Invalid
2624                {
2625                    diag.push_error(
2626                        format!("Cannot export '{internal_name}' because it is not a component",),
2627                        internal_name_node,
2628                    );
2629                    None
2630                } else {
2631                    diag.push_error(format!("'{internal_name}' not found",), internal_name_node);
2632                    None
2633                }
2634            };
2635
2636        let mut sorted_exports_with_duplicates: Vec<(ExportedName, _)> = Vec::new();
2637
2638        let mut extend_exports =
2639            |it: &mut dyn Iterator<Item = (ExportedName, Either<Rc<Component>, Type>)>| {
2640                for (name, compo_or_type) in it {
2641                    let pos = sorted_exports_with_duplicates
2642                        .partition_point(|(existing_name, _)| existing_name.name <= name.name);
2643                    sorted_exports_with_duplicates.insert(pos, (name, compo_or_type));
2644                }
2645            };
2646
2647        extend_exports(
2648            &mut doc
2649                .ExportsList()
2650                // re-export are handled in the TypeLoader::load_dependencies_recursively_impl
2651                .filter(|exports| exports.ExportModule().is_none())
2652                .flat_map(|exports| exports.ExportSpecifier())
2653                .filter_map(|export_specifier| {
2654                    let (internal_name, exported_name) =
2655                        ExportedName::from_export_specifier(&export_specifier);
2656                    Some((
2657                        exported_name,
2658                        resolve_export_to_inner_component_or_import(
2659                            &internal_name,
2660                            &export_specifier.ExportIdentifier(),
2661                            diag,
2662                        )?,
2663                    ))
2664                }),
2665        );
2666
2667        extend_exports(&mut doc.ExportsList().flat_map(|exports| exports.Component()).filter_map(
2668            |component| {
2669                let name_ident: SyntaxNode = component.DeclaredIdentifier().into();
2670                let name =
2671                    parser::identifier_text(&component.DeclaredIdentifier()).unwrap_or_else(|| {
2672                        debug_assert!(diag.has_errors());
2673                        SmolStr::default()
2674                    });
2675
2676                let compo_or_type =
2677                    resolve_export_to_inner_component_or_import(&name, &name_ident, diag)?;
2678
2679                Some((ExportedName { name, name_ident }, compo_or_type))
2680            },
2681        ));
2682
2683        extend_exports(
2684            &mut doc
2685                .ExportsList()
2686                .flat_map(|exports| {
2687                    exports
2688                        .StructDeclaration()
2689                        .map(|st| st.DeclaredIdentifier())
2690                        .chain(exports.EnumDeclaration().map(|en| en.DeclaredIdentifier()))
2691                })
2692                .filter_map(|name_ident| {
2693                    let name = parser::identifier_text(&name_ident).unwrap_or_else(|| {
2694                        debug_assert!(diag.has_errors());
2695                        SmolStr::default()
2696                    });
2697
2698                    let name_ident = name_ident.into();
2699
2700                    let compo_or_type =
2701                        resolve_export_to_inner_component_or_import(&name, &name_ident, diag)?;
2702
2703                    Some((ExportedName { name, name_ident }, compo_or_type))
2704                }),
2705        );
2706
2707        let mut sorted_deduped_exports = Vec::with_capacity(sorted_exports_with_duplicates.len());
2708        let mut it = sorted_exports_with_duplicates.into_iter().peekable();
2709        while let Some((exported_name, compo_or_type)) = it.next() {
2710            let mut warning_issued_on_first_occurrence = false;
2711
2712            // Skip over duplicates and issue warnings
2713            while it.peek().map(|(name, _)| &name.name) == Some(&exported_name.name) {
2714                let message = format!("Duplicated export '{}'", exported_name.name);
2715
2716                if !warning_issued_on_first_occurrence {
2717                    diag.push_error(message.clone(), &exported_name.name_ident);
2718                    warning_issued_on_first_occurrence = true;
2719                }
2720
2721                let duplicate_loc = it.next().unwrap().0.name_ident;
2722                diag.push_error(message.clone(), &duplicate_loc);
2723            }
2724
2725            sorted_deduped_exports.push((exported_name, compo_or_type));
2726        }
2727
2728        if let Some(last_compo) = inner_components.last() {
2729            let name = last_compo.id.clone();
2730            if last_compo.is_global() {
2731                if sorted_deduped_exports.is_empty() {
2732                    diag.push_warning("Global singleton is implicitly marked for export. This is deprecated and it should be explicitly exported".into(), &last_compo.node.as_ref().map(|n| n.to_source_location()));
2733                    sorted_deduped_exports.push((
2734                        ExportedName { name, name_ident: doc.clone().into() },
2735                        Either::Left(last_compo.clone()),
2736                    ))
2737                }
2738            } else if !sorted_deduped_exports
2739                .iter()
2740                .any(|e| e.1.as_ref().left().is_some_and(|c| !c.is_global()))
2741            {
2742                diag.push_warning("Component is implicitly marked for export. This is deprecated and it should be explicitly exported".into(), &last_compo.node.as_ref().map(|n| n.to_source_location()));
2743                let insert_pos = sorted_deduped_exports
2744                    .partition_point(|(existing_export, _)| existing_export.name <= name);
2745                sorted_deduped_exports.insert(
2746                    insert_pos,
2747                    (
2748                        ExportedName { name, name_ident: doc.clone().into() },
2749                        Either::Left(last_compo.clone()),
2750                    ),
2751                )
2752            }
2753        }
2754        Self { components_or_types: sorted_deduped_exports }
2755    }
2756
2757    pub fn add_reexports(
2758        &mut self,
2759        other_exports: impl IntoIterator<Item = (ExportedName, Either<Rc<Component>, Type>)>,
2760        diag: &mut BuildDiagnostics,
2761    ) {
2762        for export in other_exports {
2763            match self.components_or_types.binary_search_by(|entry| entry.0.cmp(&export.0)) {
2764                Ok(_) => {
2765                    diag.push_warning(
2766                        format!(
2767                            "'{}' is already exported in this file; it will not be re-exported",
2768                            &*export.0
2769                        ),
2770                        &export.0.name_ident,
2771                    );
2772                }
2773                Err(insert_pos) => {
2774                    self.components_or_types.insert(insert_pos, export);
2775                }
2776            }
2777        }
2778    }
2779
2780    pub fn find(&self, name: &str) -> Option<Either<Rc<Component>, Type>> {
2781        self.components_or_types
2782            .binary_search_by(|(exported_name, _)| exported_name.as_str().cmp(name))
2783            .ok()
2784            .map(|index| self.components_or_types[index].1.clone())
2785    }
2786
2787    pub fn retain(
2788        &mut self,
2789        func: impl FnMut(&mut (ExportedName, Either<Rc<Component>, Type>)) -> bool,
2790    ) {
2791        self.components_or_types.retain_mut(func)
2792    }
2793
2794    pub(crate) fn snapshot(&self, snapshotter: &mut crate::typeloader::Snapshotter) -> Self {
2795        let components_or_types = self
2796            .components_or_types
2797            .iter()
2798            .map(|(en, either)| {
2799                let en = en.clone();
2800                let either = match either {
2801                    itertools::Either::Left(l) => itertools::Either::Left({
2802                        Weak::upgrade(&snapshotter.use_component(l))
2803                            .expect("Component should cleanly upgrade here")
2804                    }),
2805                    itertools::Either::Right(r) => itertools::Either::Right(r.clone()),
2806                };
2807                (en, either)
2808            })
2809            .collect();
2810
2811        Self { components_or_types }
2812    }
2813}
2814
2815impl std::iter::IntoIterator for Exports {
2816    type Item = (ExportedName, Either<Rc<Component>, Type>);
2817
2818    type IntoIter = std::vec::IntoIter<Self::Item>;
2819
2820    fn into_iter(self) -> Self::IntoIter {
2821        self.components_or_types.into_iter()
2822    }
2823}
2824
2825/// This function replace the root element of a repeated element. the previous root becomes the only
2826/// child of the new root element.
2827/// Note that no reference to the base component must exist outside of repeated_element.base_type
2828pub fn inject_element_as_repeated_element(repeated_element: &ElementRc, new_root: ElementRc) {
2829    let component = repeated_element.borrow().base_type.as_component().clone();
2830    // Since we're going to replace the repeated element's component, we need to assert that
2831    // outside this function no strong reference exists to it. Then we can unwrap and
2832    // replace the root element.
2833    debug_assert_eq!(Rc::strong_count(&component), 2);
2834    let old_root = &component.root_element;
2835
2836    adjust_geometry_for_injected_parent(&new_root, old_root);
2837
2838    // Any elements with a weak reference to the repeater's component will need fixing later.
2839    let mut elements_with_enclosing_component_reference = Vec::new();
2840    recurse_elem(old_root, &(), &mut |element: &ElementRc, _| {
2841        if let Some(enclosing_component) = element.borrow().enclosing_component.upgrade() {
2842            if Rc::ptr_eq(&enclosing_component, &component) {
2843                elements_with_enclosing_component_reference.push(element.clone());
2844            }
2845        }
2846    });
2847    elements_with_enclosing_component_reference
2848        .extend_from_slice(component.optimized_elements.borrow().as_slice());
2849    elements_with_enclosing_component_reference.push(new_root.clone());
2850
2851    new_root.borrow_mut().child_of_layout =
2852        std::mem::replace(&mut old_root.borrow_mut().child_of_layout, false);
2853    let layout_info_prop = old_root.borrow().layout_info_prop.clone().or_else(|| {
2854        // generate the layout_info_prop that forward to the implicit layout for that item
2855        let li_v = crate::layout::create_new_prop(
2856            &new_root,
2857            SmolStr::new_static("layoutinfo-v"),
2858            crate::typeregister::layout_info_type().into(),
2859        );
2860        let li_h = crate::layout::create_new_prop(
2861            &new_root,
2862            SmolStr::new_static("layoutinfo-h"),
2863            crate::typeregister::layout_info_type().into(),
2864        );
2865        let expr_h = crate::layout::implicit_layout_info_call(old_root, Orientation::Horizontal);
2866        let expr_v = crate::layout::implicit_layout_info_call(old_root, Orientation::Vertical);
2867        let expr_v =
2868            BindingExpression::new_with_span(expr_v, old_root.borrow().to_source_location());
2869        li_v.element().borrow_mut().bindings.insert(li_v.name().clone(), expr_v.into());
2870        let expr_h =
2871            BindingExpression::new_with_span(expr_h, old_root.borrow().to_source_location());
2872        li_h.element().borrow_mut().bindings.insert(li_h.name().clone(), expr_h.into());
2873        Some((li_h.clone(), li_v.clone()))
2874    });
2875    new_root.borrow_mut().layout_info_prop = layout_info_prop;
2876
2877    // Replace the repeated component's element with our shadow element. That requires a bit of reference counting
2878    // surgery and relies on nobody having a strong reference left to the component, which we take out of the Rc.
2879    drop(std::mem::take(&mut repeated_element.borrow_mut().base_type));
2880
2881    debug_assert_eq!(Rc::strong_count(&component), 1);
2882
2883    let mut component = Rc::try_unwrap(component).expect("internal compiler error: more than one strong reference left to repeated component when lowering shadow properties");
2884
2885    let old_root = std::mem::replace(&mut component.root_element, new_root.clone());
2886    new_root.borrow_mut().children.push(old_root);
2887
2888    let component = Rc::new(component);
2889    repeated_element.borrow_mut().base_type = ElementType::Component(component.clone());
2890
2891    for elem in elements_with_enclosing_component_reference {
2892        elem.borrow_mut().enclosing_component = Rc::downgrade(&component);
2893    }
2894}
2895
2896/// Make the geometry of the `injected_parent` that of the old_elem. And the old_elem
2897/// will cover the `injected_parent`
2898pub fn adjust_geometry_for_injected_parent(injected_parent: &ElementRc, old_elem: &ElementRc) {
2899    let mut injected_parent_mut = injected_parent.borrow_mut();
2900    injected_parent_mut.bindings.insert(
2901        "z".into(),
2902        RefCell::new(BindingExpression::new_two_way(NamedReference::new(
2903            old_elem,
2904            SmolStr::new_static("z"),
2905        ))),
2906    );
2907    // (should be removed by const propagation in the llr)
2908    injected_parent_mut.property_declarations.insert(
2909        "dummy".into(),
2910        PropertyDeclaration { property_type: Type::LogicalLength, ..Default::default() },
2911    );
2912    let mut old_elem_mut = old_elem.borrow_mut();
2913    injected_parent_mut.default_fill_parent = std::mem::take(&mut old_elem_mut.default_fill_parent);
2914    injected_parent_mut.geometry_props.clone_from(&old_elem_mut.geometry_props);
2915    drop(injected_parent_mut);
2916    old_elem_mut.geometry_props.as_mut().unwrap().x =
2917        NamedReference::new(injected_parent, SmolStr::new_static("dummy"));
2918    old_elem_mut.geometry_props.as_mut().unwrap().y =
2919        NamedReference::new(injected_parent, SmolStr::new_static("dummy"));
2920}