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