1use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
11use crate::expression_tree::{
12 self, BindingExpression, Callable, ConditionLocation, Expression, Unit,
13};
14use crate::langtype::{
15 BuiltinElement, Enumeration, EnumerationValue, Function, NativeClass, Struct, StructName, Type,
16};
17use crate::langtype::{ElementType, PropertyLookupMode, PropertyLookupResult};
18use crate::layout::{LayoutConstraints, Orientation};
19use crate::namedreference::NamedReference;
20use crate::parser::{SyntaxKind, SyntaxNode, syntax_nodes};
21use crate::typeloader::{ImportKind, ImportedTypes, LibraryInfo};
22use crate::typeregister::TypeRegister;
23use crate::{parser, reject_experimental_feature};
24use itertools::Either;
25use smol_str::{SmolStr, ToSmolStr, format_smolstr};
26use std::cell::{Cell, OnceCell, Ref, RefCell, RefMut};
27use std::collections::btree_map::Entry;
28use std::collections::{BTreeMap, HashMap, HashSet};
29use std::fmt::Display;
30use std::path::PathBuf;
31use std::rc::{Rc, Weak};
32use std::sync::Arc;
33
34pub(crate) mod forward_inherited_expression;
35mod interfaces;
36
37macro_rules! unwrap_or_continue {
38 ($e:expr ; $diag:expr) => {
39 match $e {
40 Some(x) => x,
41 None => {
42 debug_assert!($diag.has_errors()); continue;
44 }
45 }
46 };
47}
48
49#[derive(Default)]
51pub struct Document {
52 pub node: Option<syntax_nodes::Document>,
53 pub inner_components: Vec<Rc<Component>>,
54 pub inner_types: Vec<Type>,
55 pub local_registry: TypeRegister,
56 pub custom_fonts: Vec<(SmolStr, crate::parser::SyntaxToken)>,
59 pub exports: Exports,
60 pub imports: Vec<ImportedTypes>,
61 pub library_exports: HashMap<String, LibraryInfo>,
62
63 pub embedded_file_resources: RefCell<
68 typed_index_collections::TiVec<
69 crate::embedded_resources::EmbeddedResourcesIdx,
70 crate::embedded_resources::EmbeddedResources,
71 >,
72 >,
73
74 #[cfg(feature = "bundle-translations")]
75 pub translation_builder: Option<crate::translations::TranslationsBuilder>,
76
77 pub used_types: RefCell<UsedSubTypes>,
79
80 pub popup_menu_impl: Option<Rc<Component>>,
82}
83
84impl Document {
85 pub fn from_node(
86 node: syntax_nodes::Document,
87 imports: Vec<ImportedTypes>,
88 reexports: Exports,
89 diag: &mut BuildDiagnostics,
90 parent_registry: &Rc<RefCell<TypeRegister>>,
91 ignore_missing_font_files: bool,
92 symbol_counters: &Rc<crate::symbol_counters::SymbolCounters>,
93 ) -> Self {
94 debug_assert_eq!(node.kind(), SyntaxKind::Document);
95
96 let mut local_registry = TypeRegister::new(parent_registry);
97 let mut inner_components = Vec::new();
98 let mut inner_types = Vec::new();
99
100 #[cfg(feature = "slint-sc")]
104 for import in &imports {
105 match import.import_kind {
106 ImportKind::ImportList(_) => {}
107 ImportKind::FileImport => {
108 diag.slint_sc_error("File imports are", &import.import_uri_token)
109 }
110 ImportKind::ModuleReexport(_) => {
111 diag.slint_sc_error("Re-exports are", &import.import_uri_token)
112 }
113 }
114 }
115
116 let mut process_component =
117 |n: syntax_nodes::Component,
118 diag: &mut BuildDiagnostics,
119 local_registry: &mut TypeRegister| {
120 let compo = Component::from_node(n, diag, local_registry);
121 if !local_registry.add(compo.clone()) {
122 diag.push_warning(format!("Component '{}' is replacing a previously defined component with the same name", compo.id), &compo.node.clone().unwrap().DeclaredIdentifier());
123 }
124 inner_components.push(compo);
125 };
126 let process_struct = |n: syntax_nodes::StructDeclaration,
127 diag: &mut BuildDiagnostics,
128 local_registry: &mut TypeRegister,
129 inner_types: &mut Vec<Type>| {
130 let ty = type_struct_from_node(
131 n.ObjectType(),
132 diag,
133 local_registry,
134 parser::identifier_text(&n.DeclaredIdentifier()),
135 Some(symbol_counters),
136 );
137 assert!(matches!(ty, Type::Struct(_)));
138 if !local_registry.insert_type(ty.clone()) {
139 diag.push_warning(
140 format!(
141 "Struct '{ty}' is replacing a previously defined type with the same name"
142 ),
143 &n.DeclaredIdentifier(),
144 );
145 }
146 inner_types.push(ty);
147 };
148 let process_enum = |n: syntax_nodes::EnumDeclaration,
149 diag: &mut BuildDiagnostics,
150 local_registry: &mut TypeRegister,
151 inner_types: &mut Vec<Type>| {
152 let Some(name) = parser::identifier_text(&n.DeclaredIdentifier()) else {
153 assert!(diag.has_errors());
154 return;
155 };
156 let mut existing_names = HashSet::new();
157 let values = n
158 .EnumValue()
159 .filter_map(|v| {
160 let value = parser::identifier_text(&v)?;
161 if value == name {
162 diag.push_error(
163 format!("Enum '{value}' can't have a value with the same name"),
164 &v,
165 );
166 None
167 } else if !existing_names.insert(crate::generator::to_pascal_case(&value)) {
168 diag.push_error(format!("Duplicated enum value '{value}'"), &v);
169 None
170 } else {
171 Some(value)
172 }
173 })
174 .collect();
175 let en = Enumeration {
176 name: name.clone(),
177 values,
178 default_value: 0,
179 node: Some(n.to_source_location()),
180 rust_attributes: n
181 .AtRustAttr()
182 .map(|a| SmolStr::from(a.text().to_string()))
183 .collect(),
184 };
185 if en.values.is_empty() {
186 diag.push_error("Enums must have at least one value".into(), &n);
187 }
188
189 let ty = Type::Enumeration(Arc::new(en));
190 if !local_registry.insert_type_with_name(ty.clone(), name.clone()) {
191 diag.push_warning(
192 format!(
193 "Enum '{name}' is replacing a previously defined type with the same name"
194 ),
195 &n.DeclaredIdentifier(),
196 );
197 }
198 inner_types.push(ty);
199 };
200
201 for n in node.children() {
202 match n.kind() {
203 SyntaxKind::Component => {
204 process_component(n.into(), diag, &mut local_registry);
205 }
206 SyntaxKind::StructDeclaration => {
207 process_struct(n.into(), diag, &mut local_registry, &mut inner_types)
208 }
209 SyntaxKind::EnumDeclaration => {
210 process_enum(n.into(), diag, &mut local_registry, &mut inner_types)
211 }
212 SyntaxKind::ExportsList => {
213 for n in n.children() {
214 match n.kind() {
215 SyntaxKind::Component => {
216 process_component(n.into(), diag, &mut local_registry)
217 }
218 SyntaxKind::StructDeclaration => process_struct(
219 n.into(),
220 diag,
221 &mut local_registry,
222 &mut inner_types,
223 ),
224 SyntaxKind::EnumDeclaration => {
225 process_enum(n.into(), diag, &mut local_registry, &mut inner_types)
226 }
227 _ => {}
228 }
229 }
230 }
231 _ => {}
232 };
233 }
234 let mut exports = Exports::from_node(&node, &inner_components, &local_registry, diag);
235 exports.add_reexports(reexports, diag);
236
237 let custom_fonts = imports
238 .iter()
239 .filter(|import| matches!(import.import_kind, ImportKind::FileImport))
240 .filter_map(|import| {
241 if crate::pathutils::is_font_file(&import.file) {
242 let token_path = import.import_uri_token.source_file.path();
243 let import_file_path = PathBuf::from(import.file.clone());
244 let import_file_path = crate::pathutils::join(token_path, &import_file_path)
245 .unwrap_or(import_file_path);
246
247 if ignore_missing_font_files
252 || crate::pathutils::is_url(&import_file_path)
253 || crate::fileaccess::load_file(std::path::Path::new(&import_file_path))
254 .is_some()
255 {
256 Some((import_file_path.to_string_lossy().into(), import.import_uri_token.clone()))
257 } else {
258 diag.push_error(
259 format!("File \"{}\" not found", import.file),
260 &import.import_uri_token,
261 );
262 None
263 }
264 } else if import.file.ends_with(".slint") {
265 diag.push_error("Import names are missing. Please specify which types you would like to import".into(), &import.import_uri_token.parent());
266 None
267 } else {
268 diag.push_error(
269 format!("Unsupported foreign import \"{}\"", import.file),
270 &import.import_uri_token,
271 );
272 None
273 }
274 })
275 .collect();
276
277 for local_compo in &inner_components {
278 if exports
279 .components_or_types
280 .iter()
281 .filter_map(|(_, exported_compo_or_type)| exported_compo_or_type.as_ref().left())
282 .any(|exported_compo| Rc::ptr_eq(exported_compo, local_compo))
283 {
284 continue;
285 }
286 if local_compo.is_global() {
289 continue;
290 }
291 if !local_compo.used.get() {
292 diag.push_warning(
293 "Component is neither used nor exported".into(),
294 &local_compo.node.as_ref().map(|n| n.to_source_location()),
295 )
296 }
297 }
298
299 Document {
300 node: Some(node),
301 inner_components,
302 inner_types,
303 local_registry,
304 custom_fonts,
305 imports,
306 exports,
307 library_exports: Default::default(),
308 embedded_file_resources: Default::default(),
309 #[cfg(feature = "bundle-translations")]
310 translation_builder: None,
311 used_types: Default::default(),
312 popup_menu_impl: None,
313 }
314 }
315
316 pub fn exported_roots(&self) -> impl DoubleEndedIterator<Item = Rc<Component>> + '_ {
317 self.exports
318 .iter()
319 .filter_map(|e| e.1.as_ref().left())
320 .filter(|c| !c.is_global() && !c.is_interface())
321 .cloned()
322 }
323
324 pub fn last_exported_component(&self) -> Option<Rc<Component>> {
326 self.exports
327 .iter()
328 .filter_map(|e| Some((&e.0.name_ident, e.1.as_ref().left()?)))
329 .filter(|(_, c)| !c.is_global())
330 .max_by_key(|(n, _)| n.text_range().end())
331 .map(|(_, c)| c.clone())
332 }
333
334 pub fn visit_all_used_components(&self, mut v: impl FnMut(&Rc<Component>)) {
336 let used_types = self.used_types.borrow();
337 for c in &used_types.sub_components {
338 v(c);
339 }
340 for c in self.exported_roots() {
341 v(&c);
342 }
343 for c in &used_types.globals {
344 v(c);
345 }
346 if let Some(c) = &self.popup_menu_impl {
347 v(c);
348 }
349 }
350}
351
352#[derive(Debug, Clone)]
353pub struct PopupWindow {
354 pub component: Rc<Component>,
355 pub x: NamedReference,
356 pub y: NamedReference,
357 pub close_policy: EnumerationValue,
358 pub parent_element: ElementRc,
359 pub is_tooltip: bool,
360 pub is_open: Option<NamedReference>,
364}
365
366#[derive(Debug, Clone)]
367pub struct Timer {
368 pub interval: NamedReference,
369 pub triggered: NamedReference,
370 pub running: NamedReference,
371 pub element: ElementWeak,
372}
373
374pub const DEFAULT_SLOT_NAME: &str = "@children";
377
378pub fn slot_error_subject(name: &str) -> String {
379 if name == DEFAULT_SLOT_NAME {
380 "The @children placeholder".into()
381 } else {
382 format!("The slot '{name}'")
383 }
384}
385
386#[derive(Clone, Debug)]
387pub enum ChildInsertionPointNode {
388 DefaultChildrenPlaceHolder(SyntaxNode),
389 ChildrenPlaceHolder(syntax_nodes::ChildrenPlaceholder),
390 SlotPlaceholder(syntax_nodes::SubElement),
391 SlotForwarding(syntax_nodes::Expression),
392}
393
394impl ChildInsertionPointNode {
395 pub fn syntax_node(&self) -> &SyntaxNode {
396 match self {
397 Self::DefaultChildrenPlaceHolder(node) => node,
398 Self::ChildrenPlaceHolder(node) => node,
399 Self::SlotPlaceholder(node) => node,
400 Self::SlotForwarding(node) => node,
401 }
402 }
403}
404
405impl Spanned for ChildInsertionPointNode {
406 fn span(&self) -> crate::diagnostics::Span {
407 self.syntax_node().span()
408 }
409
410 fn source_file(&self) -> Option<&crate::diagnostics::SourceFile> {
411 self.syntax_node().source_file()
412 }
413}
414
415#[derive(Clone, Debug)]
416pub struct ChildrenInsertionPoint {
417 pub parent: ElementRc,
418 pub insertion_index: usize,
419 pub node: ChildInsertionPointNode,
420}
421
422#[derive(Clone, Debug)]
423pub struct DeclaredSlot {
424 pub name: SmolStr,
425 pub name_node: syntax_nodes::DeclaredIdentifier,
426 has_rejected_placeholder: bool,
427}
428
429#[derive(Clone, Debug)]
430pub struct SlotForwarding {
431 pub target: SmolStr,
432 pub source: SmolStr,
433 pub expression_node: syntax_nodes::Expression,
434}
435
436#[derive(Debug, Default)]
438pub struct UsedSubTypes {
439 pub globals: Vec<Rc<Component>>,
441 pub structs_and_enums: Vec<Type>,
443 pub sub_components: Vec<Rc<Component>>,
446 pub library_types_imports: Vec<(SmolStr, LibraryInfo)>,
449 pub library_global_imports: Vec<(SmolStr, LibraryInfo)>,
452 pub deprecated_type_aliases: Vec<(SmolStr, SmolStr)>,
455 pub collision_renamed_names: std::collections::BTreeSet<SmolStr>,
458}
459
460#[derive(Debug, Default, Clone)]
461pub struct InitCode {
462 pub constructor_code: Vec<Expression>,
464 pub focus_setting_code: Vec<Expression>,
466 pub font_registration_code: Vec<Expression>,
468
469 pub inlined_init_code: BTreeMap<usize, Expression>,
472}
473
474impl InitCode {
475 pub fn iter(&self) -> impl Iterator<Item = &Expression> {
476 self.font_registration_code.iter().chain(self.iter_without_font_registration())
477 }
478 pub fn iter_without_font_registration(&self) -> impl Iterator<Item = &Expression> {
480 self.focus_setting_code
481 .iter()
482 .chain(self.constructor_code.iter())
483 .chain(self.inlined_init_code.values())
484 }
485 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Expression> {
486 self.font_registration_code
487 .iter_mut()
488 .chain(self.focus_setting_code.iter_mut())
489 .chain(self.constructor_code.iter_mut())
490 .chain(self.inlined_init_code.values_mut())
491 }
492}
493
494#[derive(Default, Debug)]
497pub struct Component {
498 pub node: Option<syntax_nodes::Component>,
499 pub id: SmolStr,
500 pub root_element: ElementRc,
501
502 pub parent_element: RefCell<ElementWeak>,
504
505 pub optimized_elements: RefCell<Vec<ElementRc>>,
508
509 pub root_constraints: RefCell<LayoutConstraints>,
511
512 pub child_insertion_points: RefCell<BTreeMap<String, ChildrenInsertionPoint>>,
515
516 pub declared_slots: RefCell<Vec<DeclaredSlot>>,
518
519 pub init_code: RefCell<InitCode>,
520
521 pub popup_windows: RefCell<Vec<PopupWindow>>,
522 pub timers: RefCell<Vec<Timer>>,
523 pub menu_item_tree: RefCell<Vec<Rc<Component>>>,
524
525 pub inherits_popup_window: Cell<bool>,
527
528 pub exported_global_names: RefCell<Vec<ExportedName>>,
531
532 pub used: Cell<bool>,
534
535 pub private_properties: RefCell<Vec<(SmolStr, Type)>>,
538
539 pub from_library: Cell<bool>,
541}
542
543impl Component {
544 pub fn from_node(
545 node: syntax_nodes::Component,
546 diag: &mut BuildDiagnostics,
547 tr: &TypeRegister,
548 ) -> Rc<Self> {
549 let mut child_insertion_points = BTreeMap::new();
550 let mut declared_slots = Vec::new();
551 let is_legacy_syntax = node.child_token(SyntaxKind::ColonEqual).is_some();
552 let c = Component {
553 node: Some(node.clone()),
554 id: parser::identifier_text(&node.DeclaredIdentifier()).unwrap_or_default(),
555 root_element: Element::from_node(
556 node.Element(),
557 "root".into(),
558 match node.child_text(SyntaxKind::Identifier) {
559 Some(t) if t == "global" => {
560 #[cfg(feature = "slint-sc")]
561 diag.slint_sc_error("Globals are", &node.DeclaredIdentifier());
562 ElementType::Global
563 }
564 Some(t) if t == "interface" => {
565 if reject_experimental_feature(diag, tr, "interface", &node) {
566 ElementType::Error
567 } else {
568 ElementType::Interface
569 }
570 }
571 _ => ElementType::Error,
572 },
573 &mut child_insertion_points,
574 &mut declared_slots,
575 is_legacy_syntax,
576 diag,
577 tr,
578 ),
579 child_insertion_points: RefCell::new(child_insertion_points),
580 declared_slots: RefCell::new(declared_slots),
581 ..Default::default()
582 };
583 c.check_slot_validity(diag);
584 let c = Rc::new(c);
585 if c.root_element
587 .borrow()
588 .builtin_type()
589 .is_some_and(|b| matches!(b.name.as_str(), "Window" | "Dialog"))
590 {
591 for prop in ["x", "y"] {
592 if let Some(b) = c.root_element.borrow().binding_cell_including_synthetic(prop) {
593 #[cfg(feature = "slint-sc")]
594 if diag.slint_sc {
595 diag.slint_sc_error(&format!("The property '{prop}' is"), &*b.borrow());
596 continue;
597 }
598 diag.push_warning(
599 format!(
600 "Setting '{prop}' on a Window is deprecated, it doesn't affect the position of the window"
601 ),
602 &*b.borrow(),
603 );
604 }
605 }
606 #[cfg(feature = "slint-sc")]
609 for prop in ["width", "height"] {
610 if let Some(b) = c.root_element.borrow().binding_cell_including_synthetic(prop) {
611 diag.slint_sc_error(
612 &format!("Binding the '{prop}' of the root element is"),
613 &*b.borrow(),
614 );
615 }
616 }
617 }
618 let weak = Rc::downgrade(&c);
619 recurse_elem(&c.root_element, &(), &mut |e, _| {
620 e.borrow_mut().enclosing_component = weak.clone();
621 if let Some(qualified_id) =
622 e.borrow_mut().debug.first_mut().and_then(|x| x.qualified_id.as_mut())
623 {
624 *qualified_id = format_smolstr!("{}::{}", c.id, qualified_id);
625 }
626 });
627 c
628 }
629
630 fn check_slot_validity(&self, diagnostics: &mut BuildDiagnostics) {
631 if !diagnostics.enable_experimental {
632 return;
633 }
634 if self.is_global() || self.is_interface() {
635 return;
636 }
637 let mut declared_slot_nodes = BTreeMap::<SmolStr, syntax_nodes::DeclaredIdentifier>::new();
638 for slot in self.declared_slots.borrow().iter() {
639 if slot.name == "children" {
640 diagnostics.push_error(
641 format!(
642 "The name '{}' is reserved for the default slot. Use @children instead",
643 slot.name
644 ),
645 &slot.name_node,
646 );
647 continue;
648 }
649 if declared_slot_nodes.insert(slot.name.clone(), slot.name_node.clone()).is_some() {
650 diagnostics.push_error(
651 format!("Duplicate slot declaration '{}'", slot.name),
652 &slot.name_node,
653 );
654 }
655 }
656 for (name, cip) in self.child_insertion_points.borrow().iter() {
657 if name == DEFAULT_SLOT_NAME {
658 continue;
659 }
660 if !declared_slot_nodes.contains_key(name.as_str()) {
661 diagnostics
662 .push_error(format!("The slot '{name}' is used but not declared"), &cip.node);
663 }
664 }
665 for (name, node) in declared_slot_nodes.iter() {
666 let has_rejected_placeholder = self
667 .declared_slots
668 .borrow()
669 .iter()
670 .any(|slot| slot.has_rejected_placeholder && &slot.name == name);
671 if !self.child_insertion_points.borrow().contains_key(name.as_str())
672 && !has_rejected_placeholder
673 {
674 diagnostics.push_error(format!("The slot '{name}' is declared but not used"), node);
675 }
676 }
677 }
678
679 pub fn is_global(&self) -> bool {
681 match &self.root_element.borrow().base_type {
682 ElementType::Global => true,
683 ElementType::Builtin(c) => c.is_global,
684 _ => false,
685 }
686 }
687
688 pub fn is_interface(&self) -> bool {
690 matches!(&self.root_element.borrow().base_type, ElementType::Interface)
691 }
692
693 pub fn inherits_system_tray_icon(&self) -> bool {
698 self.root_element
699 .borrow()
700 .native_class()
701 .is_some_and(|n| n.class_name.as_str() == "SystemTrayIcon")
702 }
703
704 pub fn global_aliases(&self) -> Vec<SmolStr> {
707 self.exported_global_names
708 .borrow()
709 .iter()
710 .filter(|name| name.as_str() != self.root_element.borrow().id)
711 .map(|name| name.original_name())
712 .collect()
713 }
714
715 pub fn repeater_count(&self) -> u32 {
717 let mut count = 0;
718 recurse_elem(&self.root_element, &(), &mut |element, _| {
719 let element = element.borrow();
720 if let Some(sub_component) = element.sub_component() {
721 count += sub_component.repeater_count();
722 } else if element.repeated.is_some() {
723 count += 1;
724 }
725 });
726 count
727 }
728
729 pub fn parent_element(&self) -> Option<ElementRc> {
735 self.parent_element.borrow().upgrade()
736 }
737}
738
739#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
740pub enum PropertyVisibility {
741 #[default]
742 Private,
743 Input,
744 Output,
745 InOut,
746 Constexpr,
748 Fake,
751 Public,
753 Protected,
754}
755
756impl Display for PropertyVisibility {
757 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
758 match self {
759 PropertyVisibility::Private => f.write_str("private"),
760 PropertyVisibility::Input => f.write_str("in"),
761 PropertyVisibility::Output => f.write_str("out"),
762 PropertyVisibility::InOut => f.write_str("in-out"),
763 PropertyVisibility::Constexpr => f.write_str("constexpr"),
764 PropertyVisibility::Public => f.write_str("public"),
765 PropertyVisibility::Protected => f.write_str("protected"),
766 PropertyVisibility::Fake => f.write_str("fake"),
767 }
768 }
769}
770
771#[derive(Clone, Debug, Default)]
772pub struct PropertyDeclaration {
773 pub property_type: Type,
774 pub node: Option<SyntaxNode>,
775 pub expose_in_public_api: bool,
777 pub is_alias: Option<NamedReference>,
779 pub visibility: PropertyVisibility,
780 pub pure: Option<bool>,
782 pub shadowed_name: Option<SmolStr>,
785 pub shadowable: bool,
787 pub moved_from: Option<SmolStr>,
793 pub deprecated: Option<SmolStr>,
797}
798
799impl PropertyDeclaration {
800 pub fn type_node(&self) -> Option<SyntaxNode> {
802 let node = self.node.as_ref()?;
803 if let Some(x) = syntax_nodes::PropertyDeclaration::new(node.clone()) {
804 Some(x.Type().map_or_else(|| x.into(), |x| x.into()))
805 } else {
806 node.clone().into()
807 }
808 }
809
810 pub fn declared_name<'a>(&'a self, internal_name: &'a SmolStr) -> &'a SmolStr {
812 self.shadowed_name.as_ref().unwrap_or(internal_name)
813 }
814
815 pub fn is_private_shadow(&self) -> bool {
818 self.shadowed_name.is_some() && self.visibility == PropertyVisibility::Private
819 }
820
821 pub fn has_derived_deprecation(&self) -> bool {
824 self.deprecated.is_some()
825 && self
826 .node
827 .as_ref()
828 .and_then(|n| syntax_nodes::PropertyDeclaration::new(n.clone()))
829 .and_then(|p| p.PropertyDeprecation())
830 .is_some_and(|d| d.child_token(SyntaxKind::StringLiteral).is_none())
831 }
832}
833
834fn shadowable_attribute(
836 node: Option<syntax_nodes::ShadowableAttribute>,
837 tr: &TypeRegister,
838 diag: &mut BuildDiagnostics,
839) -> bool {
840 node.is_some_and(|node| !reject_experimental_feature(diag, tr, "@shadowable", &node))
841}
842
843enum DeprecationHint {
845 TwoWayBinding(Option<syntax_nodes::QualifiedName>),
847 MessageRequired,
849}
850
851fn member_deprecation(
854 deprecation: Option<syntax_nodes::PropertyDeprecation>,
855 hint: DeprecationHint,
856 tr: &TypeRegister,
857 diag: &mut BuildDiagnostics,
858) -> Option<SmolStr> {
859 let deprecation = deprecation?;
860 if reject_experimental_feature(diag, tr, "@deprecated", &deprecation) {
861 return None;
862 }
863 if let Some(message) = deprecation.child_token(SyntaxKind::StringLiteral) {
864 return crate::literals::unescape_string(message.text());
865 }
866 let message = match hint {
867 DeprecationHint::TwoWayBinding(target) => {
868 if let Some(qn) = target {
872 let mut segments = qn
873 .children_with_tokens()
874 .filter(|t| t.kind() == SyntaxKind::Identifier)
875 .map(|t| parser::normalize_identifier(t.as_token().unwrap().text()))
876 .peekable();
877 if segments.peek().is_some_and(|s| matches!(s.as_str(), "self" | "root")) {
878 segments.next();
879 }
880 let path = segments.collect::<Vec<_>>().join(".");
881 if !path.is_empty() {
882 return Some(format_smolstr!("Please use '{path}' instead"));
883 }
884 }
885 "@deprecated without a message requires a two-way binding to derive the replacement from"
886 }
887 DeprecationHint::MessageRequired => "@deprecated on a function requires a message",
888 };
889 diag.push_error(message.into(), &deprecation);
890 None
891}
892
893fn from_base(mut r: PropertyLookupResult<'_>) -> PropertyLookupResult<'_> {
895 r.is_in_direct_base = r.is_local_to_component;
896 r.is_local_to_component = false;
897 r
898}
899
900fn cannot_override_message(
903 kind: Option<&str>,
904 name: &SmolStr,
905 declared_in: &Option<Rc<Component>>,
906) -> String {
907 let kind = kind.map_or_else(String::new, |kind| format!("{kind} "));
908 match declared_in {
909 Some(base) => format!("Cannot override {kind}'{name}' declared in '{}'", base.id),
910 None => format!("Cannot override {kind}'{name}'"),
911 }
912}
913
914enum MemberDeclaration {
917 New,
919 Shadow {
922 internal_name: SmolStr,
923 warning: Option<String>,
925 },
926 Conflict {
928 existing_type: Type,
929 declared_in: Option<Rc<Component>>,
931 },
932}
933
934impl MemberDeclaration {
935 fn register(
938 self,
939 elem: &mut Element,
940 source_name: &SmolStr,
941 node: &dyn Spanned,
942 diag: &mut BuildDiagnostics,
943 ) -> SmolStr {
944 let Self::Shadow { internal_name, warning } = self else {
945 return source_name.clone();
946 };
947 elem.shadowing_members.insert(source_name.clone(), internal_name.clone());
948 if let Some(warning) = warning {
949 diag.push_warning(warning, node);
950 }
951 internal_name
952 }
953}
954
955impl From<Type> for PropertyDeclaration {
956 fn from(ty: Type) -> Self {
957 PropertyDeclaration { property_type: ty, ..Self::default() }
958 }
959}
960
961#[derive(Debug, Clone, Copy, PartialEq)]
962pub enum TransitionDirection {
963 In,
964 Out,
965 InOut,
966}
967
968#[derive(Debug, Clone)]
969pub struct TransitionPropertyAnimation {
970 pub state_id: i32,
972 pub direction: TransitionDirection,
974 pub animation: ElementRc,
976}
977
978impl TransitionPropertyAnimation {
979 pub fn condition(&self, state: Expression) -> Expression {
982 match self.direction {
983 TransitionDirection::In => Expression::BinaryExpression {
984 lhs: Box::new(Expression::StructFieldAccess {
985 base: Box::new(state),
986 name: "current-state".into(),
987 }),
988 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
989 op: '=',
990 source_location: None,
991 },
992 TransitionDirection::Out => Expression::BinaryExpression {
993 lhs: Box::new(Expression::StructFieldAccess {
994 base: Box::new(state),
995 name: "previous-state".into(),
996 }),
997 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
998 op: '=',
999 source_location: None,
1000 },
1001 TransitionDirection::InOut => Expression::BinaryExpression {
1002 lhs: Box::new(Expression::BinaryExpression {
1003 source_location: None,
1004 lhs: Box::new(Expression::StructFieldAccess {
1005 base: Box::new(state.clone()),
1006 name: "current-state".into(),
1007 }),
1008 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
1009 op: '=',
1010 }),
1011 rhs: Box::new(Expression::BinaryExpression {
1012 source_location: None,
1013 lhs: Box::new(Expression::StructFieldAccess {
1014 base: Box::new(state),
1015 name: "previous-state".into(),
1016 }),
1017 rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
1018 op: '=',
1019 }),
1020 op: '|',
1021 source_location: None,
1022 },
1023 }
1024 }
1025}
1026
1027#[derive(Debug)]
1028pub enum PropertyAnimation {
1029 Static(ElementRc),
1030 Transition { state_ref: Expression, animations: Vec<TransitionPropertyAnimation> },
1031}
1032
1033impl Clone for PropertyAnimation {
1034 fn clone(&self) -> Self {
1035 fn deep_clone(e: &ElementRc) -> ElementRc {
1036 let e = e.borrow();
1037 debug_assert!(e.children.is_empty());
1038 debug_assert!(e.property_declarations.is_empty());
1039 debug_assert!(e.states.is_empty() && e.transitions.is_empty());
1040 Rc::new(RefCell::new(Element {
1041 id: e.id.clone(),
1042 base_type: e.base_type.clone(),
1043 bindings: e.bindings.clone(),
1044 property_analysis: e.property_analysis.clone(),
1045 enclosing_component: e.enclosing_component.clone(),
1046 repeated: None,
1047 debug: e.debug.clone(),
1048 ..Default::default()
1049 }))
1050 }
1051 match self {
1052 PropertyAnimation::Static(e) => PropertyAnimation::Static(deep_clone(e)),
1053 PropertyAnimation::Transition { state_ref, animations } => {
1054 PropertyAnimation::Transition {
1055 state_ref: state_ref.clone(),
1056 animations: animations
1057 .iter()
1058 .map(|t| TransitionPropertyAnimation {
1059 state_id: t.state_id,
1060 direction: t.direction,
1061 animation: deep_clone(&t.animation),
1062 })
1063 .collect(),
1064 }
1065 }
1066 }
1067 }
1068}
1069
1070#[derive(Default, Clone)]
1072pub struct AccessibilityProps(pub BTreeMap<String, NamedReference>);
1073
1074#[derive(Clone, Debug)]
1075pub struct GeometryProps {
1076 pub x: NamedReference,
1077 pub y: NamedReference,
1078 pub width: NamedReference,
1079 pub height: NamedReference,
1080}
1081
1082#[derive(Clone, Debug)]
1084pub enum ZOrder {
1085 Constant(f32),
1087 Dynamic(NamedReference),
1089 PerInstance(NamedReference),
1094}
1095
1096impl GeometryProps {
1097 pub fn new(element: &ElementRc) -> Self {
1098 Self {
1099 x: NamedReference::new(element, SmolStr::new_static("x")),
1100 y: NamedReference::new(element, SmolStr::new_static("y")),
1101 width: NamedReference::new(element, SmolStr::new_static("width")),
1102 height: NamedReference::new(element, SmolStr::new_static("height")),
1103 }
1104 }
1105}
1106
1107pub type BindingsMap = BTreeMap<SmolStr, RefCell<BindingExpression>>;
1108
1109#[derive(Clone, Default)]
1116pub struct Bindings(BindingsMap);
1117
1118impl std::iter::FromIterator<(SmolStr, RefCell<BindingExpression>)> for Bindings {
1119 fn from_iter<T: IntoIterator<Item = (SmolStr, RefCell<BindingExpression>)>>(iter: T) -> Self {
1120 Bindings(iter.into_iter().collect())
1121 }
1122}
1123
1124impl From<BindingsMap> for Bindings {
1125 fn from(map: BindingsMap) -> Self {
1126 Bindings(map)
1127 }
1128}
1129
1130impl Bindings {
1131 pub fn binding_cell_including_synthetic(
1136 &self,
1137 name: &str,
1138 ) -> Option<&RefCell<BindingExpression>> {
1139 self.0.get(name)
1140 }
1141}
1142
1143#[derive(Clone, Debug)]
1144pub struct ElementDebugInfo {
1145 pub qualified_id: Option<SmolStr>,
1147 pub type_name: String,
1148 pub element_hash: u64,
1153 pub node: syntax_nodes::Element,
1154 pub layout: Option<crate::layout::Layout>,
1157 pub element_boundary: bool,
1161}
1162
1163impl ElementDebugInfo {
1164 fn encoded_element_info(&self) -> String {
1168 let mut info = self.type_name.clone();
1169 info.push(',');
1170 if let Some(id) = self.qualified_id.as_ref() {
1171 info.push_str(id);
1172 }
1173 info.push(',');
1174 if let Some(layout) = &self.layout {
1175 use crate::layout::{Layout, Orientation};
1176 match layout {
1177 Layout::BoxLayout(b) => match b.orientation {
1178 Orientation::Horizontal => info.push_str("h-box"),
1179 Orientation::Vertical => info.push_str("v-box"),
1180 },
1181 Layout::GridLayout(_) => info.push_str("grid"),
1182 Layout::FlexboxLayout(_) => info.push_str("flex-box"),
1183 }
1184 }
1185 info
1186 }
1187}
1188
1189#[derive(Default)]
1191pub struct Element {
1192 pub id: SmolStr,
1198 pub base_type: ElementType,
1200 pub bindings: Bindings,
1202 pub change_callbacks: BTreeMap<SmolStr, RefCell<Vec<Expression>>>,
1203 pub property_analysis: RefCell<BTreeMap<SmolStr, PropertyAnalysis>>,
1204
1205 pub children: Vec<ElementRc>,
1206 pub enclosing_component: Weak<Component>,
1208
1209 pub property_declarations: BTreeMap<SmolStr, PropertyDeclaration>,
1210
1211 pub shadowing_members: BTreeMap<SmolStr, SmolStr>,
1214
1215 pub named_references: crate::namedreference::NamedReferenceContainer,
1217
1218 pub repeated: Option<RepeatedElementInfo>,
1220 pub is_component_placeholder: bool,
1222 pub is_injected_wrapper_element: bool,
1227
1228 pub z_order: Option<ZOrder>,
1232
1233 pub states: Vec<State>,
1234 pub transitions: Vec<Transition>,
1235 pub match_elements: Vec<MatchElementInfo>,
1236 pub child_of_layout: bool,
1238 pub child_of_flexbox: bool,
1242 pub parent_box_layout_orientation: Option<Orientation>,
1247 pub layout_info_prop: Option<(NamedReference, NamedReference)>,
1254 pub layout_info_v_with_constraint: Option<NamedReference>,
1259 pub layout_info_h_at_own_height: Option<NamedReference>,
1265 pub height_is_literal: bool,
1272 pub default_fill_parent: (bool, bool),
1274
1275 pub accessibility_props: AccessibilityProps,
1276
1277 pub geometry_props: Option<GeometryProps>,
1280
1281 pub is_flickable_content: bool,
1283
1284 pub has_popup_child: bool,
1287
1288 pub is_tooltip: bool,
1290
1291 pub item_index: OnceCell<u32>,
1294 pub item_index_of_first_children: OnceCell<u32>,
1296
1297 pub is_legacy_syntax: bool,
1299
1300 pub inline_depth: i32,
1302
1303 pub slot_target: Option<SmolStr>,
1305
1306 pub forwarded_slots: Vec<SlotForwarding>,
1308
1309 pub grid_layout_cell: Option<Rc<RefCell<crate::layout::GridLayoutCell>>>,
1311
1312 pub debug: Vec<ElementDebugInfo>,
1318}
1319
1320impl Spanned for Element {
1321 fn span(&self) -> crate::diagnostics::Span {
1322 self.debug
1323 .first()
1324 .map(|n| {
1325 n.node.QualifiedName().as_ref().map(Spanned::span).unwrap_or_else(|| n.node.span())
1330 })
1331 .unwrap_or_default()
1332 }
1333
1334 fn source_file(&self) -> Option<&crate::diagnostics::SourceFile> {
1335 self.debug.first().map(|n| &n.node.source_file)
1336 }
1337}
1338
1339impl core::fmt::Debug for Element {
1340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1341 pretty_print(f, self, 0)
1342 }
1343}
1344
1345pub fn pretty_print(
1346 f: &mut impl std::fmt::Write,
1347 e: &Element,
1348 indentation: usize,
1349) -> std::fmt::Result {
1350 if let Some(repeated) = &e.repeated {
1351 write!(f, "for {}[{}] in ", repeated.model_data_id, repeated.index_id)?;
1352 expression_tree::pretty_print(f, &repeated.model)?;
1353 write!(f, ":")?;
1354 if let ElementType::Component(base) = &e.base_type {
1355 write!(f, "(base) ")?;
1356 if base.parent_element().is_some() {
1357 pretty_print(f, &base.root_element.borrow(), indentation)?;
1358 return Ok(());
1359 }
1360 }
1361 }
1362 if e.is_component_placeholder {
1363 write!(f, "/* Component Placeholder */ ")?;
1364 }
1365 writeln!(f, "{} := {} {{ /* {} */", e.id, e.base_type, e.element_infos())?;
1366 let mut indentation = indentation + 1;
1367 macro_rules! indent {
1368 () => {
1369 for _ in 0..indentation {
1370 write!(f, " ")?
1371 }
1372 };
1373 }
1374 for (name, ty) in &e.property_declarations {
1375 indent!();
1376 if let Some(alias) = &ty.is_alias {
1377 writeln!(f, "alias<{}> {} <=> {:?};", ty.property_type, name, alias)?
1378 } else {
1379 writeln!(f, "property<{}> {};", ty.property_type, name)?
1380 }
1381 }
1382 for (name, expr) in &e.bindings.0 {
1383 indent!();
1384 write!(f, "{name}: ")?;
1385 let Ok(expr) = expr.try_borrow() else {
1386 writeln!(f, "<borrowed>")?;
1387 continue;
1388 };
1389 expression_tree::pretty_print(f, &expr.expression)?;
1390 if expr.analysis.as_ref().is_some_and(|a| a.is_const) {
1391 write!(f, "/*const*/")?;
1392 }
1393 writeln!(f, ";")?;
1394 if let Some(anim) = &expr.animation {
1396 indent!();
1397 writeln!(f, "animate {name} {anim:?}")?;
1398 }
1399 for nr in &expr.two_way_bindings {
1400 indent!();
1401 writeln!(f, "{name} <=> {nr:?};")?;
1402 }
1403 }
1404 for (name, ch) in &e.change_callbacks {
1405 for ex in &*ch.borrow() {
1406 indent!();
1407 write!(f, "changed {name} => ")?;
1408 expression_tree::pretty_print(f, ex)?;
1409 writeln!(f)?;
1410 }
1411 }
1412 if !e.states.is_empty() {
1413 indent!();
1414 writeln!(f, "states {:?}", e.states)?;
1415 }
1416 if !e.transitions.is_empty() {
1417 indent!();
1418 writeln!(f, "transitions {:?} ", e.transitions)?;
1419 }
1420 for c in &e.children {
1421 indent!();
1422 pretty_print(f, &c.borrow(), indentation)?
1423 }
1424 if let Some(g) = &e.geometry_props {
1425 indent!();
1426 writeln!(f, "geometry {g:?} ")?;
1427 }
1428
1429 indentation -= 1;
1433 indent!();
1434 writeln!(f, "}}")
1435}
1436
1437#[derive(Clone, Default, Debug)]
1438pub struct PropertyAnalysis {
1439 pub is_set: bool,
1441
1442 pub is_set_externally: bool,
1444
1445 pub is_read: bool,
1448
1449 pub is_read_externally: bool,
1451
1452 pub is_linked_to_read_only: bool,
1454
1455 pub is_linked: bool,
1457}
1458
1459impl PropertyAnalysis {
1460 pub fn merge_with_base(&mut self, other: &PropertyAnalysis) {
1465 self.is_set |= other.is_set;
1466 self.is_read |= other.is_read;
1467 }
1468
1469 pub fn merge(&mut self, other: &PropertyAnalysis) {
1471 self.is_set |= other.is_set;
1472 self.is_read |= other.is_read;
1473 self.is_read_externally |= other.is_read_externally;
1474 self.is_set_externally |= other.is_set_externally;
1475 }
1476
1477 pub fn is_used(&self) -> bool {
1479 self.is_read || self.is_read_externally || self.is_set || self.is_set_externally
1480 }
1481}
1482
1483#[derive(Debug, Clone)]
1484pub struct ListViewInfo {
1485 pub content_y: NamedReference,
1486 pub content_height: Option<NamedReference>,
1489 pub content_width: Option<NamedReference>,
1492 pub listview_height: NamedReference,
1494 pub listview_width: NamedReference,
1496}
1497
1498#[derive(Debug, Clone)]
1499pub struct RepeatedElementInfo {
1501 pub model: Expression,
1502 pub model_data_id: SmolStr,
1503 pub index_id: SmolStr,
1504 pub is_conditional_element: bool,
1508 pub is_listview: Option<ListViewInfo>,
1510}
1511
1512pub struct MatchElementInfo {
1514 pub node: syntax_nodes::MatchElement,
1516 pub subject: Expression,
1518 pub cases: Vec<MatchCaseInfo>,
1520 pub wildcard: WildcardMatchCaseInfo,
1522}
1523
1524pub enum WildcardMatchCaseInfo {
1525 None,
1526 Empty,
1527 Element(ElementRc),
1528}
1529
1530pub struct MatchCaseInfo {
1532 pub value: Expression,
1534 pub node: syntax_nodes::Expression,
1536 pub element: Option<ElementRc>,
1538}
1539
1540impl MatchElementInfo {
1541 pub fn elements(&self) -> impl Iterator<Item = ElementRc> + '_ {
1543 self.cases.iter().filter_map(|case| case.element.clone()).chain(match &self.wildcard {
1544 WildcardMatchCaseInfo::Element(e) => Some(e.clone()),
1545 WildcardMatchCaseInfo::None | WildcardMatchCaseInfo::Empty => None,
1546 })
1547 }
1548
1549 pub fn lower_to_conditional_elements(&self) {
1551 let compare = |value: &Expression, op| Expression::BinaryExpression {
1552 lhs: Box::new(self.subject.clone()),
1553 rhs: Box::new(value.clone()),
1554 op,
1555 source_location: None,
1556 };
1557 let show_when = |element: &ElementRc, condition| {
1558 element.borrow_mut().repeated = Some(RepeatedElementInfo {
1559 model: condition,
1560 model_data_id: SmolStr::default(),
1561 index_id: SmolStr::default(),
1562 is_conditional_element: true,
1563 is_listview: None,
1564 });
1565 };
1566
1567 for case in &self.cases {
1568 if let Some(element) = &case.element {
1569 show_when(element, compare(&case.value, '='));
1570 }
1571 }
1572 if let WildcardMatchCaseInfo::Element(wildcard) = &self.wildcard {
1573 let condition = self
1574 .cases
1575 .iter()
1576 .map(|case| compare(&case.value, '!'))
1577 .reduce(|lhs, rhs| Expression::BinaryExpression {
1578 lhs: Box::new(lhs),
1579 rhs: Box::new(rhs),
1580 op: '&',
1581 source_location: None,
1582 })
1583 .unwrap_or(Expression::BoolLiteral(true));
1584 show_when(wildcard, condition);
1585 }
1586 }
1587}
1588
1589pub type ElementRc = Rc<RefCell<Element>>;
1590pub type ElementWeak = Weak<RefCell<Element>>;
1591
1592impl Element {
1593 pub fn make_rc(self) -> ElementRc {
1594 let r = ElementRc::new(RefCell::new(self));
1595 let g = GeometryProps::new(&r);
1596 r.borrow_mut().geometry_props = Some(g);
1597 r
1598 }
1599
1600 pub fn from_node(
1601 node: syntax_nodes::Element,
1602 id: SmolStr,
1603 parent_type: ElementType,
1604 component_child_insertion_points: &mut BTreeMap<String, ChildrenInsertionPoint>,
1605 declared_slots: &mut Vec<DeclaredSlot>,
1606 is_legacy_syntax: bool,
1607 diag: &mut BuildDiagnostics,
1608 tr: &TypeRegister,
1609 ) -> ElementRc {
1610 #[cfg(feature = "slint-sc")]
1613 let is_component_root =
1614 !matches!(parent_type, ElementType::Builtin(_) | ElementType::Component(_));
1615 let base_type = if let Some(base_node) = node.QualifiedName() {
1616 let base = QualifiedTypeName::from_node(base_node.clone());
1617 let base_string = base.to_smolstr();
1618 match parent_type.lookup_type_for_child_element(&base_string, tr) {
1619 Ok(ElementType::Component(c)) if c.is_global() => {
1620 diag.push_error(
1621 "Cannot create an instance of a global component".into(),
1622 &base_node,
1623 );
1624 ElementType::Error
1625 }
1626 Ok(ty) => {
1627 #[cfg(feature = "slint-sc")]
1630 if let ElementType::Builtin(b) = &ty
1631 && !b.slint_sc
1632 {
1633 diag.slint_sc_error(
1634 &format!("The builtin element '{}' is", b.name),
1635 &base_node,
1636 );
1637 }
1638 ty
1639 }
1640 Err(err) => {
1641 diag.push_error(err, &base_node);
1642 ElementType::Error
1643 }
1644 }
1645 } else if parent_type == ElementType::Global || parent_type == ElementType::Interface {
1646 let mut error_on = |node: &dyn Spanned, what: &str| {
1648 let element_type = match parent_type {
1649 ElementType::Global => "A global component",
1650 ElementType::Interface => "An interface",
1651 _ => "An unexpected type",
1652 };
1653 diag.push_error(format!("{element_type} cannot have {what}"), node);
1654 };
1655 node.SubElement().for_each(|n| error_on(&n, "sub elements"));
1656 node.RepeatedElement().for_each(|n| error_on(&n, "sub elements"));
1657 if let Some(n) = node.ChildrenPlaceholder() {
1658 error_on(&n, "sub elements");
1659 }
1660 node.PropertyAnimation().for_each(|n| error_on(&n, "animations"));
1661 node.States().for_each(|n| error_on(&n, "states"));
1662 node.Transitions().for_each(|n| error_on(&n, "transitions"));
1663 node.CallbackDeclaration().for_each(|cb| {
1664 if parser::identifier_text(&cb.DeclaredIdentifier()).is_some_and(|s| s == "init") {
1665 error_on(&cb, "an 'init' callback")
1666 }
1667 });
1668 node.CallbackConnection().for_each(|cb| {
1669 if parser::identifier_text(&cb).is_some_and(|s| s == "init") {
1670 error_on(&cb, "an 'init' callback")
1671 }
1672 });
1673 node.MatchElement().for_each(|n| error_on(&n, "match elements"));
1674 node.SlotDeclaration().for_each(|n| error_on(&n, "slots"));
1675
1676 if parent_type == ElementType::Interface {
1677 node.Binding().for_each(|n| error_on(&n, "bindings"));
1678 node.TwoWayBinding().for_each(|n| error_on(&n, "two-way bindings"));
1679
1680 node.ImplementStatement().for_each(|stmt| {
1681 diag.push_error("Interfaces cannot implement another interface".into(), &stmt);
1682 });
1683 } else {
1684 node.ImplementStatement().for_each(|stmt| {
1685 diag.push_error("Globals cannot implement an interface".into(), &stmt);
1686 });
1687 }
1688
1689 parent_type
1690 } else if parent_type != ElementType::Error {
1691 assert!(diag.has_errors());
1693 return ElementRc::default();
1694 } else {
1695 tr.empty_type()
1696 };
1697 let is_interface = base_type == ElementType::Interface;
1698 let qualified_id = (!id.is_empty()).then(|| id.clone());
1700 if let ElementType::Component(c) = &base_type {
1701 c.used.set(true);
1702 }
1703 let type_name = base_type
1704 .type_name()
1705 .filter(|_| base_type != tr.empty_type())
1706 .unwrap_or_default()
1707 .to_string();
1708 let mut r = Element {
1709 id,
1710 base_type: base_type.clone(),
1711 debug: vec![ElementDebugInfo {
1712 qualified_id,
1713 element_hash: 0,
1714 type_name,
1715 node: node.clone(),
1716 layout: None,
1717 element_boundary: false,
1718 }],
1719 is_legacy_syntax,
1720 ..Default::default()
1721 };
1722
1723 let mut property_bindings: Vec<(
1724 SmolStr,
1725 syntax_nodes::BindingExpression,
1726 syntax_nodes::DeclaredIdentifier,
1727 )> = Vec::new();
1728
1729 let mut two_way_bindings: Vec<(
1730 SmolStr,
1731 syntax_nodes::TwoWayBinding,
1732 syntax_nodes::DeclaredIdentifier,
1733 )> = Vec::new();
1734
1735 for prop_decl in node.PropertyDeclaration() {
1736 #[cfg(feature = "slint-sc")]
1738 if !is_component_root {
1739 diag.slint_sc_error(
1740 "Declaring a property on an element other than the root is",
1741 &prop_decl,
1742 );
1743 }
1744 let prop_type = prop_decl
1745 .Type()
1746 .map(|type_node| type_from_node(type_node, diag, tr))
1747 .unwrap_or(Type::InferredProperty);
1749
1750 let unresolved_prop_name =
1751 unwrap_or_continue!(parser::identifier_text(&prop_decl.DeclaredIdentifier()); diag);
1752 let declaration = r.member_declaration(&unresolved_prop_name);
1753 let name_token =
1754 prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap();
1755 if let MemberDeclaration::Conflict { existing_type, declared_in } = &declaration {
1756 match existing_type {
1757 Type::Callback { .. } => diag.push_error(
1758 format!("Cannot declare property '{unresolved_prop_name}' when a callback with the same name exists"),
1759 &name_token,
1760 ),
1761 Type::Function { .. } => diag.push_error(
1762 format!("Cannot declare property '{unresolved_prop_name}' when a function with the same name exists"),
1763 &name_token,
1764 ),
1765 _ => diag.push_error(
1766 cannot_override_message(Some("property"), &unresolved_prop_name, declared_in),
1767 &name_token,
1768 ),
1769 }
1770 continue;
1771 }
1772 let prop_name = declaration.register(&mut r, &unresolved_prop_name, &name_token, diag);
1773 let shadowed_name =
1774 (prop_name != unresolved_prop_name).then(|| unresolved_prop_name.clone());
1775
1776 let mut visibility = None;
1777 for token in prop_decl.children_with_tokens() {
1778 if token.kind() != SyntaxKind::Identifier {
1779 continue;
1780 }
1781 match (token.as_token().unwrap().text(), visibility) {
1782 ("in", None) => visibility = Some(PropertyVisibility::Input),
1783 ("in", Some(_)) => diag.push_error("Extra 'in' keyword".into(), &token),
1784 ("out", None) => visibility = Some(PropertyVisibility::Output),
1785 ("out", Some(_)) => diag.push_error("Extra 'out' keyword".into(), &token),
1786 ("in-out" | "in_out", None) => visibility = Some(PropertyVisibility::InOut),
1787 ("in-out" | "in_out", Some(_)) => {
1788 diag.push_error("Extra 'in-out' keyword".into(), &token)
1789 }
1790 ("private", None) => visibility = Some(PropertyVisibility::Private),
1791 ("private", Some(_)) => {
1792 diag.push_error("Extra 'private' keyword".into(), &token)
1793 }
1794 _ => (),
1795 }
1796 }
1797 let visibility = visibility.unwrap_or({
1798 if is_legacy_syntax {
1799 PropertyVisibility::InOut
1800 } else {
1801 PropertyVisibility::Private
1802 }
1803 });
1804
1805 if is_interface {
1806 if let Some(binding_expression) = &prop_decl.BindingExpression() {
1807 diag.push_error(
1808 "Interface properties cannot have default values".into(),
1809 binding_expression,
1810 )
1811 }
1812 if let Some(two_way) = &prop_decl.TwoWayBinding() {
1813 diag.push_error(
1814 "Interface properties cannot have default bindings".into(),
1815 two_way,
1816 )
1817 }
1818 if visibility == PropertyVisibility::Private {
1819 diag.push_error(
1820 "'private' properties are inaccessible in an interface".into(),
1821 &prop_decl,
1822 );
1823 }
1824 }
1825
1826 let deprecated = member_deprecation(
1827 prop_decl.PropertyDeprecation(),
1828 DeprecationHint::TwoWayBinding(
1829 prop_decl.TwoWayBinding().and_then(|twb| twb.Expression().QualifiedName()),
1830 ),
1831 tr,
1832 diag,
1833 );
1834
1835 r.property_declarations.insert(
1836 prop_name.clone(),
1837 PropertyDeclaration {
1838 property_type: prop_type,
1839 node: Some(prop_decl.clone().into()),
1840 visibility,
1841 shadowed_name,
1842 shadowable: shadowable_attribute(prop_decl.ShadowableAttribute(), tr, diag),
1843 deprecated,
1844 ..Default::default()
1845 },
1846 );
1847
1848 if let Some(csn) = prop_decl.BindingExpression() {
1849 property_bindings.push((prop_name.clone(), csn, prop_decl.DeclaredIdentifier()));
1850 }
1851
1852 if let Some(csn) = prop_decl.TwoWayBinding() {
1853 #[cfg(feature = "slint-sc")]
1854 diag.slint_sc_error("Two-way bindings are", &csn);
1855 two_way_bindings.push((prop_name, csn, prop_decl.DeclaredIdentifier()));
1856 }
1857 }
1858
1859 let (implemented_interfaces, child_implements) =
1860 if matches!(r.base_type, ElementType::Global | ElementType::Interface) {
1861 (Vec::new(), Vec::new())
1863 } else if r.id == "root" {
1864 interfaces::get_implemented_interfaces(&r, &node, tr, diag)
1865 } else {
1866 interfaces::disallow_implement_in_non_root(&node, tr, diag);
1867 (Vec::new(), Vec::new())
1868 };
1869
1870 for (prop_name, csn, source) in property_bindings {
1871 match r.bindings.0.entry(prop_name.clone()) {
1872 Entry::Vacant(e) => {
1873 e.insert(BindingExpression::new_uncompiled(csn.into()).into());
1874 }
1875 Entry::Occupied(_) => {
1876 diag.push_error("Duplicated property binding".into(), &source);
1877 }
1878 }
1879 }
1880
1881 for (prop_name, csn, source) in two_way_bindings {
1882 if r.bindings
1883 .0
1884 .insert(prop_name, BindingExpression::new_uncompiled(csn.into()).into())
1885 .is_some()
1886 {
1887 diag.push_error("Duplicated property binding".into(), &source);
1888 }
1889 }
1890
1891 r.parse_bindings(
1892 node.Binding().filter_map(|b| {
1893 Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into()))
1894 }),
1895 is_legacy_syntax,
1896 diag,
1897 );
1898 r.parse_bindings(
1899 node.TwoWayBinding()
1900 .filter_map(|b| Some((b.child_token(SyntaxKind::Identifier)?, b.into()))),
1901 is_legacy_syntax,
1902 diag,
1903 );
1904
1905 apply_default_type_properties(&mut r);
1906
1907 for sig_decl in node.CallbackDeclaration() {
1908 let name =
1909 unwrap_or_continue!(parser::identifier_text(&sig_decl.DeclaredIdentifier()); diag);
1910
1911 let pure = Some(
1912 sig_decl.child_token(SyntaxKind::Identifier).is_some_and(|t| t.text() == "pure"),
1913 );
1914
1915 #[cfg(feature = "slint-sc")]
1916 {
1917 if !is_component_root {
1919 diag.slint_sc_error(
1920 "Declaring a callback on an element other than the root is",
1921 &sig_decl,
1922 );
1923 }
1924 if pure == Some(true) {
1925 diag.slint_sc_error("Pure callbacks are", &sig_decl);
1926 }
1927 if let Some(param) = sig_decl.CallbackDeclarationParameter().next() {
1928 diag.slint_sc_error("Callback parameters are", ¶m);
1929 }
1930 if let Some(ret) = sig_decl.ReturnType() {
1931 diag.slint_sc_error("Callback return types are", &ret);
1932 }
1933 }
1934
1935 let declaration = r.member_declaration(&name);
1936 if let MemberDeclaration::Conflict { existing_type, declared_in } = &declaration {
1937 if matches!(existing_type, Type::Callback { .. }) {
1938 if r.declaration(&name).is_some() {
1940 diag.push_error(
1941 "Duplicated callback declaration".into(),
1942 &sig_decl.DeclaredIdentifier(),
1943 );
1944 } else {
1945 diag.push_error(
1946 cannot_override_message(Some("callback"), &name, declared_in),
1947 &sig_decl.DeclaredIdentifier(),
1948 )
1949 }
1950 } else {
1951 diag.push_error(
1952 format!(
1953 "Cannot declare callback '{name}' when a {} with the same name exists",
1954 if matches!(existing_type, Type::Function { .. }) {
1955 "function"
1956 } else {
1957 "property"
1958 }
1959 ),
1960 &sig_decl.DeclaredIdentifier(),
1961 );
1962 }
1963 continue;
1964 }
1965 let shadowable = shadowable_attribute(sig_decl.ShadowableAttribute(), tr, diag);
1966 let deprecated = member_deprecation(
1967 sig_decl.PropertyDeprecation(),
1968 DeprecationHint::TwoWayBinding(
1969 sig_decl.TwoWayBinding().and_then(|twb| twb.Expression().QualifiedName()),
1970 ),
1971 tr,
1972 diag,
1973 );
1974 let source_name = name;
1975 let name =
1976 declaration.register(&mut r, &source_name, &sig_decl.DeclaredIdentifier(), diag);
1977 let shadowed_name = (name != source_name).then_some(source_name);
1978
1979 if let Some(csn) = sig_decl.TwoWayBinding() {
1980 #[cfg(feature = "slint-sc")]
1981 diag.slint_sc_error("Callback aliases are", &csn);
1982 r.bindings
1983 .0
1984 .insert(name.clone(), BindingExpression::new_uncompiled(csn.into()).into());
1985 r.property_declarations.insert(
1986 name,
1987 PropertyDeclaration {
1988 property_type: Type::InferredCallback,
1989 node: Some(sig_decl.into()),
1990 visibility: PropertyVisibility::InOut,
1991 pure,
1992 shadowed_name,
1993 shadowable,
1994 deprecated,
1995 ..Default::default()
1996 },
1997 );
1998 continue;
1999 }
2000
2001 let args = sig_decl
2002 .CallbackDeclarationParameter()
2003 .map(|p| type_from_node(p.Type(), diag, tr))
2004 .collect();
2005 let return_type = sig_decl
2006 .ReturnType()
2007 .map(|ret_ty| type_from_node(ret_ty.Type(), diag, tr))
2008 .unwrap_or(Type::Void);
2009 let arg_names = sig_decl
2010 .CallbackDeclarationParameter()
2011 .map(|a| {
2012 a.DeclaredIdentifier()
2013 .and_then(|x| parser::identifier_text(&x))
2014 .unwrap_or_default()
2015 })
2016 .collect();
2017 r.property_declarations.insert(
2018 name,
2019 PropertyDeclaration {
2020 property_type: Type::Callback(Arc::new(Function {
2021 return_type,
2022 args,
2023 arg_names,
2024 })),
2025 node: Some(sig_decl.into()),
2026 visibility: PropertyVisibility::InOut,
2027 pure,
2028 shadowed_name,
2029 shadowable,
2030 deprecated,
2031 ..Default::default()
2032 },
2033 );
2034 }
2035
2036 for func in node.Function() {
2037 #[cfg(feature = "slint-sc")]
2038 diag.slint_sc_error("Function declarations are", &func);
2039 let name =
2040 unwrap_or_continue!(parser::identifier_text(&func.DeclaredIdentifier()); diag);
2041
2042 let member_decl = r.member_declaration(&name);
2043 if let MemberDeclaration::Conflict { existing_type, declared_in } = &member_decl {
2044 if matches!(existing_type, Type::Callback { .. } | Type::Function { .. }) {
2045 diag.push_error(
2046 cannot_override_message(None, &name, declared_in),
2047 &func.DeclaredIdentifier(),
2048 )
2049 } else {
2050 diag.push_error(
2051 format!("Cannot declare function '{name}' when a property with the same name exists"),
2052 &func.DeclaredIdentifier(),
2053 );
2054 }
2055 continue;
2056 }
2057 let source_name = name;
2058 let name = member_decl.register(&mut r, &source_name, &func.DeclaredIdentifier(), diag);
2059 let shadowed_name = (name != source_name).then_some(source_name);
2060
2061 let mut args = Vec::new();
2062 let mut arg_names = Vec::new();
2063 for a in func.ArgumentDeclaration() {
2064 args.push(type_from_node(a.Type(), diag, tr));
2065 let name =
2066 unwrap_or_continue!(parser::identifier_text(&a.DeclaredIdentifier()); diag);
2067 if arg_names.contains(&name) {
2068 diag.push_error(
2069 format!("Duplicated argument name '{name}'"),
2070 &a.DeclaredIdentifier(),
2071 );
2072 }
2073 arg_names.push(name);
2074 }
2075 let return_type = func
2076 .ReturnType()
2077 .map_or(Type::Void, |ret_ty| type_from_node(ret_ty.Type(), diag, tr));
2078
2079 let mut visibility = PropertyVisibility::Private;
2080 let mut pure = None;
2081 for token in func.children_with_tokens() {
2082 if token.kind() != SyntaxKind::Identifier {
2083 continue;
2084 }
2085 match token.as_token().unwrap().text() {
2086 "pure" => pure = Some(true),
2087 "public" => {
2088 visibility = PropertyVisibility::Public;
2089 pure = pure.or(Some(false));
2090 }
2091 "protected" => {
2092 visibility = PropertyVisibility::Protected;
2093 pure = pure.or(Some(false));
2094 }
2095 _ => (),
2096 }
2097 }
2098
2099 if is_interface && visibility != PropertyVisibility::Public {
2100 diag.push_error(
2101 "Function declarations in an interface must be public".into(),
2102 &func,
2103 );
2104 }
2105
2106 let declaration = PropertyDeclaration {
2107 property_type: Type::Function(Arc::new(Function { return_type, args, arg_names })),
2108 node: Some(func.clone().into()),
2109 visibility,
2110 pure,
2111 shadowed_name,
2112 shadowable: shadowable_attribute(func.ShadowableAttribute(), tr, diag),
2113 deprecated: member_deprecation(
2114 func.PropertyDeprecation(),
2115 DeprecationHint::MessageRequired,
2116 tr,
2117 diag,
2118 ),
2119 ..Default::default()
2120 };
2121
2122 match (base_type.clone(), func.CodeBlock()) {
2123 (ElementType::Interface, Some(code_block)) => {
2124 diag.push_error(
2125 "Function declarations in interfaces must not have a body".into(),
2126 &code_block,
2127 );
2128 continue;
2129 }
2130 (ElementType::Interface, None) => {
2131 r.property_declarations.insert(name, declaration);
2134 continue;
2135 }
2136 (_, None) => {
2137 diag.push_error("Functions must have a code block".into(), &func);
2138 }
2139 (_, Some(_)) => {}
2140 }
2141
2142 if r.bindings
2143 .0
2144 .insert(name.clone(), BindingExpression::new_uncompiled(func.clone().into()).into())
2145 .is_some()
2146 {
2147 assert!(diag.has_errors());
2148 }
2149
2150 r.property_declarations.insert(name, declaration);
2151 }
2152
2153 for con_node in node.CallbackConnection() {
2154 let unresolved_name = unwrap_or_continue!(parser::identifier_text(&con_node); diag);
2155 let lookup_result =
2156 r.lookup_property(&unresolved_name, PropertyLookupMode::ComponentLocal);
2157 #[cfg(feature = "slint-sc")]
2158 {
2159 if !r.is_user_declared_member(&unresolved_name) && !lookup_result.is_slint_sc {
2163 diag.slint_sc_error(
2164 &format!("The callback '{unresolved_name}' is"),
2165 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
2166 );
2167 }
2168 if is_component_root
2171 && r.property_declarations
2172 .get(lookup_result.internal_or_resolved_name().as_str())
2173 .is_some_and(|d| d.node.is_some())
2174 {
2175 diag.slint_sc_error(
2176 "A handler for a callback declared on the root element is",
2177 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
2178 );
2179 }
2180 if let Some(param) = con_node.DeclaredIdentifier().next() {
2181 diag.slint_sc_error("Callback handler parameters are", ¶m);
2182 }
2183 }
2184 let deprecation =
2187 lookup_result.deprecated.clone().filter(|_| !lookup_result.is_local_to_component);
2188 let resolved_name = lookup_result.internal_or_resolved_name();
2189 let property_type = lookup_result.property_type;
2190 if let Type::Callback(callback) = &property_type {
2191 let num_arg = con_node.DeclaredIdentifier().count();
2192 if num_arg > callback.args.len() {
2193 diag.push_error(
2194 format!(
2195 "'{}' only has {} arguments, but {} were provided",
2196 unresolved_name,
2197 callback.args.len(),
2198 num_arg
2199 ),
2200 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
2201 );
2202 }
2203 } else if property_type == Type::InferredCallback {
2204 } else {
2206 if r.base_type != ElementType::Error {
2207 diag.push_error(
2208 format!("'{}' is not a callback in {}", unresolved_name, r.base_type),
2209 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
2210 );
2211 }
2212 continue;
2213 }
2214 if let Some(message) = &deprecation {
2215 diag.push_property_deprecation_warning_with_message(
2216 &unresolved_name,
2217 message,
2218 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
2219 );
2220 }
2221 match r.bindings.0.entry(resolved_name) {
2222 Entry::Vacant(e) => {
2223 e.insert(BindingExpression::new_uncompiled(con_node.clone().into()).into());
2224 }
2225 Entry::Occupied(mut e) => {
2226 let is_global_alias = r.base_type == ElementType::Global
2231 && matches!(
2232 &e.get().borrow().expression,
2233 Expression::Uncompiled(node) if node.kind() == SyntaxKind::TwoWayBinding
2234 );
2235 if is_global_alias {
2236 let mut handler =
2241 BindingExpression::new_uncompiled(con_node.clone().into());
2242 if let Some(name) = con_node.child_token(SyntaxKind::Identifier) {
2243 handler.span = Some(name.to_source_location());
2244 }
2245 e.insert(handler.into());
2246 } else {
2247 diag.push_error(
2248 "Duplicated callback".into(),
2249 &con_node.child_token(SyntaxKind::Identifier).unwrap(),
2250 );
2251 }
2252 }
2253 }
2254 }
2255
2256 for anim in node.PropertyAnimation() {
2257 #[cfg(feature = "slint-sc")]
2258 diag.slint_sc_error("Animations are", &anim);
2259 if let Some(star) = anim.child_token(SyntaxKind::Star) {
2260 diag.push_error(
2261 "catch-all property is only allowed within transitions".into(),
2262 &star,
2263 )
2264 };
2265 for prop_name_token in anim.QualifiedName() {
2266 match QualifiedTypeName::from_node(prop_name_token.clone()).members.as_slice() {
2267 [unresolved_prop_name] => {
2268 if r.base_type == ElementType::Error {
2269 continue;
2270 };
2271 let lookup_result = r.lookup_property(
2272 unresolved_prop_name,
2273 PropertyLookupMode::ComponentLocal,
2274 );
2275 let valid_assign = lookup_result.is_valid_for_assignment();
2276 let binding_name = lookup_result.internal_or_resolved_name();
2277 if let Some(anim_element) = animation_element_from_node(
2278 &anim,
2279 &prop_name_token,
2280 lookup_result.property_type.clone(),
2281 diag,
2282 tr,
2283 ) {
2284 if !valid_assign {
2285 diag.push_error(
2286 format!(
2287 "Cannot animate '{}' property '{}'",
2288 lookup_result.property_visibility, unresolved_prop_name
2289 ),
2290 &prop_name_token,
2291 );
2292 }
2293
2294 if unresolved_prop_name != lookup_result.resolved_name.as_ref() {
2295 diag.push_property_deprecation_warning(
2296 unresolved_prop_name,
2297 &lookup_result.resolved_name,
2298 &prop_name_token,
2299 );
2300 } else if let Some(message) = lookup_result
2301 .deprecated
2302 .as_ref()
2303 .filter(|_| !lookup_result.is_local_to_component)
2304 {
2305 diag.push_property_deprecation_warning_with_message(
2306 unresolved_prop_name,
2307 message,
2308 &prop_name_token,
2309 );
2310 }
2311
2312 let expr_binding =
2313 r.bindings.0.entry(binding_name).or_insert_with(|| {
2314 let mut r = BindingExpression::from(Expression::Invalid);
2315 r.priority = 1;
2316 r.span = Some(prop_name_token.to_source_location());
2317 r.into()
2318 });
2319 if expr_binding
2320 .get_mut()
2321 .animation
2322 .replace(PropertyAnimation::Static(anim_element))
2323 .is_some()
2324 {
2325 diag.push_error("Duplicated animation".into(), &prop_name_token)
2326 }
2327 }
2328 }
2329 _ => diag.push_error(
2330 "Can only refer to property in the current element".into(),
2331 &prop_name_token,
2332 ),
2333 }
2334 }
2335 }
2336
2337 for ch in node.PropertyChangedCallback() {
2338 #[cfg(feature = "slint-sc")]
2339 diag.slint_sc_error("Change callbacks are", &ch);
2340 let Some(prop) = parser::identifier_text(&ch.DeclaredIdentifier()) else { continue };
2341 let lookup_result = r.lookup_property(&prop, PropertyLookupMode::ComponentLocal);
2342 if !lookup_result.is_valid() {
2343 if r.base_type != ElementType::Error {
2344 diag.push_error(
2345 format!("Property '{prop}' does not exist"),
2346 &ch.DeclaredIdentifier(),
2347 );
2348 }
2349 } else if !lookup_result.property_type.is_property_type() {
2350 let what = match lookup_result.property_type {
2351 Type::Function { .. } => "a function",
2352 Type::Callback { .. } => "a callback",
2353 _ => "not a property",
2354 };
2355 diag.push_error(
2356 format!(
2357 "Change callback can only be set on properties, and '{prop}' is {what}"
2358 ),
2359 &ch.DeclaredIdentifier(),
2360 );
2361 } else if lookup_result.property_visibility == PropertyVisibility::Private
2362 && !lookup_result.is_local_to_component
2363 {
2364 diag.push_error(
2365 format!("Change callback on a private property '{prop}'"),
2366 &ch.DeclaredIdentifier(),
2367 );
2368 }
2369 let handler = Expression::Uncompiled(ch.clone().into());
2370 match r.change_callbacks.entry(lookup_result.internal_or_resolved_name()) {
2371 Entry::Vacant(e) => {
2372 e.insert(vec![handler].into());
2373 }
2374 Entry::Occupied(mut e) => {
2375 diag.push_error(
2376 format!("Duplicated change callback on '{prop}'"),
2377 &ch.DeclaredIdentifier(),
2378 );
2379 e.get_mut().get_mut().push(handler);
2380 }
2381 }
2382 }
2383
2384 let r = r.make_rc();
2385
2386 for se in node.children() {
2387 if se.kind() != SyntaxKind::SlotForwarding {
2388 continue;
2389 }
2390 if !Self::assert_experimental_slots(diag, &se, "slot forwarding") {
2391 continue;
2392 }
2393
2394 let target_node = se.child_node(SyntaxKind::DeclaredIdentifier).unwrap();
2395 let target = parser::identifier_text(&target_node.clone()).unwrap_or_default();
2396
2397 if target == "children" {
2398 diag.push_error(
2399 format!(
2400 "The name '{target}' is reserved for the default slot. Use @children instead"
2401 ),
2402 &target_node,
2403 );
2404 continue;
2405 }
2406
2407 if r.borrow().forwarded_slots.iter().any(|f| f.target == target) {
2408 diag.push_error(format!("Duplicate assignment to slot '{target}'"), &target_node);
2409 continue;
2410 }
2411
2412 match &r.borrow().base_type {
2413 ElementType::Component(component)
2414 if !component
2415 .declared_slots
2416 .borrow()
2417 .iter()
2418 .any(|slot| slot.name == target) =>
2419 {
2420 diag.push_error(
2421 format!("Unknown slot '{target}' in '{}'", component.id),
2422 &target_node,
2423 );
2424 continue;
2425 }
2426 ElementType::Component(_) => {}
2427 _ => {
2428 diag.push_error("Slot forwarding can only be used on components".into(), &se);
2429 continue;
2430 }
2431 }
2432
2433 let Some(expression_node) = se.child_node(SyntaxKind::Expression) else {
2434 diag.push_error(
2435 "Slot forwarding requires a slot identifier on the right-hand side".into(),
2436 &se,
2437 );
2438 continue;
2439 };
2440 let Some(source) = Self::slot_forwarding_expr_identifier(&expression_node) else {
2441 diag.push_error(
2442 "Slot forwarding requires a slot identifier on the right-hand side".into(),
2443 &expression_node,
2444 );
2445 continue;
2446 };
2447
2448 if source == "children" {
2449 diag.push_error(
2450 format!(
2451 "The name '{source}' is reserved for the default slot. Use @children instead"
2452 ),
2453 &expression_node,
2454 );
2455 continue;
2456 }
2457
2458 r.borrow_mut().forwarded_slots.push(SlotForwarding {
2459 target,
2460 source,
2461 expression_node: expression_node.into(),
2462 });
2463 }
2464
2465 for forwarding in r.borrow().forwarded_slots.clone() {
2466 let source = forwarding.source.clone();
2467 if let Some(existing_cip) = component_child_insertion_points.get(source.as_str()) {
2468 if matches!(existing_cip.node, ChildInsertionPointNode::SlotPlaceholder(_)) {
2469 diag.push_error(
2470 format!(
2471 "The slot '{source}' cannot be forwarded and used as a placeholder in the same component"
2472 ),
2473 &forwarding.expression_node,
2474 );
2475 } else {
2476 diag.push_error(
2477 format!(
2478 "{} can only appear once in an element",
2479 slot_error_subject(&source)
2480 ),
2481 &forwarding.expression_node,
2482 );
2483 }
2484 continue;
2485 }
2486 component_child_insertion_points.insert(
2487 source.to_string(),
2488 ChildrenInsertionPoint {
2489 parent: r.clone(),
2490 insertion_index: 0,
2491 node: ChildInsertionPointNode::SlotForwarding(forwarding.expression_node),
2492 },
2493 );
2494 }
2495
2496 let mut assigned_slots = HashSet::new();
2497
2498 for se in node.children() {
2499 if se.kind() == SyntaxKind::SubElement {
2500 if let Some(slot_name) =
2501 Self::sub_element_slot_placeholder_name(&se, declared_slots)
2502 {
2503 Self::register_slot_placeholder(
2504 &se,
2505 slot_name,
2506 &r,
2507 component_child_insertion_points,
2508 diag,
2509 tr,
2510 );
2511 continue;
2512 }
2513 let parent_type = r.borrow().base_type.clone();
2514 r.borrow_mut().children.push(Element::from_sub_element_node(
2515 se.into(),
2516 parent_type,
2517 component_child_insertion_points,
2518 declared_slots,
2519 is_legacy_syntax,
2520 diag,
2521 tr,
2522 ));
2523 } else if se.kind() == SyntaxKind::RepeatedElement {
2524 let mut sub_child_insertion_points = BTreeMap::new();
2525 let rep = Element::from_repeated_node(
2526 se.into(),
2527 &r,
2528 &mut sub_child_insertion_points,
2529 declared_slots,
2530 is_legacy_syntax,
2531 diag,
2532 tr,
2533 );
2534 Self::reject_slot_placeholders(
2535 diag,
2536 declared_slots,
2537 sub_child_insertion_points,
2538 "a repeated element",
2539 );
2540 r.borrow_mut().children.push(rep);
2541 } else if se.kind() == SyntaxKind::ConditionalElement {
2542 let mut sub_child_insertion_points = BTreeMap::new();
2543 let rep = Element::from_conditional_node(
2544 se.into(),
2545 r.borrow().base_type.clone(),
2546 &mut sub_child_insertion_points,
2547 declared_slots,
2548 is_legacy_syntax,
2549 diag,
2550 tr,
2551 );
2552 Self::reject_slot_placeholders(
2553 diag,
2554 declared_slots,
2555 sub_child_insertion_points,
2556 "a conditional element",
2557 );
2558 r.borrow_mut().children.push(rep);
2559 } else if se.kind() == SyntaxKind::MatchElement {
2560 let mut sub_child_insertion_points = BTreeMap::new();
2561 let match_element = Element::from_match_node(
2562 se.into(),
2563 r.borrow().base_type.clone(),
2564 &mut sub_child_insertion_points,
2565 declared_slots,
2566 is_legacy_syntax,
2567 diag,
2568 tr,
2569 );
2570 Self::reject_slot_placeholders(
2571 diag,
2572 declared_slots,
2573 sub_child_insertion_points,
2574 "a match element",
2575 );
2576 let mut r = r.borrow_mut();
2577 r.children.extend(match_element.elements());
2578 r.match_elements.push(match_element);
2579 } else if se.kind() == SyntaxKind::ChildrenPlaceholder {
2580 #[cfg(feature = "slint-sc")]
2581 diag.slint_sc_error("The @children placeholder is", &se);
2582 if component_child_insertion_points.contains_key(DEFAULT_SLOT_NAME) {
2583 diag.push_error(
2584 format!(
2585 "{} can only appear once in an element",
2586 slot_error_subject(DEFAULT_SLOT_NAME)
2587 ),
2588 &se,
2589 );
2590 } else {
2591 component_child_insertion_points.insert(
2592 DEFAULT_SLOT_NAME.into(),
2593 ChildrenInsertionPoint {
2594 parent: r.clone(),
2595 insertion_index: r.borrow().children.len(),
2596 node: ChildInsertionPointNode::ChildrenPlaceHolder(se.into()),
2597 },
2598 );
2599 }
2600 } else if se.kind() == SyntaxKind::SlotDeclaration {
2601 Self::assert_experimental_slots(diag, &se, "named slots");
2602 let decl: syntax_nodes::SlotDeclaration = se.into();
2603 let name_node = decl.DeclaredIdentifier();
2604 let name = parser::identifier_text(&name_node).unwrap_or_default();
2605 declared_slots.push(DeclaredSlot {
2606 name,
2607 name_node,
2608 has_rejected_placeholder: false,
2609 });
2610 } else if se.kind() == SyntaxKind::SlotAssignment {
2611 if !Self::assert_experimental_slots(diag, &se, "named slots") {
2612 continue;
2613 }
2614 let name_node = se.child_node(SyntaxKind::DeclaredIdentifier).unwrap();
2615 let name = parser::identifier_text(&name_node).unwrap_or_default();
2616 if name == "children" {
2617 diag.push_error(
2618 format!(
2619 "The name '{name}' is reserved for the default slot. Use @children instead"
2620 ),
2621 &name_node,
2622 );
2623 }
2624 if !assigned_slots.insert(name.clone()) {
2625 diag.push_error(format!("Duplicate assignment to slot '{name}'"), &name_node);
2626 }
2627 if r.borrow().forwarded_slots.iter().any(|f| f.target == name) {
2628 diag.push_error(format!("Duplicate assignment to slot '{name}'"), &name_node);
2629 }
2630 let sub_element_node = se.child_node(SyntaxKind::SubElement).unwrap();
2631 let parent_type = r.borrow().base_type.clone();
2632 match &parent_type {
2633 ElementType::Component(component)
2634 if !component
2635 .declared_slots
2636 .borrow()
2637 .iter()
2638 .any(|slot| slot.name == name) =>
2639 {
2640 diag.push_error(
2641 format!("Unknown slot '{name}' in '{}'", component.id),
2642 &name_node,
2643 );
2644 }
2645 ElementType::Component(_) => {}
2646 _ => {
2647 diag.push_error(
2648 "Slot assignments can only be used on components".to_string(),
2649 &se,
2650 );
2651 }
2652 }
2653 let element = Element::from_sub_element_node(
2654 sub_element_node.into(),
2655 parent_type,
2656 component_child_insertion_points,
2657 declared_slots,
2658 is_legacy_syntax,
2659 diag,
2660 tr,
2661 );
2662 element.borrow_mut().slot_target = Some(name);
2663 r.borrow_mut().children.push(element);
2664 }
2665 }
2666
2667 for state in node.States().flat_map(|s| s.State()) {
2668 let condition = state.Expression();
2669 let when = state.child_token(SyntaxKind::Identifier).filter(|t| t.text() == "when");
2672 #[cfg(feature = "slint-sc")]
2675 if condition.is_none() {
2676 diag.slint_sc_error(
2677 "A state without a 'when' condition is",
2678 &state.DeclaredIdentifier(),
2679 );
2680 }
2681 let s = State {
2682 id: parser::identifier_text(&state.DeclaredIdentifier()).unwrap_or_default(),
2683 condition: condition.map(|e| Expression::Uncompiled(e.into())),
2684 property_changes: state
2685 .StatePropertyChange()
2686 .filter_map(|s| {
2687 lookup_property_from_qualified_name_for_state(s.QualifiedName(), &r, diag)
2688 .map(|(ne, _)| {
2689 (ne, Expression::Uncompiled(s.BindingExpression().into()), s)
2690 })
2691 })
2692 .collect(),
2693 selection: when.map(|when| ConditionLocation::StateSelection {
2694 name: state.DeclaredIdentifier().to_source_location(),
2695 when: when.to_source_location(),
2696 }),
2697 };
2698 for trs in state.Transition() {
2699 #[cfg(feature = "slint-sc")]
2700 diag.slint_sc_error("Transitions are", &trs);
2701 let mut t = Transition::from_node(trs, &r, tr, diag);
2702 t.state_id.clone_from(&s.id);
2703 r.borrow_mut().transitions.push(t);
2704 }
2705 r.borrow_mut().states.push(s);
2706 }
2707
2708 for ts in node.Transitions() {
2709 #[cfg(feature = "slint-sc")]
2710 diag.slint_sc_error("Transitions are", &ts);
2711 if !is_legacy_syntax {
2712 diag.push_error("'transitions' block are no longer supported. Use 'in {...}' and 'out {...}' directly in the state definition".into(), &ts);
2713 }
2714 for trs in ts.Transition() {
2715 let trans = Transition::from_node(trs, &r, tr, diag);
2716 r.borrow_mut().transitions.push(trans);
2717 }
2718 }
2719
2720 if r.borrow().base_type.to_smolstr() == "ListView" {
2721 let mut seen_for = false;
2722 for se in node.children() {
2723 if se.kind() == SyntaxKind::RepeatedElement && !seen_for {
2724 seen_for = true;
2725 } else if matches!(
2726 se.kind(),
2727 SyntaxKind::SubElement
2728 | SyntaxKind::ConditionalElement
2729 | SyntaxKind::RepeatedElement
2730 | SyntaxKind::ChildrenPlaceholder
2731 ) {
2732 diag.push_error("A ListView can just have a single 'for' as children. Anything else is not supported".into(), &se)
2733 }
2734 }
2735 }
2736
2737 interfaces::validate_self_implement_statements(&r.borrow(), &implemented_interfaces, diag);
2738 interfaces::apply_child_implement_statements(&r, child_implements, diag);
2739
2740 r
2741 }
2742
2743 fn from_sub_element_node(
2744 node: syntax_nodes::SubElement,
2745 parent_type: ElementType,
2746 component_child_insertion_points: &mut BTreeMap<String, ChildrenInsertionPoint>,
2747 declared_slots: &mut Vec<DeclaredSlot>,
2748 is_in_legacy_component: bool,
2749 diag: &mut BuildDiagnostics,
2750 tr: &TypeRegister,
2751 ) -> ElementRc {
2752 let mut id = parser::identifier_text(&node).unwrap_or_default();
2753 if matches!(id.as_ref(), "parent" | "self" | "root") {
2754 diag.push_error(
2755 format!("'{id}' is a reserved id"),
2756 &node.child_token(SyntaxKind::Identifier).unwrap(),
2757 );
2758 id = SmolStr::default();
2759 }
2760 Element::from_node(
2761 node.Element(),
2762 id,
2763 parent_type,
2764 component_child_insertion_points,
2765 declared_slots,
2766 is_in_legacy_component,
2767 diag,
2768 tr,
2769 )
2770 }
2771
2772 fn assert_experimental_slots(
2773 diagnostics: &mut BuildDiagnostics,
2774 node: &SyntaxNode,
2775 what: &str,
2776 ) -> bool {
2777 if diagnostics.enable_experimental {
2778 return true;
2779 }
2780 diagnostics.push_error(format!("'{what}' is an experimental feature"), node);
2781 false
2782 }
2783
2784 fn sub_element_slot_placeholder_name(
2785 node: &SyntaxNode,
2786 declared_slots: &[DeclaredSlot],
2787 ) -> Option<SmolStr> {
2788 if node.child_token(SyntaxKind::ColonEqual).is_some() {
2789 return None;
2790 }
2791 let element = node.child_node(SyntaxKind::Element)?;
2792 if element.children().any(|c| c.kind() != SyntaxKind::QualifiedName) {
2793 return None;
2794 }
2795 let qualified_name = element.child_node(SyntaxKind::QualifiedName)?;
2796 if qualified_name.child_token(SyntaxKind::Dot).is_some() {
2797 return None;
2798 }
2799 let name = parser::identifier_text(&qualified_name)?;
2800 declared_slots.iter().any(|slot| slot.name == name).then_some(name)
2801 }
2802
2803 fn mark_placeholder_rejected(declared_slots: &mut [DeclaredSlot], name: &str) {
2804 if let Some(slot) = declared_slots.iter_mut().find(|slot| slot.name.as_str() == name) {
2805 slot.has_rejected_placeholder = true;
2806 }
2807 }
2808
2809 fn reject_slot_placeholders(
2810 diagnostics: &mut BuildDiagnostics,
2811 declared_slots: &mut [DeclaredSlot],
2812 insertion_points: BTreeMap<String, ChildrenInsertionPoint>,
2813 context: &str,
2814 ) {
2815 for (name, ChildrenInsertionPoint { node, .. }) in insertion_points {
2816 Self::mark_placeholder_rejected(declared_slots, &name);
2817 diagnostics.push_error(
2818 format!("{} cannot appear in {context}", slot_error_subject(&name)),
2819 &node,
2820 );
2821 }
2822 }
2823
2824 fn register_slot_placeholder(
2825 node: &SyntaxNode,
2826 slot_name: SmolStr,
2827 parent: &ElementRc,
2828 component_child_insertion_points: &mut BTreeMap<String, ChildrenInsertionPoint>,
2829 diagnostics: &mut BuildDiagnostics,
2830 type_register: &TypeRegister,
2831 ) {
2832 Self::assert_experimental_slots(diagnostics, node, "named slots");
2833 if let Some(existing) = component_child_insertion_points.get(slot_name.as_str()) {
2834 if matches!(existing.node, ChildInsertionPointNode::SlotForwarding(_)) {
2835 diagnostics.push_error(
2836 format!(
2837 "The slot '{slot_name}' cannot be forwarded and used as a placeholder in the same component"
2838 ),
2839 node,
2840 );
2841 } else {
2842 diagnostics.push_error(
2843 format!(
2844 "{} can only appear once in an element",
2845 slot_error_subject(&slot_name)
2846 ),
2847 node,
2848 );
2849 }
2850 return;
2851 }
2852 if type_register.lookup_element(slot_name.as_str()).is_ok() {
2853 diagnostics.push_warning(
2854 format!(
2855 "{} shadows an element type of the same name. This element is a slot placeholder, not an instance of '{slot_name}'",
2856 slot_error_subject(&slot_name)
2857 ),
2858 node,
2859 );
2860 }
2861 let insertion_index = parent.borrow().children.len();
2862 component_child_insertion_points.insert(
2863 slot_name.to_string(),
2864 ChildrenInsertionPoint {
2865 parent: parent.clone(),
2866 insertion_index,
2867 node: ChildInsertionPointNode::SlotPlaceholder(node.clone().into()),
2868 },
2869 );
2870 }
2871
2872 fn from_repeated_node(
2873 node: syntax_nodes::RepeatedElement,
2874 parent: &ElementRc,
2875 component_child_insertion_points: &mut BTreeMap<String, ChildrenInsertionPoint>,
2876 declared_slots: &mut Vec<DeclaredSlot>,
2877 is_in_legacy_component: bool,
2878 diag: &mut BuildDiagnostics,
2879 tr: &TypeRegister,
2880 ) -> ElementRc {
2881 #[cfg(feature = "slint-sc")]
2882 diag.slint_sc_error("Repeated elements (for-in) are", &node);
2883 let e = Element::from_sub_element_node(
2884 node.SubElement(),
2885 parent.borrow().base_type.clone(),
2886 component_child_insertion_points,
2887 declared_slots,
2888 is_in_legacy_component,
2889 diag,
2890 tr,
2891 );
2892 let parent_is_listview = {
2893 let parent = parent.borrow();
2894 parent.base_type.to_string() == "ListView"
2895 && [
2897 "content-y",
2898 "content-height",
2899 "content-width",
2900 "visible-height",
2901 "visible-width",
2902 ]
2903 .iter()
2904 .all(|p| parent.lookup_property(p, PropertyLookupMode::InternalName).property_type == Type::LogicalLength)
2905 };
2906 let is_listview = if parent_is_listview
2907 && let Some(geometry_props) = e.borrow().geometry_props.as_ref()
2908 {
2909 let parent_elem = parent.borrow();
2910 let (content_width_is_explicitly_set, content_height_is_explicitly_set) = {
2913 let has_binding = |name| parent_elem.binding(name).is_some_and(|b| b.has_binding());
2914 (
2915 has_binding("content-width") || has_binding("viewport-width"),
2916 has_binding("content-height") || has_binding("viewport-height"),
2917 )
2918 };
2919 drop(parent_elem); let lvi = ListViewInfo {
2922 content_y: NamedReference::new(parent, SmolStr::new_static("content-y")),
2923 content_height: (!content_height_is_explicitly_set)
2924 .then(|| NamedReference::new(parent, SmolStr::new_static("content-height"))),
2925 content_width: (!content_width_is_explicitly_set)
2926 .then(|| NamedReference::new(parent, SmolStr::new_static("content-width"))),
2927 listview_height: NamedReference::new(parent, SmolStr::new_static("visible-height")),
2928 listview_width: NamedReference::new(parent, SmolStr::new_static("visible-width")),
2929 };
2930 if let Some(content_height) = &lvi.content_height {
2932 content_height.mark_as_set();
2933 }
2934 if let Some(content_width) = &lvi.content_width {
2935 content_width.mark_as_set();
2936 }
2937 geometry_props.y.mark_as_set();
2938 Some(lvi)
2939 } else {
2940 None
2941 };
2942 let rei = RepeatedElementInfo {
2943 model: Expression::Uncompiled(node.Expression().into()),
2944 model_data_id: node
2945 .DeclaredIdentifier()
2946 .and_then(|n| parser::identifier_text(&n))
2947 .unwrap_or_default(),
2948 index_id: node
2949 .RepeatedIndex()
2950 .and_then(|r| parser::identifier_text(&r))
2951 .unwrap_or_default(),
2952 is_conditional_element: false,
2953 is_listview,
2954 };
2955 e.borrow_mut().repeated = Some(rei);
2956 e
2957 }
2958
2959 fn from_conditional_node(
2960 node: syntax_nodes::ConditionalElement,
2961 parent_type: ElementType,
2962 component_child_insertion_points: &mut BTreeMap<String, ChildrenInsertionPoint>,
2963 declared_slots: &mut Vec<DeclaredSlot>,
2964 is_in_legacy_component: bool,
2965 diag: &mut BuildDiagnostics,
2966 tr: &TypeRegister,
2967 ) -> ElementRc {
2968 #[cfg(feature = "slint-sc")]
2969 diag.slint_sc_error("Conditional elements (if) are", &node);
2970 let rei = RepeatedElementInfo {
2971 model: Expression::Uncompiled(node.Expression().into()),
2972 model_data_id: SmolStr::default(),
2973 index_id: SmolStr::default(),
2974 is_conditional_element: true,
2975 is_listview: None,
2976 };
2977 let e = Element::from_sub_element_node(
2978 node.SubElement(),
2979 parent_type,
2980 component_child_insertion_points,
2981 declared_slots,
2982 is_in_legacy_component,
2983 diag,
2984 tr,
2985 );
2986 e.borrow_mut().repeated = Some(rei);
2987 e
2988 }
2989
2990 fn from_match_node(
2991 node: syntax_nodes::MatchElement,
2992 parent_type: ElementType,
2993 component_child_insertion_points: &mut BTreeMap<String, ChildrenInsertionPoint>,
2994 declared_slots: &mut Vec<DeclaredSlot>,
2995 is_in_legacy_component: bool,
2996 diag: &mut BuildDiagnostics,
2997 tr: &TypeRegister,
2998 ) -> MatchElementInfo {
2999 if !diag.enable_experimental {
3000 diag.push_error("match elements are an experimental feature".into(), &node);
3001 }
3002 if node.MatchCase().next().is_none() && node.WildcardMatchCase().is_none() {
3003 diag.push_error("Expected at least one case".into(), &node);
3004 }
3005 if let Some(wildcard) = node.WildcardMatchCase()
3006 && node.MatchCase().next().is_none()
3007 {
3008 diag.push_warning(
3009 "Unnecessary match statement always matches the '*' case".into(),
3010 &wildcard,
3011 );
3012 }
3013 let mut element_of = |sub_element| {
3014 Element::from_sub_element_node(
3015 sub_element,
3016 parent_type.clone(),
3017 component_child_insertion_points,
3018 declared_slots,
3019 is_in_legacy_component,
3020 diag,
3021 tr,
3022 )
3023 };
3024 let cases = node
3026 .MatchCase()
3027 .map(|case| {
3028 let node = case.Expression();
3029 MatchCaseInfo {
3030 value: Expression::Uncompiled(node.clone().into()),
3031 node,
3032 element: case.SubElement().map(&mut element_of),
3033 }
3034 })
3035 .collect();
3036 let wildcard = match node.WildcardMatchCase() {
3037 None => WildcardMatchCaseInfo::None,
3038 Some(w) => match w.SubElement().map(&mut element_of) {
3039 None => WildcardMatchCaseInfo::Empty,
3040 Some(element) => WildcardMatchCaseInfo::Element(element),
3041 },
3042 };
3043 MatchElementInfo {
3044 subject: Expression::Uncompiled(node.Expression().into()),
3045 node,
3046 cases,
3047 wildcard,
3048 }
3049 }
3050
3051 #[cfg(feature = "slint-sc")]
3055 pub fn is_user_declared_member(&self, name: &str) -> bool {
3056 match self.declaration(name) {
3057 Some((_, declaration)) => declaration.node.is_some(),
3058 None => match &self.base_type {
3059 ElementType::Component(c) => c.root_element.borrow().is_user_declared_member(name),
3060 _ => false,
3061 },
3062 }
3063 }
3064
3065 pub fn lookup_property<'a>(
3068 &self,
3069 name: &'a str,
3070 mode: PropertyLookupMode,
3071 ) -> PropertyLookupResult<'a> {
3072 let declaration = match mode {
3073 PropertyLookupMode::InternalName => self.property_declarations.get_key_value(name),
3074 PropertyLookupMode::ComponentLocal | PropertyLookupMode::FromOutside => {
3075 self.declaration(name)
3076 }
3077 };
3078 if let Some((internal_name, decl)) = declaration {
3079 if mode == PropertyLookupMode::FromOutside && decl.is_private_shadow() {
3080 return from_base(
3081 self.base_type.lookup_property(name, PropertyLookupMode::FromOutside),
3082 );
3083 }
3084 let mut r = self.lookup_result_for_declaration(name.into(), decl);
3085 if internal_name != name {
3086 r.internal_name = Some(internal_name.clone());
3087 }
3088 return r;
3089 }
3090 let base_mode = match mode {
3092 PropertyLookupMode::InternalName => PropertyLookupMode::InternalName,
3093 _ => PropertyLookupMode::FromOutside,
3094 };
3095 from_base(self.base_type.lookup_property(name, base_mode))
3096 }
3097
3098 pub fn declaration(&self, name: &str) -> Option<(&SmolStr, &PropertyDeclaration)> {
3102 if let Some(internal_name) = self.shadowing_members.get(name) {
3103 return self.property_declarations.get_key_value(internal_name);
3104 }
3105 self.property_declarations.get_key_value(name).filter(|(_, d)| d.shadowed_name.is_none())
3106 }
3107
3108 pub fn visible_shadowing_members(&self) -> impl Iterator<Item = &SmolStr> {
3112 self.shadowing_members.iter().filter_map(|(source, internal)| {
3113 self.property_declarations
3114 .get(internal)
3115 .filter(|d| !d.is_private_shadow())
3116 .map(|_| source)
3117 })
3118 }
3119
3120 fn member_declaration(&self, name: &SmolStr) -> MemberDeclaration {
3123 if self.property_declarations.get(name.as_str()).is_some_and(|d| d.shadowed_name.is_some())
3126 {
3127 return MemberDeclaration::Shadow {
3128 internal_name: self.unique_member_name(name),
3129 warning: None,
3130 };
3131 }
3132 let existing = self.lookup_property(name, PropertyLookupMode::ComponentLocal);
3133 if !existing.is_valid() {
3134 return MemberDeclaration::New;
3135 }
3136 if existing.is_local_to_component {
3137 return MemberDeclaration::Conflict {
3138 existing_type: existing.property_type,
3139 declared_in: None,
3140 };
3141 }
3142 let declared_in = self.declaring_base_component(name);
3143 let private =
3146 declared_in.is_some() && existing.property_visibility == PropertyVisibility::Private;
3147 if !private && !existing.is_shadowable {
3148 return MemberDeclaration::Conflict {
3149 existing_type: existing.property_type,
3150 declared_in,
3151 };
3152 }
3153 let origin = if declared_in.is_some() { "inherited" } else { "builtin" };
3154 MemberDeclaration::Shadow {
3155 internal_name: self.unique_member_name(name),
3156 warning: (!private).then(|| {
3157 let kind = match existing.property_type {
3158 Type::Callback { .. } => "callback",
3159 Type::Function { .. } => "function",
3160 _ => "property",
3161 };
3162 format!("'{name}' shadows the {origin} {kind} of the same name")
3163 }),
3164 }
3165 }
3166
3167 pub fn unique_member_name(&self, base: &str) -> SmolStr {
3169 (1..)
3170 .map(|counter| format_smolstr!("{base}-{counter}"))
3171 .find(|n| !self.lookup_property(n, PropertyLookupMode::InternalName).is_valid())
3172 .unwrap()
3173 }
3174
3175 fn declaring_base_component(&self, name: &str) -> Option<Rc<Component>> {
3178 let mut base = self.base_type.clone();
3179 loop {
3180 let ElementType::Component(c) = base else { return None };
3181 let declares = {
3182 let root = c.root_element.borrow();
3183 root.shadowing_members.contains_key(name)
3184 || root.property_declarations.contains_key(name)
3185 };
3186 if declares {
3187 return Some(c);
3188 }
3189 base = c.root_element.borrow().base_type.clone();
3190 }
3191 }
3192
3193 fn lookup_result_for_declaration<'a>(
3194 &self,
3195 resolved_name: std::borrow::Cow<'a, str>,
3196 p: &PropertyDeclaration,
3197 ) -> PropertyLookupResult<'a> {
3198 PropertyLookupResult {
3199 resolved_name,
3200 property_type: p.property_type.clone(),
3201 property_visibility: p.visibility,
3202 declared_pure: p.pure,
3203 is_local_to_component: true,
3204 is_in_direct_base: false,
3205 is_shadowable: p.shadowable,
3206 builtin_function: None,
3207 is_slint_sc: true,
3208 deprecated: p.deprecated.clone(),
3209 internal_name: None,
3210 }
3211 }
3212
3213 fn parse_bindings(
3214 &mut self,
3215 bindings: impl Iterator<Item = (crate::parser::SyntaxToken, SyntaxNode)>,
3216 is_in_legacy_component: bool,
3217 diag: &mut BuildDiagnostics,
3218 ) {
3219 for (name_token, b) in bindings {
3220 let unresolved_name = crate::parser::normalize_identifier(name_token.text());
3221 let lookup_result =
3222 self.lookup_property(&unresolved_name, PropertyLookupMode::ComponentLocal);
3223 #[cfg(feature = "slint-sc")]
3224 if b.kind() == SyntaxKind::TwoWayBinding {
3225 diag.slint_sc_error("Two-way bindings are", &b);
3226 } else {
3227 lookup_result.check_slint_sc(&unresolved_name, &name_token, diag);
3228 }
3229 if !lookup_result.property_type.is_property_type() {
3230 match lookup_result.property_type {
3231 Type::Invalid => {
3232 if self.base_type != ElementType::Error {
3233 let msg = if let Some(suggestion) = css_property_suggestion(&unresolved_name, &self.base_type) {
3234 suggestion
3235 } else if self.base_type.to_smolstr() == "Empty" {
3236 format!( "Unknown property {unresolved_name}")
3237 } else {
3238 format!( "Unknown property {unresolved_name} in {}", self.base_type)
3239 };
3240 diag.push_error(msg, &name_token);
3241 }
3242 }
3243 Type::Callback { .. } => {
3244 diag.push_error(format!("'{unresolved_name}' is a callback. Use `=>` to connect"),
3245 &name_token)
3246 }
3247 _ => diag.push_error(format!(
3248 "Cannot assign to {} in {} because it does not have a valid property type",
3249 unresolved_name, self.base_type,
3250 ),
3251 &name_token),
3252 }
3253 } else if !lookup_result.is_local_to_component
3254 && (lookup_result.property_visibility == PropertyVisibility::Private
3255 || lookup_result.property_visibility == PropertyVisibility::Output)
3256 {
3257 if is_in_legacy_component
3258 && lookup_result.property_visibility == PropertyVisibility::Output
3259 {
3260 diag.push_warning(
3261 format!(
3262 "Assigning to '{}' property '{unresolved_name}' is deprecated",
3263 PropertyVisibility::Output
3264 ),
3265 &name_token,
3266 );
3267 } else {
3268 diag.push_error(
3269 format!(
3270 "Cannot assign to '{}' property '{}'",
3271 lookup_result.property_visibility, unresolved_name
3272 ),
3273 &name_token,
3274 );
3275 }
3276 }
3277
3278 if *lookup_result.resolved_name != *unresolved_name {
3279 diag.push_property_deprecation_warning(
3280 &unresolved_name,
3281 &lookup_result.resolved_name,
3282 &name_token,
3283 );
3284 } else if let Some(message) =
3285 lookup_result.deprecated.as_ref().filter(|_| !lookup_result.is_local_to_component)
3286 {
3287 diag.push_property_deprecation_warning_with_message(
3288 &unresolved_name,
3289 message,
3290 &name_token,
3291 );
3292 }
3293
3294 match self.bindings.0.entry(lookup_result.internal_or_resolved_name()) {
3295 Entry::Occupied(_) => {
3296 diag.push_error("Duplicated property binding".into(), &name_token);
3297 }
3298 Entry::Vacant(entry) => {
3299 entry.insert(BindingExpression::new_uncompiled(b).into());
3300 }
3301 };
3302 }
3303 }
3304
3305 pub fn property_declaration_node(&self, name: &str) -> Option<SyntaxNode> {
3307 self.property_declarations
3308 .get(name)
3309 .and_then(|declaration| declaration.node.clone())
3310 .or_else(|| self.base_type.property_declaration_node(name))
3311 }
3312
3313 fn slot_forwarding_expr_identifier(expression: &SyntaxNode) -> Option<SmolStr> {
3314 if expression.kind() != SyntaxKind::Expression {
3315 return None;
3316 }
3317
3318 let mut expr_children = expression.children();
3319 let qualified_name = expr_children.find(|n| n.kind() == SyntaxKind::QualifiedName)?;
3320 if expr_children.next().is_some() {
3321 return None;
3322 }
3323
3324 let mut identifiers = qualified_name
3325 .children_with_tokens()
3326 .filter(|n| n.kind() == SyntaxKind::Identifier)
3327 .filter_map(|n| n.into_token());
3328 let identifier = identifiers.next()?;
3329 if identifiers.next().is_some() {
3330 return None;
3331 }
3332
3333 Some(crate::parser::normalize_identifier(identifier.text()))
3334 }
3335
3336 pub fn callback_alias_declaration_node(
3342 &self,
3343 name: &str,
3344 ) -> Option<syntax_nodes::TwoWayBinding> {
3345 self.property_declarations
3346 .get(name)
3347 .and_then(|d| d.node.clone())
3348 .and_then(syntax_nodes::CallbackDeclaration::new)
3349 .and_then(|cb| cb.TwoWayBinding())
3350 }
3351
3352 pub fn two_way_binding_node(&self, name: &str) -> Option<syntax_nodes::TwoWayBinding> {
3359 if let Some(binding) = self.bindings.0.get(name)
3360 && let Ok(b) = binding.try_borrow()
3361 && let Expression::Uncompiled(node) = b.value_expression()
3362 && let Some(twb) = syntax_nodes::TwoWayBinding::new(node.clone())
3363 {
3364 return Some(twb);
3365 }
3366 self.callback_alias_declaration_node(name)
3367 }
3368
3369 pub fn native_class(&self) -> Option<Arc<NativeClass>> {
3370 let mut base_type = self.base_type.clone();
3371 loop {
3372 match &base_type {
3373 ElementType::Component(component) => {
3374 base_type = component.root_element.clone().borrow().base_type.clone();
3375 }
3376 ElementType::Builtin(builtin) => break Some(builtin.native_class.clone()),
3377 ElementType::Native(native) => break Some(native.clone()),
3378 _ => break None,
3379 }
3380 }
3381 }
3382
3383 pub fn builtin_type(&self) -> Option<Rc<BuiltinElement>> {
3384 let mut base_type = self.base_type.clone();
3385 loop {
3386 match &base_type {
3387 ElementType::Component(component) => {
3388 base_type = component.root_element.clone().borrow().base_type.clone();
3389 }
3390 ElementType::Builtin(builtin) => break Some(builtin.clone()),
3391 _ => break None,
3392 }
3393 }
3394 }
3395
3396 pub(crate) fn effective_layout_info_prop(
3401 &self,
3402 orientation: Orientation,
3403 ) -> Option<&NamedReference> {
3404 let prop = self.layout_info_prop.as_ref()?;
3405 match orientation {
3406 Orientation::Horizontal => Some(
3407 self.layout_info_h_at_own_height
3408 .as_ref()
3409 .filter(|_| self.height_is_literal)
3410 .unwrap_or(&prop.0),
3411 ),
3412 Orientation::Vertical => Some(&prop.1),
3413 }
3414 }
3415
3416 pub fn is_builtin_height_for_width(&self) -> bool {
3424 let Some(builtin) = self.builtin_type() else { return false };
3425 match builtin.name.as_str() {
3426 "Text" | "TextInput" => self.is_binding_set("wrap", false),
3429 "Image" | "ClippedImage" => !self.is_binding_set("height", true),
3432 "StyledText" => true,
3434 _ => false,
3435 }
3436 }
3437
3438 pub fn inherited_layout_info_v_with_constraint(&self) -> Option<NamedReference> {
3442 if let Some(nr) = &self.layout_info_v_with_constraint {
3443 return Some(nr.clone());
3444 }
3445 let mut base = self.base_type.clone();
3446 while let ElementType::Component(base_comp) = base {
3447 let root = base_comp.root_element.borrow();
3448 if let Some(nr) = &root.layout_info_v_with_constraint {
3449 return Some(nr.clone());
3450 }
3451 base = root.base_type.clone();
3452 }
3453 None
3454 }
3455
3456 pub fn has_inherited_layout_info_v_with_constraint(&self) -> bool {
3459 if self.layout_info_v_with_constraint.is_some() {
3460 return true;
3461 }
3462 let mut base = self.base_type.clone();
3463 while let ElementType::Component(base_comp) = base {
3464 let root = base_comp.root_element.borrow();
3465 if root.layout_info_v_with_constraint.is_some() {
3466 return true;
3467 }
3468 base = root.base_type.clone();
3469 }
3470 false
3471 }
3472
3473 pub fn layout_info_includes_own_constraints(&self, orientation: Orientation) -> bool {
3484 self.effective_layout_info_prop(orientation).is_some()
3485 || (orientation == Orientation::Vertical
3486 && self.has_inherited_layout_info_v_with_constraint())
3487 }
3488
3489 pub fn original_name(&self) -> SmolStr {
3491 self.debug
3492 .first()
3493 .and_then(|n| n.node.child_token(parser::SyntaxKind::Identifier))
3494 .map(|n| n.to_smolstr())
3495 .unwrap_or_else(|| self.id.clone())
3496 }
3497
3498 pub fn has_dynamic_z_order(&self) -> bool {
3500 self.children.iter().any(|c| c.borrow().z_order.is_some())
3501 }
3502
3503 pub fn is_binding_set(self: &Element, property_name: &str, need_explicit: bool) -> bool {
3511 self.any_in_inheritance_chain(|element| {
3512 element.bindings.0.get(property_name).is_some_and(|binding| {
3513 let binding = binding.borrow();
3514 binding.has_binding() && (!need_explicit || binding.priority > 0)
3515 })
3516 })
3517 }
3518
3519 pub(crate) fn base_layout_info_prop(
3524 &self,
3525 orientation: Orientation,
3526 height_settled: bool,
3527 ) -> Option<NamedReference> {
3528 let ElementType::Component(base) = &self.base_type else { return None };
3529 let root = base.root_element.borrow();
3530 root.layout_info_h_at_own_height
3531 .clone()
3532 .filter(|_| orientation == Orientation::Horizontal && height_settled)
3533 .or_else(|| root.effective_layout_info_prop(orientation).cloned())
3534 }
3535
3536 pub(crate) fn compute_height_is_literal(elem: &ElementRc) -> bool {
3545 let overridable_root = elem.borrow().enclosing_component.upgrade().is_some_and(|c| {
3549 Rc::ptr_eq(&c.root_element, elem)
3550 && c.used.get()
3551 && c.parent_element.borrow().upgrade().is_none()
3552 });
3553 if overridable_root {
3554 return false;
3555 }
3556 crate::layout::find_binding(elem, "height", |b, _, _| {
3557 matches!(b.value_expression(), Expression::NumberLiteral(_, unit) if *unit != Unit::Percent)
3558 })
3559 .unwrap_or(false)
3560 }
3561
3562 pub fn is_property_set(self: &Element, property_name: &str) -> bool {
3566 self.any_in_inheritance_chain(|element| {
3567 element
3568 .bindings
3569 .0
3570 .get(property_name)
3571 .is_some_and(|binding| !binding.borrow().expression.is_synthetic_debug_hook())
3572 || element
3573 .property_analysis
3574 .borrow()
3575 .get(property_name)
3576 .is_some_and(|analysis| analysis.is_set || analysis.is_linked)
3577 })
3578 }
3579
3580 pub(crate) fn is_property_target_of_two_way_binding(&self, property_name: &str) -> bool {
3581 self.any_in_inheritance_chain(|element| {
3582 element
3583 .property_analysis
3584 .borrow()
3585 .get(property_name)
3586 .is_some_and(|analysis| analysis.is_linked)
3587 })
3588 }
3589
3590 pub fn any_in_inheritance_chain(&self, predicate: impl Fn(&Element) -> bool + Copy) -> bool {
3592 predicate(self)
3593 || matches!(
3594 &self.base_type,
3595 ElementType::Component(base)
3596 if base.root_element.borrow().any_in_inheritance_chain(predicate)
3597 )
3598 }
3599
3600 pub fn binding(&self, property_name: &str) -> Option<Ref<'_, BindingExpression>> {
3607 self.bindings
3608 .0
3609 .get(property_name)
3610 .filter(|binding| !binding.borrow().expression.is_synthetic_debug_hook())
3611 .map(|binding| binding.borrow())
3612 }
3613
3614 pub fn binding_mut(&self, property_name: &str) -> Option<RefMut<'_, BindingExpression>> {
3616 self.bindings
3617 .0
3618 .get(property_name)
3619 .filter(|binding| !binding.borrow().expression.is_synthetic_debug_hook())
3620 .map(|binding| binding.borrow_mut())
3621 }
3622
3623 pub fn real_bindings(&self) -> impl Iterator<Item = (&SmolStr, &RefCell<BindingExpression>)> {
3628 self.bindings
3629 .0
3630 .iter()
3631 .filter(|(_, binding)| !binding.borrow().expression.is_synthetic_debug_hook())
3632 }
3633
3634 pub fn bindings_including_synthetic(
3639 &self,
3640 ) -> impl Iterator<Item = (&SmolStr, &RefCell<BindingExpression>)> {
3641 self.bindings.0.iter()
3642 }
3643
3644 pub fn binding_cell_including_synthetic(
3650 &self,
3651 property_name: &str,
3652 ) -> Option<&RefCell<BindingExpression>> {
3653 self.bindings.0.get(property_name)
3654 }
3655
3656 pub fn set_binding_if_not_set(
3665 &mut self,
3666 property_name: SmolStr,
3667 expression_fn: impl FnOnce() -> Expression,
3668 ) -> bool {
3669 if self.is_binding_set(&property_name, false) {
3670 return false;
3671 }
3672
3673 match self.bindings.0.entry(property_name) {
3674 Entry::Vacant(vacant_entry) => {
3675 let mut binding: BindingExpression = expression_fn().into();
3676 binding.priority = i32::MAX;
3677 vacant_entry.insert(binding.into());
3678 }
3679 Entry::Occupied(mut existing_entry) => {
3680 let inner = existing_entry.get_mut().get_mut();
3681 let mut binding: BindingExpression = expression_fn().into();
3682 binding.priority = i32::MAX;
3683 inner.merge_with(&binding);
3685 }
3686 };
3687 true
3688 }
3689
3690 pub fn set_binding(
3702 &mut self,
3703 property_name: SmolStr,
3704 mut new_binding: BindingExpression,
3705 ) -> Option<BindingExpression> {
3706 match self.bindings.0.entry(property_name) {
3707 Entry::Vacant(v) => {
3708 v.insert(RefCell::new(new_binding));
3709 None
3710 }
3711 Entry::Occupied(mut e) => {
3712 let existing = e.get_mut().get_mut();
3713 if let expression_tree::Expression::DebugHook { expression: _, synthetic, id } =
3714 &mut existing.expression
3715 && *synthetic
3716 {
3717 let new_debug_hook = expression_tree::Expression::DebugHook {
3720 expression: Box::new(new_binding.expression),
3721 id: id.clone(),
3722 synthetic: false,
3723 };
3724 new_binding.expression = new_debug_hook;
3725 *existing = new_binding;
3726 return None;
3729 }
3730 Some(std::mem::replace(e.get_mut().get_mut(), new_binding))
3732 }
3733 }
3734 }
3735
3736 pub fn take_binding(&mut self, property_name: &str) -> Option<BindingExpression> {
3743 self.take_binding_including_synthetic(property_name)
3744 .filter(|binding| !binding.expression.is_synthetic_debug_hook())
3745 }
3746
3747 pub fn take_binding_including_synthetic(
3749 &mut self,
3750 property_name: &str,
3751 ) -> Option<BindingExpression> {
3752 self.bindings.0.remove(property_name).map(RefCell::into_inner)
3753 }
3754
3755 pub(crate) fn take_bindings_including_synthetic(&mut self) -> BindingsMap {
3759 std::mem::take(&mut self.bindings.0)
3760 }
3761
3762 pub(crate) fn extend_bindings_including_synthetic(
3767 &mut self,
3768 bindings: impl IntoIterator<Item = (SmolStr, RefCell<BindingExpression>)>,
3769 ) {
3770 self.bindings.0.extend(bindings);
3771 }
3772
3773 pub fn sub_component(&self) -> Option<&Rc<Component>> {
3774 if self.repeated.is_some() {
3775 None
3776 } else if let ElementType::Component(sub_component) = &self.base_type {
3777 Some(sub_component)
3778 } else {
3779 None
3780 }
3781 }
3782
3783 pub fn element_infos(&self) -> String {
3784 let mut debug_infos = self.debug.clone();
3785 let mut base = self.base_type.clone();
3786 while let ElementType::Component(b) = base {
3787 let elem = b.root_element.borrow();
3788 base = elem.base_type.clone();
3789 debug_infos.extend(elem.debug.iter().cloned());
3790 }
3791
3792 let (infos, _, _) = debug_infos.into_iter().fold(
3793 (String::new(), false, true),
3794 |(mut infos, elem_boundary, first), debug_info| {
3795 if elem_boundary {
3796 infos.push('/');
3797 } else if !first {
3798 infos.push(';');
3799 }
3800
3801 infos.push_str(&debug_info.encoded_element_info());
3802 (infos, debug_info.element_boundary, false)
3803 },
3804 );
3805 infos
3806 }
3807}
3808
3809fn css_property_suggestion(property_name: &str, base_type: &ElementType) -> Option<String> {
3811 let base_name = base_type.to_smolstr();
3812 if base_name != "FlexboxLayout" {
3813 return None;
3814 }
3815 match property_name {
3816 "gap" => Some("Use spacing instead of gap".into()),
3817 "row-gap" => Some("Use spacing-vertical instead of row-gap".into()),
3818 "column-gap" => Some("Use spacing-horizontal instead of column-gap".into()),
3819 "justify-content" => Some("Use alignment instead of justify-content".into()),
3820 _ => None,
3821 }
3822}
3823
3824pub(crate) fn apply_default_type_properties(element: &mut Element) {
3826 if let ElementType::Builtin(builtin_base) = &element.base_type {
3828 for (prop, info) in &builtin_base.properties {
3829 if element.property_declarations.contains_key(prop) {
3833 continue;
3834 }
3835 if let Some(expr) = info.default_value.expr_without_element() {
3836 element.bindings.0.entry(prop.clone()).or_insert_with(|| {
3837 let mut binding = BindingExpression::from(expr);
3838 binding.priority = i32::MAX;
3839 RefCell::new(binding)
3840 });
3841 }
3842 }
3843 }
3844}
3845
3846pub fn type_from_node(
3848 node: syntax_nodes::Type,
3849 diag: &mut BuildDiagnostics,
3850 tr: &TypeRegister,
3851) -> Type {
3852 if let Some(qualified_type_node) = node.QualifiedName() {
3853 let qualified_type = QualifiedTypeName::from_node(qualified_type_node.clone());
3854
3855 let prop_type = tr.lookup_qualified(&qualified_type.members);
3856
3857 #[cfg(feature = "slint-sc")]
3858 if !prop_type.is_slint_sc() {
3859 diag.slint_sc_error(&format!("The type '{qualified_type}' is"), &qualified_type_node);
3860 }
3861
3862 if prop_type == Type::Invalid && tr.lookup_element(&qualified_type.to_smolstr()).is_err() {
3863 diag.push_error(format!("Unknown type '{qualified_type}'"), &qualified_type_node);
3864 } else if !prop_type.is_property_type() {
3865 diag.push_error(
3866 format!("'{qualified_type}' is not a valid type"),
3867 &qualified_type_node,
3868 );
3869 }
3870 prop_type
3871 } else if let Some(object_node) = node.ObjectType() {
3872 #[cfg(feature = "slint-sc")]
3873 diag.slint_sc_error("Inline struct types are", &object_node);
3874 type_struct_from_node(object_node, diag, tr, None, None)
3875 } else if let Some(array_node) = node.ArrayType() {
3876 #[cfg(feature = "slint-sc")]
3877 diag.slint_sc_error("Array types are", &array_node);
3878 Type::Array(Arc::new(type_from_node(array_node.Type(), diag, tr)))
3879 } else {
3880 assert!(diag.has_errors());
3881 Type::Invalid
3882 }
3883}
3884
3885pub fn type_struct_from_node(
3890 object_node: syntax_nodes::ObjectType,
3891 diag: &mut BuildDiagnostics,
3892 tr: &TypeRegister,
3893 name: Option<SmolStr>,
3894 symbol_counters: Option<&Rc<crate::symbol_counters::SymbolCounters>>,
3895) -> Type {
3896 let mut field_defaults = BTreeMap::default();
3897 let mut field_order = Vec::new();
3898 let fields: BTreeMap<SmolStr, Type> = object_node
3899 .ObjectTypeMember()
3900 .map(|member| {
3901 let field_name = parser::identifier_text(&member).unwrap_or_default();
3902 field_order.push(field_name.clone());
3903 let field_ty = type_from_node(member.Type(), diag, tr);
3904 if let Some(default_value_node) = member.Expression() {
3905 if name.is_none() {
3906 diag.push_error(
3907 "Field default values are only supported in named struct declarations"
3908 .into(),
3909 &default_value_node,
3910 );
3911 } else if let Some(expr) = resolve_struct_field_default_value(
3912 default_value_node,
3913 &field_ty,
3914 diag,
3915 tr,
3916 symbol_counters.expect("named struct declarations have symbol counters"),
3917 ) {
3918 field_defaults.insert(field_name.clone(), expr);
3919 }
3920 }
3921 (field_name, field_ty)
3922 })
3923 .collect();
3924 let struct_decl = object_node.parent();
3927 Type::Struct(Arc::new(Struct {
3928 fields,
3929 field_defaults,
3930 name: name.map_or(StructName::None, |name| {
3931 let rust_attributes = struct_decl
3932 .as_ref()
3933 .and_then(|p| syntax_nodes::StructDeclaration::new(p.clone()))
3934 .map(|d| d.AtRustAttr().map(|a| SmolStr::from(a.text().to_string())).collect())
3935 .unwrap_or_default();
3936 let node = struct_decl.as_ref().unwrap_or(&object_node).to_source_location();
3937 StructName::User { name, node, rust_attributes, field_order }
3938 }),
3939 }))
3940}
3941
3942fn resolve_struct_field_default_value(
3945 node: syntax_nodes::Expression,
3946 field_ty: &Type,
3947 diag: &mut BuildDiagnostics,
3948 tr: &TypeRegister,
3949 symbol_counters: &Rc<crate::symbol_counters::SymbolCounters>,
3950) -> Option<crate::langtype::ConstantExpression> {
3951 #[cfg(feature = "slint-sc")]
3952 diag.slint_sc_error("Struct field default values are", &node);
3953 let mut expr = {
3954 let mut ctx = crate::lookup::LookupCtx::empty_context(tr, diag, symbol_counters.clone());
3955 ctx.property_type = field_ty.clone();
3956 Expression::from_expression_node(node.clone(), &mut ctx).maybe_convert_to(
3957 field_ty.clone(),
3958 &node,
3959 ctx.diag,
3960 &ctx.symbol_counters,
3961 )
3962 };
3963 crate::passes::const_propagation::fold_const_expression(&mut expr);
3964 let mut has_invalid = false;
3967 expr.visit_recursive(&mut |e| has_invalid |= matches!(e, Expression::Invalid));
3968 if has_invalid {
3969 return None;
3970 }
3971 let constant = crate::langtype::ConstantExpression::from_expression(&expr);
3972 if constant.is_none() {
3973 let reason = non_constant_expression_reason(&expr)
3974 .map_or_else(Default::default, |reason| format!(": {reason}"));
3975 diag.push_error(
3976 format!("The default value of a struct field must be a constant expression{reason}"),
3977 &node,
3978 );
3979 }
3980 constant
3981}
3982
3983fn non_constant_expression_reason(expr: &Expression) -> Option<String> {
3986 use crate::expression_tree::{BuiltinFunction, Callable};
3987 let mut reason = None;
3988 expr.visit_recursive(&mut |e| {
3989 if reason.is_some() {
3990 return;
3991 }
3992 reason = match e {
3993 Expression::PropertyReference(nr) => {
3994 Some(format!("it references the property '{}'", nr.name()))
3995 }
3996 Expression::FunctionCall { function, .. } => match function {
3997 Callable::Function(nr) => Some(format!("it calls the function '{}'", nr.name())),
3998 Callable::Callback(nr) => Some(format!("it calls the callback '{}'", nr.name())),
3999 Callable::Builtin(BuiltinFunction::GetWindowScaleFactor) => Some(
4000 "the conversion to logical pixels depends on the window's scale factor".into(),
4001 ),
4002 Callable::Builtin(BuiltinFunction::GetWindowDefaultFontSize) => Some(
4003 "the conversion from 'rem' depends on the window's default font size".into(),
4004 ),
4005 Callable::Builtin(BuiltinFunction::Translate) => {
4006 Some("the translation is selected at run-time".into())
4007 }
4008 Callable::Builtin(_) => Some("functions are not evaluated at compile time".into()),
4009 },
4010 Expression::Cast { to: Type::String, .. } => {
4011 Some("the conversion from a number to a string depends on the locale".into())
4012 }
4013 _ => None,
4014 };
4015 });
4016 reason
4017}
4018
4019fn animation_element_from_node(
4020 anim: &syntax_nodes::PropertyAnimation,
4021 prop_name: &syntax_nodes::QualifiedName,
4022 prop_type: Type,
4023 diag: &mut BuildDiagnostics,
4024 tr: &TypeRegister,
4025) -> Option<ElementRc> {
4026 let anim_type = tr.property_animation_type_for_property(prop_type);
4027 if !matches!(anim_type, ElementType::Builtin(..)) {
4028 diag.push_error(
4029 format!(
4030 "'{}' is not a property that can be animated",
4031 prop_name.text().to_string().trim()
4032 ),
4033 prop_name,
4034 );
4035 None
4036 } else {
4037 let mut anim_element =
4038 Element { id: "".into(), base_type: anim_type, ..Default::default() };
4039 anim_element.parse_bindings(
4040 anim.Binding().filter_map(|b| {
4041 Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into()))
4042 }),
4043 false,
4044 diag,
4045 );
4046
4047 apply_default_type_properties(&mut anim_element);
4048
4049 Some(Rc::new(RefCell::new(anim_element)))
4050 }
4051}
4052
4053#[derive(Default, Debug, Clone)]
4054pub struct QualifiedTypeName {
4055 pub members: Vec<SmolStr>,
4056}
4057
4058impl QualifiedTypeName {
4059 pub fn from_node(node: syntax_nodes::QualifiedName) -> Self {
4060 debug_assert_eq!(node.kind(), SyntaxKind::QualifiedName);
4061 let members = node
4062 .children_with_tokens()
4063 .filter(|n| n.kind() == SyntaxKind::Identifier)
4064 .filter_map(|x| x.as_token().map(|x| crate::parser::normalize_identifier(x.text())))
4065 .collect();
4066 Self { members }
4067 }
4068
4069 pub fn to_smolstr(&self) -> SmolStr {
4070 self.members.join(".").into()
4071 }
4072}
4073
4074impl Display for QualifiedTypeName {
4075 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4076 write!(f, "{}", self.members.join("."))
4077 }
4078}
4079
4080fn lookup_property_from_qualified_name_for_state(
4083 node: syntax_nodes::QualifiedName,
4084 r: &ElementRc,
4085 diag: &mut BuildDiagnostics,
4086) -> Option<(NamedReference, Type)> {
4087 let qualname = QualifiedTypeName::from_node(node.clone());
4088 let check = |lookup: &PropertyLookupResult<'_>, diag: &mut BuildDiagnostics| {
4089 #[cfg(feature = "slint-sc")]
4090 lookup.check_slint_sc(&qualname, &node, diag);
4091 if !lookup.property_type.is_property_type() {
4092 diag.push_error(format!("'{qualname}' is not a valid property"), &node);
4093 } else if !lookup.is_valid_for_assignment() {
4094 diag.push_error(
4095 format!(
4096 "'{}' cannot be set in a state because it is '{}'",
4097 qualname, lookup.property_visibility
4098 ),
4099 &node,
4100 );
4101 }
4102 };
4103 match qualname.members.as_slice() {
4104 [unresolved_prop_name] => {
4105 let lookup_result = r
4106 .borrow()
4107 .lookup_property(unresolved_prop_name.as_ref(), PropertyLookupMode::ComponentLocal);
4108 check(&lookup_result, diag);
4109 Some((
4110 NamedReference::new(r, lookup_result.internal_or_resolved_name()),
4111 lookup_result.property_type,
4112 ))
4113 }
4114 [elem_id, unresolved_prop_name] => {
4115 if let Some(element) = find_element_by_id(r, elem_id.as_ref()) {
4116 let lookup_result = element.borrow().lookup_property(
4117 unresolved_prop_name.as_ref(),
4118 PropertyLookupMode::ComponentLocal,
4119 );
4120 if !lookup_result.is_valid() {
4121 diag.push_error(
4122 format!("'{unresolved_prop_name}' not found in '{elem_id}'"),
4123 &node,
4124 );
4125 } else {
4126 check(&lookup_result, diag);
4127 }
4128 Some((
4129 NamedReference::new(&element, lookup_result.internal_or_resolved_name()),
4130 lookup_result.property_type,
4131 ))
4132 } else {
4133 diag.push_error(format!("'{elem_id}' is not a valid element id"), &node);
4134 None
4135 }
4136 }
4137 _ => {
4138 diag.push_error(format!("'{qualname}' is not a valid property"), &node);
4139 None
4140 }
4141 }
4142}
4143
4144fn find_element_by_id(e: &ElementRc, name: &str) -> Option<ElementRc> {
4146 if e.borrow().id == name {
4147 return Some(e.clone());
4148 }
4149 for x in &e.borrow().children {
4150 if x.borrow().repeated.is_some() {
4151 continue;
4152 }
4153 if let Some(x) = find_element_by_id(x, name) {
4154 return Some(x);
4155 }
4156 }
4157
4158 None
4159}
4160
4161pub fn find_parent_element(e: &ElementRc) -> Option<ElementRc> {
4164 fn recurse(base: &ElementRc, e: &ElementRc) -> Option<ElementRc> {
4165 for child in &base.borrow().children {
4166 if Rc::ptr_eq(child, e) {
4167 return Some(base.clone());
4168 }
4169 if let Some(x) = recurse(child, e) {
4170 return Some(x);
4171 }
4172 }
4173 None
4174 }
4175
4176 let root = e.borrow().enclosing_component.upgrade().unwrap().root_element.clone();
4177 if Rc::ptr_eq(&root, e) {
4178 return None;
4179 }
4180 recurse(&root, e)
4181}
4182
4183pub fn recurse_elem<State>(
4187 elem: &ElementRc,
4188 state: &State,
4189 vis: &mut impl FnMut(&ElementRc, &State) -> State,
4190) {
4191 recurse_elem_dyn(elem, state, vis)
4192}
4193
4194fn recurse_elem_dyn<State>(
4195 elem: &ElementRc,
4196 state: &State,
4197 vis: &mut dyn FnMut(&ElementRc, &State) -> State,
4198) {
4199 let state = vis(elem, state);
4200 for sub in &elem.borrow().children {
4201 recurse_elem_dyn(sub, &state, vis);
4202 }
4203}
4204
4205pub fn recurse_elem_including_sub_components<State>(
4207 component: &Component,
4208 state: &State,
4209 vis: &mut impl FnMut(&ElementRc, &State) -> State,
4210) {
4211 recurse_elem_including_sub_components_dyn(component, state, vis)
4212}
4213
4214fn recurse_elem_including_sub_components_dyn<State>(
4215 component: &Component,
4216 state: &State,
4217 vis: &mut dyn FnMut(&ElementRc, &State) -> State,
4218) {
4219 recurse_elem_dyn(&component.root_element, state, &mut |elem, state| {
4220 debug_assert!(std::ptr::eq(
4221 component as *const Component,
4222 (&*elem.borrow().enclosing_component.upgrade().unwrap()) as *const Component
4223 ));
4224 if elem.borrow().repeated.is_some()
4225 && let ElementType::Component(base) = &elem.borrow().base_type
4226 && base.parent_element().is_some()
4227 {
4228 recurse_elem_including_sub_components_dyn(base, state, vis);
4229 }
4230 vis(elem, state)
4231 });
4232 component
4233 .popup_windows
4234 .borrow()
4235 .iter()
4236 .for_each(|p| recurse_elem_including_sub_components_dyn(&p.component, state, vis));
4237 component
4238 .menu_item_tree
4239 .borrow()
4240 .iter()
4241 .for_each(|c| recurse_elem_including_sub_components_dyn(c, state, vis));
4242}
4243
4244pub fn recurse_elem_no_borrow<State>(
4246 elem: &ElementRc,
4247 state: &State,
4248 vis: &mut impl FnMut(&ElementRc, &State) -> State,
4249) {
4250 recurse_elem_no_borrow_dyn(elem, state, vis)
4251}
4252
4253fn recurse_elem_no_borrow_dyn<State>(
4254 elem: &ElementRc,
4255 state: &State,
4256 vis: &mut dyn FnMut(&ElementRc, &State) -> State,
4257) {
4258 let state = vis(elem, state);
4259 let children = elem.borrow().children.clone();
4260 for sub in &children {
4261 recurse_elem_no_borrow_dyn(sub, &state, vis);
4262 }
4263}
4264
4265pub fn recurse_elem_including_sub_components_no_borrow<State>(
4267 component: &Component,
4268 state: &State,
4269 vis: &mut impl FnMut(&ElementRc, &State) -> State,
4270) {
4271 recurse_elem_including_sub_components_no_borrow_dyn(component, state, vis)
4272}
4273
4274fn recurse_elem_including_sub_components_no_borrow_dyn<State>(
4275 component: &Component,
4276 state: &State,
4277 vis: &mut dyn FnMut(&ElementRc, &State) -> State,
4278) {
4279 recurse_elem_no_borrow_dyn(&component.root_element, state, &mut |elem, state| {
4280 let base = if elem.borrow().repeated.is_some() {
4281 if let ElementType::Component(base) = &elem.borrow().base_type {
4282 if base.parent_element().is_some() {
4283 Some(base.clone())
4284 } else {
4285 None
4287 }
4288 } else {
4289 None
4290 }
4291 } else {
4292 None
4293 };
4294 if let Some(base) = base {
4295 recurse_elem_including_sub_components_no_borrow_dyn(&base, state, vis);
4296 }
4297 vis(elem, state)
4298 });
4299 component.popup_windows.borrow().iter().for_each(|p| {
4300 recurse_elem_including_sub_components_no_borrow_dyn(&p.component, state, vis)
4301 });
4302 component
4303 .menu_item_tree
4304 .borrow()
4305 .iter()
4306 .for_each(|c| recurse_elem_including_sub_components_no_borrow_dyn(c, state, vis));
4307}
4308
4309pub fn visit_repeater_model_expression(
4314 elem: &ElementRc,
4315 mut vis: impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
4316) {
4317 let repeated = elem
4318 .borrow_mut()
4319 .repeated
4320 .as_mut()
4321 .map(|r| (std::mem::take(&mut r.model), r.is_conditional_element));
4322 if let Some((mut model, is_cond)) = repeated {
4323 vis(&mut model, None, &|| if is_cond { Type::Bool } else { Type::Model });
4324 elem.borrow_mut().repeated.as_mut().unwrap().model = model;
4325 }
4326}
4327
4328pub fn visit_element_expressions_excluding_repeater_model(
4331 elem: &ElementRc,
4332 mut vis: impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
4333) {
4334 visit_element_expressions_excluding_repeater_model_dyn(elem, &mut vis)
4335}
4336
4337fn visit_element_expressions_excluding_repeater_model_dyn(
4338 elem: &ElementRc,
4339 vis: &mut dyn FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
4340) {
4341 fn visit_element_expressions_simple(
4342 elem: &ElementRc,
4343 vis: &mut dyn FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
4344 ) {
4345 for (name, expr) in elem.borrow().bindings_including_synthetic() {
4346 vis(&mut expr.borrow_mut(), Some(name.as_str()), &|| {
4347 elem.borrow().lookup_property(name, PropertyLookupMode::InternalName).property_type
4348 });
4349
4350 for twb in &mut expr.borrow_mut().two_way_bindings {
4351 if let expression_tree::TwoWayBinding::ModelData { repeated_element, .. } = twb {
4352 let mut e =
4353 Expression::RepeaterModelReference { element: repeated_element.clone() };
4354 vis(&mut e, None, &|| Type::Invalid);
4355 if let Expression::RepeaterModelReference { element } = e {
4356 *repeated_element = element;
4357 }
4358 }
4359 }
4360
4361 match &mut expr.borrow_mut().animation {
4362 Some(PropertyAnimation::Static(e)) => visit_element_expressions_simple(e, vis),
4363 Some(PropertyAnimation::Transition { animations, state_ref }) => {
4364 vis(state_ref, None, &|| Type::Int32);
4365 for a in animations {
4366 visit_element_expressions_simple(&a.animation, vis)
4367 }
4368 }
4369 None => (),
4370 }
4371 }
4372 }
4373
4374 visit_element_expressions_simple(elem, vis);
4375
4376 for expr in elem.borrow().change_callbacks.values() {
4377 for expr in expr.borrow_mut().iter_mut() {
4378 vis(expr, Some("$change callback$"), &|| Type::Void);
4379 }
4380 }
4381
4382 let mut states = std::mem::take(&mut elem.borrow_mut().states);
4383 for s in &mut states {
4384 if let Some(cond) = s.condition.as_mut() {
4385 vis(cond, None, &|| Type::Bool)
4386 }
4387 for (ne, e, _) in &mut s.property_changes {
4388 vis(e, Some(ne.name()), &|| {
4389 ne.element()
4390 .borrow()
4391 .lookup_property(ne.name(), PropertyLookupMode::InternalName)
4392 .property_type
4393 });
4394 }
4395 }
4396 elem.borrow_mut().states = states;
4397
4398 let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions);
4399 for t in &mut transitions {
4400 for (_, _, a) in &mut t.property_animations {
4401 visit_element_expressions_simple(a, vis);
4402 }
4403 }
4404 elem.borrow_mut().transitions = transitions;
4405
4406 let component = elem.borrow().enclosing_component.upgrade().unwrap();
4407 if Rc::ptr_eq(&component.root_element, elem) {
4408 for e in component.init_code.borrow_mut().iter_mut() {
4409 vis(e, None, &|| Type::Void);
4410 }
4411 }
4412}
4413
4414pub fn visit_element_expressions(
4415 elem: &ElementRc,
4416 mut vis: impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
4417) {
4418 visit_repeater_model_expression(elem, &mut vis);
4419 visit_element_expressions_excluding_repeater_model(elem, &mut vis);
4420}
4421
4422pub fn visit_named_references_in_expression(
4423 expr: &mut Expression,
4424 vis: &mut impl FnMut(&mut NamedReference),
4425) {
4426 visit_named_references_in_expression_dyn(expr, vis)
4427}
4428
4429fn visit_named_references_in_expression_dyn(
4430 expr: &mut Expression,
4431 vis: &mut dyn FnMut(&mut NamedReference),
4432) {
4433 expr.visit_mut(|sub| visit_named_references_in_expression_dyn(sub, vis));
4434 match expr {
4435 Expression::PropertyReference(r) => vis(r),
4436 Expression::FunctionCall {
4437 function: Callable::Callback(r) | Callable::Function(r),
4438 ..
4439 } => vis(r),
4440 Expression::LayoutCacheAccess { layout_cache_prop, .. } => vis(layout_cache_prop),
4441 Expression::GridRepeaterCacheAccess { layout_cache_prop, .. } => vis(layout_cache_prop),
4442 Expression::OrganizeGridLayout(l) => l.visit_named_references(vis),
4443 Expression::ComputeBoxLayoutInfo { layout, .. } => layout.visit_named_references(vis),
4444 Expression::ComputeFlexboxLayoutInfo { layout, .. } => layout.visit_named_references(vis),
4445 Expression::ComputeGridLayoutInfo { layout_organized_data_prop, layout, .. } => {
4446 vis(layout_organized_data_prop);
4447 layout.visit_named_references(vis);
4448 }
4449 Expression::SolveBoxLayout(l, _) => l.visit_named_references(vis),
4450 Expression::SolveFlexboxLayout(l) => l.visit_named_references(vis),
4451 Expression::SolveGridLayout { layout_organized_data_prop, layout, .. } => {
4452 vis(layout_organized_data_prop);
4453 layout.visit_named_references(vis);
4454 }
4455 Expression::RepeaterModelReference { element }
4458 | Expression::RepeaterIndexReference { element } => {
4459 let mut nc =
4461 NamedReference::new(&element.upgrade().unwrap(), SmolStr::new_static("$model"));
4462 vis(&mut nc);
4463 debug_assert!(nc.element().borrow().repeated.is_some());
4464 *element = Rc::downgrade(&nc.element());
4465 }
4466 _ => {}
4467 }
4468}
4469
4470pub fn visit_all_named_references_in_element(
4473 elem: &ElementRc,
4474 mut vis: impl FnMut(&mut NamedReference),
4475) {
4476 visit_all_named_references_in_element_dyn(elem, &mut vis)
4477}
4478
4479fn visit_all_named_references_in_element_dyn(
4480 elem: &ElementRc,
4481 mut vis: &mut dyn FnMut(&mut NamedReference),
4482) {
4483 visit_element_expressions(elem, |expr, _, _| {
4484 visit_named_references_in_expression_dyn(expr, vis)
4485 });
4486 let mut states = std::mem::take(&mut elem.borrow_mut().states);
4487 for s in &mut states {
4488 for (r, _, _) in &mut s.property_changes {
4489 vis(r);
4490 }
4491 }
4492 elem.borrow_mut().states = states;
4493 let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions);
4494 for t in &mut transitions {
4495 for (r, _, _) in &mut t.property_animations {
4496 vis(r)
4497 }
4498 }
4499 elem.borrow_mut().transitions = transitions;
4500 let mut repeated = std::mem::take(&mut elem.borrow_mut().repeated);
4501 if let Some(r) = &mut repeated
4502 && let Some(lv) = &mut r.is_listview
4503 {
4504 vis(&mut lv.content_y);
4505 if let Some(content_height) = &mut lv.content_height {
4506 vis(content_height);
4507 }
4508 if let Some(content_width) = &mut lv.content_width {
4509 vis(content_width);
4510 }
4511 vis(&mut lv.listview_height);
4512 vis(&mut lv.listview_width);
4513 }
4514 elem.borrow_mut().repeated = repeated;
4515 let mut layout_info_prop = std::mem::take(&mut elem.borrow_mut().layout_info_prop);
4516 layout_info_prop.as_mut().map(|(h, b)| (vis(h), vis(b)));
4517 elem.borrow_mut().layout_info_prop = layout_info_prop;
4518 let mut constrained_v = std::mem::take(&mut elem.borrow_mut().layout_info_v_with_constraint);
4519 if let Some(nr) = constrained_v.as_mut() {
4520 vis(nr);
4521 }
4522 elem.borrow_mut().layout_info_v_with_constraint = constrained_v;
4523 let mut at_own_height = std::mem::take(&mut elem.borrow_mut().layout_info_h_at_own_height);
4524 if let Some(nr) = at_own_height.as_mut() {
4525 vis(nr);
4526 }
4527 elem.borrow_mut().layout_info_h_at_own_height = at_own_height;
4528 let mut debug = std::mem::take(&mut elem.borrow_mut().debug);
4529 for d in debug.iter_mut() {
4530 if let Some(l) = d.layout.as_mut() {
4531 l.visit_named_references(vis)
4532 }
4533 }
4534 elem.borrow_mut().debug = debug;
4535
4536 let mut accessibility_props = std::mem::take(&mut elem.borrow_mut().accessibility_props);
4537 accessibility_props.0.iter_mut().for_each(|(_, x)| vis(x));
4538 elem.borrow_mut().accessibility_props = accessibility_props;
4539
4540 let geometry_props = elem.borrow_mut().geometry_props.take();
4541 if let Some(mut geometry_props) = geometry_props {
4542 vis(&mut geometry_props.x);
4543 vis(&mut geometry_props.y);
4544 vis(&mut geometry_props.width);
4545 vis(&mut geometry_props.height);
4546 elem.borrow_mut().geometry_props = Some(geometry_props);
4547 }
4548
4549 let z_order = elem.borrow_mut().z_order.take();
4550 if let Some(mut zo) = z_order {
4551 if let ZOrder::Dynamic(ref mut nr) | ZOrder::PerInstance(ref mut nr) = zo {
4552 vis(nr);
4553 }
4554 elem.borrow_mut().z_order = Some(zo);
4555 }
4556
4557 for (_, expr) in elem.borrow().real_bindings() {
4559 for twb in &mut expr.borrow_mut().two_way_bindings {
4560 if let expression_tree::TwoWayBinding::Property { property, .. } = twb {
4561 vis(property);
4562 }
4563 }
4564 }
4565
4566 let mut property_declarations = std::mem::take(&mut elem.borrow_mut().property_declarations);
4567 for pd in property_declarations.values_mut() {
4568 pd.is_alias.as_mut().map(&mut vis);
4569 }
4570 elem.borrow_mut().property_declarations = property_declarations;
4571
4572 let grid_layout_cell = elem.borrow_mut().grid_layout_cell.take();
4574 if let Some(grid_layout_cell) = grid_layout_cell {
4575 grid_layout_cell.borrow_mut().visit_named_references(&mut vis);
4576 elem.borrow_mut().grid_layout_cell = Some(grid_layout_cell);
4577 }
4578}
4579
4580pub fn visit_all_named_references(
4582 component: &Component,
4583 vis: &mut impl FnMut(&mut NamedReference),
4584) {
4585 visit_all_named_references_dyn(component, vis)
4586}
4587
4588fn visit_all_named_references_dyn(component: &Component, vis: &mut dyn FnMut(&mut NamedReference)) {
4589 recurse_elem_including_sub_components_no_borrow_dyn(
4590 component,
4591 &Weak::new(),
4592 &mut |elem, parent_compo| {
4593 visit_all_named_references_in_element_dyn(elem, vis);
4594 let compo = elem.borrow().enclosing_component.clone();
4595 if !Weak::ptr_eq(parent_compo, &compo) {
4596 let compo = compo.upgrade().unwrap();
4597 compo.root_constraints.borrow_mut().visit_named_references(vis);
4598 compo.popup_windows.borrow_mut().iter_mut().for_each(|p| {
4599 vis(&mut p.x);
4600 vis(&mut p.y);
4601 if let Some(is_open) = &mut p.is_open {
4602 vis(is_open);
4603 }
4604 });
4605 compo.timers.borrow_mut().iter_mut().for_each(|t| {
4606 vis(&mut t.interval);
4607 vis(&mut t.triggered);
4608 vis(&mut t.running);
4609 });
4610 for o in compo.optimized_elements.borrow().iter() {
4611 visit_element_expressions(o, |expr, _, _| {
4612 visit_named_references_in_expression_dyn(expr, vis)
4613 });
4614 }
4615 }
4616 compo
4617 },
4618 );
4619}
4620
4621pub fn visit_all_expressions(
4625 component: &Component,
4626 mut vis: impl FnMut(&mut Expression, &dyn Fn() -> Type),
4627) {
4628 visit_all_expressions_dyn(component, &mut vis)
4629}
4630
4631fn visit_all_expressions_dyn(
4632 component: &Component,
4633 vis: &mut dyn FnMut(&mut Expression, &dyn Fn() -> Type),
4634) {
4635 recurse_elem_including_sub_components_dyn(component, &Weak::new(), &mut |elem, parent_compo| {
4636 visit_element_expressions(elem, |expr, _, ty| vis(expr, ty));
4637 let compo = elem.borrow().enclosing_component.clone();
4638 if !Weak::ptr_eq(parent_compo, &compo) {
4639 let compo = compo.upgrade().unwrap();
4640 for o in compo.optimized_elements.borrow().iter() {
4641 visit_element_expressions(o, |expr, _, ty| vis(expr, ty));
4642 }
4643 }
4644 compo
4645 })
4646}
4647
4648#[derive(Debug, Clone)]
4649pub struct State {
4650 pub id: SmolStr,
4651 pub condition: Option<Expression>,
4652 pub property_changes: Vec<(NamedReference, Expression, syntax_nodes::StatePropertyChange)>,
4653 pub selection: Option<ConditionLocation>,
4656}
4657
4658#[derive(Debug, Clone)]
4659pub struct Transition {
4660 pub direction: TransitionDirection,
4661 pub state_id: SmolStr,
4662 pub property_animations: Vec<(NamedReference, SourceLocation, ElementRc)>,
4663 pub node: syntax_nodes::Transition,
4664}
4665
4666impl Transition {
4667 fn from_node(
4668 trs: syntax_nodes::Transition,
4669 r: &ElementRc,
4670 tr: &TypeRegister,
4671 diag: &mut BuildDiagnostics,
4672 ) -> Transition {
4673 if let Some(star) = trs.child_token(SyntaxKind::Star) {
4674 diag.push_error("catch-all not yet implemented".into(), &star);
4675 };
4676 let direction_text = trs
4677 .first_child_or_token()
4678 .and_then(|t| t.as_token().map(|tok| tok.text().to_string()))
4679 .unwrap_or_default();
4680
4681 Transition {
4682 direction: match direction_text.as_str() {
4683 "in" => TransitionDirection::In,
4684 "out" => TransitionDirection::Out,
4685 "in-out" => TransitionDirection::InOut,
4686 "in_out" => TransitionDirection::InOut,
4687 _ => {
4688 unreachable!("Unknown transition direction: '{}'", direction_text);
4689 }
4690 },
4691 state_id: trs
4692 .DeclaredIdentifier()
4693 .and_then(|x| parser::identifier_text(&x))
4694 .unwrap_or_default(),
4695 property_animations: trs
4696 .PropertyAnimation()
4697 .flat_map(|pa| pa.QualifiedName().map(move |qn| (pa.clone(), qn)))
4698 .filter_map(|(pa, qn)| {
4699 lookup_property_from_qualified_name_for_state(qn.clone(), r, diag).and_then(
4700 |(ne, prop_type)| {
4701 animation_element_from_node(&pa, &qn, prop_type, diag, tr)
4702 .map(|anim_element| (ne, qn.to_source_location(), anim_element))
4703 },
4704 )
4705 })
4706 .collect(),
4707 node: trs.clone(),
4708 }
4709 }
4710}
4711
4712#[derive(Clone, Debug, derive_more::Deref)]
4713pub struct ExportedName {
4714 #[deref]
4715 pub name: SmolStr, pub name_ident: SyntaxNode,
4717}
4718
4719impl ExportedName {
4720 pub fn original_name(&self) -> SmolStr {
4721 self.name_ident
4722 .child_token(parser::SyntaxKind::Identifier)
4723 .map(|n| n.to_smolstr())
4724 .unwrap_or_else(|| self.name.clone())
4725 }
4726
4727 pub fn from_export_specifier(
4728 export_specifier: &syntax_nodes::ExportSpecifier,
4729 ) -> (SmolStr, ExportedName) {
4730 let internal_name =
4731 parser::identifier_text(&export_specifier.ExportIdentifier()).unwrap_or_default();
4732
4733 let (name, name_ident): (SmolStr, SyntaxNode) = export_specifier
4734 .ExportName()
4735 .and_then(|ident| {
4736 parser::identifier_text(&ident).map(|text| (text, ident.clone().into()))
4737 })
4738 .unwrap_or_else(|| (internal_name.clone(), export_specifier.ExportIdentifier().into()));
4739 (internal_name, ExportedName { name, name_ident })
4740 }
4741}
4742
4743#[derive(Default, Debug, derive_more::Deref)]
4744pub struct Exports {
4745 #[deref]
4746 components_or_types: Vec<(ExportedName, Either<Rc<Component>, Type>)>,
4747}
4748
4749impl Exports {
4750 pub fn from_node(
4751 doc: &syntax_nodes::Document,
4752 inner_components: &[Rc<Component>],
4753 type_registry: &TypeRegister,
4754 diag: &mut BuildDiagnostics,
4755 ) -> Self {
4756 let resolve_export_to_inner_component_or_import =
4757 |internal_name: &str, internal_name_node: &dyn Spanned, diag: &mut BuildDiagnostics| {
4758 if let Ok(ElementType::Component(c)) = type_registry.lookup_element(internal_name) {
4759 Some(Either::Left(c))
4760 } else if let ty @ Type::Struct { .. } | ty @ Type::Enumeration(_) =
4761 type_registry.lookup(internal_name)
4762 {
4763 Some(Either::Right(ty))
4764 } else if type_registry.lookup_element(internal_name).is_ok()
4765 || type_registry.lookup(internal_name) != Type::Invalid
4766 {
4767 diag.push_error(
4768 format!("Cannot export '{internal_name}' because it is not a component",),
4769 internal_name_node,
4770 );
4771 None
4772 } else {
4773 diag.push_error(format!("'{internal_name}' not found",), internal_name_node);
4774 None
4775 }
4776 };
4777
4778 let mut exports_with_duplicates: Vec<(ExportedName, Either<Rc<Component>, Type>)> =
4781 Vec::new();
4782
4783 exports_with_duplicates.extend(
4785 doc.ExportsList()
4786 .filter(|exports| exports.ExportModule().is_none())
4788 .flat_map(|exports| exports.ExportSpecifier())
4789 .filter_map(|export_specifier| {
4790 let (internal_name, exported_name) =
4791 ExportedName::from_export_specifier(&export_specifier);
4792 Some((
4793 exported_name,
4794 resolve_export_to_inner_component_or_import(
4795 &internal_name,
4796 &export_specifier.ExportIdentifier(),
4797 diag,
4798 )?,
4799 ))
4800 }),
4801 );
4802
4803 exports_with_duplicates.extend(
4805 doc.ExportsList().flat_map(|exports| exports.Component()).filter_map(|component| {
4806 let name_ident: SyntaxNode = component.DeclaredIdentifier().into();
4807 let name =
4808 parser::identifier_text(&component.DeclaredIdentifier()).unwrap_or_else(|| {
4809 debug_assert!(diag.has_errors());
4810 SmolStr::default()
4811 });
4812
4813 let compo_or_type =
4814 resolve_export_to_inner_component_or_import(&name, &name_ident, diag)?;
4815
4816 Some((ExportedName { name, name_ident }, compo_or_type))
4817 }),
4818 );
4819
4820 exports_with_duplicates.extend(
4822 doc.ExportsList()
4823 .flat_map(|exports| {
4824 exports
4825 .StructDeclaration()
4826 .map(|st| st.DeclaredIdentifier())
4827 .chain(exports.EnumDeclaration().map(|en| en.DeclaredIdentifier()))
4828 })
4829 .filter_map(|name_ident| {
4830 let name = parser::identifier_text(&name_ident).unwrap_or_else(|| {
4831 debug_assert!(diag.has_errors());
4832 SmolStr::default()
4833 });
4834
4835 let name_ident = name_ident.into();
4836
4837 let compo_or_type =
4838 resolve_export_to_inner_component_or_import(&name, &name_ident, diag)?;
4839
4840 Some((ExportedName { name, name_ident }, compo_or_type))
4841 }),
4842 );
4843
4844 exports_with_duplicates.sort_by(|(a, _), (b, _)| a.name.cmp(&b.name));
4845
4846 let mut sorted_deduped_exports = Vec::with_capacity(exports_with_duplicates.len());
4847 let mut it = exports_with_duplicates.into_iter().peekable();
4848 while let Some((exported_name, compo_or_type)) = it.next() {
4849 let mut warning_issued_on_first_occurrence = false;
4850
4851 while it.peek().map(|(name, _)| &name.name) == Some(&exported_name.name) {
4853 let message = format!("Duplicated export '{}'", exported_name.name);
4854
4855 if !warning_issued_on_first_occurrence {
4856 diag.push_error(message.clone(), &exported_name.name_ident);
4857 warning_issued_on_first_occurrence = true;
4858 }
4859
4860 let duplicate_loc = it.next().unwrap().0.name_ident;
4861 diag.push_error(message.clone(), &duplicate_loc);
4862 }
4863
4864 sorted_deduped_exports.push((exported_name, compo_or_type));
4865 }
4866
4867 if let Some(last_compo) = inner_components.last() {
4868 let name = last_compo.id.clone();
4869 if last_compo.is_global() {
4870 if sorted_deduped_exports.is_empty() {
4871 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()));
4872 sorted_deduped_exports.push((
4873 ExportedName { name, name_ident: doc.clone().into() },
4874 Either::Left(last_compo.clone()),
4875 ))
4876 }
4877 } else if !sorted_deduped_exports
4878 .iter()
4879 .any(|e| e.1.as_ref().left().is_some_and(|c| !c.is_global()))
4880 {
4881 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()));
4882 let insert_pos = sorted_deduped_exports
4883 .partition_point(|(existing_export, _)| existing_export.name <= name);
4884 sorted_deduped_exports.insert(
4885 insert_pos,
4886 (
4887 ExportedName { name, name_ident: doc.clone().into() },
4888 Either::Left(last_compo.clone()),
4889 ),
4890 )
4891 }
4892 }
4893 Self { components_or_types: sorted_deduped_exports }
4894 }
4895
4896 pub fn add_reexports(
4897 &mut self,
4898 other_exports: impl IntoIterator<Item = (ExportedName, Either<Rc<Component>, Type>)>,
4899 diag: &mut BuildDiagnostics,
4900 ) {
4901 for export in other_exports {
4902 match self.components_or_types.binary_search_by(|entry| entry.0.cmp(&export.0)) {
4903 Ok(_) => {
4904 diag.push_warning(
4905 format!(
4906 "'{}' is already exported in this file; it will not be re-exported",
4907 *export.0
4908 ),
4909 &export.0.name_ident,
4910 );
4911 }
4912 Err(insert_pos) => {
4913 self.components_or_types.insert(insert_pos, export);
4914 }
4915 }
4916 }
4917 }
4918
4919 pub fn find(&self, name: &str) -> Option<Either<Rc<Component>, Type>> {
4920 self.components_or_types
4921 .binary_search_by(|(exported_name, _)| exported_name.as_str().cmp(name))
4922 .ok()
4923 .map(|index| self.components_or_types[index].1.clone())
4924 }
4925
4926 pub fn named_type_aliases(&self) -> Vec<(SmolStr, SmolStr)> {
4931 self.iter()
4932 .filter_map(|(exported, item)| match item {
4933 Either::Left(component) if !component.is_global() => {
4934 Some((component.id.clone(), exported.name.clone()))
4935 }
4936 Either::Right(ty) => match ty {
4937 Type::Struct(s) if s.node().is_some() => match &s.name {
4938 StructName::User { name, .. } => {
4939 Some((name.clone(), exported.name.clone()))
4940 }
4941 _ => None,
4942 },
4943 Type::Enumeration(en) => Some((en.name.clone(), exported.name.clone())),
4944 _ => None,
4945 },
4946 _ => None,
4947 })
4948 .filter(|(original, alias)| original != alias)
4949 .collect()
4950 }
4951
4952 pub fn retain(
4953 &mut self,
4954 func: impl FnMut(&mut (ExportedName, Either<Rc<Component>, Type>)) -> bool,
4955 ) {
4956 self.components_or_types.retain_mut(func)
4957 }
4958
4959 pub(crate) fn snapshot(&self, snapshotter: &mut crate::typeloader::Snapshotter) -> Self {
4960 let components_or_types = self
4961 .components_or_types
4962 .iter()
4963 .map(|(en, either)| {
4964 let en = en.clone();
4965 let either = match either {
4966 itertools::Either::Left(l) => itertools::Either::Left({
4967 Weak::upgrade(&snapshotter.use_component(l))
4968 .expect("Component should cleanly upgrade here")
4969 }),
4970 itertools::Either::Right(r) => itertools::Either::Right(r.clone()),
4971 };
4972 (en, either)
4973 })
4974 .collect();
4975
4976 Self { components_or_types }
4977 }
4978}
4979
4980impl std::iter::IntoIterator for Exports {
4981 type Item = (ExportedName, Either<Rc<Component>, Type>);
4982
4983 type IntoIter = std::vec::IntoIter<Self::Item>;
4984
4985 fn into_iter(self) -> Self::IntoIter {
4986 self.components_or_types.into_iter()
4987 }
4988}
4989
4990fn forward_layout_info_with_constraint(new_root: &ElementRc, old_root: &ElementRc) {
4996 let width = Expression::FunctionParameterReference { index: 0, ty: Type::LogicalLength };
4997 let body = if let Some(nr) = old_root.borrow().inherited_layout_info_v_with_constraint() {
4998 Some(Expression::FunctionCall {
4999 function: Callable::Function(NamedReference::new(old_root, nr.name().clone())),
5000 arguments: vec![width],
5001 source_location: None,
5002 })
5003 } else if old_root.borrow().is_builtin_height_for_width() {
5004 crate::layout::implicit_layout_info_call(
5005 old_root,
5006 Orientation::Vertical,
5007 crate::layout::BuiltinFilter::All,
5008 Some(width),
5009 )
5010 } else {
5011 None
5012 };
5013 if let Some(body) = body {
5014 crate::passes::lower_layout::synthesize_layoutinfo_v_with_constraint_on(
5015 new_root,
5016 old_root.borrow().to_source_location(),
5017 body,
5018 );
5019 }
5020}
5021
5022pub fn inject_element_as_repeated_element(repeated_element: &ElementRc, new_root: ElementRc) {
5026 let component = repeated_element.borrow().base_type.as_component().clone();
5027 debug_assert_eq!(Rc::strong_count(&component), 2);
5031 let old_root = &component.root_element;
5032
5033 adjust_geometry_for_injected_parent(&new_root, old_root);
5034
5035 let mut elements_with_enclosing_component_reference = Vec::new();
5037 recurse_elem(old_root, &(), &mut |element: &ElementRc, _| {
5038 if let Some(enclosing_component) = element.borrow().enclosing_component.upgrade()
5039 && Rc::ptr_eq(&enclosing_component, &component)
5040 {
5041 elements_with_enclosing_component_reference.push(element.clone());
5042 }
5043 });
5044 elements_with_enclosing_component_reference
5045 .extend_from_slice(component.optimized_elements.borrow().as_slice());
5046 elements_with_enclosing_component_reference.push(new_root.clone());
5047
5048 new_root.borrow_mut().child_of_layout =
5049 std::mem::replace(&mut old_root.borrow_mut().child_of_layout, false);
5050 new_root.borrow_mut().grid_layout_cell = old_root.borrow_mut().grid_layout_cell.take();
5052 if old_root.borrow().child_of_flexbox {
5055 new_root.borrow_mut().child_of_flexbox = true;
5056 }
5057 new_root.borrow_mut().parent_box_layout_orientation =
5058 old_root.borrow().parent_box_layout_orientation;
5059 for prop in ["layout-order", "cross-axis-self-alignment"].iter() {
5064 if old_root.borrow().binding(prop).is_some() {
5065 new_root.borrow_mut().set_binding(
5066 SmolStr::new_static(prop),
5067 BindingExpression::new_two_way(
5068 NamedReference::new(old_root, SmolStr::new_static(prop)).into(),
5069 ),
5070 );
5071 }
5072 }
5073 let layout_info_prop = {
5076 let old = old_root.borrow();
5077 old.effective_layout_info_prop(Orientation::Horizontal)
5078 .cloned()
5079 .zip(old.effective_layout_info_prop(Orientation::Vertical).cloned())
5080 }
5081 .or_else(|| {
5082 let li_v = crate::layout::create_new_prop(
5084 &new_root,
5085 SmolStr::new_static("layoutinfo-v"),
5086 crate::typeregister::layout_info_type().into(),
5087 );
5088 let li_h = crate::layout::create_new_prop(
5089 &new_root,
5090 SmolStr::new_static("layoutinfo-h"),
5091 crate::typeregister::layout_info_type().into(),
5092 );
5093 let expr_h = crate::layout::implicit_layout_info_call(
5094 old_root,
5095 Orientation::Horizontal,
5096 crate::layout::BuiltinFilter::All,
5097 None,
5098 )
5099 .unwrap();
5100 let expr_v = crate::layout::implicit_layout_info_call(
5101 old_root,
5102 Orientation::Vertical,
5103 crate::layout::BuiltinFilter::All,
5104 None,
5105 )
5106 .unwrap();
5107 let expr_v =
5108 BindingExpression::new_with_span(expr_v, old_root.borrow().to_source_location());
5109 li_v.element().borrow_mut().set_binding(li_v.name().clone(), expr_v);
5110 let expr_h =
5111 BindingExpression::new_with_span(expr_h, old_root.borrow().to_source_location());
5112 li_h.element().borrow_mut().set_binding(li_h.name().clone(), expr_h);
5113 Some((li_h.clone(), li_v.clone()))
5114 });
5115 new_root.borrow_mut().layout_info_prop = layout_info_prop;
5116 forward_layout_info_with_constraint(&new_root, old_root);
5117
5118 drop(std::mem::take(&mut repeated_element.borrow_mut().base_type));
5121
5122 debug_assert_eq!(Rc::strong_count(&component), 1);
5123
5124 let mut component = Rc::try_unwrap(component).expect("internal compiler error: more than one strong reference left to repeated component when lowering shadow properties");
5125
5126 let old_root = std::mem::replace(&mut component.root_element, new_root.clone());
5127 new_root.borrow_mut().children.push(old_root);
5128
5129 let component = Rc::new(component);
5130 repeated_element.borrow_mut().base_type = ElementType::Component(component.clone());
5131
5132 for elem in elements_with_enclosing_component_reference {
5133 elem.borrow_mut().enclosing_component = Rc::downgrade(&component);
5134 }
5135}
5136
5137pub fn adjust_geometry_for_injected_parent(injected_parent: &ElementRc, old_elem: &ElementRc) {
5140 let mut injected_parent_mut = injected_parent.borrow_mut();
5141 injected_parent_mut.set_binding(
5142 "z".into(),
5143 BindingExpression::new_two_way(
5144 NamedReference::new(old_elem, SmolStr::new_static("z")).into(),
5145 ),
5146 );
5147 injected_parent_mut.property_declarations.insert(
5149 "dummy".into(),
5150 PropertyDeclaration { property_type: Type::LogicalLength, ..Default::default() },
5151 );
5152 let mut old_elem_mut = old_elem.borrow_mut();
5153 injected_parent_mut.default_fill_parent = std::mem::take(&mut old_elem_mut.default_fill_parent);
5154 injected_parent_mut.geometry_props.clone_from(&old_elem_mut.geometry_props);
5155 injected_parent_mut.z_order = old_elem_mut.z_order.take();
5157 drop(injected_parent_mut);
5158 old_elem_mut.geometry_props.as_mut().unwrap().x =
5159 NamedReference::new(injected_parent, SmolStr::new_static("dummy"));
5160 old_elem_mut.geometry_props.as_mut().unwrap().y =
5161 NamedReference::new(injected_parent, SmolStr::new_static("dummy"));
5162}