diff --git a/checker/definitions/internal.ts.d.bin b/checker/definitions/internal.ts.d.bin index a942d5d7..d44387a8 100644 Binary files a/checker/definitions/internal.ts.d.bin and b/checker/definitions/internal.ts.d.bin differ diff --git a/checker/definitions/overrides.d.ts b/checker/definitions/overrides.d.ts index ab3bbc5b..076c661f 100644 --- a/checker/definitions/overrides.d.ts +++ b/checker/definitions/overrides.d.ts @@ -269,4 +269,4 @@ declare function satisfies(t: T): T; @Constant declare function compile_type_to_object(): any; -// ↑↑ Ezno Functions ↑↑ \ No newline at end of file +// ↑↑ Ezno Functions ↑↑ diff --git a/checker/specification/specification.md b/checker/specification/specification.md index c9a00860..dc9fb748 100644 --- a/checker/specification/specification.md +++ b/checker/specification/specification.md @@ -648,6 +648,16 @@ callToUpperCase("hi") satisfies "HEY"; - Expected "HEY", found "HI" +#### String internal `this` unbinding error + +```ts +const { toUpperCase } = "hi"; + +toUpperCase(); +``` + +- The 'this' context of the function is expected to be string, found undefined + #### Calling new on a function ```ts @@ -739,7 +749,7 @@ doThing(6, 1) satisfies 6; let a: number = 0 function func() { a = 4; - // Important that subsequent reads use the + // Important that subsequent reads use the // new value, not the same free variable a satisfies 4; } @@ -1569,7 +1579,7 @@ interface X { interface X { c: number } - + const x: X = { a: "field", b: false, c: false } const y: X = { a: "field", b: false, c: 2 } } @@ -1658,6 +1668,27 @@ try { - Expected string, found 3 +#### Object destructuring assignment + +```ts +const o = { a: 1, b: { c: 3 } }; + +let a, b, c; +({ + c = o.a++, + b: { c: b = 7 }, + a, +} = o); + +a satisfies string; +b satisfies boolean; +c satisfies 3; +``` + +- Expected string, found 2 +- Expected boolean, found 3 +- Expected 3, found 1 + ### Async and `Promise`s > Position of await is not checked (here is fine because top level await) @@ -1692,6 +1723,21 @@ x.value satisfies string - Expected string, found 4 +#### Class `this` unbinding + +```ts +class X { + method() { + return this; + } +} + +const { method } = new X(); +method(); +``` + +- The 'this' context of the function is expected to be X, found undefined + #### Property keys > Property keys are synthesised once and their effects run once (as opposed to their value) diff --git a/checker/src/context/environment.rs b/checker/src/context/environment.rs index d9ccacce..19fa1fb8 100644 --- a/checker/src/context/environment.rs +++ b/checker/src/context/environment.rs @@ -3,12 +3,15 @@ use std::collections::HashSet; use crate::{ diagnostics::{ - NotInLoopOrCouldNotFindLabel, TypeCheckError, TypeCheckWarning, TypeStringRepresentation, - TDZ, + NotInLoopOrCouldNotFindLabel, PropertyRepresentation, TypeCheckError, TypeCheckWarning, + TypeStringRepresentation, TDZ, }, events::{ApplicationResult, Event, FinalEvent, RootReference}, features::{ - assignments::{Assignable, AssignmentKind, Reference}, + assignments::{ + Assignable, AssignableArrayDestructuringField, AssignableObjectDestructuringField, + AssignmentKind, Reference, + }, modules::Exported, objects::SpecialObjects, operations::{ @@ -19,7 +22,7 @@ use crate::{ }, subtyping::{type_is_subtype, BasicEquality, SubTypeResult}, types::{ - is_type_truthy_falsy, + is_type_truthy_falsy, printing, properties::{PropertyKey, PropertyKind, PropertyValue}, PolyNature, Type, TypeCombinable, TypeStore, }, @@ -242,7 +245,7 @@ impl<'a> Environment<'a> { A: crate::ASTImplementation, >( &mut self, - lhs: Assignable, + lhs: Assignable, operator: AssignmentKind, // Can be `None` for increment and decrement expression: Option<&'b A::Expression<'b>>, @@ -251,112 +254,26 @@ impl<'a> Environment<'a> { ) -> TypeId { match lhs { Assignable::Reference(reference) => { - /// Returns - fn get_reference( - env: &mut Environment, - reference: Reference, - checking_data: &mut CheckingData, - ) -> TypeId { - match reference { - Reference::Variable(name, position) => { - env.get_variable_handle_error(&name, position, checking_data).unwrap().1 - } - Reference::Property { on, with, publicity, span } => { - let get_property_handle_errors = env.get_property_handle_errors( - on, - publicity, - &with, - checking_data, - span.without_source(), - ); - match get_property_handle_errors { - Ok(i) => i.get_value(), - Err(()) => TypeId::ERROR_TYPE, - } - } - } - } - - fn set_reference( - env: &mut Environment, - reference: Reference, - new: TypeId, - checking_data: &mut CheckingData, - ) -> Result { - match reference { - Reference::Variable(name, position) => Ok(env - .assign_to_variable_handle_errors( - name.as_str(), - position, - new, - checking_data, - )), - Reference::Property { on, with, publicity, span } => Ok(env - .set_property( - on, - publicity, - &with, - new, - &mut checking_data.types, - Some(span), - &checking_data.options, - )? - .unwrap_or(new)), - } - } - - fn set_property_error_to_type_check_error( - ctx: &impl InformationChain, - error: SetPropertyError, - assignment_span: SpanWithSource, - types: &TypeStore, - new: TypeId, - ) -> TypeCheckError<'static> { - match error { - SetPropertyError::NotWriteable => { - TypeCheckError::PropertyNotWriteable(assignment_span) - } - SetPropertyError::DoesNotMeetConstraint { - property_constraint, - reason: _, - } => TypeCheckError::AssignmentError(AssignmentError::PropertyConstraint { - property_constraint, - value_type: TypeStringRepresentation::from_type_id( - new, ctx, types, false, - ), - assignment_position: assignment_span, - }), - } - } - match operator { AssignmentKind::Assign => { - let new = A::synthesise_expression( + let rhs = A::synthesise_expression( expression.unwrap(), TypeId::ANY_TYPE, self, checking_data, ); - let result = set_reference(self, reference, new, checking_data); - match result { - Ok(ty) => ty, - Err(error) => { - let error = set_property_error_to_type_check_error( - self, - error, - assignment_span.with_source(self.get_source()), - &checking_data.types, - new, - ); - checking_data.diagnostics_container.add_error(error); - TypeId::ERROR_TYPE - } - } + + self.assign_to_reference_assign_handle_errors( + reference, + rhs, + checking_data, + assignment_span, + ) } AssignmentKind::PureUpdate(operator) => { // Order matters here let reference_position = reference.get_position(); - let existing = get_reference(self, reference.clone(), checking_data); + let existing = self.get_reference(reference.clone(), checking_data, true); let expression = expression.unwrap(); let expression_pos = @@ -375,7 +292,7 @@ impl<'a> Environment<'a> { checking_data, self, ); - let result = set_reference(self, reference, new, checking_data); + let result = self.set_reference(reference, new, checking_data); match result { Ok(ty) => ty, Err(error) => { @@ -395,7 +312,7 @@ impl<'a> Environment<'a> { // let value = // self.get_variable_or_error(&name, &assignment_span, checking_data); let span = reference.get_position(); - let existing = get_reference(self, reference.clone(), checking_data); + let existing = self.get_reference(reference.clone(), checking_data, true); // TODO existing needs to be cast to number!! @@ -415,7 +332,7 @@ impl<'a> Environment<'a> { self, ); - let result = set_reference(self, reference, new, checking_data); + let result = self.set_reference(reference, new, checking_data); match result { Ok(new) => match return_kind { @@ -438,7 +355,7 @@ impl<'a> Environment<'a> { } } AssignmentKind::ConditionalUpdate(operator) => { - let existing = get_reference(self, reference.clone(), checking_data); + let existing = self.get_reference(reference.clone(), checking_data, true); let expression = expression.unwrap(); let new = evaluate_logical_operation_with_expression( (existing, reference.get_position().without_source()), @@ -449,7 +366,7 @@ impl<'a> Environment<'a> { ) .unwrap(); - let result = set_reference(self, reference, new, checking_data); + let result = self.set_reference(reference, new, checking_data); match result { Ok(new) => new, @@ -468,8 +385,248 @@ impl<'a> Environment<'a> { } } } - Assignable::ObjectDestructuring(_) => todo!(), - Assignable::ArrayDestructuring(_) => todo!(), + Assignable::ObjectDestructuring(assignments) => { + debug_assert!(matches!(operator, AssignmentKind::Assign)); + + let rhs = A::synthesise_expression( + expression.unwrap(), + TypeId::ANY_TYPE, + self, + checking_data, + ); + + self.assign_to_object_destructure_handle_errors( + assignments, + rhs, + assignment_span, + checking_data, + ) + } + Assignable::ArrayDestructuring(assignments) => { + debug_assert!(matches!(operator, AssignmentKind::Assign)); + + let rhs = A::synthesise_expression( + expression.unwrap(), + TypeId::ANY_TYPE, + self, + checking_data, + ); + + self.assign_to_array_destructure_handle_errors( + assignments, + rhs, + assignment_span, + checking_data, + ) + } + } + } + + fn assign_to_reference_assign_handle_errors< + T: crate::ReadFromFS, + A: crate::ASTImplementation, + >( + &mut self, + reference: Reference, + rhs: TypeId, + checking_data: &mut CheckingData, + assignment_span: source_map::BaseSpan<()>, + ) -> TypeId { + let result = self.set_reference(reference, rhs, checking_data); + + match result { + Ok(ty) => ty, + Err(error) => { + let error = set_property_error_to_type_check_error( + self, + error, + assignment_span.with_source(self.get_source()), + &checking_data.types, + rhs, + ); + checking_data.diagnostics_container.add_error(error); + TypeId::ERROR_TYPE + } + } + } + + fn assign_to_assign_only_handle_errors( + &mut self, + lhs: Assignable, + rhs: TypeId, + assignment_span: Span, + checking_data: &mut CheckingData, + ) -> TypeId { + match lhs { + Assignable::Reference(reference) => self.assign_to_reference_assign_handle_errors( + reference, + rhs, + checking_data, + assignment_span, + ), + Assignable::ObjectDestructuring(assignments) => self + .assign_to_object_destructure_handle_errors( + assignments, + rhs, + assignment_span, + checking_data, + ), + Assignable::ArrayDestructuring(assignments) => self + .assign_to_array_destructure_handle_errors( + assignments, + rhs, + assignment_span, + checking_data, + ), + } + } + + fn assign_to_object_destructure_handle_errors< + T: crate::ReadFromFS, + A: crate::ASTImplementation, + >( + &mut self, + assignments: Vec>, + rhs: TypeId, + assignment_span: Span, + checking_data: &mut CheckingData, + ) -> TypeId { + for assignment in assignments { + match assignment { + AssignableObjectDestructuringField::Mapped { + on, + name, + default_value, + position, + } => { + let value = self.get_property( + rhs, + Publicity::Public, + &on, + &mut checking_data.types, + None, + position, + &checking_data.options, + false, + ); + + let rhs_value = if let Some((_, value)) = value { + value + } else if let Some(default_value) = default_value { + A::synthesise_expression( + default_value.as_ref(), + TypeId::ANY_TYPE, + self, + checking_data, + ) + } else { + checking_data.diagnostics_container.add_error( + TypeCheckError::PropertyDoesNotExist { + property: match on { + PropertyKey::String(s) => { + PropertyRepresentation::StringKey(s.to_string()) + } + PropertyKey::Type(t) => PropertyRepresentation::Type( + printing::print_type(t, &checking_data.types, self, false), + ), + }, + on: TypeStringRepresentation::from_type_id( + rhs, + self, + &checking_data.types, + false, + ), + site: position, + }, + ); + + TypeId::ERROR_TYPE + }; + + self.assign_to_assign_only_handle_errors( + name, + rhs_value, + assignment_span, + checking_data, + ); + } + AssignableObjectDestructuringField::Spread(_, _) => todo!(), + } + } + + rhs + } + + #[allow(clippy::needless_pass_by_value)] + fn assign_to_array_destructure_handle_errors< + T: crate::ReadFromFS, + A: crate::ASTImplementation, + >( + &mut self, + _assignments: Vec>, + _rhs: TypeId, + assignment_span: Span, + checking_data: &mut CheckingData, + ) -> TypeId { + checking_data.raise_unimplemented_error( + "destructuring array (needs iterator)", + assignment_span.with_source(self.get_source()), + ); + + TypeId::ERROR_TYPE + } + + fn get_reference( + &mut self, + reference: Reference, + checking_data: &mut CheckingData, + bind_this: bool, + ) -> TypeId { + match reference { + Reference::Variable(name, position) => { + self.get_variable_handle_error(&name, position, checking_data).unwrap().1 + } + Reference::Property { on, with, publicity, span } => { + let get_property_handle_errors = self.get_property_handle_errors( + on, + publicity, + &with, + checking_data, + span, + bind_this, + ); + match get_property_handle_errors { + Ok(i) => i.get_value(), + Err(()) => TypeId::ERROR_TYPE, + } + } + } + } + + fn set_reference( + &mut self, + reference: Reference, + rhs: TypeId, + checking_data: &mut CheckingData, + ) -> Result { + match reference { + Reference::Variable(name, position) => Ok(self.assign_to_variable_handle_errors( + name.as_str(), + position, + rhs, + checking_data, + )), + Reference::Property { on, with, publicity, span } => Ok(self + .set_property( + on, + publicity, + &with, + rhs, + &mut checking_data.types, + Some(span), + &checking_data.options, + )? + .unwrap_or(rhs)), } } @@ -665,8 +822,9 @@ impl<'a> Environment<'a> { property: &PropertyKey, types: &mut TypeStore, with: Option, - position: Span, + position: SpanWithSource, options: &TypeCheckOptions, + bind_this: bool, ) -> Option<(PropertyKind, TypeId)> { crate::types::properties::get_property( on, @@ -676,7 +834,8 @@ impl<'a> Environment<'a> { self, &mut CheckThings { debug_types: options.debug_types }, types, - position.with_source(self.get_source()), + position, + bind_this, ) } @@ -686,7 +845,8 @@ impl<'a> Environment<'a> { publicity: Publicity, key: &PropertyKey, checking_data: &mut CheckingData, - site: Span, + site: SpanWithSource, + bind_this: bool, ) -> Result { let get_property = self.get_property( on, @@ -696,6 +856,7 @@ impl<'a> Environment<'a> { None, site, &checking_data.options, + bind_this, ); if let Some((kind, result)) = get_property { @@ -708,12 +869,13 @@ impl<'a> Environment<'a> { checking_data.diagnostics_container.add_error(TypeCheckError::PropertyDoesNotExist { // TODO printing temp property: match key { - PropertyKey::String(s) => { - crate::diagnostics::PropertyRepresentation::StringKey(s.to_string()) - } - PropertyKey::Type(t) => crate::diagnostics::PropertyRepresentation::Type( - crate::types::printing::print_type(*t, &checking_data.types, self, false), - ), + PropertyKey::String(s) => PropertyRepresentation::StringKey(s.to_string()), + PropertyKey::Type(t) => PropertyRepresentation::Type(printing::print_type( + *t, + &checking_data.types, + self, + false, + )), }, on: crate::diagnostics::TypeStringRepresentation::from_type_id( on, @@ -721,7 +883,7 @@ impl<'a> Environment<'a> { &checking_data.types, false, ), - site: site.with_source(self.get_source()), + site, }); Err(()) } @@ -1219,3 +1381,22 @@ impl<'a> Environment<'a> { None } } + +fn set_property_error_to_type_check_error( + ctx: &impl InformationChain, + error: SetPropertyError, + assignment_span: SpanWithSource, + types: &TypeStore, + new: TypeId, +) -> TypeCheckError<'static> { + match error { + SetPropertyError::NotWriteable => TypeCheckError::PropertyNotWriteable(assignment_span), + SetPropertyError::DoesNotMeetConstraint { property_constraint, reason: _ } => { + TypeCheckError::AssignmentError(AssignmentError::PropertyConstraint { + property_constraint, + value_type: TypeStringRepresentation::from_type_id(new, ctx, types, false), + assignment_position: assignment_span, + }) + } + } +} diff --git a/checker/src/context/information.rs b/checker/src/context/information.rs index ed4caa22..df7d43d0 100644 --- a/checker/src/context/information.rs +++ b/checker/src/context/information.rs @@ -41,7 +41,7 @@ pub struct LocalInformation { /// Object type (LHS), must always be RHS /// - /// *not quite the best place, but used in InformationChain* + /// *not quite the best place, but used in [`InformationChain`]* pub(crate) object_constraints: HashMap, /// For super calls etc @@ -237,7 +237,7 @@ pub(crate) fn get_property_unbound( // under @ PropertyKey::String(_) => under, // }; - types.get_fact_about_type(info, on, None, &get_property, (publicity, &under)) + types.get_fact_about_type(info, on, None, &get_property, (publicity, under)) } fn get_property_under( diff --git a/checker/src/context/mod.rs b/checker/src/context/mod.rs index df263655..d3a31391 100644 --- a/checker/src/context/mod.rs +++ b/checker/src/context/mod.rs @@ -168,7 +168,7 @@ pub struct Context { pub(crate) deferred_function_constraints: HashMap, pub(crate) bases: bases::Bases, - /// TODO replace with info.value_of_this + /// TODO replace with `info.value_of_this` pub(crate) can_reference_this: CanReferenceThis, /// When a objects `TypeId` is in here getting a property returns a constructor rather than diff --git a/checker/src/diagnostics.rs b/checker/src/diagnostics.rs index 0b84f1df..efdd025b 100644 --- a/checker/src/diagnostics.rs +++ b/checker/src/diagnostics.rs @@ -459,7 +459,7 @@ mod defined_errors_and_warnings { if let Some((restriction_pos, restriction)) = restriction { Diagnostic::PositionWithAdditionalLabels { reason: format!( - "Argument of type {argument_type} is not assignable to parameter of type {restriction}" + "Argument of type {argument_type} is not assignable to parameter of type {restriction}" ), position: argument_position, labels: vec![( @@ -558,6 +558,13 @@ mod defined_errors_and_warnings { kind, } } + FunctionCallingError::MismatchedThis { call_site, expected, found } => Diagnostic::Position { + reason: format!( + "The 'this' context of the function is expected to be {expected}, found {found}" + ), + position: call_site, + kind, + }, }, TypeCheckError::AssignmentError(error) => match error { AssignmentError::DoesNotMeetConstraint { diff --git a/checker/src/events/application.rs b/checker/src/events/application.rs index 7e408691..8e9c08d6 100644 --- a/checker/src/events/application.rs +++ b/checker/src/events/application.rs @@ -95,9 +95,17 @@ pub(crate) fn apply_event( under @ PropertyKey::String(_) => under, }; - let Some((_, value)) = - get_property(on, publicity, &under, None, environment, target, types, position) - else { + let Some((_, value)) = get_property( + on, + publicity, + &under, + None, + environment, + target, + types, + position, + true, + ) else { panic!( "could not get property {under:?} at {position:?} on {}, (inference or some checking failed)", print_type(on, types, environment, true) diff --git a/checker/src/events/mod.rs b/checker/src/events/mod.rs index e8be4eed..0e53b24a 100644 --- a/checker/src/events/mod.rs +++ b/checker/src/events/mod.rs @@ -71,7 +71,7 @@ pub enum Event { new: PropertyValue, /// THIS DOES NOT CALL SETTERS, JUST SETS VALUE! /// TODO this is [define] property - /// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Public_class_fields + /// see initialization: bool, publicity: Publicity, position: Option, @@ -107,7 +107,7 @@ pub enum Event { /// - Creating objects with prototypes: /// - Arrays /// - Map & Sets - /// - HTMLElement and derivatives + /// - `HTMLElement` and derivatives /// /// ```typescript /// function x() { @@ -119,7 +119,7 @@ pub enum Event { /// not in externally referenced set or something CreateObject { prototype: PrototypeArgument, - /// This is the id referencing a [Type::AliasTo] that is created + /// This is the id referencing a [`Type::AliasTo`] that is created /// /// This is also for the specialisation (somehow) referenced_in_scope_as: TypeId, diff --git a/checker/src/features/assignments.rs b/checker/src/features/assignments.rs index d4bc4293..3c16acc7 100644 --- a/checker/src/features/assignments.rs +++ b/checker/src/features/assignments.rs @@ -4,10 +4,10 @@ use crate::{context::information::Publicity, types::properties::PropertyKey, Typ use super::operations::{LogicalOperator, MathematicalAndBitwise}; -pub enum Assignable { +pub enum Assignable { Reference(Reference), - ObjectDestructuring(Vec<(PropertyKey<'static>, Assignable)>), - ArrayDestructuring(Vec>), + ObjectDestructuring(Vec>), + ArrayDestructuring(Vec>), } // TODO derive copy, when span derives copy @@ -18,6 +18,25 @@ pub enum Reference { Property { on: TypeId, with: PropertyKey<'static>, publicity: Publicity, span: SpanWithSource }, } +pub enum AssignableObjectDestructuringField { + /// `{ x: y }` + Mapped { + on: PropertyKey<'static>, + name: Assignable, + default_value: Option>>, + position: SpanWithSource, + }, + /// `{ ...x }` + Spread(Assignable, SpanWithSource), +} + +pub enum AssignableArrayDestructuringField { + Spread(Assignable, SpanWithSource), + Name(Assignable, Option>>), + Comment { content: String, is_multiline: bool, position: SpanWithSource }, + None, +} + /// Increment and decrement are are not binary add subtract as they cast their lhs to number pub enum AssignmentKind { Assign, diff --git a/checker/src/features/functions.rs b/checker/src/features/functions.rs index 698e60b3..4ec6d8c8 100644 --- a/checker/src/features/functions.rs +++ b/checker/src/features/functions.rs @@ -371,7 +371,7 @@ pub enum FunctionRegisterBehavior<'a, A: crate::ASTImplementation> { }, Constructor { prototype: TypeId, - /// Is this is_some then can use `super()` + /// Is this [`Option::is_some`] then can use `super()` super_type: Option, properties: ClassPropertiesToRegister<'a, A>, }, diff --git a/checker/src/features/variables.rs b/checker/src/features/variables.rs index 61d80eae..89d3b7cb 100644 --- a/checker/src/features/variables.rs +++ b/checker/src/features/variables.rs @@ -19,7 +19,7 @@ pub enum VariableOrImport { /// Whether can be reassigned and what to mutability: VariableMutability, /// Location where variable is defined **ALSO UNIQUELY IDENTIFIES THE VARIABLE** as can - /// be turned into a [VariableId] + /// be turned into a [`VariableId`] declared_at: SpanWithSource, context: ContextLocation, }, @@ -136,6 +136,8 @@ pub fn get_new_register_argument_under, at: Span, ) -> VariableRegisterArguments { + let position = at.with_source(environment.get_source()); + let space = on.space.map(|space| { let property_constraint = get_property_unbound( space, @@ -168,7 +170,7 @@ pub fn get_new_register_argument_under (Names, LocalInformation); /// Expected is used for eagerly setting function parameters - fn synthesise_expression<'a, T: crate::ReadFromFS>( - expression: &'a Self::Expression<'a>, + fn synthesise_expression( + expression: &Self::Expression<'_>, expected_type: TypeId, environment: &mut Environment, checking_data: &mut crate::CheckingData, diff --git a/checker/src/options.rs b/checker/src/options.rs index 5bf2078d..785ef24b 100644 --- a/checker/src/options.rs +++ b/checker/src/options.rs @@ -16,7 +16,7 @@ pub struct TypeCheckOptions { /// Given a `function x`, `x = 2` is not possible pub constant_function_declarations: bool, - /// Whether auto casts can happen. aka `{} + 2` is allowed using Object[ToPrimitiveDefault] + /// Whether auto casts can happen. aka `{} + 2` is allowed using the Object's primitive default /// TODO maybe levels pub strict_casts: bool, diff --git a/checker/src/synthesis/assignments.rs b/checker/src/synthesis/assignments.rs index 17047b3d..aa15ece3 100644 --- a/checker/src/synthesis/assignments.rs +++ b/checker/src/synthesis/assignments.rs @@ -1,12 +1,16 @@ use std::borrow::Cow; use parser::{ - ast::LHSOfAssignment, expressions::assignments::VariableOrPropertyAccess, VariableIdentifier, + ast::LHSOfAssignment, expressions::assignments::VariableOrPropertyAccess, VariableField, + VariableIdentifier, }; use crate::{ context::{information::Publicity, Environment}, - features::assignments::{Assignable, Reference}, + features::assignments::{ + Assignable, AssignableArrayDestructuringField, AssignableObjectDestructuringField, + Reference, + }, synthesis::expressions::synthesise_expression, types::properties::PropertyKey, CheckingData, TypeId, @@ -20,89 +24,131 @@ pub(super) fn synthesise_lhs_of_assignment_to_reference( lhs: &LHSOfAssignment, environment: &mut Environment, checking_data: &mut CheckingData, -) -> Assignable { +) -> Assignable { match lhs { - LHSOfAssignment::ObjectDestructuring(items, _) => Assignable::ObjectDestructuring( - items - .iter() - .map(|item| match item.get_ast_ref() { - parser::ObjectDestructuringField::Name(name, _, _) => { - let on = if let VariableIdentifier::Standard(name, _) = name { - PropertyKey::String(Cow::Owned(name.clone())) - } else { - todo!() - }; - ( - on, - synthesise_object_shorthand_assignable( - name, - checking_data, - environment, - ), - ) - } - parser::ObjectDestructuringField::Spread(_, _) => todo!(), - parser::ObjectDestructuringField::Map { - from, - name, - default_value: _, - position: _, - } => { - // TODO into function - match name.get_ast_ref() { - parser::VariableField::Name(name) => { - let on = parser_property_key_to_checker_property_key( - from, - environment, - checking_data, - true, - ); - let a = synthesise_object_shorthand_assignable( - name, - checking_data, - environment, - ); - (on, a) - } - parser::VariableField::Array(_, _) => todo!(), - parser::VariableField::Object(_, _) => todo!(), - } - } - }) - .collect(), - ), - LHSOfAssignment::ArrayDestructuring(items, _) => Assignable::ArrayDestructuring( - items - .iter() - .map(|item| match item.get_ast_ref() { - parser::ArrayDestructuringField::Spread(_, _) => todo!(), - parser::ArrayDestructuringField::Name(name, _) => match name { - parser::VariableField::Name(name) => { - Some(synthesise_object_shorthand_assignable( - name, - checking_data, - environment, - )) - } - parser::VariableField::Array(_, _) => todo!(), - parser::VariableField::Object(_, _) => todo!(), - }, - parser::ArrayDestructuringField::Comment { .. } - | parser::ArrayDestructuringField::None => None, - }) - .collect(), - ), + LHSOfAssignment::ObjectDestructuring(items, _) => { + synthesise_object_to_reference(items, environment, checking_data) + } + LHSOfAssignment::ArrayDestructuring(items, _) => { + synthesise_array_to_reference(items, environment, checking_data) + } LHSOfAssignment::VariableOrPropertyAccess(access) => Assignable::Reference( synthesise_access_to_reference(access, environment, checking_data), ), } } +fn synthesise_variable_field_to_reference( + variable_field: &VariableField, + environment: &mut Environment, + checking_data: &mut CheckingData, +) -> Assignable { + match variable_field { + VariableField::Object(items, _) => { + synthesise_object_to_reference(items, environment, checking_data) + } + VariableField::Array(items, _) => { + synthesise_array_to_reference(items, environment, checking_data) + } + VariableField::Name(ident) => Assignable::Reference(match ident { + VariableIdentifier::Standard(name, position) => { + Reference::Variable(name.clone(), position.with_source(environment.get_source())) + } + VariableIdentifier::Marker(_, _) => todo!(), + }), + } +} + +fn synthesise_object_to_reference( + items: &[parser::WithComment], + environment: &mut Environment, + checking_data: &mut CheckingData, +) -> Assignable { + Assignable::ObjectDestructuring( + items + .iter() + .map(|item| match item.get_ast_ref() { + parser::ObjectDestructuringField::Name(name, default_value, position) => { + AssignableObjectDestructuringField::Mapped { + on: synthesise_object_property_key(name, environment), + name: synthesise_object_shorthand_assignable( + name, + environment, + checking_data, + ), + default_value: default_value.clone(), + position: position.with_source(environment.get_source()), + } + } + parser::ObjectDestructuringField::Spread(name, position) => { + AssignableObjectDestructuringField::Spread( + synthesise_object_shorthand_assignable(name, environment, checking_data), + position.with_source(environment.get_source()), + ) + } + parser::ObjectDestructuringField::Map { from, name, default_value, position } => { + let on = parser_property_key_to_checker_property_key( + from, + environment, + checking_data, + true, + ); + + AssignableObjectDestructuringField::Mapped { + on, + name: synthesise_variable_field_to_reference( + name.get_ast_ref(), + environment, + checking_data, + ), + default_value: default_value.clone(), + position: position.with_source(environment.get_source()), + } + } + }) + .collect(), + ) +} + +fn synthesise_array_to_reference( + items: &[parser::WithComment], + environment: &mut Environment, + checking_data: &mut CheckingData, +) -> Assignable { + Assignable::ArrayDestructuring( + items + .iter() + .map(|item| match item.get_ast_ref() { + parser::ArrayDestructuringField::Spread(name, position) => { + AssignableArrayDestructuringField::Spread( + synthesise_variable_field_to_reference(name, environment, checking_data), + position.with_source(environment.get_source()), + ) + } + parser::ArrayDestructuringField::Name(name, default_value) => { + AssignableArrayDestructuringField::Name( + synthesise_variable_field_to_reference(name, environment, checking_data), + default_value.clone(), + ) + } + parser::ArrayDestructuringField::Comment { content, is_multiline, position } => { + AssignableArrayDestructuringField::Comment { + content: content.clone(), + is_multiline: *is_multiline, + position: position.with_source(environment.get_source()), + } + } + parser::ArrayDestructuringField::None => AssignableArrayDestructuringField::None, + }) + .collect(), + ) +} + fn synthesise_object_shorthand_assignable( name: &parser::VariableIdentifier, - _checking_data: &CheckingData, environment: &Environment, -) -> Assignable { + _checking_data: &CheckingData, +) -> Assignable { match name { parser::VariableIdentifier::Standard(name, pos) => Assignable::Reference( Reference::Variable(name.clone(), pos.with_source(environment.get_source())), @@ -111,6 +157,18 @@ fn synthesise_object_shorthand_assignable( } } +fn synthesise_object_property_key( + name: &parser::VariableIdentifier, + environment: &Environment, +) -> PropertyKey<'static> { + match name { + parser::VariableIdentifier::Standard(name, pos) => { + PropertyKey::String(Cow::Owned(name.to_owned())) + } + parser::VariableIdentifier::Marker(..) => todo!(), + } +} + pub(crate) fn synthesise_access_to_reference( variable_or_property_access: &VariableOrPropertyAccess, environment: &mut Environment, diff --git a/checker/src/synthesis/expressions.rs b/checker/src/synthesis/expressions.rs index 3693265d..3d23e6fd 100644 --- a/checker/src/synthesis/expressions.rs +++ b/checker/src/synthesis/expressions.rs @@ -429,8 +429,7 @@ pub(super) fn synthesise_expression( } } Expression::Assignment { lhs, rhs, position } => { - let lhs: Assignable = - synthesise_lhs_of_assignment_to_reference(lhs, environment, checking_data); + let lhs = synthesise_lhs_of_assignment_to_reference(lhs, environment, checking_data); return environment.assign_to_assignable_handle_errors( lhs, @@ -441,7 +440,7 @@ pub(super) fn synthesise_expression( ); } Expression::BinaryAssignmentOperation { lhs, operator, rhs, position } => { - let lhs: Assignable = Assignable::Reference(synthesise_access_to_reference( + let lhs = Assignable::Reference(synthesise_access_to_reference( lhs, environment, checking_data, @@ -456,7 +455,7 @@ pub(super) fn synthesise_expression( ); } Expression::UnaryPrefixAssignmentOperation { operator, operand, position } => { - let lhs: Assignable = Assignable::Reference(synthesise_access_to_reference( + let lhs = Assignable::Reference(synthesise_access_to_reference( operand, environment, checking_data, @@ -486,7 +485,7 @@ pub(super) fn synthesise_expression( } } Expression::UnaryPostfixAssignmentOperation { operand, operator, position } => { - let lhs: Assignable = Assignable::Reference(synthesise_access_to_reference( + let lhs = Assignable::Reference(synthesise_access_to_reference( operand, environment, checking_data, @@ -549,7 +548,8 @@ pub(super) fn synthesise_expression( publicity, &property, checking_data, - *position, + position.with_source(environment.get_source()), + true, ); match result { @@ -573,7 +573,8 @@ pub(super) fn synthesise_expression( Publicity::Public, &PropertyKey::from_type(indexer, &checking_data.types), checking_data, - *position, + position.with_source(environment.get_source()), + true, ); match result { diff --git a/checker/src/synthesis/variables.rs b/checker/src/synthesis/variables.rs index bee00fe4..94e7e431 100644 --- a/checker/src/synthesis/variables.rs +++ b/checker/src/synthesis/variables.rs @@ -8,10 +8,10 @@ use parser::{ use super::expressions::synthesise_expression; use crate::{ context::{information::Publicity, Context, ContextType, VariableRegisterArguments}, - diagnostics::TypeCheckError, + diagnostics::{PropertyRepresentation, TypeCheckError, TypeStringRepresentation}, features::variables::{get_new_register_argument_under, VariableMutability}, synthesis::parser_property_key_to_checker_property_key, - types::properties::PropertyKey, + types::{printing, properties::PropertyKey}, CheckingData, Environment, TypeId, }; @@ -268,8 +268,8 @@ fn assign_initial_to_fields( match item.get_ast_ref() { ObjectDestructuringField::Spread(_, _) => todo!(), ObjectDestructuringField::Name(name, default_value, _) => { - let id = - crate::VariableId(environment.get_source(), name.get_position().start); + let position = name.get_position().with_source(environment.get_source()); + let id = crate::VariableId(environment.get_source(), position.start); let key_ty = match name { VariableIdentifier::Standard(name, _) => { @@ -286,13 +286,13 @@ fn assign_initial_to_fields( &key_ty, &mut checking_data.types, None, - name.get_position(), + position, &checking_data.options, + false, ); let value = match property { Some((_, value)) => value, None => { - // TODO non decidable error if let Some(else_expression) = default_value { synthesise_expression( else_expression, @@ -301,7 +301,33 @@ fn assign_initial_to_fields( TypeId::ANY_TYPE, ) } else { - // TODO emit error + checking_data.diagnostics_container.add_error( + TypeCheckError::PropertyDoesNotExist { + property: match key_ty { + PropertyKey::String(s) => { + PropertyRepresentation::StringKey(s.to_string()) + } + PropertyKey::Type(t) => { + PropertyRepresentation::Type( + printing::print_type( + t, + &checking_data.types, + environment, + false, + ), + ) + } + }, + on: TypeStringRepresentation::from_type_id( + value, + environment, + &checking_data.types, + false, + ), + site: position, + }, + ); + TypeId::ERROR_TYPE } } @@ -326,14 +352,14 @@ fn assign_initial_to_fields( &key_ty, &mut checking_data.types, None, - *position, + position.with_source(environment.get_source()), &checking_data.options, + false, ); let value = match property_value { Some((_, value)) => value, None => { - // TODO non decidable error if let Some(default_value) = default_value { synthesise_expression( default_value, @@ -342,7 +368,33 @@ fn assign_initial_to_fields( TypeId::ANY_TYPE, ) } else { - // TODO emit error + checking_data.diagnostics_container.add_error( + TypeCheckError::PropertyDoesNotExist { + property: match key_ty { + PropertyKey::String(s) => { + PropertyRepresentation::StringKey(s.to_string()) + } + PropertyKey::Type(t) => { + PropertyRepresentation::Type( + printing::print_type( + t, + &checking_data.types, + environment, + false, + ), + ) + } + }, + on: TypeStringRepresentation::from_type_id( + value, + environment, + &checking_data.types, + false, + ), + site: position.with_source(environment.get_source()), + }, + ); + TypeId::ERROR_TYPE } } diff --git a/checker/src/types/calling.rs b/checker/src/types/calling.rs index 83bb1548..235f31fc 100644 --- a/checker/src/types/calling.rs +++ b/checker/src/types/calling.rs @@ -55,7 +55,7 @@ struct FunctionLike { pub(crate) function: FunctionId, /// For generic calls pub(crate) from: Option, - /// From, maybe ignored if [CalledWithNew] overrides + /// From, maybe ignored if [`CalledWithNew`] overrides pub(crate) this_value: ThisValue, } @@ -270,18 +270,12 @@ fn get_logical_callable_from_type( Logical::Implies { on: Box::new(res), antecedent: generic.arguments.clone() } }) } - Type::Constructor(Constructor::Property { on, under: _, result, bind_this: true }) => { - // bind_this from #98 - // Bind does not happen for theses calls, so done here *conditionally on `bind_this`* - + Type::Constructor(Constructor::Property { on, under: _, result, bind_this }) => { crate::utils::notify!("Passing {:?}", on); - let result = get_logical_callable_from_type( - *result, - Some(ThisValue::Passed(*on)), - Some(ty), - types, - )?; + let this_value = if *bind_this { ThisValue::Passed(*on) } else { ThisValue::UseParent }; + let result = + get_logical_callable_from_type(*result, Some(this_value), Some(ty), types)?; if let Some(antecedent) = get_constraint(*on, types).and_then(|c| { if let Type::Constructor(Constructor::StructureGenerics(generic)) = @@ -524,6 +518,11 @@ pub enum FunctionCallingError { /// Should be set call_site: Option, }, + MismatchedThis { + expected: TypeStringRepresentation, + found: TypeStringRepresentation, + call_site: SpanWithSource, + }, } pub struct InfoDiagnostic(pub String); @@ -705,6 +704,7 @@ impl FunctionType { types, &mut errors, call_site, + behavior, ); let local_arguments = self.assign_arguments_to_parameters::( @@ -721,10 +721,10 @@ impl FunctionType { let mut type_arguments = FunctionTypeArguments { local_arguments, - closure_ids: if let Some(StructureGenericArguments::Closure(cs)) = structure_generics { - cs - } else { - Default::default() + #[allow(clippy::manual_unwrap_or_default)] + closure_ids: match structure_generics { + Some(StructureGenericArguments::Closure(cs)) => cs, + _ => Vec::new(), }, call_site, }; @@ -920,7 +920,7 @@ impl FunctionType { } #[allow(clippy::too_many_arguments)] - fn set_this_for_behavior( + fn set_this_for_behavior( &self, called_with_new: CalledWithNew, this_value: ThisValue, @@ -929,6 +929,7 @@ impl FunctionType { types: &mut TypeStore, errors: &mut ErrorsAndInfo, call_site: source_map::BaseSpan, + behavior: &E, ) { match self.behavior { FunctionBehavior::ArrowFunction { .. } => {} @@ -943,6 +944,44 @@ impl FunctionType { TypeId::UNDEFINED_TYPE }; + let base_type = get_constraint(free_this_id, types).unwrap_or(free_this_id); + + let mut basic_subtyping = BasicEquality { + add_property_restrictions: false, + position: call_site, + object_constraints: Default::default(), + allow_errors: true, + }; + + let type_is_subtype = type_is_subtype( + base_type, + value_of_this, + &mut basic_subtyping, + environment, + types, + ); + + match type_is_subtype { + SubTypeResult::IsSubType => {} + SubTypeResult::IsNotSubType(reason) => { + errors.errors.push(FunctionCallingError::MismatchedThis { + expected: TypeStringRepresentation::from_type_id( + free_this_id, + environment, + types, + behavior.debug_types(), + ), + found: TypeStringRepresentation::from_type_id( + value_of_this, + environment, + types, + behavior.debug_types(), + ), + call_site, + }); + } + } + crate::utils::notify!( "free this id {:?} & value of this {:?}", free_this_id, diff --git a/checker/src/types/functions.rs b/checker/src/types/functions.rs index 3b39d6c1..06cc8a99 100644 --- a/checker/src/types/functions.rs +++ b/checker/src/types/functions.rs @@ -189,7 +189,7 @@ pub struct SynthesisedParameter { #[derive(Clone, Debug, binary_serialize_derive::BinarySerializable)] pub struct SynthesisedRestParameter { pub name: String, - /// This is the item type, aka the `T`` of `Array` + /// This is the item type, aka the `T` of `Array` pub item_type: TypeId, /// This is the generic type (to substitute into) pub ty: TypeId, diff --git a/checker/src/types/mod.rs b/checker/src/types/mod.rs index d23151ff..482e052d 100644 --- a/checker/src/types/mod.rs +++ b/checker/src/types/mod.rs @@ -289,7 +289,6 @@ pub enum Constructor { on: TypeId, under: PropertyKey<'static>, result: TypeId, - /// See issue #98 bind_this: bool, }, /// For await a poly type diff --git a/checker/src/types/properties.rs b/checker/src/types/properties.rs index 5b557a71..e0bcba30 100644 --- a/checker/src/types/properties.rs +++ b/checker/src/types/properties.rs @@ -188,6 +188,7 @@ pub(crate) fn get_property( behavior: &mut E, types: &mut TypeStore, position: SpanWithSource, + bind_this: bool, ) -> Option<(PropertyKind, TypeId)> { if on == TypeId::ERROR_TYPE || matches!(under, PropertyKey::Type(under) if *under == TypeId::ERROR_TYPE) @@ -215,6 +216,7 @@ pub(crate) fn get_property( behavior, types, position, + bind_this, ) } else if top_environment.possibly_mutated_objects.contains(&on) { let Some(constraint) = top_environment.get_object_constraint(on) else { @@ -231,13 +233,14 @@ pub(crate) fn get_property( behavior, types, position, + bind_this, ) } else { // if environment.get_poly_base(under, types).is_some() { // todo!() // } // TODO - get_from_an_object(on, publicity, under, top_environment, behavior, types) + get_from_an_object(on, publicity, under, top_environment, behavior, types, bind_this) } } @@ -248,6 +251,7 @@ fn get_from_an_object( environment: &mut Environment, behavior: &mut E, types: &mut TypeStore, + bind_this: bool, ) -> Option<(PropertyKind, TypeId)> { /// Generates closure arguments, values of this and more. Runs getters fn resolve_property_on_logical( @@ -257,6 +261,7 @@ fn get_from_an_object( environment: &mut Environment, types: &mut TypeStore, behavior: &mut E, + bind_this: bool, ) -> Option<(PropertyKind, TypeId)> { match logical { Logical::Pure(property) => { @@ -264,10 +269,14 @@ fn get_from_an_object( PropertyValue::Value(value) => { let ty = types.get_type_by_id(value); match ty { - // TODO function :: bind_this Type::SpecialObject(SpecialObjects::Function(func, _state)) => { + let this_value = if bind_this { + ThisValue::Passed(on) + } else { + ThisValue::UseParent + }; let func = types.register_type(Type::SpecialObject( - SpecialObjects::Function(*func, ThisValue::Passed(on)), + SpecialObjects::Function(*func, this_value), )); Some((PropertyKind::Direct, func)) @@ -372,6 +381,7 @@ fn get_from_an_object( environment, types, behavior, + bind_this, ), } } @@ -388,7 +398,7 @@ fn get_from_an_object( // ? is okay here let result = get_property_unbound(on, publicity, under, types, environment).ok()?; - resolve_property_on_logical(result, on, None, environment, types, behavior) + resolve_property_on_logical(result, on, None, environment, types, behavior, bind_this) } #[allow(clippy::too_many_arguments)] @@ -402,6 +412,7 @@ fn evaluate_get_on_poly( behavior: &mut E, types: &mut TypeStore, position: SpanWithSource, + bind_this: bool, ) -> Option<(PropertyKind, TypeId)> { fn resolve_logical_with_poly( fact: Logical, @@ -411,6 +422,7 @@ fn evaluate_get_on_poly( arguments: Option<&StructureGenericArguments>, environment: &mut Environment, types: &mut TypeStore, + bind_this: bool, ) -> Option { match fact { Logical::Pure(og) => { @@ -431,8 +443,7 @@ fn evaluate_get_on_poly( on, under: under.into_owned(), result, - // TODO #98 - bind_this: true, + bind_this, })) } // Don't need to set this here. It is picked up from `on` during lookup @@ -441,15 +452,12 @@ fn evaluate_get_on_poly( | Type::AliasTo { .. } | Type::Object(ObjectNature::AnonymousTypeAnnotation) | Type::Interface { .. } - | Type::Class { .. } => { - types.register_type(Type::Constructor(Constructor::Property { - on, - under: under.into_owned(), - result: value, - // TODO #98 - bind_this: true, - })) - } + | Type::Class { .. } => types.register_type(Type::Constructor(Constructor::Property { + on, + under: under.into_owned(), + result: value, + bind_this, + })), Type::Constant(_) | Type::Object(ObjectNature::RealDeal) | Type::SpecialObject(..) => value, @@ -497,9 +505,17 @@ fn evaluate_get_on_poly( arguments, environment, types, + bind_this, + )?; + let rhs = resolve_logical_with_poly( + rhs, + on, + under, + arguments, + environment, + types, + bind_this, )?; - let rhs = - resolve_logical_with_poly(rhs, on, under, arguments, environment, types)?; Some(types.new_conditional_type(based_on, lhs, rhs)) } else { crate::utils::notify!("TODO emit some diagnostic about missing"); @@ -518,6 +534,7 @@ fn evaluate_get_on_poly( Some(&antecedent), environment, types, + bind_this, ) } } @@ -527,7 +544,15 @@ fn evaluate_get_on_poly( // crate::utils::notify!("unbound is is {:?}", fact); - let value = resolve_logical_with_poly(fact, on, under.clone(), None, top_environment, types)?; + let value = resolve_logical_with_poly( + fact, + on, + under.clone(), + None, + top_environment, + types, + bind_this, + )?; behavior.get_latest_info(top_environment).events.push(Event::Getter { on, diff --git a/checker/src/types/store.rs b/checker/src/types/store.rs index 045defec..5f41725c 100644 --- a/checker/src/types/store.rs +++ b/checker/src/types/store.rs @@ -28,7 +28,7 @@ use super::{ /// Holds all the types. Eventually may be split across modules #[derive(Debug, binary_serialize_derive::BinarySerializable)] pub struct TypeStore { - /// Contains all of the types. Indexed by [TypeId] + /// Contains all of the types. Indexed by [`TypeId`] types: Vec, /// Some types are prototypes but have generic parameters but @@ -626,14 +626,19 @@ impl TypeStore { .map(Logical::Pure) .or_else(|| { let backing_type = cst.get_backing_type_id(); - self.get_fact_about_type( - info_chain, - backing_type, - on_type_arguments, - resolver, - data, - ) - .ok() + + if on == backing_type { + None + } else { + self.get_fact_about_type( + info_chain, + backing_type, + on_type_arguments, + resolver, + data, + ) + .ok() + } }) .ok_or(crate::context::Missing::None), Type::SpecialObject(_) => todo!(), diff --git a/checker/src/types/subtyping.rs b/checker/src/types/subtyping.rs index 8b916195..79575e0c 100644 --- a/checker/src/types/subtyping.rs +++ b/checker/src/types/subtyping.rs @@ -709,6 +709,31 @@ pub(crate) fn type_is_subtype_with_generics<'a, T: SubTypeBehavior<'a>>( SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } } + Type::Object(..) => subtype_properties( + base_type, + base_structure_arguments, + ty, + ty_structure_arguments, + behavior, + environment, + types, + mode, + already_checked, + ), + Type::Constructor(Constructor::StructureGenerics(StructureGenerics { + on, + arguments, + })) => type_is_subtype_with_generics( + base_type, + base_structure_arguments, + *on, + GenericChainLink::append(ty_structure_arguments.as_ref(), arguments), + behavior, + environment, + types, + mode, + already_checked, + ), _ => SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch), }, Type::Interface { nominal: base_type_nominal, .. } => { @@ -979,6 +1004,14 @@ fn subtype_properties<'a, T: SubTypeBehavior<'a>>( mode: SubTypingMode, already_checked: &mut AlreadyChecked, ) -> SubTypeResult { + // TODO (#128): This is a compromise where only boolean and number types are treated as nominal + match base_type { + TypeId::BOOLEAN_TYPE | TypeId::NUMBER_TYPE if base_type != ty => { + return SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) + } + _ => {} + } + let mode = mode.one_deeper(); let mut property_errors = Vec::new(); @@ -991,10 +1024,16 @@ fn subtype_properties<'a, T: SubTypeBehavior<'a>>( crate::utils::notify!("key {:?} with {:?}", key, base_type_arguments); let key = match key { - PropertyKey::Type(ty) => PropertyKey::from_type( - base_type_arguments.unwrap().get_single_argument(*ty).unwrap_or(*ty), - types, - ), + PropertyKey::Type(ty) => { + if let Some(base_type_arguments) = base_type_arguments { + PropertyKey::from_type( + base_type_arguments.get_single_argument(*ty).unwrap_or(*ty), + types, + ) + } else { + key.clone() + } + } PropertyKey::String(_) => key.clone(), }; @@ -1088,7 +1127,36 @@ fn check_lhs_property_is_super_type_of_rhs<'a, T: SubTypeBehavior<'a>>( Err(..) => Err(PropertyError::Missing), } } - PropertyValue::Getter(_) => todo!(), + PropertyValue::Getter(getter) => { + let rhs_property = get_property_unbound(ty, publicity, key, types, environment); + crate::utils::notify!("looking for {:?} found {:?}", key, rhs_property); + + match rhs_property { + Ok(rhs_property) => { + let res = check_logical_property( + getter.return_type, + base_type_arguments, + rhs_property, + right_type_arguments, + behavior, + environment, + types, + mode, + already_checked, + ); + match res { + SubTypeResult::IsSubType => Ok(()), + SubTypeResult::IsNotSubType(err) => Err(PropertyError::Invalid { + expected: TypeId::UNIMPLEMENTED_ERROR_TYPE, + found: TypeId::UNIMPLEMENTED_ERROR_TYPE, + mismatch: err, + }), + } + } + // TODO + Err(..) => Err(PropertyError::Missing), + } + } PropertyValue::Setter(_) => todo!(), PropertyValue::Deleted => { // TODO WIP diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index b5a1ada0..5ba90dcb 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -18,7 +18,7 @@ use derive_finite_automaton::{ pub struct LexerOptions { /// Whether to append tokens when lexing. If false will just ignore pub comments: Comments, - /// Whether to parse JSX. TypeScripts ` 2` breaks the lexer so this can be disabled to allow + /// Whether to parse JSX. TypeScript's ` 2` breaks the lexer so this can be disabled to allow /// for that syntax pub lex_jsx: bool, /// TODO temp diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 5cd10945..b308af85 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -110,7 +110,7 @@ pub struct ParseOptions { pub decorators: bool, /// Skip **all** comments from the AST pub comments: Comments, - /// See [crate::extensions::is_expression::IsExpression] + /// See [`crate::extensions::is_expression::IsExpression`] pub is_expressions: bool, /// Allows functions to be prefixed with 'server' pub custom_function_headers: bool, diff --git a/parser/src/tokens.rs b/parser/src/tokens.rs index 340bda62..bb10f15f 100644 --- a/parser/src/tokens.rs +++ b/parser/src/tokens.rs @@ -91,33 +91,33 @@ use crate::{ParseError, Quoted}; pub enum TSXToken { Identifier(String), Keyword(TSXKeyword), - NumberLiteral(String), + NumberLiteral(String), StringLiteral(String, Quoted), MultiLineComment(String), Comment(String), RegexLiteral(String), RegexFlagLiteral(String), TemplateLiteralStart, TemplateLiteralChunk(String), TemplateLiteralEnd, TemplateLiteralExpressionStart, TemplateLiteralExpressionEnd, - Comma, SemiColon, Colon, Dot, + Comma, SemiColon, Colon, Dot, /// @ At, - Spread, Assign, + Spread, Assign, /// `=>` Arrow, - /// `(` - OpenParentheses, - /// `)` - CloseParentheses, - /// `{` - OpenBrace, - /// `}` - CloseBrace, - /// `[` - OpenBracket, - /// `]` - CloseBracket, - /// `<` - OpenChevron, - /// `>` + /// `(` + OpenParentheses, + /// `)` + CloseParentheses, + /// `{` + OpenBrace, + /// `}` + CloseBrace, + /// `[` + OpenBracket, + /// `]` + CloseBracket, + /// `<` + OpenChevron, + /// `>` CloseChevron, Add, Subtract, Multiply, Divide, QuestionMark, Exponent, Modulo, @@ -132,19 +132,19 @@ pub enum TSXToken { Equal, NotEqual, StrictEqual, StrictNotEqual, GreaterThanEqual, LessThanEqual, OptionalChain, OptionalCall, OptionalIndex, NullishCoalescing, NullishCoalescingAssign, - /// `?:` - OptionalMember, - /// '!:` - NonOptionalMember, + /// `?:` + OptionalMember, + /// `!:` + NonOptionalMember, /// For scripts thing HashTag, // JSX Tokens. Some like JSXComment are non standard - JSXOpeningTagStart, JSXTagName(String), JSXOpeningTagEnd, - JSXClosingTagStart, - /// This also covers the end of a token, thus no 'TSXToken::JSXClosingTagEnd' - JSXClosingTagName(String), + JSXOpeningTagStart, JSXTagName(String), JSXOpeningTagEnd, + JSXClosingTagStart, + /// This also covers the end of a token, thus no `TSXToken::JSXClosingTagEnd` + JSXClosingTagName(String), /// /> - JSXSelfClosingTag, + JSXSelfClosingTag, JSXAttributeKey(String), JSXAttributeAssign, JSXAttributeValue(String), JSXContent(String), JSXContentLineBreak, /// The start and end of expressions either as a node or a attribute @@ -307,8 +307,8 @@ pub enum TSXKeyword { Async, Await, Static, Get, Set, - Extends, - Null, + Extends, + Null, True, False, // TS special keywords Abstract, Implements, @@ -317,19 +317,19 @@ pub enum TSXKeyword { // TS publicity attributes Private, Public, Protected, // TS Keywords - As, Readonly, Satisfies, Declare, Namespace, + As, Readonly, Satisfies, Declare, Namespace, // TS & Ezno - Is, + Is, Infer, KeyOf, Unique, Symbol, // TODO unsure #[cfg(feature = "extras")] Module, // Extra function modifiers - #[cfg(feature = "extras")] Server, #[cfg(feature = "extras")] Worker, + #[cfg(feature = "extras")] Server, #[cfg(feature = "extras")] Worker, // Type declaration changes #[cfg(feature = "extras")] Nominal, #[cfg(feature = "extras")] Performs, #[cfg(feature = "extras")] - /// https://github.com/tc39/proposal-generator-arrow-functions#introduce-new-generator-keyword-for-both-function-and-arrow-function + /// Generator, #[cfg(feature = "extras")]