Skip to main content

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