1use std::collections::BTreeMap;
7use std::rc::Rc;
8use std::sync::Arc;
9
10use itertools::Itertools;
11use smol_str::SmolStr;
12
13use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
14use crate::expression_tree::{BindingExpression, Callable, Expression};
15use crate::langtype::{ElementType, Function, PropertyLookupMode, PropertyLookupResult, Type};
16use crate::namedreference::NamedReference;
17use crate::object_tree::{
18 Element, ElementRc, PropertyDeclaration, PropertyVisibility, QualifiedTypeName,
19 find_element_by_id,
20};
21use crate::parser::{self, SyntaxNode, SyntaxToken};
22use crate::parser::{SyntaxKind, syntax_nodes};
23use crate::reject_experimental_feature;
24use crate::typeregister::TypeRegister;
25
26fn check_property_declaration_conflicts(
27 result: &PropertyLookupResult,
28 base_type: &ElementType,
29) -> Result<(), String> {
30 match result.property_type {
31 Type::Invalid => Ok(()),
32 Type::Callback { .. } => Err(format!(
33 "- '{}' conflicts with an existing callback in '{}'",
34 result.resolved_name, base_type
35 )),
36 Type::Function { .. } => Err(format!(
37 "- '{}' conflicts with an existing function in '{}'",
38 result.resolved_name, base_type
39 )),
40 _ => Err(format!(
41 "- '{}' conflicts with an existing property in '{}'",
42 result.resolved_name, base_type
43 )),
44 }
45}
46
47#[derive(Debug, PartialEq)]
48pub(super) enum ImplementBinding {
49 OnSelf,
50 OnChild {
51 child_id: SmolStr,
53 child_name: SmolStr,
55 },
56}
57
58impl ImplementBinding {
59 fn from_target(target_id: &SmolStr, target_name: &SmolStr) -> ImplementBinding {
60 if target_id.as_str() == "self" {
61 ImplementBinding::OnSelf
62 } else {
63 ImplementBinding::OnChild {
64 child_id: target_id.clone(),
65 child_name: target_name.clone(),
66 }
67 }
68 }
69}
70
71pub(super) struct ImplementedInterface {
72 node: syntax_nodes::ImplementStatement,
73 interface: ElementRc,
74 interface_name: SmolStr,
75 binding: ImplementBinding,
76}
77
78fn resolve_implement_statement(
79 element: &Element,
80 node: syntax_nodes::ImplementStatement,
81 type_register: &TypeRegister,
82 diagnostics: &mut BuildDiagnostics,
83) -> Option<ImplementedInterface> {
84 #[cfg(feature = "slint-sc")]
85 diagnostics.slint_sc_error("'implement' is", &node);
86
87 if reject_experimental_feature(diagnostics, type_register, "implement", &node) {
88 return None;
89 }
90
91 let qualified_name = node.QualifiedName();
92 let interface_name = QualifiedTypeName::from_node(qualified_name.clone()).to_smolstr();
93 let target_name =
94 node.DeclaredIdentifier().child_text(SyntaxKind::Identifier).unwrap_or_default();
95 let target_id = parser::normalize_identifier(&target_name);
96
97 if let Some(target) = match target_id.as_str() {
98 "parent" => Some("a parent element"),
99 "root" => Some("the root element; use 'self' instead"),
100 _ => None,
101 } {
102 diagnostics.push_error(
103 format!("Cannot implement an interface based on {}", target),
104 &node.DeclaredIdentifier(),
105 );
106 return None;
107 }
108
109 match element.base_type.lookup_type_for_child_element(&interface_name, type_register) {
110 Ok(ElementType::Component(c)) => {
111 if !c.is_interface() {
112 diagnostics.push_error(
113 format!("Cannot implement {}. It is not an interface", interface_name),
114 &qualified_name,
115 );
116 return None;
117 }
118
119 c.used.set(true);
120 Some(ImplementedInterface {
121 node,
122 interface: c.root_element.clone(),
123 interface_name,
124 binding: ImplementBinding::from_target(&target_id, &target_name),
125 })
126 }
127 Ok(_) => {
128 let message = match type_register.lookup_element(&interface_name) {
134 Err(context_restricted_message) => context_restricted_message,
135 Ok(_) => format!("Cannot implement {}. It is not an interface", interface_name),
136 };
137 diagnostics.push_error(message, &qualified_name);
138 None
139 }
140 Err(err) => {
141 diagnostics.push_error(err, &qualified_name);
142 None
143 }
144 }
145}
146
147fn filter_conflicting_implement_statements(
148 diagnostics: &mut BuildDiagnostics,
149 statements: Vec<ImplementedInterface>,
150) -> Vec<ImplementedInterface> {
151 let mut seen_interfaces: Vec<ElementRc> = Vec::new();
152 let mut seen_interface_api: BTreeMap<SmolStr, SmolStr> = BTreeMap::new();
153 statements
154 .into_iter()
155 .filter(|stmt| {
156 if seen_interfaces.iter().any(|seen| Rc::ptr_eq(seen, &stmt.interface)) {
159 diagnostics.push_error(
160 format!("'{}' is implemented multiple times", stmt.interface_name),
161 &stmt.node,
162 );
163 return false;
164 }
165 seen_interfaces.push(stmt.interface.clone());
166
167 let mut valid = true;
168 for prop_name in stmt.interface.borrow().property_declarations.keys() {
169 if let Some(existing_interface) = seen_interface_api.get(prop_name) {
170 diagnostics.push_error(
171 format!(
172 "'{}' occurs in '{}' and '{}'",
173 prop_name, stmt.interface_name, existing_interface
174 ),
175 &stmt.node.QualifiedName(),
176 );
177 valid = false;
178 } else {
179 seen_interface_api.insert(prop_name.clone(), stmt.interface_name.clone());
180 }
181 }
182 valid
183 })
184 .collect()
185}
186
187pub(super) fn get_implemented_interfaces(
188 element: &Element,
189 node: &syntax_nodes::Element,
190 type_register: &TypeRegister,
191 diagnostics: &mut BuildDiagnostics,
192) -> (Vec<ImplementedInterface>, Vec<ImplementedInterface>) {
193 let resolved: Vec<ImplementedInterface> = node
194 .ImplementStatement()
195 .filter_map(|stmt| resolve_implement_statement(element, stmt, type_register, diagnostics))
196 .collect();
197
198 let filtered = filter_conflicting_implement_statements(diagnostics, resolved);
199
200 let mut self_interfaces = Vec::new();
201 let mut child_implements = Vec::new();
202 for stmt in filtered {
203 if stmt.binding == ImplementBinding::OnSelf {
204 self_interfaces.push(stmt);
205 } else {
206 child_implements.push(stmt);
207 }
208 }
209 (self_interfaces, child_implements)
210}
211
212pub(super) fn disallow_implement_in_non_root(
213 node: &syntax_nodes::Element,
214 type_register: &TypeRegister,
215 diagnostics: &mut BuildDiagnostics,
216) {
217 for stmt in node.ImplementStatement() {
218 if reject_experimental_feature(diagnostics, type_register, "implement", &stmt) {
219 continue;
220 }
221 diagnostics.push_error("'implement' is only allowed in the root element".into(), &stmt);
222 }
223}
224
225pub(super) fn validate_self_implement_statements(
226 element: &Element,
227 implemented_interfaces: &[ImplementedInterface],
228 diagnostics: &mut BuildDiagnostics,
229) {
230 for ImplementedInterface { interface, node, interface_name, binding } in implemented_interfaces
231 {
232 validate_interface_implementation(
233 element,
234 interface,
235 interface_name,
236 &node.QualifiedName(),
237 binding,
238 diagnostics,
239 );
240 }
241}
242
243struct NoteWithSource {
244 note: String,
245 source: SourceLocation,
246}
247
248struct InterfaceMemberDiagnostics {
249 error: String,
250 notes: Vec<NoteWithSource>,
251}
252
253impl From<String> for InterfaceMemberDiagnostics {
254 fn from(error: String) -> Self {
255 Self { error, notes: Default::default() }
256 }
257}
258
259enum DeclarationAnchor {
260 Name,
261 PropertyType,
262 Argument(usize),
264 ReturnType,
265 Visibility(PropertyVisibility),
266 Purity,
267}
268
269impl DeclarationAnchor {
270 fn source_location(&self, declaration: &SyntaxNode) -> SourceLocation {
271 self.narrow(declaration)
272 .or_else(|| {
273 Some(declaration.child_node(SyntaxKind::DeclaredIdentifier)?.to_source_location())
274 })
275 .unwrap_or_else(|| declaration.to_source_location())
276 }
277
278 fn narrow(&self, declaration: &SyntaxNode) -> Option<SourceLocation> {
279 let node = match self {
280 Self::Name => return None,
281 Self::PropertyType => declaration.child_node(SyntaxKind::Type)?,
282 Self::Argument(index) => parameter_type(declaration, *index)?,
283 Self::ReturnType => declaration.child_node(SyntaxKind::ReturnType)?,
284 Self::Visibility(visibility) => {
285 return Some(
286 keyword_token(declaration, &visibility.to_string())?.to_source_location(),
287 );
288 }
289 Self::Purity => {
290 return Some(keyword_token(declaration, "pure")?.to_source_location());
291 }
292 };
293 Some(node.to_source_location())
294 }
295}
296
297fn parameter_type(declaration: &SyntaxNode, index: usize) -> Option<SyntaxNode> {
298 let parameter_kind = match declaration.kind() {
299 SyntaxKind::Function => SyntaxKind::ArgumentDeclaration,
300 SyntaxKind::CallbackDeclaration => SyntaxKind::CallbackDeclarationParameter,
301 _ => return None,
302 };
303 declaration
304 .children()
305 .filter(|child| child.kind() == parameter_kind)
306 .nth(index)?
307 .child_node(SyntaxKind::Type)
308}
309
310fn keyword_token(declaration: &SyntaxNode, keyword: &str) -> Option<SyntaxToken> {
313 declaration.children_with_tokens().filter_map(|child| child.into_token()).find(|token| {
314 token.kind() == SyntaxKind::Identifier
315 && parser::normalize_identifier(token.text()) == keyword
316 })
317}
318
319struct MemberViolation {
320 error: String,
321 expected_syntax: String,
322 anchor: DeclarationAnchor,
323}
324
325fn validate_interface_implementation(
326 element: &Element,
327 interface: &ElementRc,
328 interface_name: &SmolStr,
329 node: &SyntaxNode,
330 binding: &ImplementBinding,
331 diagnostics: &mut BuildDiagnostics,
332) -> bool {
333 let mut errors = Vec::new();
334 let mut notes = Vec::new();
335 for (member_name, member_declaration) in interface.borrow().property_declarations.iter() {
336 if let Some(mut conflict) = validate_interface_member_implementation(
337 element,
338 member_name,
339 member_declaration,
340 interface_name,
341 binding,
342 ) {
343 errors.push(conflict.error);
344 notes.append(&mut conflict.notes);
345 };
346 }
347
348 if !errors.is_empty() {
349 let based_on = match binding {
350 ImplementBinding::OnChild { child_name, .. } => {
351 format!(" based on '{child_name}'")
352 }
353 ImplementBinding::OnSelf => String::new(),
354 };
355 diagnostics.push_error(
356 format!("Cannot implement '{interface_name}'{based_on}.\n{}", errors.join("\n")),
357 node,
358 );
359
360 for note in notes {
361 diagnostics.push_note_with_span(note.note, note.source);
362 }
363 }
364 errors.is_empty()
365}
366
367fn validate_interface_member_implementation(
368 element: &Element,
369 member_name: &SmolStr,
370 interface_member: &PropertyDeclaration,
371 interface_name: &SmolStr,
372 binding: &ImplementBinding,
373) -> Option<InterfaceMemberDiagnostics> {
374 if matches!(interface_member.property_type, Type::Invalid) {
375 return None;
379 }
380
381 let lookup_result = element.lookup_property(member_name, PropertyLookupMode::ComponentLocal);
382 let Err(violations) =
383 property_matches_interface(&lookup_result, interface_member, member_name, binding)
384 else {
385 return None;
386 };
387
388 let joined_errors = violations.iter().map(|v| v.error.as_str()).join("\n");
389 let mut conflicts = InterfaceMemberDiagnostics::from(joined_errors);
390
391 let source = element
394 .declaration(member_name)
395 .and_then(|(_, declaration)| declaration.node.clone())
396 .or_else(|| element.base_type.property_declaration_node(member_name));
397 if lookup_result.is_valid()
398 && let Some(source) = source
399 {
400 conflicts.notes = violations
401 .into_iter()
402 .map(|violation| NoteWithSource {
403 note: declared_here_note(
404 member_name,
405 interface_name,
406 &violation.expected_syntax,
407 &source,
408 ),
409 source: violation.anchor.source_location(&source),
410 })
411 .collect();
412 }
413 Some(conflicts)
414}
415
416pub(super) fn apply_child_implement_statements(
417 element: &ElementRc,
418 child_implements: Vec<ImplementedInterface>,
419 diagnostics: &mut BuildDiagnostics,
420) {
421 for ImplementedInterface { node, interface, interface_name, binding } in child_implements {
422 debug_assert_ne!(binding, ImplementBinding::OnSelf);
423 let ImplementBinding::OnChild { child_id, child_name } = &binding else {
424 continue;
425 };
426 let Some(child) = find_element_by_id(element, child_id) else {
427 diagnostics
428 .push_error(format!("'{}' does not exist", child_name), &node.DeclaredIdentifier());
429 continue;
430 };
431
432 if !validate_interface_implementation(
433 &child.borrow(),
434 &interface,
435 &interface_name,
436 &node.DeclaredIdentifier(),
437 &binding,
438 diagnostics,
439 ) {
440 continue;
441 }
442
443 let mut conflicts = Vec::new();
444 let mut notes = Vec::new();
445 for (name, prop_decl) in interface.borrow().property_declarations.iter() {
446 let lookup_result = element
447 .borrow()
448 .base_type
449 .lookup_property(name, PropertyLookupMode::ComponentLocal);
450 if let Err(message) =
451 check_property_declaration_conflicts(&lookup_result, &element.borrow().base_type)
452 {
453 conflicts.push(message);
454 if let Some(source) = element.borrow().property_declaration_node(name) {
455 notes.push(NoteWithSource {
456 note: declared_here_note(
457 name,
458 &interface_name,
459 &syntax_for_declaration(prop_decl, name),
460 &source,
461 ),
462 source: DeclarationAnchor::Name.source_location(&source),
463 });
464 }
465 continue;
466 }
467
468 let mut prop_decl = prop_decl.clone();
471 prop_decl.node = Some(node.QualifiedName().into());
472
473 let shadowing =
475 element.borrow().declaration(name).map(|(_, declaration)| declaration.node.clone());
476 let existing_node = shadowing.or_else(|| {
477 element
478 .borrow_mut()
479 .property_declarations
480 .insert(name.clone(), prop_decl.clone())
481 .map(|existing| existing.node.clone())
482 });
483 if let Some(existing_node) = existing_node {
484 let source = existing_node
485 .as_ref()
486 .and_then(|node| node.child_node(SyntaxKind::DeclaredIdentifier))
487 .and_then(|node| node.child_token(SyntaxKind::Identifier))
488 .map_or_else(
489 || parser::NodeOrToken::Node(node.DeclaredIdentifier().into()),
490 parser::NodeOrToken::Token,
491 );
492
493 diagnostics.push_error(
494 format!("Cannot override '{}' from '{}'", name, interface_name),
495 &source,
496 );
497 diagnostics.push_note(
498 declares_as_note(
499 &interface_name,
500 name,
501 &syntax_for_declaration(&prop_decl, name),
502 ),
503 &node.QualifiedName(),
504 );
505 continue;
506 }
507
508 let existing_binding = match &prop_decl.property_type {
509 Type::Function(func) => {
510 apply_uses_statement_function_binding(element, &child, name, func)
511 }
512 _ => element.borrow_mut().set_binding(
513 name.clone(),
514 BindingExpression::new_two_way(member_reference(&child, name).into()),
515 ),
516 };
517 debug_assert!(
518 existing_binding.is_none(),
519 "Duplicate bindings should have been caught earlier"
520 );
521 }
522
523 if !conflicts.is_empty() {
524 diagnostics.push_error(
525 format!(
526 "Cannot implement '{interface_name}' based on '{child_id}'.\n{}",
527 conflicts.join("\n")
528 ),
529 &node.QualifiedName(),
530 );
531 for note in notes {
532 diagnostics.push_note(note.note, ¬e.source);
533 }
534 }
535 }
536}
537
538fn purity_description(purity: &Option<bool>) -> &str {
539 if purity.unwrap_or(false) { "pure " } else { "" }
540}
541
542fn syntax_for(
543 name: &SmolStr,
544 property_type: &Type,
545 pure: &Option<bool>,
546 visibility: &PropertyVisibility,
547) -> String {
548 match property_type {
549 Type::Function(function) => {
550 format!("{}{} function {name}{} {{ }}", purity_description(pure), visibility, function)
551 }
552 Type::Callback(function) => {
553 format!("{}callback {name}{};", purity_description(pure), function)
554 }
555 _ if property_type.is_property_type() => {
556 format!("{} property <{}> {name};", visibility, property_type)
557 }
558 _ => name.to_string(),
559 }
560}
561
562fn syntax_for_declaration(interface_declaration: &PropertyDeclaration, name: &SmolStr) -> String {
563 syntax_for(
564 name,
565 &interface_declaration.property_type,
566 &interface_declaration.pure,
567 &interface_declaration.visibility,
568 )
569}
570
571fn syntax_for_lookup_result(lookup_result: &PropertyLookupResult, name: &SmolStr) -> String {
572 syntax_for(
573 name,
574 &lookup_result.property_type,
575 &lookup_result.declared_pure,
576 &lookup_result.property_visibility,
577 )
578}
579
580fn missing_type_error(name: &SmolStr, interface_declaration: &PropertyDeclaration) -> String {
581 format!("- missing '{}'", syntax_for_declaration(interface_declaration, name))
582}
583
584fn declares_as_note(interface_name: &SmolStr, name: &SmolStr, expected_syntax: &String) -> String {
585 format!("'{interface_name}' declares '{name}' as '{expected_syntax}'")
586}
587
588fn declaring_component_name(declaration: SyntaxNode) -> Option<SmolStr> {
589 std::iter::successors(Some(declaration), SyntaxNode::parent).find_map(|node| {
590 match node.kind() {
591 SyntaxKind::SubElement => node.child_text(SyntaxKind::Identifier),
592 SyntaxKind::Component => {
593 parser::identifier_text(&node.child_node(SyntaxKind::DeclaredIdentifier)?)
594 }
595 _ => None,
596 }
597 })
598}
599
600fn declared_here_note(
601 member_name: &SmolStr,
602 interface_name: &SmolStr,
603 expected_syntax: &String,
604 property_declaration_source: &SyntaxNode,
605) -> String {
606 let Some(declaring_type) = declaring_component_name(property_declaration_source.clone()) else {
607 return declares_as_note(interface_name, member_name, expected_syntax);
608 };
609 format!(
610 "'{declaring_type}' declares '{member_name}' here, '{interface_name}' expects '{expected_syntax}'"
611 )
612}
613
614fn signature_anchor(interface_declaration: &Function, declaration: &Function) -> DeclarationAnchor {
615 if let Some(index) = interface_declaration
616 .args
617 .iter()
618 .zip(declaration.args.iter())
619 .position(|(expected, declared)| expected != declared)
620 {
621 DeclarationAnchor::Argument(index)
622 } else if declaration.args.len() > interface_declaration.args.len() {
623 DeclarationAnchor::Argument(interface_declaration.args.len())
624 } else if declaration.args.len() < interface_declaration.args.len() {
625 DeclarationAnchor::Name
626 } else {
627 DeclarationAnchor::ReturnType
628 }
629}
630
631fn function_matches_for_interface(lhs: &Function, rhs: &Function) -> bool {
633 lhs.return_type == rhs.return_type && lhs.args == rhs.args
634}
635
636fn property_type_matches_for_interface(lhs: &Type, rhs: &Type) -> bool {
637 match (lhs, rhs) {
638 (Type::Callback(lhs), Type::Callback(rhs)) => function_matches_for_interface(lhs, rhs),
639 (Type::Function(lhs), Type::Function(rhs)) => function_matches_for_interface(lhs, rhs),
640 _ => lhs == rhs,
641 }
642}
643
644fn property_matches_interface(
645 property: &PropertyLookupResult,
646 interface_declaration: &PropertyDeclaration,
647 name: &SmolStr,
648 binding: &ImplementBinding,
649) -> Result<(), Vec<MemberViolation>> {
650 let expected_syntax = syntax_for_declaration(interface_declaration, name);
651 if property.property_type == Type::Invalid {
652 return Err(vec![MemberViolation {
653 error: missing_type_error(name, interface_declaration),
654 expected_syntax,
655 anchor: DeclarationAnchor::Name,
656 }]);
657 }
658
659 let mut errors = Vec::new();
660
661 let member_name = if let ImplementBinding::OnChild { child_name, .. } = binding {
662 format!("{child_name}.{name}")
663 } else {
664 name.to_string()
665 };
666
667 if !property_type_matches_for_interface(
668 &property.property_type,
669 &interface_declaration.property_type,
670 ) {
671 let is_same_type = match (&interface_declaration.property_type, &property.property_type) {
672 (Type::Callback(..), Type::Callback(..)) | (Type::Function(..), Type::Function(..)) => {
673 true
674 }
675 (lhs, rhs) => lhs.is_property_type() && rhs.is_property_type(),
676 };
677
678 let property_description = |property_type: &Type| format!("a '{}' property", property_type);
679
680 let expected = if is_same_type && interface_declaration.property_type.is_property_type() {
681 property_description(&interface_declaration.property_type)
682 } else {
683 format!("'{}'", syntax_for_declaration(interface_declaration, name))
684 };
685
686 let actual = if property.property_type.is_property_type() {
687 property_description(&property.property_type)
688 } else {
689 format!("'{}'", syntax_for_lookup_result(property, name))
690 };
691
692 let error = format!("- '{member_name}' must be {expected} (found {actual})");
693
694 if !is_same_type {
695 return Err(vec![MemberViolation {
697 error,
698 expected_syntax,
699 anchor: DeclarationAnchor::Name,
700 }]);
701 }
702
703 let anchor = match (&interface_declaration.property_type, &property.property_type) {
704 (Type::Callback(expected), Type::Callback(declared))
705 | (Type::Function(expected), Type::Function(declared)) => {
706 signature_anchor(expected, declared)
707 }
708
709 (_, _) => DeclarationAnchor::PropertyType,
710 };
711 errors.push(MemberViolation { error, expected_syntax: expected_syntax.clone(), anchor });
712 }
713
714 if property.property_visibility != interface_declaration.visibility {
715 errors.push(MemberViolation {
716 error: format!(
717 "- '{member_name}' must be '{}' (found '{}')",
718 interface_declaration.visibility, property.property_visibility
719 ),
720 expected_syntax: expected_syntax.clone(),
721 anchor: DeclarationAnchor::Visibility(property.property_visibility),
722 });
723 }
724
725 if interface_declaration.pure.unwrap_or(false) && !property.declared_pure.unwrap_or(false) {
727 errors.push(MemberViolation {
728 error: format!("- '{member_name}' must be 'pure'"),
729 expected_syntax,
730 anchor: DeclarationAnchor::Purity,
731 });
732 }
733
734 if errors.is_empty() { Ok(()) } else { Err(errors) }
735}
736
737fn member_reference(elem: &ElementRc, name: &SmolStr) -> NamedReference {
740 let internal_name =
741 elem.borrow().declaration(name).map_or_else(|| name.clone(), |(n, _)| n.clone());
742 NamedReference::new(elem, internal_name)
743}
744
745fn apply_uses_statement_function_binding(
746 element: &ElementRc,
747 child: &ElementRc,
748 name: &SmolStr,
749 function: &Arc<Function>,
750) -> Option<BindingExpression> {
751 let args_expr: Vec<Expression> = function
752 .args
753 .iter()
754 .enumerate()
755 .map(|(i, ty)| Expression::FunctionParameterReference { index: i, ty: ty.clone() })
756 .collect();
757
758 let call_expr = Expression::FunctionCall {
759 function: Callable::Function(member_reference(child, name)),
760 arguments: args_expr,
761 source_location: None,
762 };
763
764 let body = Expression::CodeBlock(vec![call_expr]);
765 element.borrow_mut().set_binding(name.clone(), BindingExpression::from(body))
766}