1use 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
32mod interfaces;
33
34macro_rules! unwrap_or_continue {
35 ($e:expr ; $diag:expr) => {
36 match $e {
37 Some(x) => x,
38 None => {
39 debug_assert!($diag.has_errors()); continue;
41 }
42 }
43 };
44}
45
46#[derive(Default)]
48pub struct Document {
49 pub node: Option<syntax_nodes::Document>,
50 pub inner_components: Vec<Rc<Component>>,
51 pub inner_types: Vec<Type>,
52 pub local_registry: TypeRegister,
53 pub custom_fonts: Vec<(SmolStr, crate::parser::SyntaxToken)>,
56 pub exports: Exports,
57 pub imports: Vec<ImportedTypes>,
58 pub library_exports: HashMap<String, LibraryInfo>,
59
60 pub embedded_file_resources: RefCell<
65 typed_index_collections::TiVec<
66 crate::embedded_resources::EmbeddedResourcesIdx,
67 crate::embedded_resources::EmbeddedResources,
68 >,
69 >,
70
71 #[cfg(feature = "bundle-translations")]
72 pub translation_builder: Option<crate::translations::TranslationsBuilder>,
73
74 pub used_types: RefCell<UsedSubTypes>,
76
77 pub popup_menu_impl: Option<Rc<Component>>,
79}
80
81impl Document {
82 pub fn from_node(
83 node: syntax_nodes::Document,
84 imports: Vec<ImportedTypes>,
85 reexports: Exports,
86 diag: &mut BuildDiagnostics,
87 parent_registry: &Rc<RefCell<TypeRegister>>,
88 ignore_missing_font_files: bool,
89 ) -> Self {
90 debug_assert_eq!(node.kind(), SyntaxKind::Document);
91
92 let mut local_registry = TypeRegister::new(parent_registry);
93 let mut inner_components = Vec::new();
94 let mut inner_types = Vec::new();
95
96 #[cfg(feature = "slint-sc")]
97 for import in &imports {
98 if matches!(
99 import.import_kind,
100 ImportKind::ImportList(_) | ImportKind::ModuleReexport(_)
101 ) {
102 diag.slint_sc_error("Imports are", &import.import_uri_token);
103 }
104 }
105
106 #[cfg(feature = "slint-sc")]
107 let mut sc_exported_count: u32 = 0;
108
109 let mut process_component =
110 |n: syntax_nodes::Component,
111 diag: &mut BuildDiagnostics,
112 local_registry: &mut TypeRegister,
113 #[cfg(feature = "slint-sc")] sc_exported_count: &mut u32,
114 #[cfg(feature = "slint-sc")] is_exported: bool| {
115 #[cfg(feature = "slint-sc")]
117 if n.child_text(SyntaxKind::Identifier).as_deref() != Some("global") {
118 if !is_exported {
119 diag.slint_sc_error("Component declarations are", &n.DeclaredIdentifier());
120 } else {
121 *sc_exported_count += 1;
122 if *sc_exported_count > 1 {
123 diag.slint_sc_error(
124 "Multiple exported components per file are",
125 &n.DeclaredIdentifier(),
126 );
127 }
128 }
129 }
130 let compo = Component::from_node(n, diag, local_registry);
131 if !local_registry.add(compo.clone()) {
132 diag.push_warning(format!("Component '{}' is replacing a previously defined component with the same name", compo.id), &compo.node.clone().unwrap().DeclaredIdentifier());
133 }
134 inner_components.push(compo);
135 };
136 let process_struct = |n: syntax_nodes::StructDeclaration,
137 diag: &mut BuildDiagnostics,
138 local_registry: &mut TypeRegister,
139 inner_types: &mut Vec<Type>| {
140 #[cfg(feature = "slint-sc")]
141 diag.slint_sc_error("Struct declarations are", &n.DeclaredIdentifier());
142 let ty = type_struct_from_node(
143 n.ObjectType(),
144 diag,
145 local_registry,
146 parser::identifier_text(&n.DeclaredIdentifier()),
147 );
148 assert!(matches!(ty, Type::Struct(_)));
149 if !local_registry.insert_type(ty.clone()) {
150 diag.push_warning(
151 format!(
152 "Struct '{ty}' is replacing a previously defined type with the same name"
153 ),
154 &n.DeclaredIdentifier(),
155 );
156 }
157 inner_types.push(ty);
158 };
159 let process_enum = |n: syntax_nodes::EnumDeclaration,
160 diag: &mut BuildDiagnostics,
161 local_registry: &mut TypeRegister,
162 inner_types: &mut Vec<Type>| {
163 #[cfg(feature = "slint-sc")]
164 diag.slint_sc_error("Enum declarations are", &n.DeclaredIdentifier());
165 let Some(name) = parser::identifier_text(&n.DeclaredIdentifier()) else {
166 assert!(diag.has_errors());
167 return;
168 };
169 let mut existing_names = HashSet::new();
170 let values = n
171 .EnumValue()
172 .filter_map(|v| {
173 let value = parser::identifier_text(&v)?;
174 if value == name {
175 diag.push_error(
176 format!("Enum '{value}' can't have a value with the same name"),
177 &v,
178 );
179 None
180 } else if !existing_names.insert(crate::generator::to_pascal_case(&value)) {
181 diag.push_error(format!("Duplicated enum value '{value}'"), &v);
182 None
183 } else {
184 Some(value)
185 }
186 })
187 .collect();
188 let en =
189 Enumeration { name: name.clone(), values, default_value: 0, node: Some(n.clone()) };
190 if en.values.is_empty() {
191 diag.push_error("Enums must have at least one value".into(), &n);
192 }
193
194 let ty = Type::Enumeration(Rc::new(en));
195 if !local_registry.insert_type_with_name(ty.clone(), name.clone()) {
196 diag.push_warning(
197 format!(
198 "Enum '{name}' is replacing a previously defined type with the same name"
199 ),
200 &n.DeclaredIdentifier(),
201 );
202 }
203 inner_types.push(ty);
204 };
205
206 for n in node.children() {
207 match n.kind() {
208 SyntaxKind::Component => {
209 process_component(
210 n.into(),
211 diag,
212 &mut local_registry,
213 #[cfg(feature = "slint-sc")]
214 &mut sc_exported_count,
215 #[cfg(feature = "slint-sc")]
216 false,
217 );
218 }
219 SyntaxKind::StructDeclaration => {
220 process_struct(n.into(), diag, &mut local_registry, &mut inner_types)
221 }
222 SyntaxKind::EnumDeclaration => {
223 process_enum(n.into(), diag, &mut local_registry, &mut inner_types)
224 }
225 SyntaxKind::ExportsList => {
226 for n in n.children() {
227 match n.kind() {
228 SyntaxKind::Component => process_component(
229 n.into(),
230 diag,
231 &mut local_registry,
232 #[cfg(feature = "slint-sc")]
233 &mut sc_exported_count,
234 #[cfg(feature = "slint-sc")]
235 true,
236 ),
237 SyntaxKind::StructDeclaration => process_struct(
238 n.into(),
239 diag,
240 &mut local_registry,
241 &mut inner_types,
242 ),
243 SyntaxKind::EnumDeclaration => {
244 process_enum(n.into(), diag, &mut local_registry, &mut inner_types)
245 }
246 _ => {}
247 }
248 }
249 }
250 _ => {}
251 };
252 }
253 let mut exports = Exports::from_node(&node, &inner_components, &local_registry, diag);
254 exports.add_reexports(reexports, diag);
255
256 let custom_fonts = imports
257 .iter()
258 .filter(|import| matches!(import.import_kind, ImportKind::FileImport))
259 .filter_map(|import| {
260 if crate::pathutils::is_font_file(&import.file) {
261 let token_path = import.import_uri_token.source_file.path();
262 let import_file_path = PathBuf::from(import.file.clone());
263 let import_file_path = crate::pathutils::join(token_path, &import_file_path)
264 .unwrap_or(import_file_path);
265
266 if ignore_missing_font_files
271 || crate::pathutils::is_url(&import_file_path)
272 || crate::fileaccess::load_file(std::path::Path::new(&import_file_path))
273 .is_some()
274 {
275 Some((import_file_path.to_string_lossy().into(), import.import_uri_token.clone()))
276 } else {
277 diag.push_error(
278 format!("File \"{}\" not found", import.file),
279 &import.import_uri_token,
280 );
281 None
282 }
283 } else if import.file.ends_with(".slint") {
284 diag.push_error("Import names are missing. Please specify which types you would like to import".into(), &import.import_uri_token.parent());
285 None
286 } else {
287 diag.push_error(
288 format!("Unsupported foreign import \"{}\"", import.file),
289 &import.import_uri_token,
290 );
291 None
292 }
293 })
294 .collect();
295
296 for local_compo in &inner_components {
297 if exports
298 .components_or_types
299 .iter()
300 .filter_map(|(_, exported_compo_or_type)| exported_compo_or_type.as_ref().left())
301 .any(|exported_compo| Rc::ptr_eq(exported_compo, local_compo))
302 {
303 continue;
304 }
305 if local_compo.is_global() {
308 continue;
309 }
310 if !local_compo.used.get() {
311 diag.push_warning(
312 "Component is neither used nor exported".into(),
313 &local_compo.node.as_ref().map(|n| n.to_source_location()),
314 )
315 }
316 }
317
318 Document {
319 node: Some(node),
320 inner_components,
321 inner_types,
322 local_registry,
323 custom_fonts,
324 imports,
325 exports,
326 library_exports: Default::default(),
327 embedded_file_resources: Default::default(),
328 #[cfg(feature = "bundle-translations")]
329 translation_builder: None,
330 used_types: Default::default(),
331 popup_menu_impl: None,
332 }
333 }
334
335 pub fn exported_roots(&self) -> impl DoubleEndedIterator<Item = Rc<Component>> + '_ {
336 self.exports
337 .iter()
338 .filter_map(|e| e.1.as_ref().left())
339 .filter(|c| !c.is_global() && !c.is_interface())
340 .cloned()
341 }
342
343 pub fn last_exported_component(&self) -> Option<Rc<Component>> {
345 self.exports
346 .iter()
347 .filter_map(|e| Some((&e.0.name_ident, e.1.as_ref().left()?)))
348 .filter(|(_, c)| !c.is_global())
349 .max_by_key(|(n, _)| n.text_range().end())
350 .map(|(_, c)| c.clone())
351 }
352
353 pub fn visit_all_used_components(&self, mut v: impl FnMut(&Rc<Component>)) {
355 let used_types = self.used_types.borrow();
356 for c in &used_types.sub_components {
357 v(c);
358 }
359 for c in self.exported_roots() {
360 v(&c);
361 }
362 for c in &used_types.globals {
363 v(c);
364 }
365 if let Some(c) = &self.popup_menu_impl {
366 v(c);
367 }
368 }
369}
370
371#[derive(Debug, Clone)]
372pub struct PopupWindow {
373 pub component: Rc<Component>,
374 pub x: NamedReference,
375 pub y: NamedReference,
376 pub close_policy: EnumerationValue,
377 pub parent_element: ElementRc,
378 pub is_tooltip: bool,
379 pub is_open: Option<NamedReference>,
383}
384
385#[derive(Debug, Clone)]
386pub struct Timer {
387 pub interval: NamedReference,
388 pub triggered: NamedReference,
389 pub running: NamedReference,
390 pub element: ElementWeak,
391}
392
393#[derive(Clone, Debug)]
394pub struct ChildrenInsertionPoint {
395 pub parent: ElementRc,
396 pub insertion_index: usize,
397 pub node: syntax_nodes::ChildrenPlaceholder,
398}
399
400#[derive(Debug, Default)]
402pub struct UsedSubTypes {
403 pub globals: Vec<Rc<Component>>,
405 pub structs_and_enums: Vec<Type>,
407 pub sub_components: Vec<Rc<Component>>,
410 pub library_types_imports: Vec<(SmolStr, LibraryInfo)>,
413 pub library_global_imports: Vec<(SmolStr, LibraryInfo)>,
416}
417
418#[derive(Debug, Default, Clone)]
419pub struct InitCode {
420 pub constructor_code: Vec<Expression>,
422 pub focus_setting_code: Vec<Expression>,
424 pub font_registration_code: Vec<Expression>,
426
427 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.iter().chain(self.iter_without_font_registration())
435 }
436 pub fn iter_without_font_registration(&self) -> impl Iterator<Item = &Expression> {
438 self.focus_setting_code
439 .iter()
440 .chain(self.constructor_code.iter())
441 .chain(self.inlined_init_code.values())
442 }
443 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Expression> {
444 self.font_registration_code
445 .iter_mut()
446 .chain(self.focus_setting_code.iter_mut())
447 .chain(self.constructor_code.iter_mut())
448 .chain(self.inlined_init_code.values_mut())
449 }
450}
451
452#[derive(Default, Debug)]
455pub struct Component {
456 pub node: Option<syntax_nodes::Component>,
457 pub id: SmolStr,
458 pub root_element: ElementRc,
459
460 pub parent_element: RefCell<ElementWeak>,
462
463 pub optimized_elements: RefCell<Vec<ElementRc>>,
466
467 pub root_constraints: RefCell<LayoutConstraints>,
469
470 pub child_insertion_point: RefCell<Option<ChildrenInsertionPoint>>,
473
474 pub init_code: RefCell<InitCode>,
475
476 pub popup_windows: RefCell<Vec<PopupWindow>>,
477 pub timers: RefCell<Vec<Timer>>,
478 pub menu_item_tree: RefCell<Vec<Rc<Component>>>,
479
480 pub inherits_popup_window: Cell<bool>,
482
483 pub exported_global_names: RefCell<Vec<ExportedName>>,
486
487 pub used: Cell<bool>,
489
490 pub private_properties: RefCell<Vec<(SmolStr, Type)>>,
493
494 pub from_library: Cell<bool>,
496}
497
498impl Component {
499 pub fn from_node(
500 node: syntax_nodes::Component,
501 diag: &mut BuildDiagnostics,
502 tr: &TypeRegister,
503 ) -> Rc<Self> {
504 let mut child_insertion_point = None;
505 let is_legacy_syntax = node.child_token(SyntaxKind::ColonEqual).is_some();
506 let c = Component {
507 node: Some(node.clone()),
508 id: parser::identifier_text(&node.DeclaredIdentifier()).unwrap_or_default(),
509 root_element: Element::from_node(
510 node.Element(),
511 "root".into(),
512 match node.child_text(SyntaxKind::Identifier) {
513 Some(t) if t == "global" => {
514 #[cfg(feature = "slint-sc")]
515 diag.slint_sc_error("Globals are", &node.DeclaredIdentifier());
516 ElementType::Global
517 }
518 Some(t) if t == "interface" => {
519 if !diag.enable_experimental && !tr.expose_internal_types {
520 diag.push_error("'interface' is an experimental feature".into(), &node);
521 ElementType::Error
522 } else {
523 ElementType::Interface
524 }
525 }
526 _ => ElementType::Error,
527 },
528 &mut child_insertion_point,
529 is_legacy_syntax,
530 diag,
531 tr,
532 ),
533 child_insertion_point: RefCell::new(child_insertion_point),
534 ..Default::default()
535 };
536 let c = Rc::new(c);
537 let weak = Rc::downgrade(&c);
538 recurse_elem(&c.root_element, &(), &mut |e, _| {
539 e.borrow_mut().enclosing_component = weak.clone();
540 if let Some(qualified_id) =
541 e.borrow_mut().debug.first_mut().and_then(|x| x.qualified_id.as_mut())
542 {
543 *qualified_id = format_smolstr!("{}::{}", c.id, qualified_id);
544 }
545 });
546 interfaces::apply_uses_statement(&c.root_element, node.UsesSpecifier(), tr, diag);
547 c
548 }
549
550 pub fn is_global(&self) -> bool {
552 match &self.root_element.borrow().base_type {
553 ElementType::Global => true,
554 ElementType::Builtin(c) => c.is_global,
555 _ => false,
556 }
557 }
558
559 pub fn is_interface(&self) -> bool {
561 matches!(&self.root_element.borrow().base_type, ElementType::Interface)
562 }
563
564 pub fn inherits_system_tray_icon(&self) -> bool {
569 self.root_element
570 .borrow()
571 .native_class()
572 .is_some_and(|n| n.class_name.as_str() == "SystemTrayIcon")
573 }
574
575 pub fn global_aliases(&self) -> Vec<SmolStr> {
578 self.exported_global_names
579 .borrow()
580 .iter()
581 .filter(|name| name.as_str() != self.root_element.borrow().id)
582 .map(|name| name.original_name())
583 .collect()
584 }
585
586 pub fn repeater_count(&self) -> u32 {
588 let mut count = 0;
589 recurse_elem(&self.root_element, &(), &mut |element, _| {
590 let element = element.borrow();
591 if let Some(sub_component) = element.sub_component() {
592 count += sub_component.repeater_count();
593 } else if element.repeated.is_some() {
594 count += 1;
595 }
596 });
597 count
598 }
599
600 pub fn parent_element(&self) -> Option<ElementRc> {
606 self.parent_element.borrow().upgrade()
607 }
608}
609
610#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
611pub enum PropertyVisibility {
612 #[default]
613 Private,
614 Input,
615 Output,
616 InOut,
617 Constexpr,
619 Fake,
622 Public,
624 Protected,
625}
626
627impl Display for PropertyVisibility {
628 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
629 match self {
630 PropertyVisibility::Private => f.write_str("private"),
631 PropertyVisibility::Input => f.write_str("input"),
632 PropertyVisibility::Output => f.write_str("output"),
633 PropertyVisibility::InOut => f.write_str("input output"),
634 PropertyVisibility::Constexpr => f.write_str("constexpr"),
635 PropertyVisibility::Public => f.write_str("public"),
636 PropertyVisibility::Protected => f.write_str("protected"),
637 PropertyVisibility::Fake => f.write_str("fake"),
638 }
639 }
640}
641
642#[derive(Clone, Debug, Default)]
643pub struct PropertyDeclaration {
644 pub property_type: Type,
645 pub node: Option<SyntaxNode>,
646 pub expose_in_public_api: bool,
648 pub is_alias: Option<NamedReference>,
650 pub visibility: PropertyVisibility,
651 pub pure: Option<bool>,
653 pub shadows_builtin: bool,
656}
657
658impl PropertyDeclaration {
659 pub fn type_node(&self) -> Option<SyntaxNode> {
661 let node = self.node.as_ref()?;
662 if let Some(x) = syntax_nodes::PropertyDeclaration::new(node.clone()) {
663 Some(x.Type().map_or_else(|| x.into(), |x| x.into()))
664 } else {
665 node.clone().into()
666 }
667 }
668}
669
670impl From<Type> for PropertyDeclaration {
671 fn from(ty: Type) -> Self {
672 PropertyDeclaration { property_type: ty, ..Self::default() }
673 }
674}
675
676#[derive(Debug, Clone, Copy, PartialEq)]
677pub enum TransitionDirection {
678 In,
679 Out,
680 InOut,
681}
682
683#[derive(Debug, Clone)]
684pub struct TransitionPropertyAnimation {
685 pub state_id: i32,
687 pub direction: TransitionDirection,
689 pub animation: ElementRc,
691}
692
693impl TransitionPropertyAnimation {
694 pub fn condition(&self, state: Expression) -> Expression {
697 match self.direction {
698 TransitionDirection::In => Expression::BinaryExpression {
699 lhs: Box::new(Expression::StructFieldAccess {
700 base: Box::new(state),
701 name: "current-state".into(),
702 }),
703 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
704 op: '=',
705 },
706 TransitionDirection::Out => Expression::BinaryExpression {
707 lhs: Box::new(Expression::StructFieldAccess {
708 base: Box::new(state),
709 name: "previous-state".into(),
710 }),
711 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
712 op: '=',
713 },
714 TransitionDirection::InOut => Expression::BinaryExpression {
715 lhs: Box::new(Expression::BinaryExpression {
716 lhs: Box::new(Expression::StructFieldAccess {
717 base: Box::new(state.clone()),
718 name: "current-state".into(),
719 }),
720 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
721 op: '=',
722 }),
723 rhs: Box::new(Expression::BinaryExpression {
724 lhs: Box::new(Expression::StructFieldAccess {
725 base: Box::new(state),
726 name: "previous-state".into(),
727 }),
728 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
729 op: '=',
730 }),
731 op: '|',
732 },
733 }
734 }
735}
736
737#[derive(Debug)]
738pub enum PropertyAnimation {
739 Static(ElementRc),
740 Transition { state_ref: Expression, animations: Vec<TransitionPropertyAnimation> },
741}
742
743impl Clone for PropertyAnimation {
744 fn clone(&self) -> Self {
745 fn deep_clone(e: &ElementRc) -> ElementRc {
746 let e = e.borrow();
747 debug_assert!(e.children.is_empty());
748 debug_assert!(e.property_declarations.is_empty());
749 debug_assert!(e.states.is_empty() && e.transitions.is_empty());
750 Rc::new(RefCell::new(Element {
751 id: e.id.clone(),
752 base_type: e.base_type.clone(),
753 bindings: e.bindings.clone(),
754 property_analysis: e.property_analysis.clone(),
755 enclosing_component: e.enclosing_component.clone(),
756 repeated: None,
757 debug: e.debug.clone(),
758 ..Default::default()
759 }))
760 }
761 match self {
762 PropertyAnimation::Static(e) => PropertyAnimation::Static(deep_clone(e)),
763 PropertyAnimation::Transition { state_ref, animations } => {
764 PropertyAnimation::Transition {
765 state_ref: state_ref.clone(),
766 animations: animations
767 .iter()
768 .map(|t| TransitionPropertyAnimation {
769 state_id: t.state_id,
770 direction: t.direction,
771 animation: deep_clone(&t.animation),
772 })
773 .collect(),
774 }
775 }
776 }
777 }
778}
779
780#[derive(Default, Clone)]
782pub struct AccessibilityProps(pub BTreeMap<String, NamedReference>);
783
784#[derive(Clone, Debug)]
785pub struct GeometryProps {
786 pub x: NamedReference,
787 pub y: NamedReference,
788 pub width: NamedReference,
789 pub height: NamedReference,
790}
791
792impl GeometryProps {
793 pub fn new(element: &ElementRc) -> Self {
794 Self {
795 x: NamedReference::new(element, SmolStr::new_static("x")),
796 y: NamedReference::new(element, SmolStr::new_static("y")),
797 width: NamedReference::new(element, SmolStr::new_static("width")),
798 height: NamedReference::new(element, SmolStr::new_static("height")),
799 }
800 }
801}
802
803pub type BindingsMap = BTreeMap<SmolStr, RefCell<BindingExpression>>;
804
805#[derive(Clone, Debug)]
806pub struct ElementDebugInfo {
807 pub qualified_id: Option<SmolStr>,
809 pub type_name: String,
810 pub element_hash: u64,
815 pub node: syntax_nodes::Element,
816 pub layout: Option<crate::layout::Layout>,
819 pub element_boundary: bool,
823}
824
825impl ElementDebugInfo {
826 fn encoded_element_info(&self) -> String {
830 let mut info = self.type_name.clone();
831 info.push(',');
832 if let Some(id) = self.qualified_id.as_ref() {
833 info.push_str(id);
834 }
835 info.push(',');
836 if let Some(layout) = &self.layout {
837 use crate::layout::{Layout, Orientation};
838 match layout {
839 Layout::BoxLayout(b) => match b.orientation {
840 Orientation::Horizontal => info.push_str("h-box"),
841 Orientation::Vertical => info.push_str("v-box"),
842 },
843 Layout::GridLayout(_) => info.push_str("grid"),
844 Layout::FlexboxLayout(_) => info.push_str("flex-box"),
845 }
846 }
847 info
848 }
849}
850
851#[derive(Default)]
853pub struct Element {
854 pub id: SmolStr,
860 pub base_type: ElementType,
862 pub bindings: BindingsMap,
864 pub change_callbacks: BTreeMap<SmolStr, RefCell<Vec<Expression>>>,
865 pub property_analysis: RefCell<BTreeMap<SmolStr, PropertyAnalysis>>,
866
867 pub children: Vec<ElementRc>,
868 pub enclosing_component: Weak<Component>,
870
871 pub property_declarations: BTreeMap<SmolStr, PropertyDeclaration>,
872
873 pub named_references: crate::namedreference::NamedReferenceContainer,
875
876 pub repeated: Option<RepeatedElementInfo>,
878 pub is_component_placeholder: bool,
880
881 pub states: Vec<State>,
882 pub transitions: Vec<Transition>,
883
884 pub child_of_layout: bool,
886 pub layout_info_prop: Option<(NamedReference, NamedReference)>,
888 pub layout_info_v_with_constraint: Option<NamedReference>,
893 pub layout_info_h_with_constraint: Option<NamedReference>,
897 pub default_fill_parent: (bool, bool),
899
900 pub accessibility_props: AccessibilityProps,
901
902 pub geometry_props: Option<GeometryProps>,
905
906 pub is_flickable_viewport: bool,
908
909 pub has_popup_child: bool,
912
913 pub is_tooltip: bool,
915
916 pub item_index: OnceCell<u32>,
919 pub item_index_of_first_children: OnceCell<u32>,
921
922 pub is_legacy_syntax: bool,
924
925 pub inline_depth: i32,
927
928 pub grid_layout_cell: Option<Rc<RefCell<crate::layout::GridLayoutCell>>>,
930
931 pub debug: Vec<ElementDebugInfo>,
937}
938
939impl Spanned for Element {
940 fn span(&self) -> crate::diagnostics::Span {
941 self.debug
942 .first()
943 .map(|n| {
944 n.node.QualifiedName().as_ref().map(Spanned::span).unwrap_or_else(|| n.node.span())
949 })
950 .unwrap_or_default()
951 }
952
953 fn source_file(&self) -> Option<&crate::diagnostics::SourceFile> {
954 self.debug.first().map(|n| &n.node.source_file)
955 }
956}
957
958impl core::fmt::Debug for Element {
959 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
960 pretty_print(f, self, 0)
961 }
962}
963
964pub fn pretty_print(
965 f: &mut impl std::fmt::Write,
966 e: &Element,
967 indentation: usize,
968) -> std::fmt::Result {
969 if let Some(repeated) = &e.repeated {
970 write!(f, "for {}[{}] in ", repeated.model_data_id, repeated.index_id)?;
971 expression_tree::pretty_print(f, &repeated.model)?;
972 write!(f, ":")?;
973 if let ElementType::Component(base) = &e.base_type {
974 write!(f, "(base) ")?;
975 if base.parent_element().is_some() {
976 pretty_print(f, &base.root_element.borrow(), indentation)?;
977 return Ok(());
978 }
979 }
980 }
981 if e.is_component_placeholder {
982 write!(f, "/* Component Placeholder */ ")?;
983 }
984 writeln!(f, "{} := {} {{ /* {} */", e.id, e.base_type, e.element_infos())?;
985 let mut indentation = indentation + 1;
986 macro_rules! indent {
987 () => {
988 for _ in 0..indentation {
989 write!(f, " ")?
990 }
991 };
992 }
993 for (name, ty) in &e.property_declarations {
994 indent!();
995 if let Some(alias) = &ty.is_alias {
996 writeln!(f, "alias<{}> {} <=> {:?};", ty.property_type, name, alias)?
997 } else {
998 writeln!(f, "property<{}> {};", ty.property_type, name)?
999 }
1000 }
1001 for (name, expr) in &e.bindings {
1002 indent!();
1003 write!(f, "{name}: ")?;
1004 let Ok(expr) = expr.try_borrow() else {
1005 writeln!(f, "<borrowed>")?;
1006 continue;
1007 };
1008 expression_tree::pretty_print(f, &expr.expression)?;
1009 if expr.analysis.as_ref().is_some_and(|a| a.is_const) {
1010 write!(f, "/*const*/")?;
1011 }
1012 writeln!(f, ";")?;
1013 if let Some(anim) = &expr.animation {
1015 indent!();
1016 writeln!(f, "animate {name} {anim:?}")?;
1017 }
1018 for nr in &expr.two_way_bindings {
1019 indent!();
1020 writeln!(f, "{name} <=> {nr:?};")?;
1021 }
1022 }
1023 for (name, ch) in &e.change_callbacks {
1024 for ex in &*ch.borrow() {
1025 indent!();
1026 write!(f, "changed {name} => ")?;
1027 expression_tree::pretty_print(f, ex)?;
1028 writeln!(f)?;
1029 }
1030 }
1031 if !e.states.is_empty() {
1032 indent!();
1033 writeln!(f, "states {:?}", e.states)?;
1034 }
1035 if !e.transitions.is_empty() {
1036 indent!();
1037 writeln!(f, "transitions {:?} ", e.transitions)?;
1038 }
1039 for c in &e.children {
1040 indent!();
1041 pretty_print(f, &c.borrow(), indentation)?
1042 }
1043 if let Some(g) = &e.geometry_props {
1044 indent!();
1045 writeln!(f, "geometry {g:?} ")?;
1046 }
1047
1048 indentation -= 1;
1052 indent!();
1053 writeln!(f, "}}")
1054}
1055
1056#[derive(Clone, Default, Debug)]
1057pub struct PropertyAnalysis {
1058 pub is_set: bool,
1060
1061 pub is_set_externally: bool,
1063
1064 pub is_read: bool,
1067
1068 pub is_read_externally: bool,
1070
1071 pub is_linked_to_read_only: bool,
1073
1074 pub is_linked: bool,
1076}
1077
1078impl PropertyAnalysis {
1079 pub fn merge_with_base(&mut self, other: &PropertyAnalysis) {
1084 self.is_set |= other.is_set;
1085 self.is_read |= other.is_read;
1086 }
1087
1088 pub fn merge(&mut self, other: &PropertyAnalysis) {
1090 self.is_set |= other.is_set;
1091 self.is_read |= other.is_read;
1092 self.is_read_externally |= other.is_read_externally;
1093 self.is_set_externally |= other.is_set_externally;
1094 }
1095
1096 pub fn is_used(&self) -> bool {
1098 self.is_read || self.is_read_externally || self.is_set || self.is_set_externally
1099 }
1100}
1101
1102#[derive(Debug, Clone)]
1103pub struct ListViewInfo {
1104 pub viewport_y: NamedReference,
1105 pub viewport_height: NamedReference,
1106 pub viewport_width: NamedReference,
1107 pub listview_height: NamedReference,
1109 pub listview_width: NamedReference,
1111}
1112
1113#[derive(Debug, Clone)]
1114pub struct RepeatedElementInfo {
1116 pub model: Expression,
1117 pub model_data_id: SmolStr,
1118 pub index_id: SmolStr,
1119 pub is_conditional_element: bool,
1123 pub is_listview: Option<ListViewInfo>,
1125}
1126
1127pub type ElementRc = Rc<RefCell<Element>>;
1128pub type ElementWeak = Weak<RefCell<Element>>;
1129
1130impl Element {
1131 pub fn make_rc(self) -> ElementRc {
1132 let r = ElementRc::new(RefCell::new(self));
1133 let g = GeometryProps::new(&r);
1134 r.borrow_mut().geometry_props = Some(g);
1135 r
1136 }
1137
1138 pub fn from_node(
1139 node: syntax_nodes::Element,
1140 id: SmolStr,
1141 parent_type: ElementType,
1142 component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1143 is_legacy_syntax: bool,
1144 diag: &mut BuildDiagnostics,
1145 tr: &TypeRegister,
1146 ) -> ElementRc {
1147 let base_type = if let Some(base_node) = node.QualifiedName() {
1148 let base = QualifiedTypeName::from_node(base_node.clone());
1149 let base_string = base.to_smolstr();
1150 match parent_type.lookup_type_for_child_element(&base_string, tr) {
1151 Ok(ElementType::Component(c)) if c.is_global() => {
1152 diag.push_error(
1153 "Cannot create an instance of a global component".into(),
1154 &base_node,
1155 );
1156 ElementType::Error
1157 }
1158 Ok(ty) => {
1159 #[cfg(feature = "slint-sc")]
1160 match &ty {
1161 ElementType::Builtin(b) if !b.slint_sc => {
1162 diag.slint_sc_error(
1163 &format!("The builtin element '{}' is", b.name),
1164 &base_node,
1165 );
1166 }
1167 ElementType::Component(_) => {
1168 diag.slint_sc_error(
1169 "Inheriting from user-defined components is",
1170 &base_node,
1171 );
1172 }
1173 _ => {}
1174 }
1175 ty
1176 }
1177 Err(err) => {
1178 diag.push_error(err, &base_node);
1179 ElementType::Error
1180 }
1181 }
1182 } else if parent_type == ElementType::Global || parent_type == ElementType::Interface {
1183 let mut error_on = |node: &dyn Spanned, what: &str| {
1185 let element_type = match parent_type {
1186 ElementType::Global => "A global component",
1187 ElementType::Interface => "An interface",
1188 _ => "An unexpected type",
1189 };
1190 diag.push_error(format!("{element_type} cannot have {what}"), node);
1191 };
1192 node.SubElement().for_each(|n| error_on(&n, "sub elements"));
1193 node.RepeatedElement().for_each(|n| error_on(&n, "sub elements"));
1194 if let Some(n) = node.ChildrenPlaceholder() {
1195 error_on(&n, "sub elements");
1196 }
1197 node.PropertyAnimation().for_each(|n| error_on(&n, "animations"));
1198 node.States().for_each(|n| error_on(&n, "states"));
1199 node.Transitions().for_each(|n| error_on(&n, "transitions"));
1200 node.CallbackDeclaration().for_each(|cb| {
1201 if parser::identifier_text(&cb.DeclaredIdentifier()).is_some_and(|s| s == "init") {
1202 error_on(&cb, "an 'init' callback")
1203 }
1204 });
1205 node.CallbackConnection().for_each(|cb| {
1206 if parser::identifier_text(&cb).is_some_and(|s| s == "init") {
1207 error_on(&cb, "an 'init' callback")
1208 }
1209 });
1210
1211 parent_type
1212 } else if parent_type != ElementType::Error {
1213 assert!(diag.has_errors());
1215 return ElementRc::default();
1216 } else {
1217 tr.empty_type()
1218 };
1219 let qualified_id = (!id.is_empty()).then(|| id.clone());
1221 if let ElementType::Component(c) = &base_type {
1222 c.used.set(true);
1223 }
1224 let type_name = base_type
1225 .type_name()
1226 .filter(|_| base_type != tr.empty_type())
1227 .unwrap_or_default()
1228 .to_string();
1229 let mut r = Element {
1230 id,
1231 base_type: base_type.clone(),
1232 debug: vec![ElementDebugInfo {
1233 qualified_id,
1234 element_hash: 0,
1235 type_name,
1236 node: node.clone(),
1237 layout: None,
1238 element_boundary: false,
1239 }],
1240 is_legacy_syntax,
1241 ..Default::default()
1242 };
1243
1244 let mut property_bindings: Vec<(
1245 SmolStr,
1246 syntax_nodes::BindingExpression,
1247 syntax_nodes::DeclaredIdentifier,
1248 )> = Vec::new();
1249
1250 let mut two_way_bindings: Vec<(
1251 SmolStr,
1252 syntax_nodes::TwoWayBinding,
1253 syntax_nodes::DeclaredIdentifier,
1254 )> = Vec::new();
1255
1256 for prop_decl in node.PropertyDeclaration() {
1257 #[cfg(feature = "slint-sc")]
1258 diag.slint_sc_error("Property declarations are", &prop_decl);
1259 let prop_type = prop_decl
1260 .Type()
1261 .map(|type_node| type_from_node(type_node, diag, tr))
1262 .unwrap_or(Type::InferredProperty);
1264
1265 let unresolved_prop_name =
1266 unwrap_or_continue!(parser::identifier_text(&prop_decl.DeclaredIdentifier()); diag);
1267 let PropertyLookupResult {
1268 resolved_name: prop_name,
1269 property_type: maybe_existing_prop_type,
1270 is_shadowable,
1271 ..
1272 } = r.lookup_property(&unresolved_prop_name);
1273 let shadows_builtin = maybe_existing_prop_type != Type::Invalid && is_shadowable;
1274 match maybe_existing_prop_type {
1275 Type::Invalid => {} _ if shadows_builtin => {}
1279 Type::Callback { .. } => {
1280 diag.push_error(
1281 format!("Cannot declare property '{prop_name}' when a callback with the same name exists"),
1282 &prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(),
1283 );
1284 continue;
1285 }
1286 Type::Function { .. } => {
1287 diag.push_error(
1288 format!("Cannot declare property '{prop_name}' when a function with the same name exists"),
1289 &prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(),
1290 );
1291 continue;
1292 }
1293 _ => {
1294 diag.push_error(
1295 format!("Cannot override property '{unresolved_prop_name}'"),
1296 &prop_decl
1297 .DeclaredIdentifier()
1298 .child_token(SyntaxKind::Identifier)
1299 .unwrap(),
1300 );
1301 continue;
1302 }
1303 }
1304
1305 let mut visibility = None;
1306 for token in prop_decl.children_with_tokens() {
1307 if token.kind() != SyntaxKind::Identifier {
1308 continue;
1309 }
1310 match (token.as_token().unwrap().text(), visibility) {
1311 ("in", None) => visibility = Some(PropertyVisibility::Input),
1312 ("in", Some(_)) => diag.push_error("Extra 'in' keyword".into(), &token),
1313 ("out", None) => visibility = Some(PropertyVisibility::Output),
1314 ("out", Some(_)) => diag.push_error("Extra 'out' keyword".into(), &token),
1315 ("in-out" | "in_out", None) => visibility = Some(PropertyVisibility::InOut),
1316 ("in-out" | "in_out", Some(_)) => {
1317 diag.push_error("Extra 'in-out' keyword".into(), &token)
1318 }
1319 ("private", None) => visibility = Some(PropertyVisibility::Private),
1320 ("private", Some(_)) => {
1321 diag.push_error("Extra 'private' keyword".into(), &token)
1322 }
1323 _ => (),
1324 }
1325 }
1326 let visibility = visibility.unwrap_or({
1327 if is_legacy_syntax {
1328 PropertyVisibility::InOut
1329 } else {
1330 PropertyVisibility::Private
1331 }
1332 });
1333
1334 if base_type == ElementType::Interface && visibility == PropertyVisibility::Private {
1335 diag.push_error(
1336 "'private' properties are inaccessible in an interface".into(),
1337 &prop_decl,
1338 );
1339 }
1340
1341 r.property_declarations.insert(
1344 unresolved_prop_name.clone(),
1345 PropertyDeclaration {
1346 property_type: prop_type,
1347 node: Some(prop_decl.clone().into()),
1348 visibility,
1349 shadows_builtin,
1350 ..Default::default()
1351 },
1352 );
1353
1354 if let Some(csn) = prop_decl.BindingExpression() {
1355 property_bindings.push((
1356 unresolved_prop_name.clone(),
1357 csn,
1358 prop_decl.DeclaredIdentifier(),
1359 ));
1360 }
1361
1362 if let Some(csn) = prop_decl.TwoWayBinding() {
1363 #[cfg(feature = "slint-sc")]
1364 diag.slint_sc_error("Two-way bindings are", &csn);
1365 two_way_bindings.push((unresolved_prop_name, csn, prop_decl.DeclaredIdentifier()));
1366 }
1367 }
1368
1369 let implemented_interface = interfaces::get_implemented_interface(&r, &node, tr, diag);
1370 interfaces::apply_properties(&mut r, &implemented_interface, diag);
1371
1372 for (prop_name, csn, source) in property_bindings {
1373 match r.bindings.entry(prop_name.clone()) {
1374 Entry::Vacant(e) => {
1375 e.insert(BindingExpression::new_uncompiled(csn.into()).into());
1376 }
1377 Entry::Occupied(_) => {
1378 diag.push_error("Duplicated property binding".into(), &source);
1379 }
1380 }
1381 }
1382
1383 for (prop_name, csn, source) in two_way_bindings {
1384 if r.bindings
1385 .insert(prop_name, BindingExpression::new_uncompiled(csn.into()).into())
1386 .is_some()
1387 {
1388 diag.push_error("Duplicated property binding".into(), &source);
1389 }
1390 }
1391
1392 r.parse_bindings(
1393 node.Binding().filter_map(|b| {
1394 Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into()))
1395 }),
1396 is_legacy_syntax,
1397 diag,
1398 );
1399 r.parse_bindings(
1400 node.TwoWayBinding()
1401 .filter_map(|b| Some((b.child_token(SyntaxKind::Identifier)?, b.into()))),
1402 is_legacy_syntax,
1403 diag,
1404 );
1405
1406 apply_default_type_properties(&mut r);
1407
1408 for sig_decl in node.CallbackDeclaration() {
1409 #[cfg(feature = "slint-sc")]
1410 diag.slint_sc_error("Callback declarations are", &sig_decl);
1411 let name =
1412 unwrap_or_continue!(parser::identifier_text(&sig_decl.DeclaredIdentifier()); diag);
1413
1414 let pure = Some(
1415 sig_decl.child_token(SyntaxKind::Identifier).is_some_and(|t| t.text() == "pure"),
1416 );
1417
1418 let PropertyLookupResult {
1419 resolved_name: existing_name,
1420 property_type: maybe_existing_prop_type,
1421 is_shadowable,
1422 ..
1423 } = r.lookup_property(&name);
1424 let shadows_builtin = !matches!(maybe_existing_prop_type, Type::Invalid);
1427 if shadows_builtin && !is_shadowable {
1428 if matches!(maybe_existing_prop_type, Type::Callback { .. }) {
1429 if r.property_declarations.contains_key(&name) {
1430 diag.push_error(
1431 "Duplicated callback declaration".into(),
1432 &sig_decl.DeclaredIdentifier(),
1433 );
1434 } else {
1435 diag.push_error(
1436 format!("Cannot override callback '{existing_name}'"),
1437 &sig_decl.DeclaredIdentifier(),
1438 )
1439 }
1440 } else {
1441 diag.push_error(
1442 format!(
1443 "Cannot declare callback '{existing_name}' when a {} with the same name exists",
1444 if matches!(maybe_existing_prop_type, Type::Function { .. }) { "function" } else { "property" }
1445 ),
1446 &sig_decl.DeclaredIdentifier(),
1447 );
1448 }
1449 continue;
1450 }
1451
1452 if let Some(csn) = sig_decl.TwoWayBinding() {
1453 r.bindings
1454 .insert(name.clone(), BindingExpression::new_uncompiled(csn.into()).into());
1455 r.property_declarations.insert(
1456 name,
1457 PropertyDeclaration {
1458 property_type: Type::InferredCallback,
1459 node: Some(sig_decl.into()),
1460 visibility: PropertyVisibility::InOut,
1461 pure,
1462 shadows_builtin,
1463 ..Default::default()
1464 },
1465 );
1466 continue;
1467 }
1468
1469 let args = sig_decl
1470 .CallbackDeclarationParameter()
1471 .map(|p| type_from_node(p.Type(), diag, tr))
1472 .collect();
1473 let return_type = sig_decl
1474 .ReturnType()
1475 .map(|ret_ty| type_from_node(ret_ty.Type(), diag, tr))
1476 .unwrap_or(Type::Void);
1477 let arg_names = sig_decl
1478 .CallbackDeclarationParameter()
1479 .map(|a| {
1480 a.DeclaredIdentifier()
1481 .and_then(|x| parser::identifier_text(&x))
1482 .unwrap_or_default()
1483 })
1484 .collect();
1485 r.property_declarations.insert(
1486 name,
1487 PropertyDeclaration {
1488 property_type: Type::Callback(Rc::new(Function {
1489 return_type,
1490 args,
1491 arg_names,
1492 })),
1493 node: Some(sig_decl.into()),
1494 visibility: PropertyVisibility::InOut,
1495 pure,
1496 shadows_builtin,
1497 ..Default::default()
1498 },
1499 );
1500 }
1501
1502 interfaces::apply_callbacks(&mut r, &implemented_interface, diag);
1503
1504 for func in node.Function() {
1505 #[cfg(feature = "slint-sc")]
1506 diag.slint_sc_error("Function declarations are", &func);
1507 let name =
1508 unwrap_or_continue!(parser::identifier_text(&func.DeclaredIdentifier()); diag);
1509
1510 let PropertyLookupResult {
1511 resolved_name: existing_name,
1512 property_type: maybe_existing_prop_type,
1513 is_shadowable,
1514 ..
1515 } = r.lookup_property(&name);
1516 let shadows_builtin = !matches!(maybe_existing_prop_type, Type::Invalid);
1519 if shadows_builtin && !is_shadowable {
1520 if matches!(maybe_existing_prop_type, Type::Callback { .. } | Type::Function { .. })
1521 {
1522 diag.push_error(
1523 format!("Cannot override '{existing_name}'"),
1524 &func.DeclaredIdentifier(),
1525 )
1526 } else {
1527 diag.push_error(
1528 format!("Cannot declare function '{existing_name}' when a property with the same name exists"),
1529 &func.DeclaredIdentifier(),
1530 );
1531 }
1532 continue;
1533 }
1534
1535 let mut args = Vec::new();
1536 let mut arg_names = Vec::new();
1537 for a in func.ArgumentDeclaration() {
1538 args.push(type_from_node(a.Type(), diag, tr));
1539 let name =
1540 unwrap_or_continue!(parser::identifier_text(&a.DeclaredIdentifier()); diag);
1541 if arg_names.contains(&name) {
1542 diag.push_error(
1543 format!("Duplicated argument name '{name}'"),
1544 &a.DeclaredIdentifier(),
1545 );
1546 }
1547 arg_names.push(name);
1548 }
1549 let return_type = func
1550 .ReturnType()
1551 .map_or(Type::Void, |ret_ty| type_from_node(ret_ty.Type(), diag, tr));
1552
1553 let mut visibility = PropertyVisibility::Private;
1554 let mut pure = None;
1555 for token in func.children_with_tokens() {
1556 if token.kind() != SyntaxKind::Identifier {
1557 continue;
1558 }
1559 match token.as_token().unwrap().text() {
1560 "pure" => pure = Some(true),
1561 "public" => {
1562 visibility = PropertyVisibility::Public;
1563 pure = pure.or(Some(false));
1564 }
1565 "protected" => {
1566 visibility = PropertyVisibility::Protected;
1567 pure = pure.or(Some(false));
1568 }
1569 _ => (),
1570 }
1571 }
1572
1573 if base_type == ElementType::Interface && visibility != PropertyVisibility::Public {
1574 diag.push_error(
1575 "Function declarations in an interface must be public".into(),
1576 &func,
1577 );
1578 }
1579
1580 let declaration = PropertyDeclaration {
1581 property_type: Type::Function(Rc::new(Function { return_type, args, arg_names })),
1582 node: Some(func.clone().into()),
1583 visibility,
1584 pure,
1585 shadows_builtin,
1586 ..Default::default()
1587 };
1588
1589 match (base_type.clone(), func.CodeBlock()) {
1590 (ElementType::Interface, Some(code_block)) => {
1591 diag.push_error(
1592 "Function declarations in interfaces must not have a body".into(),
1593 &code_block,
1594 );
1595 continue;
1596 }
1597 (ElementType::Interface, None) => {
1598 r.property_declarations.insert(name, declaration);
1601 continue;
1602 }
1603 (_, None) => {
1604 diag.push_error("Functions must have a code block".into(), &func);
1605 }
1606 (_, Some(_)) => {}
1607 }
1608
1609 if r.bindings
1610 .insert(name.clone(), BindingExpression::new_uncompiled(func.clone().into()).into())
1611 .is_some()
1612 {
1613 assert!(diag.has_errors());
1614 }
1615
1616 r.property_declarations.insert(name, declaration);
1617 }
1618
1619 for con_node in node.CallbackConnection() {
1620 #[cfg(feature = "slint-sc")]
1621 diag.slint_sc_error("Callback handlers are", &con_node);
1622 let unresolved_name = unwrap_or_continue!(parser::identifier_text(&con_node); diag);
1623 let PropertyLookupResult { resolved_name, property_type, .. } =
1624 r.lookup_property(&unresolved_name);
1625 if let Type::Callback(callback) = &property_type {
1626 let num_arg = con_node.DeclaredIdentifier().count();
1627 if num_arg > callback.args.len() {
1628 diag.push_error(
1629 format!(
1630 "'{}' only has {} arguments, but {} were provided",
1631 unresolved_name,
1632 callback.args.len(),
1633 num_arg
1634 ),
1635 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
1636 );
1637 }
1638 } else if property_type == Type::InferredCallback {
1639 } else {
1641 if r.base_type != ElementType::Error {
1642 diag.push_error(
1643 format!("'{}' is not a callback in {}", unresolved_name, r.base_type),
1644 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
1645 );
1646 }
1647 continue;
1648 }
1649 match r.bindings.entry(resolved_name.into()) {
1650 Entry::Vacant(e) => {
1651 e.insert(BindingExpression::new_uncompiled(con_node.clone().into()).into());
1652 }
1653 Entry::Occupied(mut e) => {
1654 let is_global_alias = r.base_type == ElementType::Global
1659 && matches!(
1660 &e.get().borrow().expression,
1661 Expression::Uncompiled(node) if node.kind() == SyntaxKind::TwoWayBinding
1662 );
1663 if is_global_alias {
1664 let mut handler =
1669 BindingExpression::new_uncompiled(con_node.clone().into());
1670 if let Some(name) = con_node.child_token(SyntaxKind::Identifier) {
1671 handler.span = Some(name.to_source_location());
1672 }
1673 e.insert(handler.into());
1674 } else {
1675 diag.push_error(
1676 "Duplicated callback".into(),
1677 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
1678 );
1679 }
1680 }
1681 }
1682 }
1683
1684 for anim in node.PropertyAnimation() {
1685 #[cfg(feature = "slint-sc")]
1686 diag.slint_sc_error("Animations are", &anim);
1687 if let Some(star) = anim.child_token(SyntaxKind::Star) {
1688 diag.push_error(
1689 "catch-all property is only allowed within transitions".into(),
1690 &star,
1691 )
1692 };
1693 for prop_name_token in anim.QualifiedName() {
1694 match QualifiedTypeName::from_node(prop_name_token.clone()).members.as_slice() {
1695 [unresolved_prop_name] => {
1696 if r.base_type == ElementType::Error {
1697 continue;
1698 };
1699 let lookup_result = r.lookup_property(unresolved_prop_name);
1700 let valid_assign = lookup_result.is_valid_for_assignment();
1701 if let Some(anim_element) = animation_element_from_node(
1702 &anim,
1703 &prop_name_token,
1704 lookup_result.property_type,
1705 diag,
1706 tr,
1707 ) {
1708 if !valid_assign {
1709 diag.push_error(
1710 format!(
1711 "Cannot animate {} property '{}'",
1712 lookup_result.property_visibility, unresolved_prop_name
1713 ),
1714 &prop_name_token,
1715 );
1716 }
1717
1718 if unresolved_prop_name != lookup_result.resolved_name.as_ref() {
1719 diag.push_property_deprecation_warning(
1720 unresolved_prop_name,
1721 &lookup_result.resolved_name,
1722 &prop_name_token,
1723 );
1724 }
1725
1726 let expr_binding = r
1727 .bindings
1728 .entry(lookup_result.resolved_name.into())
1729 .or_insert_with(|| {
1730 let mut r = BindingExpression::from(Expression::Invalid);
1731 r.priority = 1;
1732 r.span = Some(prop_name_token.to_source_location());
1733 r.into()
1734 });
1735 if expr_binding
1736 .get_mut()
1737 .animation
1738 .replace(PropertyAnimation::Static(anim_element))
1739 .is_some()
1740 {
1741 diag.push_error("Duplicated animation".into(), &prop_name_token)
1742 }
1743 }
1744 }
1745 _ => diag.push_error(
1746 "Can only refer to property in the current element".into(),
1747 &prop_name_token,
1748 ),
1749 }
1750 }
1751 }
1752
1753 for ch in node.PropertyChangedCallback() {
1754 #[cfg(feature = "slint-sc")]
1755 diag.slint_sc_error("Change callbacks are", &ch);
1756 let Some(prop) = parser::identifier_text(&ch.DeclaredIdentifier()) else { continue };
1757 let lookup_result = r.lookup_property(&prop);
1758 if !lookup_result.is_valid() {
1759 if r.base_type != ElementType::Error {
1760 diag.push_error(
1761 format!("Property '{prop}' does not exist"),
1762 &ch.DeclaredIdentifier(),
1763 );
1764 }
1765 } else if !lookup_result.property_type.is_property_type() {
1766 let what = match lookup_result.property_type {
1767 Type::Function { .. } => "a function",
1768 Type::Callback { .. } => "a callback",
1769 _ => "not a property",
1770 };
1771 diag.push_error(
1772 format!(
1773 "Change callback can only be set on properties, and '{prop}' is {what}"
1774 ),
1775 &ch.DeclaredIdentifier(),
1776 );
1777 } else if lookup_result.property_visibility == PropertyVisibility::Private
1778 && !lookup_result.is_local_to_component
1779 {
1780 diag.push_error(
1781 format!("Change callback on a private property '{prop}'"),
1782 &ch.DeclaredIdentifier(),
1783 );
1784 }
1785 let handler = Expression::Uncompiled(ch.clone().into());
1786 match r.change_callbacks.entry(prop) {
1787 Entry::Vacant(e) => {
1788 e.insert(vec![handler].into());
1789 }
1790 Entry::Occupied(mut e) => {
1791 diag.push_error(
1792 format!("Duplicated change callback on '{}'", e.key()),
1793 &ch.DeclaredIdentifier(),
1794 );
1795 e.get_mut().get_mut().push(handler);
1796 }
1797 }
1798 }
1799
1800 let mut children_placeholder = None;
1801 let r = r.make_rc();
1802
1803 for se in node.children() {
1804 if se.kind() == SyntaxKind::SubElement {
1805 let parent_type = r.borrow().base_type.clone();
1806 r.borrow_mut().children.push(Element::from_sub_element_node(
1807 se.into(),
1808 parent_type,
1809 component_child_insertion_point,
1810 is_legacy_syntax,
1811 diag,
1812 tr,
1813 ));
1814 } else if se.kind() == SyntaxKind::RepeatedElement {
1815 let mut sub_child_insertion_point = None;
1816 let rep = Element::from_repeated_node(
1817 se.into(),
1818 &r,
1819 &mut sub_child_insertion_point,
1820 is_legacy_syntax,
1821 diag,
1822 tr,
1823 );
1824 if let Some(ChildrenInsertionPoint { node: se, .. }) = sub_child_insertion_point {
1825 diag.push_error(
1826 "The @children placeholder cannot appear in a repeated element".into(),
1827 &se,
1828 )
1829 }
1830 r.borrow_mut().children.push(rep);
1831 } else if se.kind() == SyntaxKind::ConditionalElement {
1832 let mut sub_child_insertion_point = None;
1833 let rep = Element::from_conditional_node(
1834 se.into(),
1835 r.borrow().base_type.clone(),
1836 &mut sub_child_insertion_point,
1837 is_legacy_syntax,
1838 diag,
1839 tr,
1840 );
1841 if let Some(ChildrenInsertionPoint { node: se, .. }) = sub_child_insertion_point {
1842 diag.push_error(
1843 "The @children placeholder cannot appear in a conditional element".into(),
1844 &se,
1845 )
1846 }
1847 r.borrow_mut().children.push(rep);
1848 } else if se.kind() == SyntaxKind::ChildrenPlaceholder {
1849 #[cfg(feature = "slint-sc")]
1850 diag.slint_sc_error("The @children placeholder is", &se);
1851 if children_placeholder.is_some() {
1852 diag.push_error(
1853 "The @children placeholder can only appear once in an element".into(),
1854 &se,
1855 )
1856 } else {
1857 children_placeholder = Some((se.clone().into(), r.borrow().children.len()));
1858 }
1859 }
1860 }
1861
1862 if let Some((children_placeholder, index)) = children_placeholder {
1863 if component_child_insertion_point.is_some() {
1864 diag.push_error(
1865 "The @children placeholder can only appear once in an element hierarchy".into(),
1866 &children_placeholder,
1867 )
1868 } else {
1869 *component_child_insertion_point = Some(ChildrenInsertionPoint {
1870 parent: r.clone(),
1871 insertion_index: index,
1872 node: children_placeholder,
1873 });
1874 }
1875 }
1876
1877 #[cfg(feature = "slint-sc")]
1878 for s_node in node.States() {
1879 diag.slint_sc_error("States are", &s_node);
1880 }
1881 for state in node.States().flat_map(|s| s.State()) {
1882 let s = State {
1883 id: parser::identifier_text(&state.DeclaredIdentifier()).unwrap_or_default(),
1884 condition: state.Expression().map(|e| Expression::Uncompiled(e.into())),
1885 property_changes: state
1886 .StatePropertyChange()
1887 .filter_map(|s| {
1888 lookup_property_from_qualified_name_for_state(s.QualifiedName(), &r, diag)
1889 .map(|(ne, ty)| {
1890 if !ty.is_property_type() && !matches!(ty, Type::Invalid) {
1891 diag.push_error(
1892 format!("'{}' is not a property", **s.QualifiedName()),
1893 &s,
1894 );
1895 }
1896 (ne, Expression::Uncompiled(s.BindingExpression().into()), s)
1897 })
1898 })
1899 .collect(),
1900 };
1901 for trs in state.Transition() {
1902 let mut t = Transition::from_node(trs, &r, tr, diag);
1903 t.state_id.clone_from(&s.id);
1904 r.borrow_mut().transitions.push(t);
1905 }
1906 r.borrow_mut().states.push(s);
1907 }
1908
1909 for ts in node.Transitions() {
1910 #[cfg(feature = "slint-sc")]
1911 diag.slint_sc_error("Transitions are", &ts);
1912 if !is_legacy_syntax {
1913 diag.push_error("'transitions' block are no longer supported. Use 'in {...}' and 'out {...}' directly in the state definition".into(), &ts);
1914 }
1915 for trs in ts.Transition() {
1916 let trans = Transition::from_node(trs, &r, tr, diag);
1917 r.borrow_mut().transitions.push(trans);
1918 }
1919 }
1920
1921 if r.borrow().base_type.to_smolstr() == "ListView" {
1922 let mut seen_for = false;
1923 for se in node.children() {
1924 if se.kind() == SyntaxKind::RepeatedElement && !seen_for {
1925 seen_for = true;
1926 } else if matches!(
1927 se.kind(),
1928 SyntaxKind::SubElement
1929 | SyntaxKind::ConditionalElement
1930 | SyntaxKind::RepeatedElement
1931 | SyntaxKind::ChildrenPlaceholder
1932 ) {
1933 diag.push_error("A ListView can just have a single 'for' as children. Anything else is not supported".into(), &se)
1934 }
1935 }
1936 }
1937
1938 interfaces::apply_default_property_values(&r, &implemented_interface);
1939 interfaces::validate_function_implementations(&r.borrow(), &implemented_interface, diag);
1940
1941 r
1942 }
1943
1944 fn from_sub_element_node(
1945 node: syntax_nodes::SubElement,
1946 parent_type: ElementType,
1947 component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1948 is_in_legacy_component: bool,
1949 diag: &mut BuildDiagnostics,
1950 tr: &TypeRegister,
1951 ) -> ElementRc {
1952 let mut id = parser::identifier_text(&node).unwrap_or_default();
1953 if matches!(id.as_ref(), "parent" | "self" | "root") {
1954 diag.push_error(
1955 format!("'{id}' is a reserved id"),
1956 &node.child_token(SyntaxKind::Identifier).unwrap(),
1957 );
1958 id = SmolStr::default();
1959 }
1960 Element::from_node(
1961 node.Element(),
1962 id,
1963 parent_type,
1964 component_child_insertion_point,
1965 is_in_legacy_component,
1966 diag,
1967 tr,
1968 )
1969 }
1970
1971 fn from_repeated_node(
1972 node: syntax_nodes::RepeatedElement,
1973 parent: &ElementRc,
1974 component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1975 is_in_legacy_component: bool,
1976 diag: &mut BuildDiagnostics,
1977 tr: &TypeRegister,
1978 ) -> ElementRc {
1979 #[cfg(feature = "slint-sc")]
1980 diag.slint_sc_error("Repeated elements (for-in) are", &node);
1981 let e = Element::from_sub_element_node(
1982 node.SubElement(),
1983 parent.borrow().base_type.clone(),
1984 component_child_insertion_point,
1985 is_in_legacy_component,
1986 diag,
1987 tr,
1988 );
1989 let parent_is_listview = {
1990 let parent = parent.borrow();
1991 parent.base_type.to_string() == "ListView"
1992 && [
1994 "viewport-y",
1995 "viewport-height",
1996 "viewport-width",
1997 "visible-height",
1998 "visible-width",
1999 ]
2000 .iter()
2001 .all(|p| parent.lookup_property(p).property_type == Type::LogicalLength)
2002 };
2003 let is_listview = if parent_is_listview
2004 && let Some(geometry_props) = e.borrow().geometry_props.as_ref()
2005 {
2006 let lvi = ListViewInfo {
2007 viewport_y: NamedReference::new(parent, SmolStr::new_static("viewport-y")),
2008 viewport_height: NamedReference::new(
2009 parent,
2010 SmolStr::new_static("viewport-height"),
2011 ),
2012 viewport_width: NamedReference::new(parent, SmolStr::new_static("viewport-width")),
2013 listview_height: NamedReference::new(parent, SmolStr::new_static("visible-height")),
2014 listview_width: NamedReference::new(parent, SmolStr::new_static("visible-width")),
2015 };
2016 lvi.viewport_height.mark_as_set();
2018 lvi.viewport_width.mark_as_set();
2019 geometry_props.y.mark_as_set();
2020 Some(lvi)
2021 } else {
2022 None
2023 };
2024 let rei = RepeatedElementInfo {
2025 model: Expression::Uncompiled(node.Expression().into()),
2026 model_data_id: node
2027 .DeclaredIdentifier()
2028 .and_then(|n| parser::identifier_text(&n))
2029 .unwrap_or_default(),
2030 index_id: node
2031 .RepeatedIndex()
2032 .and_then(|r| parser::identifier_text(&r))
2033 .unwrap_or_default(),
2034 is_conditional_element: false,
2035 is_listview,
2036 };
2037 e.borrow_mut().repeated = Some(rei);
2038 e
2039 }
2040
2041 fn from_conditional_node(
2042 node: syntax_nodes::ConditionalElement,
2043 parent_type: ElementType,
2044 component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
2045 is_in_legacy_component: bool,
2046 diag: &mut BuildDiagnostics,
2047 tr: &TypeRegister,
2048 ) -> ElementRc {
2049 #[cfg(feature = "slint-sc")]
2050 diag.slint_sc_error("Conditional elements (if) are", &node);
2051 let rei = RepeatedElementInfo {
2052 model: Expression::Uncompiled(node.Expression().into()),
2053 model_data_id: SmolStr::default(),
2054 index_id: SmolStr::default(),
2055 is_conditional_element: true,
2056 is_listview: None,
2057 };
2058 let e = Element::from_sub_element_node(
2059 node.SubElement(),
2060 parent_type,
2061 component_child_insertion_point,
2062 is_in_legacy_component,
2063 diag,
2064 tr,
2065 );
2066 e.borrow_mut().repeated = Some(rei);
2067 e
2068 }
2069
2070 pub fn lookup_property<'a>(&self, name: &'a str) -> PropertyLookupResult<'a> {
2074 self.property_declarations.get(name).map_or_else(
2075 || {
2076 let mut r = self.base_type.lookup_property(name);
2077 r.is_in_direct_base = r.is_local_to_component;
2078 r.is_local_to_component = false;
2079 r
2080 },
2081 |p| PropertyLookupResult {
2082 resolved_name: name.into(),
2083 property_type: p.property_type.clone(),
2084 property_visibility: p.visibility,
2085 declared_pure: p.pure,
2086 is_local_to_component: true,
2087 is_in_direct_base: false,
2088 is_shadowable: false,
2089 builtin_function: None,
2090 },
2091 )
2092 }
2093
2094 fn parse_bindings(
2095 &mut self,
2096 bindings: impl Iterator<Item = (crate::parser::SyntaxToken, SyntaxNode)>,
2097 is_in_legacy_component: bool,
2098 diag: &mut BuildDiagnostics,
2099 ) {
2100 for (name_token, b) in bindings {
2101 #[cfg(feature = "slint-sc")]
2102 diag.slint_sc_error("Bindings are", &name_token);
2103 let unresolved_name = crate::parser::normalize_identifier(name_token.text());
2104 let lookup_result = self.lookup_property(&unresolved_name);
2105 if !lookup_result.property_type.is_property_type() {
2106 match lookup_result.property_type {
2107 Type::Invalid => {
2108 if self.base_type != ElementType::Error {
2109 let msg = if let Some(suggestion) = css_property_suggestion(&unresolved_name, &self.base_type) {
2110 suggestion
2111 } else if self.base_type.to_smolstr() == "Empty" {
2112 format!( "Unknown property {unresolved_name}")
2113 } else {
2114 format!( "Unknown property {unresolved_name} in {}", self.base_type)
2115 };
2116 diag.push_error(msg, &name_token);
2117 }
2118 }
2119 Type::Callback { .. } => {
2120 diag.push_error(format!("'{unresolved_name}' is a callback. Use `=>` to connect"),
2121 &name_token)
2122 }
2123 _ => diag.push_error(format!(
2124 "Cannot assign to {} in {} because it does not have a valid property type",
2125 unresolved_name, self.base_type,
2126 ),
2127 &name_token),
2128 }
2129 } else if !lookup_result.is_local_to_component
2130 && (lookup_result.property_visibility == PropertyVisibility::Private
2131 || lookup_result.property_visibility == PropertyVisibility::Output)
2132 {
2133 if is_in_legacy_component
2134 && lookup_result.property_visibility == PropertyVisibility::Output
2135 {
2136 diag.push_warning(
2137 format!("Assigning to output property '{unresolved_name}' is deprecated"),
2138 &name_token,
2139 );
2140 } else {
2141 diag.push_error(
2142 format!(
2143 "Cannot assign to {} property '{}'",
2144 lookup_result.property_visibility, unresolved_name
2145 ),
2146 &name_token,
2147 );
2148 }
2149 }
2150
2151 if *lookup_result.resolved_name != *unresolved_name {
2152 diag.push_property_deprecation_warning(
2153 &unresolved_name,
2154 &lookup_result.resolved_name,
2155 &name_token,
2156 );
2157 }
2158
2159 match self.bindings.entry(lookup_result.resolved_name.into()) {
2160 Entry::Occupied(_) => {
2161 diag.push_error("Duplicated property binding".into(), &name_token);
2162 }
2163 Entry::Vacant(entry) => {
2164 entry.insert(BindingExpression::new_uncompiled(b).into());
2165 }
2166 };
2167 }
2168 }
2169
2170 pub fn callback_alias_declaration_node(
2176 &self,
2177 name: &str,
2178 ) -> Option<syntax_nodes::TwoWayBinding> {
2179 self.property_declarations
2180 .get(name)
2181 .and_then(|d| d.node.clone())
2182 .and_then(syntax_nodes::CallbackDeclaration::new)
2183 .and_then(|cb| cb.TwoWayBinding())
2184 }
2185
2186 pub fn two_way_binding_node(&self, name: &str) -> Option<syntax_nodes::TwoWayBinding> {
2193 if let Some(binding) = self.bindings.get(name)
2194 && let Ok(b) = binding.try_borrow()
2195 && let Expression::Uncompiled(node) = b.expression.ignore_debug_hooks()
2196 && let Some(twb) = syntax_nodes::TwoWayBinding::new(node.clone())
2197 {
2198 return Some(twb);
2199 }
2200 self.callback_alias_declaration_node(name)
2201 }
2202
2203 pub fn native_class(&self) -> Option<Rc<NativeClass>> {
2204 let mut base_type = self.base_type.clone();
2205 loop {
2206 match &base_type {
2207 ElementType::Component(component) => {
2208 base_type = component.root_element.clone().borrow().base_type.clone();
2209 }
2210 ElementType::Builtin(builtin) => break Some(builtin.native_class.clone()),
2211 ElementType::Native(native) => break Some(native.clone()),
2212 _ => break None,
2213 }
2214 }
2215 }
2216
2217 pub fn builtin_type(&self) -> Option<Rc<BuiltinElement>> {
2218 let mut base_type = self.base_type.clone();
2219 loop {
2220 match &base_type {
2221 ElementType::Component(component) => {
2222 base_type = component.root_element.clone().borrow().base_type.clone();
2223 }
2224 ElementType::Builtin(builtin) => break Some(builtin.clone()),
2225 _ => break None,
2226 }
2227 }
2228 }
2229
2230 pub fn layout_info_prop(&self, orientation: Orientation) -> Option<&NamedReference> {
2231 self.layout_info_prop.as_ref().map(|prop| match orientation {
2232 Orientation::Horizontal => &prop.0,
2233 Orientation::Vertical => &prop.1,
2234 })
2235 }
2236
2237 pub fn is_builtin_height_for_width(&self) -> bool {
2245 let Some(builtin) = self.builtin_type() else { return false };
2246 match builtin.name.as_str() {
2247 "Text" | "TextInput" => self.is_binding_set("wrap", false),
2250 "Image" | "ClippedImage" | "StyledText" => true,
2253 _ => false,
2254 }
2255 }
2256
2257 pub fn inherited_layout_info_v_with_constraint(&self) -> Option<NamedReference> {
2261 if let Some(nr) = &self.layout_info_v_with_constraint {
2262 return Some(nr.clone());
2263 }
2264 let mut base = self.base_type.clone();
2265 while let ElementType::Component(base_comp) = base {
2266 let root = base_comp.root_element.borrow();
2267 if let Some(nr) = &root.layout_info_v_with_constraint {
2268 return Some(nr.clone());
2269 }
2270 base = root.base_type.clone();
2271 }
2272 None
2273 }
2274
2275 pub fn inherited_layout_info_h_with_constraint(&self) -> Option<NamedReference> {
2278 if let Some(nr) = &self.layout_info_h_with_constraint {
2279 return Some(nr.clone());
2280 }
2281 let mut base = self.base_type.clone();
2282 while let ElementType::Component(base_comp) = base {
2283 let root = base_comp.root_element.borrow();
2284 if let Some(nr) = &root.layout_info_h_with_constraint {
2285 return Some(nr.clone());
2286 }
2287 base = root.base_type.clone();
2288 }
2289 None
2290 }
2291
2292 pub fn original_name(&self) -> SmolStr {
2294 self.debug
2295 .first()
2296 .and_then(|n| n.node.child_token(parser::SyntaxKind::Identifier))
2297 .map(|n| n.to_smolstr())
2298 .unwrap_or_else(|| self.id.clone())
2299 }
2300
2301 pub fn is_binding_set(self: &Element, property_name: &str, need_explicit: bool) -> bool {
2306 if self.bindings.get(property_name).is_some_and(|b| {
2307 b.borrow().has_binding() && (!need_explicit || b.borrow().priority > 0)
2308 }) {
2309 true
2310 } else if let ElementType::Component(base) = &self.base_type {
2311 base.root_element.borrow().is_binding_set(property_name, need_explicit)
2312 } else {
2313 false
2314 }
2315 }
2316
2317 pub fn is_property_set(self: &Element, property_name: &str) -> bool {
2319 self.bindings.contains_key(property_name)
2320 || self
2321 .property_analysis
2322 .borrow()
2323 .get(property_name)
2324 .is_some_and(|a| a.is_set || a.is_linked)
2325 || matches!(&self.base_type, ElementType::Component(base) if base.root_element.borrow().is_property_set(property_name))
2326 }
2327
2328 pub fn set_binding_if_not_set(
2333 &mut self,
2334 property_name: SmolStr,
2335 expression_fn: impl FnOnce() -> Expression,
2336 ) -> bool {
2337 if self.is_binding_set(&property_name, false) {
2338 return false;
2339 }
2340
2341 match self.bindings.entry(property_name) {
2342 Entry::Vacant(vacant_entry) => {
2343 let mut binding: BindingExpression = expression_fn().into();
2344 binding.priority = i32::MAX;
2345 vacant_entry.insert(binding.into());
2346 }
2347 Entry::Occupied(mut existing_entry) => {
2348 let mut binding: BindingExpression = expression_fn().into();
2349 binding.priority = i32::MAX;
2350 existing_entry.get_mut().get_mut().merge_with(&binding);
2351 }
2352 };
2353 true
2354 }
2355
2356 pub fn sub_component(&self) -> Option<&Rc<Component>> {
2357 if self.repeated.is_some() {
2358 None
2359 } else if let ElementType::Component(sub_component) = &self.base_type {
2360 Some(sub_component)
2361 } else {
2362 None
2363 }
2364 }
2365
2366 pub fn element_infos(&self) -> String {
2367 let mut debug_infos = self.debug.clone();
2368 let mut base = self.base_type.clone();
2369 while let ElementType::Component(b) = base {
2370 let elem = b.root_element.borrow();
2371 base = elem.base_type.clone();
2372 debug_infos.extend(elem.debug.iter().cloned());
2373 }
2374
2375 let (infos, _, _) = debug_infos.into_iter().fold(
2376 (String::new(), false, true),
2377 |(mut infos, elem_boundary, first), debug_info| {
2378 if elem_boundary {
2379 infos.push('/');
2380 } else if !first {
2381 infos.push(';');
2382 }
2383
2384 infos.push_str(&debug_info.encoded_element_info());
2385 (infos, debug_info.element_boundary, false)
2386 },
2387 );
2388 infos
2389 }
2390}
2391
2392fn css_property_suggestion(property_name: &str, base_type: &ElementType) -> Option<String> {
2394 let base_name = base_type.to_smolstr();
2395 if base_name != "FlexboxLayout" {
2396 return None;
2397 }
2398 match property_name {
2399 "gap" => Some("Use spacing instead of gap".into()),
2400 "row-gap" => Some("Use spacing-vertical instead of row-gap".into()),
2401 "column-gap" => Some("Use spacing-horizontal instead of column-gap".into()),
2402 "justify-content" => Some("Use alignment instead of justify-content".into()),
2403 _ => None,
2404 }
2405}
2406
2407pub(crate) fn apply_default_type_properties(element: &mut Element) {
2409 if let ElementType::Builtin(builtin_base) = &element.base_type {
2411 for (prop, info) in &builtin_base.properties {
2412 if let BuiltinPropertyDefault::Expr(expr) = &info.default_value {
2413 element.bindings.entry(prop.clone()).or_insert_with(|| {
2414 let mut binding = BindingExpression::from(expr.clone());
2415 binding.priority = i32::MAX;
2416 RefCell::new(binding)
2417 });
2418 }
2419 }
2420 }
2421}
2422
2423pub fn type_from_node(
2425 node: syntax_nodes::Type,
2426 diag: &mut BuildDiagnostics,
2427 tr: &TypeRegister,
2428) -> Type {
2429 if let Some(qualified_type_node) = node.QualifiedName() {
2430 let qualified_type = QualifiedTypeName::from_node(qualified_type_node.clone());
2431
2432 let prop_type = tr.lookup_qualified(&qualified_type.members);
2433
2434 #[cfg(feature = "slint-sc")]
2435 diag.slint_sc_error(&format!("The type '{qualified_type}' is"), &qualified_type_node);
2436
2437 if prop_type == Type::Invalid && tr.lookup_element(&qualified_type.to_smolstr()).is_err() {
2438 diag.push_error(format!("Unknown type '{qualified_type}'"), &qualified_type_node);
2439 } else if !prop_type.is_property_type() {
2440 diag.push_error(
2441 format!("'{qualified_type}' is not a valid type"),
2442 &qualified_type_node,
2443 );
2444 }
2445 prop_type
2446 } else if let Some(object_node) = node.ObjectType() {
2447 #[cfg(feature = "slint-sc")]
2448 diag.slint_sc_error("Inline struct types are", &object_node);
2449 type_struct_from_node(object_node, diag, tr, None)
2450 } else if let Some(array_node) = node.ArrayType() {
2451 #[cfg(feature = "slint-sc")]
2452 diag.slint_sc_error("Array types are", &array_node);
2453 Type::Array(Rc::new(type_from_node(array_node.Type(), diag, tr)))
2454 } else {
2455 assert!(diag.has_errors());
2456 Type::Invalid
2457 }
2458}
2459
2460pub fn type_struct_from_node(
2462 object_node: syntax_nodes::ObjectType,
2463 diag: &mut BuildDiagnostics,
2464 tr: &TypeRegister,
2465 name: Option<SmolStr>,
2466) -> Type {
2467 let fields = object_node
2468 .ObjectTypeMember()
2469 .map(|member| {
2470 (
2471 parser::identifier_text(&member).unwrap_or_default(),
2472 type_from_node(member.Type(), diag, tr),
2473 )
2474 })
2475 .collect();
2476 Type::Struct(Rc::new(Struct {
2477 fields,
2478 name: name.map_or(StructName::None, |name| StructName::User { name, node: object_node }),
2479 }))
2480}
2481
2482fn animation_element_from_node(
2483 anim: &syntax_nodes::PropertyAnimation,
2484 prop_name: &syntax_nodes::QualifiedName,
2485 prop_type: Type,
2486 diag: &mut BuildDiagnostics,
2487 tr: &TypeRegister,
2488) -> Option<ElementRc> {
2489 let anim_type = tr.property_animation_type_for_property(prop_type);
2490 if !matches!(anim_type, ElementType::Builtin(..)) {
2491 diag.push_error(
2492 format!(
2493 "'{}' is not a property that can be animated",
2494 prop_name.text().to_string().trim()
2495 ),
2496 prop_name,
2497 );
2498 None
2499 } else {
2500 let mut anim_element =
2501 Element { id: "".into(), base_type: anim_type, ..Default::default() };
2502 anim_element.parse_bindings(
2503 anim.Binding().filter_map(|b| {
2504 Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into()))
2505 }),
2506 false,
2507 diag,
2508 );
2509
2510 apply_default_type_properties(&mut anim_element);
2511
2512 Some(Rc::new(RefCell::new(anim_element)))
2513 }
2514}
2515
2516#[derive(Default, Debug, Clone)]
2517pub struct QualifiedTypeName {
2518 pub members: Vec<SmolStr>,
2519}
2520
2521impl QualifiedTypeName {
2522 pub fn from_node(node: syntax_nodes::QualifiedName) -> Self {
2523 debug_assert_eq!(node.kind(), SyntaxKind::QualifiedName);
2524 let members = node
2525 .children_with_tokens()
2526 .filter(|n| n.kind() == SyntaxKind::Identifier)
2527 .filter_map(|x| x.as_token().map(|x| crate::parser::normalize_identifier(x.text())))
2528 .collect();
2529 Self { members }
2530 }
2531}
2532
2533impl Display for QualifiedTypeName {
2534 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2535 write!(f, "{}", self.members.join("."))
2536 }
2537}
2538
2539fn lookup_property_from_qualified_name_for_state(
2542 node: syntax_nodes::QualifiedName,
2543 r: &ElementRc,
2544 diag: &mut BuildDiagnostics,
2545) -> Option<(NamedReference, Type)> {
2546 let qualname = QualifiedTypeName::from_node(node.clone());
2547 match qualname.members.as_slice() {
2548 [unresolved_prop_name] => {
2549 let lookup_result = r.borrow().lookup_property(unresolved_prop_name.as_ref());
2550 if !lookup_result.property_type.is_property_type() {
2551 diag.push_error(format!("'{qualname}' is not a valid property"), &node);
2552 } else if !lookup_result.is_valid_for_assignment() {
2553 diag.push_error(
2554 format!(
2555 "'{}' cannot be set in a state because it is {}",
2556 qualname, lookup_result.property_visibility
2557 ),
2558 &node,
2559 );
2560 }
2561 Some((
2562 NamedReference::new(r, lookup_result.resolved_name.to_smolstr()),
2563 lookup_result.property_type,
2564 ))
2565 }
2566 [elem_id, unresolved_prop_name] => {
2567 if let Some(element) = find_element_by_id(r, elem_id.as_ref()) {
2568 let lookup_result = element.borrow().lookup_property(unresolved_prop_name.as_ref());
2569 if !lookup_result.is_valid() {
2570 diag.push_error(
2571 format!("'{unresolved_prop_name}' not found in '{elem_id}'"),
2572 &node,
2573 );
2574 } else if !lookup_result.is_valid_for_assignment() {
2575 diag.push_error(
2576 format!(
2577 "'{}' cannot be set in a state because it is {}",
2578 qualname, lookup_result.property_visibility
2579 ),
2580 &node,
2581 );
2582 }
2583 Some((
2584 NamedReference::new(&element, lookup_result.resolved_name.to_smolstr()),
2585 lookup_result.property_type,
2586 ))
2587 } else {
2588 diag.push_error(format!("'{elem_id}' is not a valid element id"), &node);
2589 None
2590 }
2591 }
2592 _ => {
2593 diag.push_error(format!("'{qualname}' is not a valid property"), &node);
2594 None
2595 }
2596 }
2597}
2598
2599fn find_element_by_id(e: &ElementRc, name: &str) -> Option<ElementRc> {
2601 if e.borrow().id == name {
2602 return Some(e.clone());
2603 }
2604 for x in &e.borrow().children {
2605 if x.borrow().repeated.is_some() {
2606 continue;
2607 }
2608 if let Some(x) = find_element_by_id(x, name) {
2609 return Some(x);
2610 }
2611 }
2612
2613 None
2614}
2615
2616pub fn find_parent_element(e: &ElementRc) -> Option<ElementRc> {
2619 fn recurse(base: &ElementRc, e: &ElementRc) -> Option<ElementRc> {
2620 for child in &base.borrow().children {
2621 if Rc::ptr_eq(child, e) {
2622 return Some(base.clone());
2623 }
2624 if let Some(x) = recurse(child, e) {
2625 return Some(x);
2626 }
2627 }
2628 None
2629 }
2630
2631 let root = e.borrow().enclosing_component.upgrade().unwrap().root_element.clone();
2632 if Rc::ptr_eq(&root, e) {
2633 return None;
2634 }
2635 recurse(&root, e)
2636}
2637
2638pub fn recurse_elem<State>(
2642 elem: &ElementRc,
2643 state: &State,
2644 vis: &mut impl FnMut(&ElementRc, &State) -> State,
2645) {
2646 let state = vis(elem, state);
2647 for sub in &elem.borrow().children {
2648 recurse_elem(sub, &state, vis);
2649 }
2650}
2651
2652pub fn recurse_elem_including_sub_components<State>(
2654 component: &Component,
2655 state: &State,
2656 vis: &mut impl FnMut(&ElementRc, &State) -> State,
2657) {
2658 recurse_elem(&component.root_element, state, &mut |elem, state| {
2659 debug_assert!(std::ptr::eq(
2660 component as *const Component,
2661 (&*elem.borrow().enclosing_component.upgrade().unwrap()) as *const Component
2662 ));
2663 if elem.borrow().repeated.is_some()
2664 && let ElementType::Component(base) = &elem.borrow().base_type
2665 && base.parent_element().is_some()
2666 {
2667 recurse_elem_including_sub_components(base, state, vis);
2668 }
2669 vis(elem, state)
2670 });
2671 component
2672 .popup_windows
2673 .borrow()
2674 .iter()
2675 .for_each(|p| recurse_elem_including_sub_components(&p.component, state, vis));
2676 component
2677 .menu_item_tree
2678 .borrow()
2679 .iter()
2680 .for_each(|c| recurse_elem_including_sub_components(c, state, vis));
2681}
2682
2683pub fn recurse_elem_no_borrow<State>(
2685 elem: &ElementRc,
2686 state: &State,
2687 vis: &mut impl FnMut(&ElementRc, &State) -> State,
2688) {
2689 let state = vis(elem, state);
2690 let children = elem.borrow().children.clone();
2691 for sub in &children {
2692 recurse_elem_no_borrow(sub, &state, vis);
2693 }
2694}
2695
2696pub fn recurse_elem_including_sub_components_no_borrow<State>(
2698 component: &Component,
2699 state: &State,
2700 vis: &mut impl FnMut(&ElementRc, &State) -> State,
2701) {
2702 recurse_elem_no_borrow(&component.root_element, state, &mut |elem, state| {
2703 let base = if elem.borrow().repeated.is_some() {
2704 if let ElementType::Component(base) = &elem.borrow().base_type {
2705 if base.parent_element().is_some() {
2706 Some(base.clone())
2707 } else {
2708 None
2710 }
2711 } else {
2712 None
2713 }
2714 } else {
2715 None
2716 };
2717 if let Some(base) = base {
2718 recurse_elem_including_sub_components_no_borrow(&base, state, vis);
2719 }
2720 vis(elem, state)
2721 });
2722 component
2723 .popup_windows
2724 .borrow()
2725 .iter()
2726 .for_each(|p| recurse_elem_including_sub_components_no_borrow(&p.component, state, vis));
2727 component
2728 .menu_item_tree
2729 .borrow()
2730 .iter()
2731 .for_each(|c| recurse_elem_including_sub_components_no_borrow(c, state, vis));
2732}
2733
2734pub fn visit_repeater_model_expression(
2739 elem: &ElementRc,
2740 mut vis: impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
2741) {
2742 let repeated = elem
2743 .borrow_mut()
2744 .repeated
2745 .as_mut()
2746 .map(|r| (std::mem::take(&mut r.model), r.is_conditional_element));
2747 if let Some((mut model, is_cond)) = repeated {
2748 vis(&mut model, None, &|| if is_cond { Type::Bool } else { Type::Model });
2749 elem.borrow_mut().repeated.as_mut().unwrap().model = model;
2750 }
2751}
2752
2753pub fn visit_element_expressions_excluding_repeater_model(
2756 elem: &ElementRc,
2757 mut vis: impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
2758) {
2759 fn visit_element_expressions_simple(
2760 elem: &ElementRc,
2761 vis: &mut impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
2762 ) {
2763 for (name, expr) in &elem.borrow().bindings {
2764 vis(&mut expr.borrow_mut(), Some(name.as_str()), &|| {
2765 elem.borrow().lookup_property(name).property_type
2766 });
2767
2768 for twb in &mut expr.borrow_mut().two_way_bindings {
2769 if let expression_tree::TwoWayBinding::ModelData { repeated_element, .. } = twb {
2770 let mut e =
2771 Expression::RepeaterModelReference { element: repeated_element.clone() };
2772 vis(&mut e, None, &|| Type::Invalid);
2773 if let Expression::RepeaterModelReference { element } = e {
2774 *repeated_element = element;
2775 }
2776 }
2777 }
2778
2779 match &mut expr.borrow_mut().animation {
2780 Some(PropertyAnimation::Static(e)) => visit_element_expressions_simple(e, vis),
2781 Some(PropertyAnimation::Transition { animations, state_ref }) => {
2782 vis(state_ref, None, &|| Type::Int32);
2783 for a in animations {
2784 visit_element_expressions_simple(&a.animation, vis)
2785 }
2786 }
2787 None => (),
2788 }
2789 }
2790 }
2791
2792 visit_element_expressions_simple(elem, &mut vis);
2793
2794 for expr in elem.borrow().change_callbacks.values() {
2795 for expr in expr.borrow_mut().iter_mut() {
2796 vis(expr, Some("$change callback$"), &|| Type::Void);
2797 }
2798 }
2799
2800 let mut states = std::mem::take(&mut elem.borrow_mut().states);
2801 for s in &mut states {
2802 if let Some(cond) = s.condition.as_mut() {
2803 vis(cond, None, &|| Type::Bool)
2804 }
2805 for (ne, e, _) in &mut s.property_changes {
2806 vis(e, Some(ne.name()), &|| {
2807 ne.element().borrow().lookup_property(ne.name()).property_type
2808 });
2809 }
2810 }
2811 elem.borrow_mut().states = states;
2812
2813 let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions);
2814 for t in &mut transitions {
2815 for (_, _, a) in &mut t.property_animations {
2816 visit_element_expressions_simple(a, &mut vis);
2817 }
2818 }
2819 elem.borrow_mut().transitions = transitions;
2820
2821 let component = elem.borrow().enclosing_component.upgrade().unwrap();
2822 if Rc::ptr_eq(&component.root_element, elem) {
2823 for e in component.init_code.borrow_mut().iter_mut() {
2824 vis(e, None, &|| Type::Void);
2825 }
2826 }
2827}
2828
2829pub fn visit_element_expressions(
2830 elem: &ElementRc,
2831 mut vis: impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
2832) {
2833 visit_repeater_model_expression(elem, &mut vis);
2834 visit_element_expressions_excluding_repeater_model(elem, &mut vis);
2835}
2836
2837pub fn visit_named_references_in_expression(
2838 expr: &mut Expression,
2839 vis: &mut impl FnMut(&mut NamedReference),
2840) {
2841 expr.visit_mut(|sub| visit_named_references_in_expression(sub, vis));
2842 match expr {
2843 Expression::PropertyReference(r) => vis(r),
2844 Expression::FunctionCall {
2845 function: Callable::Callback(r) | Callable::Function(r),
2846 ..
2847 } => vis(r),
2848 Expression::LayoutCacheAccess { layout_cache_prop, .. } => vis(layout_cache_prop),
2849 Expression::GridRepeaterCacheAccess { layout_cache_prop, .. } => vis(layout_cache_prop),
2850 Expression::OrganizeGridLayout(l) => l.visit_named_references(vis),
2851 Expression::ComputeBoxLayoutInfo { layout, .. } => layout.visit_named_references(vis),
2852 Expression::ComputeFlexboxLayoutInfo { layout, .. } => layout.visit_named_references(vis),
2853 Expression::ComputeGridLayoutInfo { layout_organized_data_prop, layout, .. } => {
2854 vis(layout_organized_data_prop);
2855 layout.visit_named_references(vis);
2856 }
2857 Expression::SolveBoxLayout(l, _) => l.visit_named_references(vis),
2858 Expression::SolveFlexboxLayout(l) => l.visit_named_references(vis),
2859 Expression::SolveGridLayout { layout_organized_data_prop, layout, .. } => {
2860 vis(layout_organized_data_prop);
2861 layout.visit_named_references(vis);
2862 }
2863 Expression::RepeaterModelReference { element }
2866 | Expression::RepeaterIndexReference { element } => {
2867 let mut nc =
2869 NamedReference::new(&element.upgrade().unwrap(), SmolStr::new_static("$model"));
2870 vis(&mut nc);
2871 debug_assert!(nc.element().borrow().repeated.is_some());
2872 *element = Rc::downgrade(&nc.element());
2873 }
2874 _ => {}
2875 }
2876}
2877
2878pub fn visit_all_named_references_in_element(
2881 elem: &ElementRc,
2882 mut vis: impl FnMut(&mut NamedReference),
2883) {
2884 visit_element_expressions(elem, |expr, _, _| {
2885 visit_named_references_in_expression(expr, &mut vis)
2886 });
2887 let mut states = std::mem::take(&mut elem.borrow_mut().states);
2888 for s in &mut states {
2889 for (r, _, _) in &mut s.property_changes {
2890 vis(r);
2891 }
2892 }
2893 elem.borrow_mut().states = states;
2894 let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions);
2895 for t in &mut transitions {
2896 for (r, _, _) in &mut t.property_animations {
2897 vis(r)
2898 }
2899 }
2900 elem.borrow_mut().transitions = transitions;
2901 let mut repeated = std::mem::take(&mut elem.borrow_mut().repeated);
2902 if let Some(r) = &mut repeated
2903 && let Some(lv) = &mut r.is_listview
2904 {
2905 vis(&mut lv.viewport_y);
2906 vis(&mut lv.viewport_height);
2907 vis(&mut lv.viewport_width);
2908 vis(&mut lv.listview_height);
2909 vis(&mut lv.listview_width);
2910 }
2911 elem.borrow_mut().repeated = repeated;
2912 let mut layout_info_prop = std::mem::take(&mut elem.borrow_mut().layout_info_prop);
2913 layout_info_prop.as_mut().map(|(h, b)| (vis(h), vis(b)));
2914 elem.borrow_mut().layout_info_prop = layout_info_prop;
2915 let mut constrained_v = std::mem::take(&mut elem.borrow_mut().layout_info_v_with_constraint);
2916 if let Some(nr) = constrained_v.as_mut() {
2917 vis(nr);
2918 }
2919 elem.borrow_mut().layout_info_v_with_constraint = constrained_v;
2920 let mut constrained_h = std::mem::take(&mut elem.borrow_mut().layout_info_h_with_constraint);
2921 if let Some(nr) = constrained_h.as_mut() {
2922 vis(nr);
2923 }
2924 elem.borrow_mut().layout_info_h_with_constraint = constrained_h;
2925 let mut debug = std::mem::take(&mut elem.borrow_mut().debug);
2926 for d in debug.iter_mut() {
2927 if let Some(l) = d.layout.as_mut() {
2928 l.visit_named_references(&mut vis)
2929 }
2930 }
2931 elem.borrow_mut().debug = debug;
2932
2933 let mut accessibility_props = std::mem::take(&mut elem.borrow_mut().accessibility_props);
2934 accessibility_props.0.iter_mut().for_each(|(_, x)| vis(x));
2935 elem.borrow_mut().accessibility_props = accessibility_props;
2936
2937 let geometry_props = elem.borrow_mut().geometry_props.take();
2938 if let Some(mut geometry_props) = geometry_props {
2939 vis(&mut geometry_props.x);
2940 vis(&mut geometry_props.y);
2941 vis(&mut geometry_props.width);
2942 vis(&mut geometry_props.height);
2943 elem.borrow_mut().geometry_props = Some(geometry_props);
2944 }
2945
2946 for expr in elem.borrow().bindings.values() {
2948 for twb in &mut expr.borrow_mut().two_way_bindings {
2949 if let expression_tree::TwoWayBinding::Property { property, .. } = twb {
2950 vis(property);
2951 }
2952 }
2953 }
2954
2955 let mut property_declarations = std::mem::take(&mut elem.borrow_mut().property_declarations);
2956 for pd in property_declarations.values_mut() {
2957 pd.is_alias.as_mut().map(&mut vis);
2958 }
2959 elem.borrow_mut().property_declarations = property_declarations;
2960
2961 let grid_layout_cell = elem.borrow_mut().grid_layout_cell.take();
2963 if let Some(grid_layout_cell) = grid_layout_cell {
2964 grid_layout_cell.borrow_mut().visit_named_references(&mut vis);
2965 elem.borrow_mut().grid_layout_cell = Some(grid_layout_cell);
2966 }
2967}
2968
2969pub fn visit_all_named_references(
2971 component: &Component,
2972 vis: &mut impl FnMut(&mut NamedReference),
2973) {
2974 recurse_elem_including_sub_components_no_borrow(
2975 component,
2976 &Weak::new(),
2977 &mut |elem, parent_compo| {
2978 visit_all_named_references_in_element(elem, |nr| vis(nr));
2979 let compo = elem.borrow().enclosing_component.clone();
2980 if !Weak::ptr_eq(parent_compo, &compo) {
2981 let compo = compo.upgrade().unwrap();
2982 compo.root_constraints.borrow_mut().visit_named_references(vis);
2983 compo.popup_windows.borrow_mut().iter_mut().for_each(|p| {
2984 vis(&mut p.x);
2985 vis(&mut p.y);
2986 if let Some(is_open) = &mut p.is_open {
2987 vis(is_open);
2988 }
2989 });
2990 compo.timers.borrow_mut().iter_mut().for_each(|t| {
2991 vis(&mut t.interval);
2992 vis(&mut t.triggered);
2993 vis(&mut t.running);
2994 });
2995 for o in compo.optimized_elements.borrow().iter() {
2996 visit_element_expressions(o, |expr, _, _| {
2997 visit_named_references_in_expression(expr, vis)
2998 });
2999 }
3000 }
3001 compo
3002 },
3003 );
3004}
3005
3006pub fn visit_all_expressions(
3010 component: &Component,
3011 mut vis: impl FnMut(&mut Expression, &dyn Fn() -> Type),
3012) {
3013 recurse_elem_including_sub_components(component, &Weak::new(), &mut |elem, parent_compo| {
3014 visit_element_expressions(elem, |expr, _, ty| vis(expr, ty));
3015 let compo = elem.borrow().enclosing_component.clone();
3016 if !Weak::ptr_eq(parent_compo, &compo) {
3017 let compo = compo.upgrade().unwrap();
3018 for o in compo.optimized_elements.borrow().iter() {
3019 visit_element_expressions(o, |expr, _, ty| vis(expr, ty));
3020 }
3021 }
3022 compo
3023 })
3024}
3025
3026#[derive(Debug, Clone)]
3027pub struct State {
3028 pub id: SmolStr,
3029 pub condition: Option<Expression>,
3030 pub property_changes: Vec<(NamedReference, Expression, syntax_nodes::StatePropertyChange)>,
3031}
3032
3033#[derive(Debug, Clone)]
3034pub struct Transition {
3035 pub direction: TransitionDirection,
3036 pub state_id: SmolStr,
3037 pub property_animations: Vec<(NamedReference, SourceLocation, ElementRc)>,
3038 pub node: syntax_nodes::Transition,
3039}
3040
3041impl Transition {
3042 fn from_node(
3043 trs: syntax_nodes::Transition,
3044 r: &ElementRc,
3045 tr: &TypeRegister,
3046 diag: &mut BuildDiagnostics,
3047 ) -> Transition {
3048 if let Some(star) = trs.child_token(SyntaxKind::Star) {
3049 diag.push_error("catch-all not yet implemented".into(), &star);
3050 };
3051 let direction_text = trs
3052 .first_child_or_token()
3053 .and_then(|t| t.as_token().map(|tok| tok.text().to_string()))
3054 .unwrap_or_default();
3055
3056 Transition {
3057 direction: match direction_text.as_str() {
3058 "in" => TransitionDirection::In,
3059 "out" => TransitionDirection::Out,
3060 "in-out" => TransitionDirection::InOut,
3061 "in_out" => TransitionDirection::InOut,
3062 _ => {
3063 unreachable!("Unknown transition direction: '{}'", direction_text);
3064 }
3065 },
3066 state_id: trs
3067 .DeclaredIdentifier()
3068 .and_then(|x| parser::identifier_text(&x))
3069 .unwrap_or_default(),
3070 property_animations: trs
3071 .PropertyAnimation()
3072 .flat_map(|pa| pa.QualifiedName().map(move |qn| (pa.clone(), qn)))
3073 .filter_map(|(pa, qn)| {
3074 lookup_property_from_qualified_name_for_state(qn.clone(), r, diag).and_then(
3075 |(ne, prop_type)| {
3076 animation_element_from_node(&pa, &qn, prop_type, diag, tr)
3077 .map(|anim_element| (ne, qn.to_source_location(), anim_element))
3078 },
3079 )
3080 })
3081 .collect(),
3082 node: trs.clone(),
3083 }
3084 }
3085}
3086
3087#[derive(Clone, Debug, derive_more::Deref)]
3088pub struct ExportedName {
3089 #[deref]
3090 pub name: SmolStr, pub name_ident: SyntaxNode,
3092}
3093
3094impl ExportedName {
3095 pub fn original_name(&self) -> SmolStr {
3096 self.name_ident
3097 .child_token(parser::SyntaxKind::Identifier)
3098 .map(|n| n.to_smolstr())
3099 .unwrap_or_else(|| self.name.clone())
3100 }
3101
3102 pub fn from_export_specifier(
3103 export_specifier: &syntax_nodes::ExportSpecifier,
3104 ) -> (SmolStr, ExportedName) {
3105 let internal_name =
3106 parser::identifier_text(&export_specifier.ExportIdentifier()).unwrap_or_default();
3107
3108 let (name, name_ident): (SmolStr, SyntaxNode) = export_specifier
3109 .ExportName()
3110 .and_then(|ident| {
3111 parser::identifier_text(&ident).map(|text| (text, ident.clone().into()))
3112 })
3113 .unwrap_or_else(|| (internal_name.clone(), export_specifier.ExportIdentifier().into()));
3114 (internal_name, ExportedName { name, name_ident })
3115 }
3116}
3117
3118#[derive(Default, Debug, derive_more::Deref)]
3119pub struct Exports {
3120 #[deref]
3121 components_or_types: Vec<(ExportedName, Either<Rc<Component>, Type>)>,
3122}
3123
3124impl Exports {
3125 pub fn from_node(
3126 doc: &syntax_nodes::Document,
3127 inner_components: &[Rc<Component>],
3128 type_registry: &TypeRegister,
3129 diag: &mut BuildDiagnostics,
3130 ) -> Self {
3131 let resolve_export_to_inner_component_or_import =
3132 |internal_name: &str, internal_name_node: &dyn Spanned, diag: &mut BuildDiagnostics| {
3133 if let Ok(ElementType::Component(c)) = type_registry.lookup_element(internal_name) {
3134 Some(Either::Left(c))
3135 } else if let ty @ Type::Struct { .. } | ty @ Type::Enumeration(_) =
3136 type_registry.lookup(internal_name)
3137 {
3138 Some(Either::Right(ty))
3139 } else if type_registry.lookup_element(internal_name).is_ok()
3140 || type_registry.lookup(internal_name) != Type::Invalid
3141 {
3142 diag.push_error(
3143 format!("Cannot export '{internal_name}' because it is not a component",),
3144 internal_name_node,
3145 );
3146 None
3147 } else {
3148 diag.push_error(format!("'{internal_name}' not found",), internal_name_node);
3149 None
3150 }
3151 };
3152
3153 let mut exports_with_duplicates: Vec<(ExportedName, Either<Rc<Component>, Type>)> =
3156 Vec::new();
3157
3158 exports_with_duplicates.extend(
3160 doc.ExportsList()
3161 .filter(|exports| exports.ExportModule().is_none())
3163 .flat_map(|exports| exports.ExportSpecifier())
3164 .filter_map(|export_specifier| {
3165 #[cfg(feature = "slint-sc")]
3166 diag.slint_sc_error("Export specifiers are", &export_specifier);
3167 let (internal_name, exported_name) =
3168 ExportedName::from_export_specifier(&export_specifier);
3169 Some((
3170 exported_name,
3171 resolve_export_to_inner_component_or_import(
3172 &internal_name,
3173 &export_specifier.ExportIdentifier(),
3174 diag,
3175 )?,
3176 ))
3177 }),
3178 );
3179
3180 exports_with_duplicates.extend(
3182 doc.ExportsList().flat_map(|exports| exports.Component()).filter_map(|component| {
3183 let name_ident: SyntaxNode = component.DeclaredIdentifier().into();
3184 let name =
3185 parser::identifier_text(&component.DeclaredIdentifier()).unwrap_or_else(|| {
3186 debug_assert!(diag.has_errors());
3187 SmolStr::default()
3188 });
3189
3190 let compo_or_type =
3191 resolve_export_to_inner_component_or_import(&name, &name_ident, diag)?;
3192
3193 Some((ExportedName { name, name_ident }, compo_or_type))
3194 }),
3195 );
3196
3197 exports_with_duplicates.extend(
3199 doc.ExportsList()
3200 .flat_map(|exports| {
3201 exports
3202 .StructDeclaration()
3203 .map(|st| st.DeclaredIdentifier())
3204 .chain(exports.EnumDeclaration().map(|en| en.DeclaredIdentifier()))
3205 })
3206 .filter_map(|name_ident| {
3207 let name = parser::identifier_text(&name_ident).unwrap_or_else(|| {
3208 debug_assert!(diag.has_errors());
3209 SmolStr::default()
3210 });
3211
3212 let name_ident = name_ident.into();
3213
3214 let compo_or_type =
3215 resolve_export_to_inner_component_or_import(&name, &name_ident, diag)?;
3216
3217 Some((ExportedName { name, name_ident }, compo_or_type))
3218 }),
3219 );
3220
3221 exports_with_duplicates.sort_by(|(a, _), (b, _)| a.name.cmp(&b.name));
3222
3223 let mut sorted_deduped_exports = Vec::with_capacity(exports_with_duplicates.len());
3224 let mut it = exports_with_duplicates.into_iter().peekable();
3225 while let Some((exported_name, compo_or_type)) = it.next() {
3226 let mut warning_issued_on_first_occurrence = false;
3227
3228 while it.peek().map(|(name, _)| &name.name) == Some(&exported_name.name) {
3230 let message = format!("Duplicated export '{}'", exported_name.name);
3231
3232 if !warning_issued_on_first_occurrence {
3233 diag.push_error(message.clone(), &exported_name.name_ident);
3234 warning_issued_on_first_occurrence = true;
3235 }
3236
3237 let duplicate_loc = it.next().unwrap().0.name_ident;
3238 diag.push_error(message.clone(), &duplicate_loc);
3239 }
3240
3241 sorted_deduped_exports.push((exported_name, compo_or_type));
3242 }
3243
3244 if let Some(last_compo) = inner_components.last() {
3245 let name = last_compo.id.clone();
3246 if last_compo.is_global() {
3247 if sorted_deduped_exports.is_empty() {
3248 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()));
3249 sorted_deduped_exports.push((
3250 ExportedName { name, name_ident: doc.clone().into() },
3251 Either::Left(last_compo.clone()),
3252 ))
3253 }
3254 } else if !sorted_deduped_exports
3255 .iter()
3256 .any(|e| e.1.as_ref().left().is_some_and(|c| !c.is_global()))
3257 {
3258 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()));
3259 let insert_pos = sorted_deduped_exports
3260 .partition_point(|(existing_export, _)| existing_export.name <= name);
3261 sorted_deduped_exports.insert(
3262 insert_pos,
3263 (
3264 ExportedName { name, name_ident: doc.clone().into() },
3265 Either::Left(last_compo.clone()),
3266 ),
3267 )
3268 }
3269 }
3270 Self { components_or_types: sorted_deduped_exports }
3271 }
3272
3273 pub fn add_reexports(
3274 &mut self,
3275 other_exports: impl IntoIterator<Item = (ExportedName, Either<Rc<Component>, Type>)>,
3276 diag: &mut BuildDiagnostics,
3277 ) {
3278 for export in other_exports {
3279 match self.components_or_types.binary_search_by(|entry| entry.0.cmp(&export.0)) {
3280 Ok(_) => {
3281 diag.push_warning(
3282 format!(
3283 "'{}' is already exported in this file; it will not be re-exported",
3284 &*export.0
3285 ),
3286 &export.0.name_ident,
3287 );
3288 }
3289 Err(insert_pos) => {
3290 self.components_or_types.insert(insert_pos, export);
3291 }
3292 }
3293 }
3294 }
3295
3296 pub fn find(&self, name: &str) -> Option<Either<Rc<Component>, Type>> {
3297 self.components_or_types
3298 .binary_search_by(|(exported_name, _)| exported_name.as_str().cmp(name))
3299 .ok()
3300 .map(|index| self.components_or_types[index].1.clone())
3301 }
3302
3303 pub fn retain(
3304 &mut self,
3305 func: impl FnMut(&mut (ExportedName, Either<Rc<Component>, Type>)) -> bool,
3306 ) {
3307 self.components_or_types.retain_mut(func)
3308 }
3309
3310 pub(crate) fn snapshot(&self, snapshotter: &mut crate::typeloader::Snapshotter) -> Self {
3311 let components_or_types = self
3312 .components_or_types
3313 .iter()
3314 .map(|(en, either)| {
3315 let en = en.clone();
3316 let either = match either {
3317 itertools::Either::Left(l) => itertools::Either::Left({
3318 Weak::upgrade(&snapshotter.use_component(l))
3319 .expect("Component should cleanly upgrade here")
3320 }),
3321 itertools::Either::Right(r) => itertools::Either::Right(r.clone()),
3322 };
3323 (en, either)
3324 })
3325 .collect();
3326
3327 Self { components_or_types }
3328 }
3329}
3330
3331impl std::iter::IntoIterator for Exports {
3332 type Item = (ExportedName, Either<Rc<Component>, Type>);
3333
3334 type IntoIter = std::vec::IntoIter<Self::Item>;
3335
3336 fn into_iter(self) -> Self::IntoIter {
3337 self.components_or_types.into_iter()
3338 }
3339}
3340
3341pub fn inject_element_as_repeated_element(repeated_element: &ElementRc, new_root: ElementRc) {
3345 let component = repeated_element.borrow().base_type.as_component().clone();
3346 debug_assert_eq!(Rc::strong_count(&component), 2);
3350 let old_root = &component.root_element;
3351
3352 adjust_geometry_for_injected_parent(&new_root, old_root);
3353
3354 let mut elements_with_enclosing_component_reference = Vec::new();
3356 recurse_elem(old_root, &(), &mut |element: &ElementRc, _| {
3357 if let Some(enclosing_component) = element.borrow().enclosing_component.upgrade()
3358 && Rc::ptr_eq(&enclosing_component, &component)
3359 {
3360 elements_with_enclosing_component_reference.push(element.clone());
3361 }
3362 });
3363 elements_with_enclosing_component_reference
3364 .extend_from_slice(component.optimized_elements.borrow().as_slice());
3365 elements_with_enclosing_component_reference.push(new_root.clone());
3366
3367 new_root.borrow_mut().child_of_layout =
3368 std::mem::replace(&mut old_root.borrow_mut().child_of_layout, false);
3369 let layout_info_prop = old_root.borrow().layout_info_prop.clone().or_else(|| {
3370 let li_v = crate::layout::create_new_prop(
3372 &new_root,
3373 SmolStr::new_static("layoutinfo-v"),
3374 crate::typeregister::layout_info_type().into(),
3375 );
3376 let li_h = crate::layout::create_new_prop(
3377 &new_root,
3378 SmolStr::new_static("layoutinfo-h"),
3379 crate::typeregister::layout_info_type().into(),
3380 );
3381 let expr_h = crate::layout::implicit_layout_info_call(
3382 old_root,
3383 Orientation::Horizontal,
3384 crate::layout::BuiltinFilter::All,
3385 None,
3386 )
3387 .unwrap();
3388 let expr_v = crate::layout::implicit_layout_info_call(
3389 old_root,
3390 Orientation::Vertical,
3391 crate::layout::BuiltinFilter::All,
3392 None,
3393 )
3394 .unwrap();
3395 let expr_v =
3396 BindingExpression::new_with_span(expr_v, old_root.borrow().to_source_location());
3397 li_v.element().borrow_mut().bindings.insert(li_v.name().clone(), expr_v.into());
3398 let expr_h =
3399 BindingExpression::new_with_span(expr_h, old_root.borrow().to_source_location());
3400 li_h.element().borrow_mut().bindings.insert(li_h.name().clone(), expr_h.into());
3401 Some((li_h.clone(), li_v.clone()))
3402 });
3403 new_root.borrow_mut().layout_info_prop = layout_info_prop;
3404
3405 drop(std::mem::take(&mut repeated_element.borrow_mut().base_type));
3408
3409 debug_assert_eq!(Rc::strong_count(&component), 1);
3410
3411 let mut component = Rc::try_unwrap(component).expect("internal compiler error: more than one strong reference left to repeated component when lowering shadow properties");
3412
3413 let old_root = std::mem::replace(&mut component.root_element, new_root.clone());
3414 new_root.borrow_mut().children.push(old_root);
3415
3416 let component = Rc::new(component);
3417 repeated_element.borrow_mut().base_type = ElementType::Component(component.clone());
3418
3419 for elem in elements_with_enclosing_component_reference {
3420 elem.borrow_mut().enclosing_component = Rc::downgrade(&component);
3421 }
3422}
3423
3424pub fn adjust_geometry_for_injected_parent(injected_parent: &ElementRc, old_elem: &ElementRc) {
3427 let mut injected_parent_mut = injected_parent.borrow_mut();
3428 injected_parent_mut.bindings.insert(
3429 "z".into(),
3430 RefCell::new(BindingExpression::new_two_way(
3431 NamedReference::new(old_elem, SmolStr::new_static("z")).into(),
3432 )),
3433 );
3434 injected_parent_mut.property_declarations.insert(
3436 "dummy".into(),
3437 PropertyDeclaration { property_type: Type::LogicalLength, ..Default::default() },
3438 );
3439 let mut old_elem_mut = old_elem.borrow_mut();
3440 injected_parent_mut.default_fill_parent = std::mem::take(&mut old_elem_mut.default_fill_parent);
3441 injected_parent_mut.geometry_props.clone_from(&old_elem_mut.geometry_props);
3442 drop(injected_parent_mut);
3443 old_elem_mut.geometry_props.as_mut().unwrap().x =
3444 NamedReference::new(injected_parent, SmolStr::new_static("dummy"));
3445 old_elem_mut.geometry_props.as_mut().unwrap().y =
3446 NamedReference::new(injected_parent, SmolStr::new_static("dummy"));
3447}