1use std::cell::RefCell;
7use std::collections::BTreeMap;
8use std::fmt::Display;
9use std::rc::Rc;
10
11use itertools::Itertools;
12use smol_str::{SmolStr, ToSmolStr};
13
14use crate::diagnostics::BuildDiagnostics;
15use crate::expression_tree::{BindingExpression, Callable, Expression};
16use crate::langtype::{ElementType, Function, PropertyLookupResult, Type};
17use crate::namedreference::NamedReference;
18use crate::object_tree::{
19 Component, Element, ElementRc, PropertyDeclaration, QualifiedTypeName, find_element_by_id,
20 visit_named_references_in_expression,
21};
22use crate::parser;
23use crate::parser::{SyntaxKind, syntax_nodes};
24use crate::typeregister::TypeRegister;
25
26#[derive(Clone, Debug)]
28struct UsesStatement {
29 interface_name: QualifiedTypeName,
30 child_id: SmolStr,
31 node: syntax_nodes::UsesIdentifier,
32}
33
34impl UsesStatement {
35 fn interface_name_node(&self) -> syntax_nodes::QualifiedName {
37 self.node.QualifiedName()
38 }
39
40 fn child_id_node(&self) -> syntax_nodes::DeclaredIdentifier {
42 self.node.DeclaredIdentifier()
43 }
44
45 fn lookup_interface(
48 &self,
49 tr: &TypeRegister,
50 diag: &mut BuildDiagnostics,
51 ) -> Result<Rc<Component>, ()> {
52 let interface_name = self.interface_name.to_smolstr();
53 match tr.lookup_element(&interface_name) {
54 Ok(element_type) => match element_type {
55 ElementType::Component(component) => {
56 if !component.is_interface() {
57 diag.push_error(
58 format!("'{}' is not an interface", self.interface_name),
59 &self.interface_name_node(),
60 );
61 return Err(());
62 }
63
64 Ok(component)
65 }
66 _ => {
67 diag.push_error(
68 format!("'{}' is not an interface", self.interface_name),
69 &self.interface_name_node(),
70 );
71 Err(())
72 }
73 },
74 Err(error) => {
75 diag.push_error(error, &self.interface_name_node());
76 Err(())
77 }
78 }
79 }
80}
81
82impl From<&syntax_nodes::UsesIdentifier> for UsesStatement {
83 fn from(node: &syntax_nodes::UsesIdentifier) -> UsesStatement {
84 UsesStatement {
85 interface_name: QualifiedTypeName::from_node(
86 node.child_node(SyntaxKind::QualifiedName).unwrap().clone().into(),
87 ),
88 child_id: parser::identifier_text(&node.DeclaredIdentifier()).unwrap_or_default(),
89 node: node.clone(),
90 }
91 }
92}
93
94enum InterfaceUseMode {
95 Implements,
96 Uses,
97}
98
99fn validate_property_declaration_for_interface(
100 mode: InterfaceUseMode,
101 result: &PropertyLookupResult,
102 base_type: &ElementType,
103 interface_name: &dyn Display,
104) -> Result<(), String> {
105 let usage = match mode {
106 InterfaceUseMode::Implements => "implement",
107 InterfaceUseMode::Uses => "use",
108 };
109
110 match result.property_type {
111 Type::Invalid => Ok(()),
112 Type::Callback { .. } => Err(format!(
113 "Cannot {} interface '{}' because '{}' conflicts with an existing callback in '{}'",
114 usage, interface_name, result.resolved_name, base_type
115 )),
116 Type::Function { .. } => Err(format!(
117 "Cannot {} interface '{}' because '{}' conflicts with an existing function in '{}'",
118 usage, interface_name, result.resolved_name, base_type
119 )),
120 _ => Err(format!(
121 "Cannot {} interface '{}' because '{}' conflicts with an existing property in '{}'",
122 usage, interface_name, result.resolved_name, base_type
123 )),
124 }
125}
126
127pub(super) struct ImplementedInterface {
129 implements_specifier: syntax_nodes::ImplementsSpecifier,
130 interface: ElementRc,
131 interface_name: SmolStr,
132}
133
134pub(super) fn get_implemented_interface(
137 e: &Element,
138 node: &syntax_nodes::Element,
139 tr: &TypeRegister,
140 diag: &mut BuildDiagnostics,
141) -> Option<ImplementedInterface> {
142 let parent: syntax_nodes::Component =
143 node.parent().filter(|p| p.kind() == SyntaxKind::Component)?.into();
144
145 let implements_specifier = parent.ImplementsSpecifier()?;
146
147 #[cfg(feature = "slint-sc")]
148 diag.slint_sc_error("'implements' is", &implements_specifier);
149
150 if !diag.enable_experimental && !tr.expose_internal_types {
151 diag.push_error("'implements' is an experimental feature".into(), &implements_specifier);
152 return None;
153 }
154
155 let interface_name =
156 QualifiedTypeName::from_node(implements_specifier.QualifiedName()).to_smolstr();
157
158 match e.base_type.lookup_type_for_child_element(&interface_name, tr) {
159 Ok(ElementType::Component(c)) => {
160 if !c.is_interface() {
161 diag.push_error(
162 format!("Cannot implement {}. It is not an interface", interface_name),
163 &implements_specifier.QualifiedName(),
164 );
165 return None;
166 }
167
168 c.used.set(true);
169 Some(ImplementedInterface {
170 implements_specifier,
171 interface: c.root_element.clone(),
172 interface_name,
173 })
174 }
175 Ok(_) => {
176 diag.push_error(
177 format!("Cannot implement {}. It is not an interface", interface_name),
178 &implements_specifier.QualifiedName(),
179 );
180 None
181 }
182 Err(err) => {
183 diag.push_error(err, &implements_specifier.QualifiedName());
184 None
185 }
186 }
187}
188
189pub(super) fn apply_properties(
192 e: &mut Element,
193 implemented_interface: &Option<ImplementedInterface>,
194 diag: &mut BuildDiagnostics,
195) {
196 let Some(ImplementedInterface { interface, implements_specifier, interface_name }) =
197 implemented_interface
198 else {
199 return;
200 };
201
202 for (unresolved_prop_name, prop_decl) in
203 interface.borrow().property_declarations.iter().filter(|(_, prop_decl)| {
204 !matches!(prop_decl.property_type, Type::Function { .. } | Type::Callback { .. })
206 })
207 {
208 apply_interface_property_declaration(
209 e,
210 unresolved_prop_name,
211 prop_decl,
212 implements_specifier,
213 interface_name,
214 diag,
215 );
216 }
217}
218
219pub(super) fn apply_callbacks(
222 e: &mut Element,
223 implemented_interface: &Option<ImplementedInterface>,
224 diag: &mut BuildDiagnostics,
225) {
226 let Some(ImplementedInterface { interface, implements_specifier, interface_name }) =
227 implemented_interface
228 else {
229 return;
230 };
231
232 for (unresolved_prop_name, prop_decl) in
233 interface.borrow().property_declarations.iter().filter(|(_, prop_decl)| {
234 matches!(prop_decl.property_type, Type::Callback { .. })
236 })
237 {
238 apply_interface_property_declaration(
239 e,
240 unresolved_prop_name,
241 prop_decl,
242 implements_specifier,
243 interface_name,
244 diag,
245 );
246 }
247}
248
249fn apply_interface_property_declaration(
252 e: &mut Element,
253 unresolved_prop_name: &SmolStr,
254 prop_decl: &PropertyDeclaration,
255 implements_specifier: &syntax_nodes::ImplementsSpecifier,
256 interface_name: &SmolStr,
257 diag: &mut BuildDiagnostics,
258) {
259 if matches!(prop_decl.property_type, Type::Invalid) {
260 return;
264 }
265
266 let lookup_result = e.lookup_property(unresolved_prop_name);
267
268 fn find_conflicting_node(
269 e: &mut Element,
270 unresolved_prop_name: &SmolStr,
271 ) -> Option<parser::SyntaxNode> {
272 e.property_declarations.get(unresolved_prop_name).and_then(|decl| decl.node.clone())
273 }
274
275 if lookup_result.property_type != Type::Invalid {
276 if lookup_result.is_local_to_component {
277 let property_type_name = match prop_decl.property_type {
278 Type::Callback { .. } => "callback",
279 Type::Function { .. } => "function",
280 _ => "property",
281 };
282
283 let local_property_node = find_conflicting_node(e, unresolved_prop_name)
284 .expect("Expected local property to have a syntax node");
285
286 diag.push_error(
287 format!(
288 "Conflict with '{}' which declares a {} with the same name",
289 interface_name, property_type_name
290 ),
291 &local_property_node,
292 );
293 return;
294 }
295
296 match property_matches_interface(&lookup_result, prop_decl) {
297 Ok(()) => {
298 return;
300 }
301 Err(error) => {
302 if let Some(local_property_node) = find_conflicting_node(e, unresolved_prop_name) {
305 diag.push_error(
306 format!("Conflict with '{}' which {}", interface_name, error),
307 &local_property_node,
308 );
309 return;
310 }
311 }
312 }
313 }
314
315 if let Err(message) = validate_property_declaration_for_interface(
316 InterfaceUseMode::Implements,
317 &lookup_result,
318 &e.base_type,
319 &interface_name,
320 ) {
321 diag.push_error(message, &implements_specifier.QualifiedName());
322 return;
323 }
324
325 e.property_declarations.insert(unresolved_prop_name.clone(), prop_decl.clone());
326}
327
328pub(super) fn apply_default_property_values(
330 e: &ElementRc,
331 implemented_interface: &Option<ImplementedInterface>,
332) {
333 let Some(ImplementedInterface { interface, .. }) = implemented_interface else {
334 return;
335 };
336
337 let interface_root = interface.clone();
338
339 let bindings_to_apply: Vec<_> = interface
341 .borrow()
342 .property_declarations
343 .iter()
344 .filter(|(_, prop_decl)| {
345 !matches!(prop_decl.property_type, Type::Function { .. } | Type::Callback { .. })
347 })
348 .filter_map(|(property_name, _)| {
349 interface
350 .borrow()
351 .bindings
352 .get(property_name)
353 .map(|binding| (property_name.clone(), binding.clone()))
354 })
355 .filter(|(property_name, _)| {
356 !e.borrow().is_binding_set(property_name, false)
360 })
361 .collect();
362
363 for (property_name, binding) in bindings_to_apply {
364 visit_named_references_in_expression(&mut binding.borrow_mut().expression, &mut |nr| {
366 if Rc::ptr_eq(&nr.element(), &interface_root) {
367 *nr = NamedReference::new(e, nr.name().clone());
368 }
369 });
370 e.borrow_mut().bindings.insert(property_name, binding);
371 }
372}
373
374pub(super) fn validate_function_implementations(
376 e: &Element,
377 implemented_interface: &Option<ImplementedInterface>,
378 diag: &mut BuildDiagnostics,
379) {
380 let Some(ImplementedInterface { interface, implements_specifier, interface_name }) =
381 implemented_interface
382 else {
383 return;
384 };
385
386 for (function_name, function_property_decl) in interface
387 .borrow()
388 .property_declarations
389 .iter()
390 .filter(|(_, prop_decl)| matches!(prop_decl.property_type, Type::Function { .. }))
391 {
392 let Type::Function(ref function_declaration) = function_property_decl.property_type else {
393 debug_assert!(false, "Non-functions should have been filtered out already");
394 continue;
395 };
396
397 let push_interface_error = |diag: &mut BuildDiagnostics, is_local_to_component, error| {
398 if is_local_to_component {
399 let source = e
400 .property_declarations
401 .get(function_name)
402 .and_then(|decl| decl.node.clone())
403 .map_or_else(
404 || parser::NodeOrToken::Node(implements_specifier.QualifiedName().into()),
405 parser::NodeOrToken::Node,
406 );
407 diag.push_error(error, &source);
408 } else {
409 diag.push_error(error, &implements_specifier.QualifiedName());
410 }
411 };
412
413 let found_function = e.lookup_property(function_name);
414 let function_impl = match found_function.property_type {
415 Type::Invalid => {
416 diag.push_error(
417 format!("Missing implementation of function '{}'", function_name),
418 &implements_specifier.QualifiedName(),
419 );
420 None
421 }
422 Type::Function(function) => Some(function.clone()),
423 _ => {
424 push_interface_error(
425 diag,
426 found_function.is_local_to_component,
427 format!(
428 "Cannot override '{}' from interface '{}'",
429 function_name, interface_name
430 ),
431 );
432 None
433 }
434 };
435 let Some(function_impl) = function_impl else { continue };
436
437 match (function_property_decl.pure, found_function.declared_pure) {
438 (Some(true), Some(false)) | (Some(true), None) => push_interface_error(
439 diag,
440 found_function.is_local_to_component,
441 format!(
442 "Implementation of pure function '{}' from interface '{}' cannot be impure",
443 function_name, interface_name
444 ),
445 ),
446 _ => {
447 }
449 }
450
451 if function_property_decl.visibility != found_function.property_visibility {
452 push_interface_error(
453 diag,
454 found_function.is_local_to_component,
455 format!(
456 "Incorrect visibility for implementation of '{}' from interface '{}'. Expected '{}'",
457 function_name, interface_name, function_property_decl.visibility,
458 ),
459 );
460 }
461
462 if function_impl.args != function_declaration.args {
463 let display_args = |args: &Vec<Type>| -> SmolStr {
464 args.iter().map(|t| t.to_string()).join(", ").into()
465 };
466
467 push_interface_error(
468 diag,
469 found_function.is_local_to_component,
470 format!(
471 "Incorrect arguments for implementation of '{}' from interface '{}'. Expected ({}) but got ({})",
472 function_name,
473 interface_name,
474 display_args(&function_declaration.args),
475 display_args(&function_impl.args),
476 ),
477 );
478 }
479
480 if function_impl.return_type != function_declaration.return_type {
481 push_interface_error(
482 diag,
483 found_function.is_local_to_component,
484 format!(
485 "Incorrect return type for implementation of '{}' from interface '{}'. Expected '{}' but got '{}'",
486 function_name,
487 interface_name,
488 function_declaration.return_type,
489 function_impl.return_type,
490 ),
491 );
492 }
493 }
494}
495
496pub(super) fn apply_uses_statement(
497 e: &ElementRc,
498 uses_specifier: Option<syntax_nodes::UsesSpecifier>,
499 tr: &TypeRegister,
500 diag: &mut BuildDiagnostics,
501) {
502 let Some(uses_specifier) = uses_specifier else {
503 return;
504 };
505
506 #[cfg(feature = "slint-sc")]
507 diag.slint_sc_error("'uses' is", &uses_specifier);
508
509 if !diag.enable_experimental && !tr.expose_internal_types {
510 diag.push_error("'uses' is an experimental feature".into(), &uses_specifier);
511 return;
512 }
513
514 let uses_statements = gather_valid_uses_statements(e, tr, diag, uses_specifier);
515 let uses_statements = filter_conflicting_uses_statements(diag, uses_statements);
516
517 for ValidUsesStatement { uses_statement, interface, child } in uses_statements {
518 for (name, prop_decl) in interface.borrow().property_declarations.iter() {
519 let lookup_result = e.borrow().base_type.lookup_property(name);
520 if let Err(message) = validate_property_declaration_for_interface(
521 InterfaceUseMode::Uses,
522 &lookup_result,
523 &e.borrow().base_type,
524 &uses_statement.interface_name,
525 ) {
526 diag.push_error(message, &uses_statement.interface_name_node());
527 continue;
528 }
529
530 let mut prop_decl = prop_decl.clone();
533 prop_decl.node = Some(uses_statement.interface_name_node().into());
534
535 if let Some(existing_property) =
536 e.borrow_mut().property_declarations.insert(name.clone(), prop_decl.clone())
537 {
538 let source = existing_property
539 .node
540 .as_ref()
541 .and_then(|node| node.child_node(SyntaxKind::DeclaredIdentifier))
542 .and_then(|node| node.child_token(SyntaxKind::Identifier))
543 .map_or_else(
544 || parser::NodeOrToken::Node(uses_statement.child_id_node().into()),
545 parser::NodeOrToken::Token,
546 );
547
548 diag.push_error(
549 format!("Cannot override '{}' from '{}'", name, uses_statement.interface_name),
550 &source,
551 );
552 continue;
553 }
554
555 let existing_binding = match &prop_decl.property_type {
556 Type::Function(func) => {
557 apply_uses_statement_function_binding(e, &child, name, func)
558 }
559 _ => e.borrow_mut().bindings.insert(
560 name.clone(),
561 BindingExpression::new_two_way(
562 NamedReference::new(&child, name.clone()).into(),
563 )
564 .into(),
565 ),
566 };
567
568 if let Some(existing_binding) = existing_binding {
569 let message = format!(
570 "Cannot override binding for '{}' from interface '{}'",
571 name, uses_statement.interface_name
572 );
573 if let Some(location) = &existing_binding.borrow().span {
574 diag.push_error(message, location);
575 } else {
576 diag.push_error(message, &uses_statement.interface_name_node());
577 }
578 }
579 }
580 }
581}
582
583struct ValidUsesStatement {
585 uses_statement: UsesStatement,
586 interface: ElementRc,
587 child: ElementRc,
588}
589
590fn gather_valid_uses_statements(
593 e: &Rc<RefCell<Element>>,
594 tr: &TypeRegister,
595 diag: &mut BuildDiagnostics,
596 uses_specifier: syntax_nodes::UsesSpecifier,
597) -> Vec<ValidUsesStatement> {
598 let mut valid_uses_statements: Vec<ValidUsesStatement> = Vec::new();
599
600 for uses_identifier_node in uses_specifier.UsesIdentifier() {
601 let uses_statement: UsesStatement = (&uses_identifier_node).into();
602 let Ok(interface_component) = uses_statement.lookup_interface(tr, diag) else {
603 continue;
604 };
605
606 let Some(child) = find_element_by_id(e, &uses_statement.child_id) else {
607 diag.push_error(
608 format!("'{}' does not exist", uses_statement.child_id),
609 &uses_statement.child_id_node(),
610 );
611 continue;
612 };
613
614 let interface = interface_component.root_element.clone();
615 if !element_implements_interface(&child, &interface, &uses_statement, diag) {
616 continue;
617 }
618
619 valid_uses_statements.push(ValidUsesStatement { uses_statement, interface, child });
620 }
621 valid_uses_statements
622}
623
624fn filter_conflicting_uses_statements(
628 diag: &mut BuildDiagnostics,
629 uses_statements: Vec<ValidUsesStatement>,
630) -> Vec<ValidUsesStatement> {
631 let mut seen_interfaces: Vec<SmolStr> = Vec::new();
632 let mut seen_interface_api: BTreeMap<SmolStr, SmolStr> = BTreeMap::new();
633 let valid_uses_statements: Vec<ValidUsesStatement> = uses_statements
634 .into_iter()
635 .filter(|vus| {
636 let interface_name = vus.uses_statement.interface_name.to_smolstr();
637 if seen_interfaces.contains(&interface_name) {
638 diag.push_error(
639 format!("'{}' is used multiple times", vus.uses_statement.interface_name),
640 &vus.uses_statement.interface_name_node(),
641 );
642 return false;
643 }
644 seen_interfaces.push(interface_name.clone());
645
646 let mut valid = true;
647 for (prop_name, _) in vus.interface.borrow().property_declarations.iter() {
648 if let Some(existing_interface) = seen_interface_api.get(prop_name) {
649 diag.push_error(
650 format!(
651 "'{}' occurs in '{}' and '{}'",
652 prop_name, vus.uses_statement.interface_name, existing_interface
653 ),
654 &vus.uses_statement.interface_name_node(),
655 );
656 valid = false;
657 } else {
658 seen_interface_api.insert(prop_name.clone(), interface_name.clone());
659 }
660 }
661 valid
662 })
663 .collect();
664 valid_uses_statements
665}
666
667fn element_implements_interface(
669 element: &ElementRc,
670 interface: &ElementRc,
671 uses_statement: &UsesStatement,
672 diag: &mut BuildDiagnostics,
673) -> bool {
674 let mut valid = true;
675 let mut check = |property_name: &SmolStr, property_declaration: &PropertyDeclaration| {
676 let lookup_result = element.borrow().lookup_property(property_name);
677 if let Err(e) = property_matches_interface(&lookup_result, property_declaration) {
678 diag.push_error(
679 format!(
680 "'{}' does not implement '{}' from '{}' - {}",
681 uses_statement.child_id, property_name, uses_statement.interface_name, e
682 ),
683 &uses_statement.child_id_node(),
684 );
685 valid = false;
686 }
687 };
688
689 for (property_name, property_declaration) in interface.borrow().property_declarations.iter() {
690 check(property_name, property_declaration);
691 }
692
693 valid
694}
695
696fn property_matches_interface(
698 property: &PropertyLookupResult,
699 interface_declaration: &PropertyDeclaration,
700) -> Result<(), String> {
701 if property.property_type == Type::Invalid {
702 return Err("not found".into());
703 }
704
705 let mut errors = Vec::new();
706
707 if property.property_type != interface_declaration.property_type {
708 errors.push(format!("type: '{}'", interface_declaration.property_type));
709 }
710
711 if property.property_visibility != interface_declaration.visibility {
712 errors.push(format!("visibility: '{}'", interface_declaration.visibility));
713 }
714
715 if property.declared_pure.unwrap_or(false) != interface_declaration.pure.unwrap_or(false) {
716 errors
717 .push(format!("purity declaration: '{}'", interface_declaration.pure.unwrap_or(false)));
718 }
719
720 if errors.is_empty() {
721 Ok(())
722 } else {
723 Err(format!("expected {}", errors.into_iter().join(", ")))
724 }
725}
726
727fn apply_uses_statement_function_binding(
730 e: &ElementRc,
731 child: &ElementRc,
732 name: &SmolStr,
733 func: &Rc<Function>,
734) -> Option<RefCell<BindingExpression>> {
735 let args_expr: Vec<Expression> = func
737 .args
738 .iter()
739 .enumerate()
740 .map(|(i, ty)| Expression::FunctionParameterReference { index: i, ty: ty.clone() })
741 .collect();
742
743 let call_expr = Expression::FunctionCall {
745 function: Callable::Function(NamedReference::new(child, name.clone())),
746 arguments: args_expr,
747 source_location: None,
748 };
749
750 let body = Expression::CodeBlock(vec![call_expr]);
752 e.borrow_mut().bindings.insert(name.clone(), RefCell::new(BindingExpression::from(body)))
753}