Skip to content

Commit

Permalink
Check for excess generic arguments (#160)
Browse files Browse the repository at this point in the history
* Check for excess generic arguments
* Add position info to error type
---------

Co-authored-by: Ben <[email protected]>
  • Loading branch information
josh-degraw and kaleidawave authored Jun 13, 2024
1 parent aafc9a4 commit 9dd05b7
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 14 deletions.
20 changes: 20 additions & 0 deletions checker/specification/specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -2749,6 +2749,26 @@ unwrap(16) satisfies 16;

- Expected string, found 5

#### Excess generic arguments

```ts
declare function generic<T>(a: T);

generic<string, number>("something");
```

- Expected 1 type argument, but got 2

#### Passing generic type to non-generic function

```ts
declare function func();

func<string>();
```

- Cannot pass a type argument to a non-generic function

#### Across alias

```ts
Expand Down
21 changes: 18 additions & 3 deletions checker/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,7 @@ pub struct CannotRedeclareVariable<'a> {
pub name: &'a str,
}

/// `context` is the what kind of a function call the error happened in. (for example tagged template literals or JSX)
fn function_calling_error_diagnostic(
error: FunctionCallingError,
kind: crate::DiagnosticKind,
Expand All @@ -894,7 +895,7 @@ fn function_calling_error_diagnostic(
} => {
if let Some((restriction_pos, restriction)) = restriction {
Diagnostic::PositionWithAdditionalLabels {
reason: format!( "Argument of type {argument_type} is not assignable to parameter of type {restriction}{context}" ),
reason: format!("Argument of type {argument_type} is not assignable to parameter of type {restriction}{context}"),
position: argument_position,
labels: vec![(
format!("{parameter_type} was specialised with type {restriction}"),
Expand All @@ -904,7 +905,7 @@ fn function_calling_error_diagnostic(
}
} else {
Diagnostic::PositionWithAdditionalLabels {
reason: format!( "Argument of type {argument_type} is not assignable to parameter of type {parameter_type}{context}", ),
reason: format!( "Argument of type {argument_type} is not assignable to parameter of type {parameter_type}{context}"),
position: argument_position,
labels: vec![(
format!("Parameter has type {parameter_type}"),
Expand All @@ -928,6 +929,20 @@ fn function_calling_error_diagnostic(
FunctionCallingError::ExcessArguments { count: _, position } => {
Diagnostic::Position { reason: format!("Excess argument{context}"), position, kind }
}
FunctionCallingError::ExcessTypeArguments { expected_count, count, position } => {
let reason = if expected_count == 0 {
format!("Cannot pass a type argument to a non-generic function{context}")
} else if expected_count == 1 {
format!("Expected 1 type argument, but got {count}{context}")
} else {
format!("Expected {expected_count} type arguments, but got {count}{context}")
};
Diagnostic::Position {
position,
kind,
reason
}
}
FunctionCallingError::NotCallable { calling, call_site } => Diagnostic::Position {
reason: format!("Cannot call type {calling}{context}"),
position: call_site,
Expand Down Expand Up @@ -990,7 +1005,7 @@ fn function_calling_error_diagnostic(
},
FunctionCallingError::MismatchedThis { call_site, expected, found } => {
Diagnostic::Position {
reason: format!( "The 'this' context of the function is expected to be {expected}, found {found}{context}" ),
reason: format!("The 'this' context of the function is expected to be {expected}, found {found}{context}"),
position: call_site,
kind,
}
Expand Down
67 changes: 56 additions & 11 deletions checker/src/types/calling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,12 @@ pub enum FunctionCallingError {
count: usize,
position: SpanWithSource,
},

ExcessTypeArguments {
expected_count: usize,
count: usize,
position: SpanWithSource,
},
NotCallable {
calling: TypeStringRepresentation,
call_site: SpanWithSource,
Expand Down Expand Up @@ -1573,17 +1579,56 @@ fn synthesise_argument_expressions_wrt_parameters<T: ReadFromFS, A: crate::ASTIm
let function = checking_data.types.get_function_from_id(function.function);

let type_arguments_restrictions =
if let (Some(ref type_parameters), Some(call_site_type_arguments)) =
(&function.type_parameters, call_site_type_arguments)
{
Some(synthesise_call_site_type_argument_hints(
type_parameters,
call_site_type_arguments,
&checking_data.types,
environment,
))
} else {
None
match (&function.type_parameters, call_site_type_arguments) {
(None, Some(call_site_type_arguments)) => {
let first_excess_type_parameter = &call_site_type_arguments[0];
checking_data.diagnostics_container.add_error(
TypeCheckError::FunctionCallingError(
FunctionCallingError::ExcessTypeArguments {
expected_count: 0,
count: call_site_type_arguments.len(),
position: first_excess_type_parameter.1,
},
),
);

None
}
(Some(ref function_type_parameters), Some(call_site_type_arguments)) => {
let expected_parameters_length = function_type_parameters.0.len();
let provided_parameters_length = call_site_type_arguments.len();
if provided_parameters_length > expected_parameters_length {
let first_excess_type_parameter =
&call_site_type_arguments[expected_parameters_length];
let last_excess_type_parameter = call_site_type_arguments
.last()
.unwrap_or(first_excess_type_parameter);

let error_position = first_excess_type_parameter
.1
.without_source()
.union(last_excess_type_parameter.1.without_source())
.with_source(first_excess_type_parameter.1.source);

checking_data.diagnostics_container.add_error(
TypeCheckError::FunctionCallingError(
FunctionCallingError::ExcessTypeArguments {
expected_count: expected_parameters_length,
count: provided_parameters_length,
position: error_position,
},
),
);
}
Some(synthesise_call_site_type_argument_hints(
function_type_parameters,
call_site_type_arguments,
&checking_data.types,
environment,
))
}

_ => None,
};

let parameters = function.parameters.clone();
Expand Down
1 change: 1 addition & 0 deletions checker/src/types/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,7 @@ pub(crate) fn set_property<E: CallCheckingBehavior>(
| FunctionCallingError::NoLogicForIdentifier(..)
| FunctionCallingError::NotCallable { .. }
| FunctionCallingError::ExcessArguments { .. }
| FunctionCallingError::ExcessTypeArguments { .. }
| FunctionCallingError::MissingArgument { .. } => unreachable!(),
FunctionCallingError::ReferenceRestrictionDoesNotMatch { .. } => {
todo!()
Expand Down
2 changes: 2 additions & 0 deletions src/utilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ pub(crate) fn upgrade_self() -> Result<String, Box<dyn std::error::Error>> {
const EXPECTED_END: &str = "windows.exe";
#[cfg(target_os = "linux")]
const EXPECTED_END: &str = "linux";
#[cfg(target_os = "macos")]
const EXPECTED_END: &str = "macos";

let mut required_binary = None;
let mut version_name = None;
Expand Down

0 comments on commit 9dd05b7

Please sign in to comment.