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 if !diag.enable_experimental && !tr.expose_internal_types {
148 diag.push_error("'implements' is an experimental feature".into(), &implements_specifier);
149 return None;
150 }
151
152 let interface_name =
153 QualifiedTypeName::from_node(implements_specifier.QualifiedName()).to_smolstr();
154
155 match e.base_type.lookup_type_for_child_element(&interface_name, tr) {
156 Ok(ElementType::Component(c)) => {
157 if !c.is_interface() {
158 diag.push_error(
159 format!("Cannot implement {}. It is not an interface", interface_name),
160 &implements_specifier.QualifiedName(),
161 );
162 return None;
163 }
164
165 c.used.set(true);
166 Some(ImplementedInterface {
167 implements_specifier,
168 interface: c.root_element.clone(),
169 interface_name,
170 })
171 }
172 Ok(_) => {
173 diag.push_error(
174 format!("Cannot implement {}. It is not an interface", interface_name),
175 &implements_specifier.QualifiedName(),
176 );
177 None
178 }
179 Err(err) => {
180 diag.push_error(err, &implements_specifier.QualifiedName());
181 None
182 }
183 }
184}
185
186pub(super) fn apply_properties(
189 e: &mut Element,
190 implemented_interface: &Option<ImplementedInterface>,
191 diag: &mut BuildDiagnostics,
192) {
193 let Some(ImplementedInterface { interface, implements_specifier, interface_name }) =
194 implemented_interface
195 else {
196 return;
197 };
198
199 for (unresolved_prop_name, prop_decl) in
200 interface.borrow().property_declarations.iter().filter(|(_, prop_decl)| {
201 !matches!(prop_decl.property_type, Type::Function { .. } | Type::Callback { .. })
203 })
204 {
205 apply_interface_property_declaration(
206 e,
207 unresolved_prop_name,
208 prop_decl,
209 implements_specifier,
210 interface_name,
211 diag,
212 );
213 }
214}
215
216pub(super) fn apply_callbacks(
219 e: &mut Element,
220 implemented_interface: &Option<ImplementedInterface>,
221 diag: &mut BuildDiagnostics,
222) {
223 let Some(ImplementedInterface { interface, implements_specifier, interface_name }) =
224 implemented_interface
225 else {
226 return;
227 };
228
229 for (unresolved_prop_name, prop_decl) in
230 interface.borrow().property_declarations.iter().filter(|(_, prop_decl)| {
231 matches!(prop_decl.property_type, Type::Callback { .. })
233 })
234 {
235 apply_interface_property_declaration(
236 e,
237 unresolved_prop_name,
238 prop_decl,
239 implements_specifier,
240 interface_name,
241 diag,
242 );
243 }
244}
245
246fn apply_interface_property_declaration(
249 e: &mut Element,
250 unresolved_prop_name: &SmolStr,
251 prop_decl: &PropertyDeclaration,
252 implements_specifier: &syntax_nodes::ImplementsSpecifier,
253 interface_name: &SmolStr,
254 diag: &mut BuildDiagnostics,
255) {
256 let lookup_result = e.lookup_property(unresolved_prop_name);
257
258 if lookup_result.property_type != Type::Invalid {
259 match property_matches_interface(&lookup_result, prop_decl) {
260 Ok(()) => {
261 return;
263 }
264 Err(error) => {
265 if let Some(local_property_node) = e
268 .property_declarations
269 .get(unresolved_prop_name)
270 .and_then(|decl| decl.node.clone())
271 {
272 diag.push_error(
273 format!("Conflict with '{}' which {}", interface_name, error),
274 &local_property_node,
275 );
276 return;
277 }
278 }
279 }
280 }
281
282 if let Err(message) = validate_property_declaration_for_interface(
283 InterfaceUseMode::Implements,
284 &lookup_result,
285 &e.base_type,
286 &interface_name,
287 ) {
288 diag.push_error(message, &implements_specifier.QualifiedName());
289 return;
290 }
291
292 e.property_declarations.insert(unresolved_prop_name.clone(), prop_decl.clone());
293}
294
295pub(super) fn apply_default_property_values(
297 e: &ElementRc,
298 implemented_interface: &Option<ImplementedInterface>,
299) {
300 let Some(ImplementedInterface { interface, .. }) = implemented_interface else {
301 return;
302 };
303
304 let interface_root = interface.clone();
305
306 let bindings_to_apply: Vec<_> = interface
308 .borrow()
309 .property_declarations
310 .iter()
311 .filter(|(_, prop_decl)| {
312 !matches!(prop_decl.property_type, Type::Function { .. } | Type::Callback { .. })
314 })
315 .filter_map(|(property_name, _)| {
316 interface
317 .borrow()
318 .bindings
319 .get(property_name)
320 .map(|binding| (property_name.clone(), binding.clone()))
321 })
322 .filter(|(property_name, _)| {
323 !e.borrow().is_binding_set(property_name, true)
325 })
326 .collect();
327
328 for (property_name, binding) in bindings_to_apply {
329 visit_named_references_in_expression(&mut binding.borrow_mut().expression, &mut |nr| {
331 if Rc::ptr_eq(&nr.element(), &interface_root) {
332 *nr = NamedReference::new(e, nr.name().clone());
333 }
334 });
335 e.borrow_mut().bindings.insert(property_name, binding);
336 }
337}
338
339pub(super) fn validate_function_implementations(
341 e: &Element,
342 implemented_interface: &Option<ImplementedInterface>,
343 diag: &mut BuildDiagnostics,
344) {
345 let Some(ImplementedInterface { interface, implements_specifier, interface_name }) =
346 implemented_interface
347 else {
348 return;
349 };
350
351 for (function_name, function_property_decl) in interface
352 .borrow()
353 .property_declarations
354 .iter()
355 .filter(|(_, prop_decl)| matches!(prop_decl.property_type, Type::Function { .. }))
356 {
357 let Type::Function(ref function_declaration) = function_property_decl.property_type else {
358 debug_assert!(false, "Non-functions should have been filtered out already");
359 continue;
360 };
361
362 let push_interface_error = |diag: &mut BuildDiagnostics, is_local_to_component, error| {
363 if is_local_to_component {
364 let source = e
365 .property_declarations
366 .get(function_name)
367 .and_then(|decl| decl.node.clone())
368 .map_or_else(
369 || parser::NodeOrToken::Node(implements_specifier.QualifiedName().into()),
370 parser::NodeOrToken::Node,
371 );
372 diag.push_error(error, &source);
373 } else {
374 diag.push_error(error, &implements_specifier.QualifiedName());
375 }
376 };
377
378 let found_function = e.lookup_property(function_name);
379 let function_impl = match found_function.property_type {
380 Type::Invalid => {
381 diag.push_error(
382 format!("Missing implementation of function '{}'", function_name),
383 &implements_specifier.QualifiedName(),
384 );
385 None
386 }
387 Type::Function(function) => Some(function.clone()),
388 _ => {
389 push_interface_error(
390 diag,
391 found_function.is_local_to_component,
392 format!(
393 "Cannot override '{}' from interface '{}'",
394 function_name, interface_name
395 ),
396 );
397 None
398 }
399 };
400 let Some(function_impl) = function_impl else { continue };
401
402 match (function_property_decl.pure, found_function.declared_pure) {
403 (Some(true), Some(false)) | (Some(true), None) => push_interface_error(
404 diag,
405 found_function.is_local_to_component,
406 format!(
407 "Implementation of pure function '{}' from interface '{}' cannot be impure",
408 function_name, interface_name
409 ),
410 ),
411 _ => {
412 }
414 }
415
416 if function_property_decl.visibility != found_function.property_visibility {
417 push_interface_error(
418 diag,
419 found_function.is_local_to_component,
420 format!(
421 "Incorrect visibility for implementation of '{}' from interface '{}'. Expected '{}'",
422 function_name, interface_name, function_property_decl.visibility,
423 ),
424 );
425 }
426
427 if function_impl.args != function_declaration.args {
428 let display_args = |args: &Vec<Type>| -> SmolStr {
429 args.iter().map(|t| t.to_string()).join(", ").into()
430 };
431
432 push_interface_error(
433 diag,
434 found_function.is_local_to_component,
435 format!(
436 "Incorrect arguments for implementation of '{}' from interface '{}'. Expected ({}) but got ({})",
437 function_name,
438 interface_name,
439 display_args(&function_declaration.args),
440 display_args(&function_impl.args),
441 ),
442 );
443 }
444
445 if function_impl.return_type != function_declaration.return_type {
446 push_interface_error(
447 diag,
448 found_function.is_local_to_component,
449 format!(
450 "Incorrect return type for implementation of '{}' from interface '{}'. Expected '{}' but got '{}'",
451 function_name,
452 interface_name,
453 function_declaration.return_type,
454 function_impl.return_type,
455 ),
456 );
457 }
458 }
459}
460
461pub(super) fn apply_uses_statement(
462 e: &ElementRc,
463 uses_specifier: Option<syntax_nodes::UsesSpecifier>,
464 tr: &TypeRegister,
465 diag: &mut BuildDiagnostics,
466) {
467 let Some(uses_specifier) = uses_specifier else {
468 return;
469 };
470
471 if !diag.enable_experimental && !tr.expose_internal_types {
472 diag.push_error("'uses' is an experimental feature".into(), &uses_specifier);
473 return;
474 }
475
476 let uses_statements = gather_valid_uses_statements(e, tr, diag, uses_specifier);
477 let uses_statements = filter_conflicting_uses_statements(diag, uses_statements);
478
479 for ValidUsesStatement { uses_statement, interface, child } in uses_statements {
480 for (name, prop_decl) in interface.borrow().property_declarations.iter() {
481 let lookup_result = e.borrow().base_type.lookup_property(name);
482 if let Err(message) = validate_property_declaration_for_interface(
483 InterfaceUseMode::Uses,
484 &lookup_result,
485 &e.borrow().base_type,
486 &uses_statement.interface_name,
487 ) {
488 diag.push_error(message, &uses_statement.interface_name_node());
489 continue;
490 }
491
492 let mut prop_decl = prop_decl.clone();
495 prop_decl.node = Some(uses_statement.interface_name_node().into());
496
497 if let Some(existing_property) =
498 e.borrow_mut().property_declarations.insert(name.clone(), prop_decl.clone())
499 {
500 let source = existing_property
501 .node
502 .as_ref()
503 .and_then(|node| node.child_node(SyntaxKind::DeclaredIdentifier))
504 .and_then(|node| node.child_token(SyntaxKind::Identifier))
505 .map_or_else(
506 || parser::NodeOrToken::Node(uses_statement.child_id_node().into()),
507 parser::NodeOrToken::Token,
508 );
509
510 diag.push_error(
511 format!("Cannot override '{}' from '{}'", name, uses_statement.interface_name),
512 &source,
513 );
514 continue;
515 }
516
517 let exisitng_binding = match &prop_decl.property_type {
518 Type::Function(func) => {
519 apply_uses_statement_function_binding(e, &child, name, func)
520 }
521 _ => e.borrow_mut().bindings.insert(
522 name.clone(),
523 BindingExpression::new_two_way(
524 NamedReference::new(&child, name.clone()).into(),
525 )
526 .into(),
527 ),
528 };
529
530 if let Some(existing_binding) = exisitng_binding {
531 let message = format!(
532 "Cannot override binding for '{}' from interface '{}'",
533 name, uses_statement.interface_name
534 );
535 if let Some(location) = &existing_binding.borrow().span {
536 diag.push_error(message, location);
537 } else {
538 diag.push_error(message, &uses_statement.interface_name_node());
539 }
540 }
541 }
542 }
543}
544
545struct ValidUsesStatement {
547 uses_statement: UsesStatement,
548 interface: ElementRc,
549 child: ElementRc,
550}
551
552fn gather_valid_uses_statements(
555 e: &Rc<RefCell<Element>>,
556 tr: &TypeRegister,
557 diag: &mut BuildDiagnostics,
558 uses_specifier: syntax_nodes::UsesSpecifier,
559) -> Vec<ValidUsesStatement> {
560 let mut valid_uses_statements: Vec<ValidUsesStatement> = Vec::new();
561
562 for uses_identifier_node in uses_specifier.UsesIdentifier() {
563 let uses_statement: UsesStatement = (&uses_identifier_node).into();
564 let Ok(interface_component) = uses_statement.lookup_interface(tr, diag) else {
565 continue;
566 };
567
568 let Some(child) = find_element_by_id(e, &uses_statement.child_id) else {
569 diag.push_error(
570 format!("'{}' does not exist", uses_statement.child_id),
571 &uses_statement.child_id_node(),
572 );
573 continue;
574 };
575
576 let interface = interface_component.root_element.clone();
577 if !element_implements_interface(&child, &interface, &uses_statement, diag) {
578 continue;
579 }
580
581 valid_uses_statements.push(ValidUsesStatement { uses_statement, interface, child });
582 }
583 valid_uses_statements
584}
585
586fn filter_conflicting_uses_statements(
590 diag: &mut BuildDiagnostics,
591 uses_statements: Vec<ValidUsesStatement>,
592) -> Vec<ValidUsesStatement> {
593 let mut seen_interfaces: Vec<SmolStr> = Vec::new();
594 let mut seen_interface_api: BTreeMap<SmolStr, SmolStr> = BTreeMap::new();
595 let valid_uses_statements: Vec<ValidUsesStatement> = uses_statements
596 .into_iter()
597 .filter(|vus| {
598 let interface_name = vus.uses_statement.interface_name.to_smolstr();
599 if seen_interfaces.contains(&interface_name) {
600 diag.push_error(
601 format!("'{}' is used multiple times", vus.uses_statement.interface_name),
602 &vus.uses_statement.interface_name_node(),
603 );
604 return false;
605 }
606 seen_interfaces.push(interface_name.clone());
607
608 let mut valid = true;
609 for (prop_name, _) in vus.interface.borrow().property_declarations.iter() {
610 if let Some(existing_interface) = seen_interface_api.get(prop_name) {
611 diag.push_error(
612 format!(
613 "'{}' occurs in '{}' and '{}'",
614 prop_name, vus.uses_statement.interface_name, existing_interface
615 ),
616 &vus.uses_statement.interface_name_node(),
617 );
618 valid = false;
619 } else {
620 seen_interface_api.insert(prop_name.clone(), interface_name.clone());
621 }
622 }
623 valid
624 })
625 .collect();
626 valid_uses_statements
627}
628
629fn element_implements_interface(
631 element: &ElementRc,
632 interface: &ElementRc,
633 uses_statement: &UsesStatement,
634 diag: &mut BuildDiagnostics,
635) -> bool {
636 let mut valid = true;
637 let mut check = |property_name: &SmolStr, property_declaration: &PropertyDeclaration| {
638 let lookup_result = element.borrow().lookup_property(property_name);
639 if let Err(e) = property_matches_interface(&lookup_result, property_declaration) {
640 diag.push_error(
641 format!(
642 "'{}' does not implement '{}' from '{}' - {}",
643 uses_statement.child_id, property_name, uses_statement.interface_name, e
644 ),
645 &uses_statement.child_id_node(),
646 );
647 valid = false;
648 }
649 };
650
651 for (property_name, property_declaration) in interface.borrow().property_declarations.iter() {
652 check(property_name, property_declaration);
653 }
654
655 valid
656}
657
658fn property_matches_interface(
660 property: &PropertyLookupResult,
661 interface_declaration: &PropertyDeclaration,
662) -> Result<(), String> {
663 if property.property_type == Type::Invalid {
664 return Err("not found".into());
665 }
666
667 let mut errors = Vec::new();
668
669 if property.property_type != interface_declaration.property_type {
670 errors.push(format!("type: '{}'", interface_declaration.property_type));
671 }
672
673 if property.property_visibility != interface_declaration.visibility {
674 errors.push(format!("visibility: '{}'", interface_declaration.visibility));
675 }
676
677 if property.declared_pure.unwrap_or(false) != interface_declaration.pure.unwrap_or(false) {
678 errors
679 .push(format!("purity declaration: '{}'", interface_declaration.pure.unwrap_or(false)));
680 }
681
682 if errors.is_empty() {
683 Ok(())
684 } else {
685 Err(format!("expected {}", errors.into_iter().join(", ")))
686 }
687}
688
689fn apply_uses_statement_function_binding(
692 e: &ElementRc,
693 child: &ElementRc,
694 name: &SmolStr,
695 func: &Rc<Function>,
696) -> Option<RefCell<BindingExpression>> {
697 let args_expr: Vec<Expression> = func
699 .args
700 .iter()
701 .enumerate()
702 .map(|(i, ty)| Expression::FunctionParameterReference { index: i, ty: ty.clone() })
703 .collect();
704
705 let call_expr = Expression::FunctionCall {
707 function: Callable::Function(NamedReference::new(child, name.clone())),
708 arguments: args_expr,
709 source_location: None,
710 };
711
712 let body = Expression::CodeBlock(vec![call_expr]);
714 e.borrow_mut().bindings.insert(name.clone(), RefCell::new(BindingExpression::from(body)))
715}