use crate::ns::*; pub(crate) struct AssignmentDestructuringSubverifier; impl AssignmentDestructuringSubverifier { /// Verifies a pattern. /// /// `init` may be a value or an `InvalidationThingy`. pub fn verify_pattern(verifier: &mut Subverifier, pattern: &Rc, init: &Thingy) -> Result<(), DeferError> { match pattern.as_ref() { Expression::QualifiedIdentifier(id) => Self::verify_identifier_pattern(verifier, pattern, id, init), Expression::ObjectInitializer(literal) => Self::verify_object_pattern(verifier, pattern, literal, init), Expression::ArrayLiteral(literal) => Self::verify_array_pattern(verifier, pattern, literal, init), Expression::Unary(e) => { if e.operator == Operator::NonNull { Self::verify_non_null_pattern(verifier, pattern, e, init) } else { Ok(()) } }, _ => Ok(()), } } pub fn verify_identifier_pattern(verifier: &mut Subverifier, pattern: &Rc, id: &QualifiedIdentifier, init: &Thingy) -> Result<(), DeferError> { if verifier.host.node_mapping().has(pattern) || id.attribute { return Ok(()); } init.defer()?; let init_st = init.static_type(&verifier.host).defer()?; let qn = ExpSubverifier::verify_qualified_identifier(verifier, id)?; if qn.is_none() { verifier.host.node_mapping().set(pattern, None); return Ok(()); } let (qual, key) = qn.unwrap(); let r = verifier.scope().lookup_in_scope_chain(&verifier.host, qual, &key); if r.is_err() { match r.unwrap_err() { PropertyLookupError::AmbiguousReference(name) => { verifier.add_verify_error(&id.location, FxDiagnosticKind::AmbiguousReference, diagarg![name.clone()]); verifier.host.node_mapping().set(pattern, None); return Ok(()); }, PropertyLookupError::Defer => { return Err(DeferError(None)); }, PropertyLookupError::VoidBase => { verifier.add_verify_error(&id.location, FxDiagnosticKind::AccessOfVoid, diagarg![]); verifier.host.node_mapping().set(pattern, None); return Ok(()); }, PropertyLookupError::NullableObject { .. } => { verifier.add_verify_error(&id.location, FxDiagnosticKind::AccessOfNullable, diagarg![]); verifier.host.node_mapping().set(pattern, None); return Ok(()); }, } } let r = r.unwrap(); if r.is_none() { verifier.add_verify_error(&id.location, FxDiagnosticKind::UndefinedProperty, diagarg![key.local_name().unwrap()]); verifier.host.node_mapping().set(pattern, None); return Ok(()); } let r = r.unwrap(); // Mark local capture verifier.detect_local_capture(&r); // Post-processing let Some(val) = verifier.reference_post_processing(r, &default())? else { verifier.host.node_mapping().set(pattern, None); return Ok(()); }; // Implicit coercion let Some(val) = TypeConversions(&verifier.host).implicit(&val, &init_st, false)? else { verifier.add_verify_error(&id.location, FxDiagnosticKind::ImplicitCoercionToUnrelatedType, diagarg![val.static_type(&verifier.host), init_st]); verifier.host.node_mapping().set(pattern, None); return Ok(()); }; verifier.host.node_mapping().set(pattern, Some(val)); Ok(()) } pub fn verify_non_null_pattern(verifier: &mut Subverifier, pattern: &Rc, literal: &UnaryExpression, init: &Thingy) -> Result<(), DeferError> { if verifier.host.node_mapping().has(pattern) { return Ok(()); } init.defer()?; init.static_type(&verifier.host).defer()?; let non_null_val = verifier.host.factory().create_non_null_value(&init)?; Self::verify_pattern(verifier, &literal.expression, &non_null_val)?; verifier.host.node_mapping().set(pattern, Some(non_null_val)); Ok(()) } pub fn verify_array_pattern(verifier: &mut Subverifier, pattern: &Rc, literal: &ArrayLiteral, init: &Thingy) -> Result<(), DeferError> { if verifier.host.node_mapping().has(pattern) { return Ok(()); } init.defer()?; let init_st = init.static_type(&verifier.host).defer()?; let init_st_esc = init_st.escape_of_non_nullable(); // Pre verification of rest operator let mut rest_loc: Option = None; let mut i: usize = 0; let mut rest_i: usize = 0; for elem in &literal.elements { match elem { Element::Expression(_) => {}, Element::Rest((_, loc)) => { if rest_loc.is_some() { verifier.add_verify_error(loc, FxDiagnosticKind::UnexpectedRest, diagarg![]); } rest_i = i; rest_loc = Some(loc.clone()); }, Element::Elision => {}, } i += 1; } if rest_loc.is_some() && rest_i != i - 1 { verifier.add_verify_error(&rest_loc.unwrap(), FxDiagnosticKind::UnexpectedRest, diagarg![]); } // Verify Vector. if let Some(elem_type) = init_st_esc.vector_element_type(&verifier.host)? { Self::verify_vector_array_pattern(verifier, literal, &init_st_esc, &elem_type)?; // Verify Array. } else if let Some(elem_type) = init_st_esc.array_element_type(&verifier.host)? { Self::verify_array_array_pattern(verifier, literal, &init_st_esc, &elem_type)?; // Verify tuple } else if init_st_esc.is::() { Self::verify_tuple_array_pattern(verifier, literal, &init_st_esc)?; // Verify * or Object } else if [verifier.host.any_type(), verifier.host.object_type().defer()?].contains(&init_st_esc) { Self::verify_untyped_array_pattern(verifier, literal)?; // Invalidation } else { Self::verify_invalidation_array_pattern(verifier, literal)?; } verifier.host.node_mapping().set(pattern, Some(init.clone())); Ok(()) } fn verify_vector_array_pattern(verifier: &mut Subverifier, literal: &ArrayLiteral, vector_type: &Thingy, elem_type: &Thingy) -> Result<(), DeferError> { for elem in &literal.elements { match elem { Element::Expression(subpat) => { Self::verify_pattern(verifier, subpat, &verifier.host.factory().create_value(elem_type))?; }, Element::Rest((restpat, _)) => { Self::verify_pattern(verifier, restpat, &verifier.host.factory().create_value(vector_type))?; }, Element::Elision => {}, } } Ok(()) } fn verify_array_array_pattern(verifier: &mut Subverifier, literal: &ArrayLiteral, array_type: &Thingy, elem_type: &Thingy) -> Result<(), DeferError> { for elem in &literal.elements { match elem { Element::Expression(subpat) => { Self::verify_pattern(verifier, subpat, &verifier.host.factory().create_value(elem_type))?; }, Element::Rest((restpat, _)) => { Self::verify_pattern(verifier, restpat, &verifier.host.factory().create_value(array_type))?; }, Element::Elision => {}, } } Ok(()) } fn verify_tuple_array_pattern(verifier: &mut Subverifier, literal: &ArrayLiteral, tuple_type: &Thingy) -> Result<(), DeferError> { let elem_types = tuple_type.element_types(); let mut i: usize = 0; let mut rest_found = false; for elem in &literal.elements { match elem { Element::Expression(subpat) => { if rest_found || i >= elem_types.length() { Self::verify_pattern(verifier, subpat, &verifier.host.invalidation_thingy())?; } else { let elem_type = elem_types.get(i).unwrap(); Self::verify_pattern(verifier, subpat, &verifier.host.factory().create_value(&elem_type))?; } }, Element::Rest((restpat, _)) => { let array_type_of_any = verifier.host.array_type_of_any()?; rest_found = true; Self::verify_pattern(verifier, restpat, &verifier.host.factory().create_value(&array_type_of_any))?; }, Element::Elision => {}, } i += 1; } if i > elem_types.length() && !rest_found { verifier.add_verify_error(&literal.location, FxDiagnosticKind::ArrayLengthNotEqualsTupleLength, diagarg![tuple_type.clone()]); } Ok(()) } fn verify_untyped_array_pattern(verifier: &mut Subverifier, literal: &ArrayLiteral) -> Result<(), DeferError> { for elem in &literal.elements { match elem { Element::Expression(subpat) => { Self::verify_pattern(verifier, subpat, &verifier.host.factory().create_value(&verifier.host.any_type()))?; }, Element::Rest((restpat, _)) => { Self::verify_pattern(verifier, restpat, &verifier.host.factory().create_value(&verifier.host.any_type()))?; }, Element::Elision => {}, } } Ok(()) } fn verify_invalidation_array_pattern(verifier: &mut Subverifier, literal: &ArrayLiteral) -> Result<(), DeferError> { for elem in &literal.elements { match elem { Element::Expression(subpat) => { Self::verify_pattern(verifier, subpat, &verifier.host.invalidation_thingy())?; }, Element::Rest((restpat, _)) => { Self::verify_pattern(verifier, restpat, &verifier.host.invalidation_thingy())?; }, Element::Elision => {}, } } Ok(()) } fn verify_object_pattern(verifier: &mut Subverifier, pattern: &Rc, literal: &ObjectInitializer, init: &Thingy) -> Result<(), DeferError> { if verifier.host.node_mapping().has(pattern) { return Ok(()); } init.defer()?; let init_st = init.static_type(&verifier.host).defer()?; for field in &literal.fields { match field.as_ref() { InitializerField::Field { name, non_null, value: subpat } => { // AssignmentFieldDestructuringResolution let resolution; if let Some(resolution1) = verifier.host.node_mapping().get(field) { resolution = resolution1; } else { resolution = verifier.host.factory().create_assignment_field_destructuring_resolution(); verifier.host.node_mapping().set(field, Some(resolution.clone())); } if resolution.field_reference().is_some() { continue; } let qn: Option<(Option, PropertyLookupKey)>; match &name.0 { FieldName::Identifier(id) => { qn = ExpSubverifier::verify_qualified_identifier(verifier, id)?; }, FieldName::Brackets(exp) | FieldName::NumericLiteral(exp) | FieldName::StringLiteral(exp) => { let val = verifier.verify_expression(exp, &default())?; qn = Some((None, PropertyLookupKey::Computed(val.unwrap_or(verifier.host.invalidation_thingy())))); }, } if qn.is_none() { if let Some(var_slot) = resolution.var_slot() { var_slot.set_static_type(verifier.host.invalidation_thingy()); } if let Some(subpat) = subpat { Self::verify_pattern(verifier, subpat, &verifier.host.invalidation_thingy())?; } resolution.set_field_reference(Some(verifier.host.invalidation_thingy())); continue; } let name_loc = &name.1; let (qual, key) = qn.unwrap(); let open_ns_set = verifier.scope().concat_open_ns_set_of_scope_chain(); let r = PropertyLookup(&verifier.host).lookup_in_object(&init, &open_ns_set, qual, &key); if r.is_err() { match r.unwrap_err() { PropertyLookupError::AmbiguousReference(name) => { if let Some(subpat) = subpat { Self::verify_pattern(verifier, subpat, &verifier.host.invalidation_thingy())?; } resolution.set_field_reference(Some(verifier.host.invalidation_thingy())); verifier.add_verify_error(name_loc, FxDiagnosticKind::AmbiguousReference, diagarg![name.clone()]); continue; }, PropertyLookupError::Defer => { return Err(DeferError(None)); }, PropertyLookupError::VoidBase => { if let Some(subpat) = subpat { Self::verify_pattern(verifier, subpat, &verifier.host.invalidation_thingy())?; } resolution.set_field_reference(Some(verifier.host.invalidation_thingy())); verifier.add_verify_error(name_loc, FxDiagnosticKind::AccessOfVoid, diagarg![]); continue; }, PropertyLookupError::NullableObject { .. } => { if let Some(subpat) = subpat { Self::verify_pattern(verifier, subpat, &verifier.host.invalidation_thingy())?; } resolution.set_field_reference(Some(verifier.host.invalidation_thingy())); verifier.add_verify_error(name_loc, FxDiagnosticKind::AccessOfNullable, diagarg![]); continue; }, } } let r = r.unwrap(); if r.is_none() { if let Some(subpat) = subpat { Self::verify_pattern(verifier, subpat, &verifier.host.invalidation_thingy())?; } resolution.set_field_reference(Some(verifier.host.invalidation_thingy())); verifier.add_verify_error(name_loc, FxDiagnosticKind::UndefinedPropertyWithStaticType, diagarg![key.local_name().unwrap(), init_st.clone()]); continue; } let r = r.unwrap(); // Post-processing let postval = verifier.reference_post_processing(r, &default())?; if let Some(mut postval) = postval { if *non_null { postval = verifier.host.factory().create_non_null_value(&postval)?; } if let Some(var_slot) = resolution.var_slot() { var_slot.set_static_type(postval.static_type(&verifier.host)); } if let Some(subpat) = subpat { Self::verify_pattern(verifier, subpat, &postval)?; } else { let Some(shorthand) = field.shorthand().and_then(|id| { if let QualifiedIdentifierIdentifier::Id(id) = &id.id { Some(id.clone()) } else { None } }) else { verifier.add_syntax_error(&name.1, FxDiagnosticKind::UnexpectedFieldNameInDestructuring, diagarg![]); continue; }; if let Some(target) = Self::verify_shorthand_target_of_object_pattern(verifier, shorthand)? { resolution.set_target_reference(Some(target.clone())); // Implicit coercion let Some(_) = TypeConversions(&verifier.host).implicit(&postval, &target.static_type(&verifier.host), false)? else { verifier.add_verify_error(&name_loc, FxDiagnosticKind::ImplicitCoercionToUnrelatedType, diagarg![postval.static_type(&verifier.host), target.static_type(&verifier.host)]); verifier.host.node_mapping().set(pattern, None); continue; }; } } resolution.set_field_reference(Some(postval)); } else { if let Some(var_slot) = resolution.var_slot() { var_slot.set_static_type(verifier.host.invalidation_thingy()); } if let Some(subpat) = subpat { Self::verify_pattern(verifier, subpat, &verifier.host.invalidation_thingy())?; } else { let Some(shorthand) = field.shorthand().and_then(|id| { if let QualifiedIdentifierIdentifier::Id(id) = &id.id { Some(id.clone()) } else { None } }) else { verifier.add_syntax_error(&name.1, FxDiagnosticKind::UnexpectedFieldNameInDestructuring, diagarg![]); continue; }; resolution.set_target_reference(Self::verify_shorthand_target_of_object_pattern(verifier, shorthand)?); } resolution.set_field_reference(Some(verifier.host.invalidation_thingy())); } }, InitializerField::Rest((restpat, loc)) => { verifier.add_verify_error(loc, FxDiagnosticKind::UnexpectedRest, diagarg![]); Self::verify_pattern(verifier, restpat, &verifier.host.invalidation_thingy())?; }, } } verifier.host.node_mapping().set(pattern, Some(init.clone())); Ok(()) } fn verify_shorthand_target_of_object_pattern(verifier: &mut Subverifier, shorthand: (String, Location)) -> Result, DeferError> { let key = PropertyLookupKey::LocalName(shorthand.0.clone()); let r = verifier.scope().lookup_in_scope_chain(&verifier.host, None, &key); if r.is_err() { match r.unwrap_err() { PropertyLookupError::AmbiguousReference(name) => { verifier.add_verify_error(&shorthand.1, FxDiagnosticKind::AmbiguousReference, diagarg![name.clone()]); return Ok(None); }, PropertyLookupError::Defer => { return Err(DeferError(None)); }, PropertyLookupError::VoidBase => { verifier.add_verify_error(&shorthand.1, FxDiagnosticKind::AccessOfVoid, diagarg![]); return Ok(None); }, PropertyLookupError::NullableObject { .. } => { verifier.add_verify_error(&shorthand.1, FxDiagnosticKind::AccessOfNullable, diagarg![]); return Ok(None); }, } } let r = r.unwrap(); if r.is_none() { verifier.add_verify_error(&shorthand.1, FxDiagnosticKind::UndefinedProperty, diagarg![key.local_name().unwrap()]); return Ok(None); } let r = r.unwrap(); // Mark local capture verifier.detect_local_capture(&r); // Post-processing verifier.reference_post_processing(r, &default()) } }