From 24e35b98da5a6c705be3cce7eb56dc17bfcb32a4 Mon Sep 17 00:00:00 2001 From: Charles Taylor Date: Sun, 14 Apr 2024 05:38:43 -0400 Subject: [PATCH] Check default parameter value type (#132) * Check default parameter value type (fixes #112) --- checker/specification/specification.md | 11 +++++++++ checker/src/diagnostics.rs | 21 ++++++++++++++--- checker/src/features/functions.rs | 31 ++++++++++++++++++++++++++ checker/src/types/mod.rs | 11 +++++++++ 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/checker/specification/specification.md b/checker/specification/specification.md index ceec1388..18bcf7e5 100644 --- a/checker/specification/specification.md +++ b/checker/specification/specification.md @@ -315,6 +315,17 @@ function func(a: number) { - Expected string, found number +#### Default parameter value type check + +```ts +function outer(a: number) { + function inner(b: string = Math.floor(a)) { + } +} +``` + +- Cannot use a default value of type number for parameter of type string + #### (simple) return type checking ```ts diff --git a/checker/src/diagnostics.rs b/checker/src/diagnostics.rs index 59aed846..a4ed0efc 100644 --- a/checker/src/diagnostics.rs +++ b/checker/src/diagnostics.rs @@ -357,6 +357,11 @@ mod defined_errors_and_warnings { name: &'a str, position: SpanWithSource, }, + InvalidDefaultParameter { + at: SpanWithSource, + expected: TypeStringRepresentation, + found: TypeStringRepresentation, + }, /// TODO temp, needs more info FunctionDoesNotMeetConstraint { function_constraint: TypeStringRepresentation, @@ -649,14 +654,24 @@ mod defined_errors_and_warnings { position: returned_position, kind, }, - TypeCheckError::CatchTypeDoesNotMatch { + TypeCheckError::InvalidDefaultParameter { + expected, + found, + at, + } => Diagnostic::Position { + reason: format!( + "Cannot use a default value of type {found} for parameter of type {expected}", + ), + position: at, + kind, + }, + TypeCheckError::CatchTypeDoesNotMatch { expected, found, at, } => Diagnostic::Position { reason: format!( - "Cannot catch type {found} because the try block throws {expected}", - + "Cannot catch type {found} because the try block throws {expected}", ), position: at, kind, diff --git a/checker/src/features/functions.rs b/checker/src/features/functions.rs index 4ec6d8c8..2bc0136f 100644 --- a/checker/src/features/functions.rs +++ b/checker/src/features/functions.rs @@ -12,7 +12,9 @@ use crate::{ information::{merge_info, LocalInformation}, CanReferenceThis, ContextType, Syntax, }, + diagnostics::{TypeCheckError, TypeStringRepresentation}, events::RootReference, + subtyping::{type_is_subtype, BasicEquality, SubTypeResult}, types::{ self, classes::ClassValue, @@ -194,6 +196,35 @@ pub fn synthesise_function_default_value<'a, T: crate::ReadFromFS, A: ASTImpleme }, ); + let mut basic_equality = BasicEquality::default(); + + let result = type_is_subtype( + parameter_constraint, + value, + &mut basic_equality, + environment, + &checking_data.types, + ); + + if let SubTypeResult::IsNotSubType(_) = result { + let expected = TypeStringRepresentation::from_type_id( + parameter_ty, + environment, + &checking_data.types, + false, + ); + + let found = + TypeStringRepresentation::from_type_id(value, environment, &checking_data.types, false); + let at = A::expression_position(expression).with_source(environment.get_source()); + + checking_data.diagnostics_container.add_error(TypeCheckError::InvalidDefaultParameter { + at, + expected, + found, + }); + } + // Abstraction of `typeof parameter === "undefined"` to generate less types. let is_undefined_condition = checking_data.types.register_type(Type::Constructor( Constructor::TypeRelationOperator(types::TypeRelationOperator::Extends { diff --git a/checker/src/types/mod.rs b/checker/src/types/mod.rs index 482e052d..8b54cc9d 100644 --- a/checker/src/types/mod.rs +++ b/checker/src/types/mod.rs @@ -387,6 +387,17 @@ pub struct BasicEquality { pub allow_errors: bool, } +impl Default for BasicEquality { + fn default() -> Self { + Self { + add_property_restrictions: false, + position: source_map::Nullable::NULL, + object_constraints: Default::default(), + allow_errors: false, + } + } +} + /// For subtyping pub trait SubTypeBehavior<'a> {