mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 15:25:06 +02:00
Improve type hovers (#9515)
# Description This PR does a few things to help improve type hovers and, in the process, fixes a few outstanding issues in the type system. Here's a list of the changes: * `for` now will try to infer the type of the iteration variable based on the expression it's given. This fixes things like `for x in [1, 2, 3] { }` where `x` now properly gets the int type. * Removed old input/output type fields from the signature, focuses on the vec of signatures. Updated a bunch of dataframe commands that hadn't moved over. This helps tie things together a bit better * Fixed inference of types from subexpressions to use the last expression in the block * Fixed handling of explicit types in `let` and `mut` calls, so we now respect that as the authoritative type I also tried to add `def` input/output type inference, but unfortunately we only know the predecl types universally, which means we won't have enough information to properly know what the types of the custom commands are. # User-Facing Changes Script typechecking will get tighter in some cases Hovers should be more accurate in some cases that previously resorted to any. # Tests + Formatting <!-- Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass - `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > toolkit check pr > ``` --> # After Submitting <!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. --> --------- Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
use crate::parser_path::ParserPath;
|
||||
use crate::{parser_path::ParserPath, type_check::type_compatible};
|
||||
use itertools::Itertools;
|
||||
use log::trace;
|
||||
use nu_path::canonicalize_with;
|
||||
@ -235,7 +235,7 @@ pub fn parse_for(working_set: &mut StateWorkingSet, spans: &[Span]) -> Expressio
|
||||
// Parsing the spans and checking that they match the register signature
|
||||
// Using a parsed call makes more sense than checking for how many spans are in the call
|
||||
// Also, by creating a call, it can be checked if it matches the declaration signature
|
||||
let (call, call_span) = match working_set.find_decl(b"for", &Type::Any) {
|
||||
let (call, call_span) = match working_set.find_decl(b"for", &Type::Nothing) {
|
||||
None => {
|
||||
working_set.error(ParseError::UnknownState(
|
||||
"internal error: for declaration not found".into(),
|
||||
@ -290,9 +290,21 @@ pub fn parse_for(working_set: &mut StateWorkingSet, spans: &[Span]) -> Expressio
|
||||
|
||||
// All positional arguments must be in the call positional vector by this point
|
||||
let var_decl = call.positional_nth(0).expect("for call already checked");
|
||||
let iteration_expr = call.positional_nth(1).expect("for call already checked");
|
||||
let block = call.positional_nth(2).expect("for call already checked");
|
||||
|
||||
let iteration_expr_ty = iteration_expr.ty.clone();
|
||||
|
||||
// Figure out the type of the variable the `for` uses for iteration
|
||||
let var_type = match iteration_expr_ty {
|
||||
Type::List(x) => *x,
|
||||
Type::Table(x) => Type::Record(x),
|
||||
x => x,
|
||||
};
|
||||
|
||||
if let (Some(var_id), Some(block_id)) = (&var_decl.as_var(), block.as_block()) {
|
||||
working_set.set_variable_type(*var_id, var_type.clone());
|
||||
|
||||
let block = working_set.get_block_mut(block_id);
|
||||
|
||||
block.signature.required_positional.insert(
|
||||
@ -300,7 +312,7 @@ pub fn parse_for(working_set: &mut StateWorkingSet, spans: &[Span]) -> Expressio
|
||||
PositionalArg {
|
||||
name: String::new(),
|
||||
desc: String::new(),
|
||||
shape: SyntaxShape::Any,
|
||||
shape: var_type.to_shape(),
|
||||
var_id: Some(*var_id),
|
||||
default_value: None,
|
||||
},
|
||||
@ -310,7 +322,7 @@ pub fn parse_for(working_set: &mut StateWorkingSet, spans: &[Span]) -> Expressio
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
span: call_span,
|
||||
ty: Type::Any,
|
||||
ty: Type::Nothing,
|
||||
custom_completion: None,
|
||||
}
|
||||
}
|
||||
@ -347,7 +359,7 @@ pub fn parse_def(
|
||||
// Parsing the spans and checking that they match the register signature
|
||||
// Using a parsed call makes more sense than checking for how many spans are in the call
|
||||
// Also, by creating a call, it can be checked if it matches the declaration signature
|
||||
let (call, call_span) = match working_set.find_decl(&def_call, &Type::Any) {
|
||||
let (call, call_span) = match working_set.find_decl(&def_call, &Type::Nothing) {
|
||||
None => {
|
||||
working_set.error(ParseError::UnknownState(
|
||||
"internal error: def declaration not found".into(),
|
||||
@ -391,12 +403,15 @@ pub fn parse_def(
|
||||
expr: Expr::Block(block_id),
|
||||
..
|
||||
}
|
||||
| Expression {
|
||||
expr: Expr::Closure(block_id),
|
||||
..
|
||||
}
|
||||
| Expression {
|
||||
expr: Expr::RowCondition(block_id),
|
||||
..
|
||||
} => {
|
||||
let block = working_set.get_block_mut(*block_id);
|
||||
|
||||
block.signature = Box::new(sig.clone());
|
||||
}
|
||||
_ => {}
|
||||
@ -469,6 +484,20 @@ pub fn parse_def(
|
||||
block.recursive = Some(calls_itself);
|
||||
block.signature = signature;
|
||||
block.redirect_env = def_call == b"def-env";
|
||||
|
||||
// Sadly we can't use this here as the inference would have to happen before
|
||||
// all the definitions had been fully parsed.
|
||||
|
||||
// infer the return type based on the output of the block
|
||||
// let block = working_set.get_block(block_id);
|
||||
|
||||
// let input_type = block.input_type(working_set);
|
||||
// let output_type = block.output_type();
|
||||
// block.signature.input_output_types = vec![(input_type, output_type)];
|
||||
block
|
||||
.signature
|
||||
.input_output_types
|
||||
.push((Type::Any, Type::Any));
|
||||
} else {
|
||||
working_set.error(ParseError::InternalError(
|
||||
"Predeclaration failed to add declaration".into(),
|
||||
@ -519,7 +548,7 @@ pub fn parse_extern(
|
||||
// Parsing the spans and checking that they match the register signature
|
||||
// Using a parsed call makes more sense than checking for how many spans are in the call
|
||||
// Also, by creating a call, it can be checked if it matches the declaration signature
|
||||
let (call, call_span) = match working_set.find_decl(&extern_call, &Type::Any) {
|
||||
let (call, call_span) = match working_set.find_decl(&extern_call, &Type::Nothing) {
|
||||
None => {
|
||||
working_set.error(ParseError::UnknownState(
|
||||
"internal error: def declaration not found".into(),
|
||||
@ -710,7 +739,7 @@ pub fn parse_alias(
|
||||
return Pipeline::from_vec(vec![garbage(*span)]);
|
||||
}
|
||||
|
||||
if let Some(decl_id) = working_set.find_decl(b"alias", &Type::Any) {
|
||||
if let Some(decl_id) = working_set.find_decl(b"alias", &Type::Nothing) {
|
||||
let (command_spans, rest_spans) = spans.split_at(split_id);
|
||||
|
||||
let original_starting_error_count = working_set.parse_errors.len();
|
||||
@ -912,7 +941,7 @@ pub fn parse_export_in_block(
|
||||
b"export".to_vec()
|
||||
};
|
||||
|
||||
if let Some(decl_id) = working_set.find_decl(&full_name, &Type::Any) {
|
||||
if let Some(decl_id) = working_set.find_decl(&full_name, &Type::Nothing) {
|
||||
let ParsedInternalCall { call, output, .. } = parse_internal_call(
|
||||
working_set,
|
||||
if full_name == b"export" {
|
||||
@ -1006,7 +1035,7 @@ pub fn parse_export_in_module(
|
||||
return (garbage_pipeline(spans), vec![]);
|
||||
};
|
||||
|
||||
let export_decl_id = if let Some(id) = working_set.find_decl(b"export", &Type::Any) {
|
||||
let export_decl_id = if let Some(id) = working_set.find_decl(b"export", &Type::Nothing) {
|
||||
id
|
||||
} else {
|
||||
working_set.error(ParseError::InternalError(
|
||||
@ -1036,7 +1065,7 @@ pub fn parse_export_in_module(
|
||||
let pipeline = parse_def(working_set, &lite_command, Some(module_name));
|
||||
|
||||
let export_def_decl_id =
|
||||
if let Some(id) = working_set.find_decl(b"export def", &Type::Any) {
|
||||
if let Some(id) = working_set.find_decl(b"export def", &Type::Nothing) {
|
||||
id
|
||||
} else {
|
||||
working_set.error(ParseError::InternalError(
|
||||
@ -1072,7 +1101,7 @@ pub fn parse_export_in_module(
|
||||
let decl_name = working_set.get_span_contents(*decl_name_span);
|
||||
let decl_name = trim_quotes(decl_name);
|
||||
|
||||
if let Some(decl_id) = working_set.find_decl(decl_name, &Type::Any) {
|
||||
if let Some(decl_id) = working_set.find_decl(decl_name, &Type::Nothing) {
|
||||
result.push(Exportable::Decl {
|
||||
name: decl_name.to_vec(),
|
||||
id: decl_id,
|
||||
@ -1095,7 +1124,7 @@ pub fn parse_export_in_module(
|
||||
let pipeline = parse_def(working_set, &lite_command, Some(module_name));
|
||||
|
||||
let export_def_decl_id =
|
||||
if let Some(id) = working_set.find_decl(b"export def-env", &Type::Any) {
|
||||
if let Some(id) = working_set.find_decl(b"export def-env", &Type::Nothing) {
|
||||
id
|
||||
} else {
|
||||
working_set.error(ParseError::InternalError(
|
||||
@ -1133,7 +1162,7 @@ pub fn parse_export_in_module(
|
||||
};
|
||||
let decl_name = trim_quotes(decl_name);
|
||||
|
||||
if let Some(decl_id) = working_set.find_decl(decl_name, &Type::Any) {
|
||||
if let Some(decl_id) = working_set.find_decl(decl_name, &Type::Nothing) {
|
||||
result.push(Exportable::Decl {
|
||||
name: decl_name.to_vec(),
|
||||
id: decl_id,
|
||||
@ -1155,7 +1184,7 @@ pub fn parse_export_in_module(
|
||||
let pipeline = parse_extern(working_set, &lite_command, Some(module_name));
|
||||
|
||||
let export_def_decl_id =
|
||||
if let Some(id) = working_set.find_decl(b"export extern", &Type::Any) {
|
||||
if let Some(id) = working_set.find_decl(b"export extern", &Type::Nothing) {
|
||||
id
|
||||
} else {
|
||||
working_set.error(ParseError::InternalError(
|
||||
@ -1193,7 +1222,7 @@ pub fn parse_export_in_module(
|
||||
};
|
||||
let decl_name = trim_quotes(decl_name);
|
||||
|
||||
if let Some(decl_id) = working_set.find_decl(decl_name, &Type::Any) {
|
||||
if let Some(decl_id) = working_set.find_decl(decl_name, &Type::Nothing) {
|
||||
result.push(Exportable::Decl {
|
||||
name: decl_name.to_vec(),
|
||||
id: decl_id,
|
||||
@ -1215,7 +1244,7 @@ pub fn parse_export_in_module(
|
||||
let pipeline = parse_alias(working_set, &lite_command, Some(module_name));
|
||||
|
||||
let export_alias_decl_id =
|
||||
if let Some(id) = working_set.find_decl(b"export alias", &Type::Any) {
|
||||
if let Some(id) = working_set.find_decl(b"export alias", &Type::Nothing) {
|
||||
id
|
||||
} else {
|
||||
working_set.error(ParseError::InternalError(
|
||||
@ -1253,7 +1282,7 @@ pub fn parse_export_in_module(
|
||||
};
|
||||
let alias_name = trim_quotes(alias_name);
|
||||
|
||||
if let Some(alias_id) = working_set.find_decl(alias_name, &Type::Any) {
|
||||
if let Some(alias_id) = working_set.find_decl(alias_name, &Type::Nothing) {
|
||||
result.push(Exportable::Decl {
|
||||
name: alias_name.to_vec(),
|
||||
id: alias_id,
|
||||
@ -1275,7 +1304,7 @@ pub fn parse_export_in_module(
|
||||
let (pipeline, exportables) = parse_use(working_set, &lite_command.parts);
|
||||
|
||||
let export_use_decl_id =
|
||||
if let Some(id) = working_set.find_decl(b"export use", &Type::Any) {
|
||||
if let Some(id) = working_set.find_decl(b"export use", &Type::Nothing) {
|
||||
id
|
||||
} else {
|
||||
working_set.error(ParseError::InternalError(
|
||||
@ -1312,7 +1341,7 @@ pub fn parse_export_in_module(
|
||||
parse_module(working_set, lite_command, Some(module_name));
|
||||
|
||||
let export_module_decl_id =
|
||||
if let Some(id) = working_set.find_decl(b"export module", &Type::Any) {
|
||||
if let Some(id) = working_set.find_decl(b"export module", &Type::Nothing) {
|
||||
id
|
||||
} else {
|
||||
working_set.error(ParseError::InternalError(
|
||||
@ -1417,7 +1446,7 @@ pub fn parse_export_env(
|
||||
return (garbage_pipeline(spans), None);
|
||||
}
|
||||
|
||||
let call = match working_set.find_decl(b"export-env", &Type::Any) {
|
||||
let call = match working_set.find_decl(b"export-env", &Type::Nothing) {
|
||||
Some(decl_id) => {
|
||||
let ParsedInternalCall { call, output } =
|
||||
parse_internal_call(working_set, spans[0], &[spans[1]], decl_id);
|
||||
@ -1929,7 +1958,7 @@ pub fn parse_module(
|
||||
1
|
||||
};
|
||||
|
||||
let (call, call_span) = match working_set.find_decl(b"module", &Type::Any) {
|
||||
let (call, call_span) = match working_set.find_decl(b"module", &Type::Nothing) {
|
||||
Some(decl_id) => {
|
||||
let (command_spans, rest_spans) = spans.split_at(split_id);
|
||||
|
||||
@ -2070,7 +2099,7 @@ pub fn parse_module(
|
||||
};
|
||||
|
||||
let module_decl_id = working_set
|
||||
.find_decl(b"module", &Type::Any)
|
||||
.find_decl(b"module", &Type::Nothing)
|
||||
.expect("internal error: missing module command");
|
||||
|
||||
let call = Box::new(Call {
|
||||
@ -2121,7 +2150,7 @@ pub fn parse_use(working_set: &mut StateWorkingSet, spans: &[Span]) -> (Pipeline
|
||||
return (garbage_pipeline(spans), vec![]);
|
||||
}
|
||||
|
||||
let (call, call_span, args_spans) = match working_set.find_decl(b"use", &Type::Any) {
|
||||
let (call, call_span, args_spans) = match working_set.find_decl(b"use", &Type::Nothing) {
|
||||
Some(decl_id) => {
|
||||
let (command_spans, rest_spans) = spans.split_at(split_id);
|
||||
|
||||
@ -2277,7 +2306,7 @@ pub fn parse_hide(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline
|
||||
return garbage_pipeline(spans);
|
||||
}
|
||||
|
||||
let (call, args_spans) = match working_set.find_decl(b"hide", &Type::Any) {
|
||||
let (call, args_spans) = match working_set.find_decl(b"hide", &Type::Nothing) {
|
||||
Some(decl_id) => {
|
||||
let ParsedInternalCall { call, output } =
|
||||
parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
|
||||
@ -2336,7 +2365,7 @@ pub fn parse_hide(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline
|
||||
(true, working_set.get_module(module_id).clone())
|
||||
} else if import_pattern.members.is_empty() {
|
||||
// The pattern head can be:
|
||||
if let Some(id) = working_set.find_decl(&import_pattern.head.name, &Type::Any) {
|
||||
if let Some(id) = working_set.find_decl(&import_pattern.head.name, &Type::Nothing) {
|
||||
// a custom command,
|
||||
let mut module = Module::new(b"tmp".to_vec());
|
||||
module.add_decl(import_pattern.head.name.clone(), id);
|
||||
@ -2767,17 +2796,19 @@ pub fn parse_overlay_hide(working_set: &mut StateWorkingSet, call: Box<Call>) ->
|
||||
}
|
||||
|
||||
pub fn parse_let_or_const(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline {
|
||||
trace!("parsing: let");
|
||||
let name = working_set.get_span_contents(spans[0]);
|
||||
|
||||
if name == b"let" || name == b"const" {
|
||||
let is_const = &name == b"const";
|
||||
|
||||
if let Some(span) = check_name(working_set, spans) {
|
||||
return Pipeline::from_vec(vec![garbage(*span)]);
|
||||
}
|
||||
// JT: Disabling check_name because it doesn't work with optional types in the declaration
|
||||
// if let Some(span) = check_name(working_set, spans) {
|
||||
// return Pipeline::from_vec(vec![garbage(*span)]);
|
||||
// }
|
||||
|
||||
if let Some(decl_id) =
|
||||
working_set.find_decl(if is_const { b"const" } else { b"let" }, &Type::Any)
|
||||
working_set.find_decl(if is_const { b"const" } else { b"let" }, &Type::Nothing)
|
||||
{
|
||||
let cmd = working_set.get_decl(decl_id);
|
||||
let call_signature = cmd.signature().call_signature();
|
||||
@ -2805,7 +2836,8 @@ pub fn parse_let_or_const(working_set: &mut StateWorkingSet, spans: &[Span]) ->
|
||||
}
|
||||
|
||||
let mut idx = 0;
|
||||
let lvalue = parse_var_with_opt_type(
|
||||
|
||||
let (lvalue, explicit_type) = parse_var_with_opt_type(
|
||||
working_set,
|
||||
&spans[1..(span.0)],
|
||||
&mut idx,
|
||||
@ -2824,8 +2856,20 @@ pub fn parse_let_or_const(working_set: &mut StateWorkingSet, spans: &[Span]) ->
|
||||
let var_id = lvalue.as_var();
|
||||
let rhs_type = rvalue.ty.clone();
|
||||
|
||||
if let Some(explicit_type) = &explicit_type {
|
||||
if !type_compatible(explicit_type, &rhs_type) {
|
||||
working_set.error(ParseError::TypeMismatch(
|
||||
explicit_type.clone(),
|
||||
rhs_type.clone(),
|
||||
nu_protocol::span(&spans[(span.0 + 1)..]),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(var_id) = var_id {
|
||||
working_set.set_variable_type(var_id, rhs_type);
|
||||
if explicit_type.is_none() {
|
||||
working_set.set_variable_type(var_id, rhs_type);
|
||||
}
|
||||
|
||||
if is_const {
|
||||
match eval_constant(working_set, &rvalue) {
|
||||
@ -2867,6 +2911,11 @@ pub fn parse_let_or_const(working_set: &mut StateWorkingSet, spans: &[Span]) ->
|
||||
ty: output,
|
||||
custom_completion: None,
|
||||
}]);
|
||||
} else {
|
||||
working_set.error(ParseError::UnknownState(
|
||||
"internal error: let or const statements not found in core language".into(),
|
||||
span(spans),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -2882,11 +2931,11 @@ pub fn parse_mut(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline
|
||||
let name = working_set.get_span_contents(spans[0]);
|
||||
|
||||
if name == b"mut" {
|
||||
if let Some(span) = check_name(working_set, spans) {
|
||||
return Pipeline::from_vec(vec![garbage(*span)]);
|
||||
}
|
||||
// if let Some(span) = check_name(working_set, spans) {
|
||||
// return Pipeline::from_vec(vec![garbage(*span)]);
|
||||
// }
|
||||
|
||||
if let Some(decl_id) = working_set.find_decl(b"mut", &Type::Any) {
|
||||
if let Some(decl_id) = working_set.find_decl(b"mut", &Type::Nothing) {
|
||||
let cmd = working_set.get_decl(decl_id);
|
||||
let call_signature = cmd.signature().call_signature();
|
||||
|
||||
@ -2913,7 +2962,7 @@ pub fn parse_mut(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline
|
||||
}
|
||||
|
||||
let mut idx = 0;
|
||||
let lvalue = parse_var_with_opt_type(
|
||||
let (lvalue, explicit_type) = parse_var_with_opt_type(
|
||||
working_set,
|
||||
&spans[1..(span.0)],
|
||||
&mut idx,
|
||||
@ -2932,8 +2981,20 @@ pub fn parse_mut(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline
|
||||
let var_id = lvalue.as_var();
|
||||
let rhs_type = rvalue.ty.clone();
|
||||
|
||||
if let Some(explicit_type) = &explicit_type {
|
||||
if &rhs_type != explicit_type && explicit_type != &Type::Any {
|
||||
working_set.error(ParseError::TypeMismatch(
|
||||
explicit_type.clone(),
|
||||
rhs_type.clone(),
|
||||
rvalue.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(var_id) = var_id {
|
||||
working_set.set_variable_type(var_id, rhs_type);
|
||||
if explicit_type.is_none() {
|
||||
working_set.set_variable_type(var_id, rhs_type);
|
||||
}
|
||||
}
|
||||
|
||||
let call = Box::new(Call {
|
||||
@ -2982,7 +3043,7 @@ pub fn parse_source(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeli
|
||||
if name == b"source" || name == b"source-env" {
|
||||
let scoped = name == b"source-env";
|
||||
|
||||
if let Some(decl_id) = working_set.find_decl(name, &Type::Any) {
|
||||
if let Some(decl_id) = working_set.find_decl(name, &Type::Nothing) {
|
||||
let cwd = working_set.get_cwd();
|
||||
|
||||
// Is this the right call to be using here?
|
||||
@ -3113,7 +3174,7 @@ pub fn parse_where_expr(working_set: &mut StateWorkingSet, spans: &[Span]) -> Ex
|
||||
return garbage(span(spans));
|
||||
}
|
||||
|
||||
let call = match working_set.find_decl(b"where", &Type::Any) {
|
||||
let call = match working_set.find_decl(b"where", &Type::List(Box::new(Type::Any))) {
|
||||
Some(decl_id) => {
|
||||
let ParsedInternalCall { call, output } =
|
||||
parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
|
||||
@ -3176,7 +3237,7 @@ pub fn parse_register(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipe
|
||||
// Parsing the spans and checking that they match the register signature
|
||||
// Using a parsed call makes more sense than checking for how many spans are in the call
|
||||
// Also, by creating a call, it can be checked if it matches the declaration signature
|
||||
let (call, call_span) = match working_set.find_decl(b"register", &Type::Any) {
|
||||
let (call, call_span) = match working_set.find_decl(b"register", &Type::Nothing) {
|
||||
None => {
|
||||
working_set.error(ParseError::UnknownState(
|
||||
"internal error: Register declaration not found".into(),
|
||||
|
@ -607,7 +607,7 @@ pub fn parse_multispan_value(
|
||||
SyntaxShape::VarWithOptType => {
|
||||
trace!("parsing: var with opt type");
|
||||
|
||||
parse_var_with_opt_type(working_set, spans, spans_idx, false)
|
||||
parse_var_with_opt_type(working_set, spans, spans_idx, false).0
|
||||
}
|
||||
SyntaxShape::RowCondition => {
|
||||
trace!("parsing: row condition");
|
||||
@ -779,7 +779,7 @@ pub fn parse_internal_call(
|
||||
|
||||
let decl = working_set.get_decl(decl_id);
|
||||
let signature = decl.signature();
|
||||
let output = signature.output_type.clone();
|
||||
let output = signature.get_output_type();
|
||||
|
||||
if decl.is_builtin() {
|
||||
attach_parser_info_builtin(working_set, decl.name(), &mut call);
|
||||
@ -1172,6 +1172,7 @@ pub fn parse_call(
|
||||
}
|
||||
|
||||
pub fn parse_binary(working_set: &mut StateWorkingSet, span: Span) -> Expression {
|
||||
trace!("parsing: binary");
|
||||
let contents = working_set.get_span_contents(span);
|
||||
if contents.starts_with(b"0x[") {
|
||||
parse_binary_with_base(working_set, span, 16, 2, b"0x[", b"]")
|
||||
@ -2007,25 +2008,7 @@ pub fn parse_full_cell_path(
|
||||
.type_scope
|
||||
.add_type(working_set.type_scope.get_last_output());
|
||||
|
||||
let ty = output
|
||||
.pipelines
|
||||
.last()
|
||||
.and_then(|Pipeline { elements, .. }| elements.last())
|
||||
.map(|element| match element {
|
||||
PipelineElement::Expression(_, expr)
|
||||
if matches!(
|
||||
expr,
|
||||
Expression {
|
||||
expr: Expr::BinaryOp(..),
|
||||
..
|
||||
}
|
||||
) =>
|
||||
{
|
||||
expr.ty.clone()
|
||||
}
|
||||
_ => working_set.type_scope.get_last_output(),
|
||||
})
|
||||
.unwrap_or_else(|| working_set.type_scope.get_last_output());
|
||||
let ty = output.output_type();
|
||||
|
||||
let block_id = working_set.add_block(output);
|
||||
tokens.next();
|
||||
@ -3081,7 +3064,7 @@ pub fn parse_var_with_opt_type(
|
||||
spans: &[Span],
|
||||
spans_idx: &mut usize,
|
||||
mutable: bool,
|
||||
) -> Expression {
|
||||
) -> (Expression, Option<Type>) {
|
||||
let bytes = working_set.get_span_contents(spans[*spans_idx]).to_vec();
|
||||
|
||||
if bytes.contains(&b' ')
|
||||
@ -3090,7 +3073,7 @@ pub fn parse_var_with_opt_type(
|
||||
|| bytes.contains(&b'`')
|
||||
{
|
||||
working_set.error(ParseError::VariableNotValid(spans[*spans_idx]));
|
||||
return garbage(spans[*spans_idx]);
|
||||
return (garbage(spans[*spans_idx]), None);
|
||||
}
|
||||
|
||||
if bytes.ends_with(b":") {
|
||||
@ -3108,17 +3091,20 @@ pub fn parse_var_with_opt_type(
|
||||
"valid variable name",
|
||||
spans[*spans_idx],
|
||||
));
|
||||
return garbage(spans[*spans_idx]);
|
||||
return (garbage(spans[*spans_idx]), None);
|
||||
}
|
||||
|
||||
let id = working_set.add_variable(var_name, spans[*spans_idx - 1], ty.clone(), mutable);
|
||||
|
||||
Expression {
|
||||
expr: Expr::VarDecl(id),
|
||||
span: span(&spans[*spans_idx - 1..*spans_idx + 1]),
|
||||
ty,
|
||||
custom_completion: None,
|
||||
}
|
||||
(
|
||||
Expression {
|
||||
expr: Expr::VarDecl(id),
|
||||
span: span(&spans[*spans_idx - 1..*spans_idx + 1]),
|
||||
ty: ty.clone(),
|
||||
custom_completion: None,
|
||||
},
|
||||
Some(ty),
|
||||
)
|
||||
} else {
|
||||
let var_name = bytes[0..(bytes.len() - 1)].to_vec();
|
||||
|
||||
@ -3127,18 +3113,21 @@ pub fn parse_var_with_opt_type(
|
||||
"valid variable name",
|
||||
spans[*spans_idx],
|
||||
));
|
||||
return garbage(spans[*spans_idx]);
|
||||
return (garbage(spans[*spans_idx]), None);
|
||||
}
|
||||
|
||||
let id = working_set.add_variable(var_name, spans[*spans_idx], Type::Any, mutable);
|
||||
|
||||
working_set.error(ParseError::MissingType(spans[*spans_idx]));
|
||||
Expression {
|
||||
expr: Expr::VarDecl(id),
|
||||
span: spans[*spans_idx],
|
||||
ty: Type::Any,
|
||||
custom_completion: None,
|
||||
}
|
||||
(
|
||||
Expression {
|
||||
expr: Expr::VarDecl(id),
|
||||
span: spans[*spans_idx],
|
||||
ty: Type::Any,
|
||||
custom_completion: None,
|
||||
},
|
||||
None,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
let var_name = bytes;
|
||||
@ -3148,7 +3137,7 @@ pub fn parse_var_with_opt_type(
|
||||
"valid variable name",
|
||||
spans[*spans_idx],
|
||||
));
|
||||
return garbage(spans[*spans_idx]);
|
||||
return (garbage(spans[*spans_idx]), None);
|
||||
}
|
||||
|
||||
let id = working_set.add_variable(
|
||||
@ -3158,12 +3147,15 @@ pub fn parse_var_with_opt_type(
|
||||
mutable,
|
||||
);
|
||||
|
||||
Expression {
|
||||
expr: Expr::VarDecl(id),
|
||||
span: span(&spans[*spans_idx..*spans_idx + 1]),
|
||||
ty: Type::Any,
|
||||
custom_completion: None,
|
||||
}
|
||||
(
|
||||
Expression {
|
||||
expr: Expr::VarDecl(id),
|
||||
span: span(&spans[*spans_idx..*spans_idx + 1]),
|
||||
ty: Type::Any,
|
||||
custom_completion: None,
|
||||
},
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -5088,11 +5080,13 @@ pub fn parse_builtin_commands(
|
||||
lite_command: &LiteCommand,
|
||||
is_subexpression: bool,
|
||||
) -> Pipeline {
|
||||
trace!("parsing: builtin commands");
|
||||
if !is_math_expression_like(working_set, lite_command.parts[0])
|
||||
&& !is_unaliasable_parser_keyword(working_set, &lite_command.parts)
|
||||
{
|
||||
trace!("parsing: not math expression or unaliasable parser keyword");
|
||||
let name = working_set.get_span_contents(lite_command.parts[0]);
|
||||
if let Some(decl_id) = working_set.find_decl(name, &Type::Any) {
|
||||
if let Some(decl_id) = working_set.find_decl(name, &Type::Nothing) {
|
||||
let cmd = working_set.get_decl(decl_id);
|
||||
if cmd.is_alias() {
|
||||
// Parse keywords that can be aliased. Note that we check for "unaliasable" keywords
|
||||
@ -5122,6 +5116,7 @@ pub fn parse_builtin_commands(
|
||||
}
|
||||
}
|
||||
|
||||
trace!("parsing: checking for keywords");
|
||||
let name = working_set.get_span_contents(lite_command.parts[0]);
|
||||
|
||||
match name {
|
||||
@ -5347,9 +5342,9 @@ pub fn parse_block(
|
||||
parse_builtin_commands(working_set, command, is_subexpression);
|
||||
|
||||
if idx == 0 {
|
||||
if let Some(let_decl_id) = working_set.find_decl(b"let", &Type::Any) {
|
||||
if let Some(let_decl_id) = working_set.find_decl(b"let", &Type::Nothing) {
|
||||
if let Some(let_env_decl_id) =
|
||||
working_set.find_decl(b"let-env", &Type::Any)
|
||||
working_set.find_decl(b"let-env", &Type::Nothing)
|
||||
{
|
||||
for element in pipeline.elements.iter_mut() {
|
||||
if let PipelineElement::Expression(
|
||||
@ -5823,7 +5818,7 @@ fn wrap_element_with_collect(
|
||||
fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression) -> Expression {
|
||||
let span = expr.span;
|
||||
|
||||
if let Some(decl_id) = working_set.find_decl(b"collect", &Type::Any) {
|
||||
if let Some(decl_id) = working_set.find_decl(b"collect", &Type::List(Box::new(Type::Any))) {
|
||||
let mut output = vec![];
|
||||
|
||||
let var_id = IN_VARIABLE_ID;
|
||||
|
@ -22,8 +22,11 @@ pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool {
|
||||
|
||||
match (lhs, rhs) {
|
||||
(Type::List(c), Type::List(d)) => type_compatible(c, d),
|
||||
(Type::List(c), Type::Table(_)) => matches!(**c, Type::Any),
|
||||
(Type::Number, Type::Int) => true,
|
||||
(Type::Int, Type::Number) => true,
|
||||
(Type::Number, Type::Float) => true,
|
||||
(Type::Float, Type::Number) => true,
|
||||
(Type::Closure, Type::Block) => true,
|
||||
(Type::Any, _) => true,
|
||||
(_, Type::Any) => true,
|
||||
@ -47,6 +50,11 @@ pub fn math_result_type(
|
||||
(Type::Float, Type::Int) => (Type::Float, None),
|
||||
(Type::Int, Type::Float) => (Type::Float, None),
|
||||
(Type::Float, Type::Float) => (Type::Float, None),
|
||||
(Type::Number, Type::Number) => (Type::Number, None),
|
||||
(Type::Number, Type::Int) => (Type::Number, None),
|
||||
(Type::Int, Type::Number) => (Type::Number, None),
|
||||
(Type::Number, Type::Float) => (Type::Number, None),
|
||||
(Type::Float, Type::Number) => (Type::Number, None),
|
||||
(Type::String, Type::String) => (Type::String, None),
|
||||
(Type::Date, Type::Duration) => (Type::Date, None),
|
||||
(Type::Duration, Type::Duration) => (Type::Duration, None),
|
||||
@ -143,6 +151,11 @@ pub fn math_result_type(
|
||||
(Type::Float, Type::Int) => (Type::Float, None),
|
||||
(Type::Int, Type::Float) => (Type::Float, None),
|
||||
(Type::Float, Type::Float) => (Type::Float, None),
|
||||
(Type::Number, Type::Number) => (Type::Number, None),
|
||||
(Type::Number, Type::Int) => (Type::Number, None),
|
||||
(Type::Int, Type::Number) => (Type::Number, None),
|
||||
(Type::Number, Type::Float) => (Type::Number, None),
|
||||
(Type::Float, Type::Number) => (Type::Number, None),
|
||||
(Type::Date, Type::Date) => (Type::Duration, None),
|
||||
(Type::Date, Type::Duration) => (Type::Date, None),
|
||||
(Type::Duration, Type::Duration) => (Type::Duration, None),
|
||||
@ -185,6 +198,11 @@ pub fn math_result_type(
|
||||
(Type::Float, Type::Int) => (Type::Float, None),
|
||||
(Type::Int, Type::Float) => (Type::Float, None),
|
||||
(Type::Float, Type::Float) => (Type::Float, None),
|
||||
(Type::Number, Type::Number) => (Type::Number, None),
|
||||
(Type::Number, Type::Int) => (Type::Number, None),
|
||||
(Type::Int, Type::Number) => (Type::Number, None),
|
||||
(Type::Number, Type::Float) => (Type::Number, None),
|
||||
(Type::Float, Type::Number) => (Type::Number, None),
|
||||
(Type::Filesize, Type::Int) => (Type::Filesize, None),
|
||||
(Type::Int, Type::Filesize) => (Type::Filesize, None),
|
||||
(Type::Filesize, Type::Float) => (Type::Filesize, None),
|
||||
@ -241,6 +259,11 @@ pub fn math_result_type(
|
||||
(Type::Float, Type::Int) => (Type::Float, None),
|
||||
(Type::Int, Type::Float) => (Type::Float, None),
|
||||
(Type::Float, Type::Float) => (Type::Float, None),
|
||||
(Type::Number, Type::Number) => (Type::Number, None),
|
||||
(Type::Number, Type::Int) => (Type::Number, None),
|
||||
(Type::Int, Type::Number) => (Type::Number, None),
|
||||
(Type::Number, Type::Float) => (Type::Number, None),
|
||||
(Type::Float, Type::Number) => (Type::Number, None),
|
||||
|
||||
(Type::Custom(a), Type::Custom(b)) if a == b => (Type::Custom(a.to_string()), None),
|
||||
(Type::Custom(a), _) => (Type::Custom(a.to_string()), None),
|
||||
@ -280,6 +303,11 @@ pub fn math_result_type(
|
||||
(Type::Float, Type::Int) => (Type::Float, None),
|
||||
(Type::Int, Type::Float) => (Type::Float, None),
|
||||
(Type::Float, Type::Float) => (Type::Float, None),
|
||||
(Type::Number, Type::Number) => (Type::Number, None),
|
||||
(Type::Number, Type::Int) => (Type::Number, None),
|
||||
(Type::Int, Type::Number) => (Type::Number, None),
|
||||
(Type::Number, Type::Float) => (Type::Number, None),
|
||||
(Type::Float, Type::Number) => (Type::Number, None),
|
||||
(Type::Filesize, Type::Filesize) => (Type::Float, None),
|
||||
(Type::Filesize, Type::Int) => (Type::Filesize, None),
|
||||
(Type::Filesize, Type::Float) => (Type::Filesize, None),
|
||||
@ -324,6 +352,11 @@ pub fn math_result_type(
|
||||
(Type::Float, Type::Int) => (Type::Int, None),
|
||||
(Type::Int, Type::Float) => (Type::Int, None),
|
||||
(Type::Float, Type::Float) => (Type::Int, None),
|
||||
(Type::Number, Type::Number) => (Type::Number, None),
|
||||
(Type::Number, Type::Int) => (Type::Number, None),
|
||||
(Type::Int, Type::Number) => (Type::Number, None),
|
||||
(Type::Number, Type::Float) => (Type::Number, None),
|
||||
(Type::Float, Type::Number) => (Type::Number, None),
|
||||
(Type::Filesize, Type::Filesize) => (Type::Int, None),
|
||||
(Type::Filesize, Type::Int) => (Type::Filesize, None),
|
||||
(Type::Filesize, Type::Float) => (Type::Filesize, None),
|
||||
@ -410,7 +443,13 @@ pub fn math_result_type(
|
||||
(Type::Float, Type::Int) => (Type::Bool, None),
|
||||
(Type::Int, Type::Float) => (Type::Bool, None),
|
||||
(Type::Float, Type::Float) => (Type::Bool, None),
|
||||
(Type::Number, Type::Number) => (Type::Bool, None),
|
||||
(Type::Number, Type::Int) => (Type::Bool, None),
|
||||
(Type::Int, Type::Number) => (Type::Bool, None),
|
||||
(Type::Number, Type::Float) => (Type::Bool, None),
|
||||
(Type::Float, Type::Number) => (Type::Bool, None),
|
||||
(Type::Duration, Type::Duration) => (Type::Bool, None),
|
||||
(Type::Date, Type::Date) => (Type::Bool, None),
|
||||
(Type::Filesize, Type::Filesize) => (Type::Bool, None),
|
||||
|
||||
(Type::Custom(a), Type::Custom(b)) if a == b => (Type::Custom(a.to_string()), None),
|
||||
@ -453,7 +492,13 @@ pub fn math_result_type(
|
||||
(Type::Float, Type::Int) => (Type::Bool, None),
|
||||
(Type::Int, Type::Float) => (Type::Bool, None),
|
||||
(Type::Float, Type::Float) => (Type::Bool, None),
|
||||
(Type::Number, Type::Number) => (Type::Bool, None),
|
||||
(Type::Number, Type::Int) => (Type::Bool, None),
|
||||
(Type::Int, Type::Number) => (Type::Bool, None),
|
||||
(Type::Number, Type::Float) => (Type::Bool, None),
|
||||
(Type::Float, Type::Number) => (Type::Bool, None),
|
||||
(Type::Duration, Type::Duration) => (Type::Bool, None),
|
||||
(Type::Date, Type::Date) => (Type::Bool, None),
|
||||
(Type::Filesize, Type::Filesize) => (Type::Bool, None),
|
||||
|
||||
(Type::Custom(a), Type::Custom(b)) if a == b => (Type::Custom(a.to_string()), None),
|
||||
@ -496,7 +541,13 @@ pub fn math_result_type(
|
||||
(Type::Float, Type::Int) => (Type::Bool, None),
|
||||
(Type::Int, Type::Float) => (Type::Bool, None),
|
||||
(Type::Float, Type::Float) => (Type::Bool, None),
|
||||
(Type::Number, Type::Number) => (Type::Bool, None),
|
||||
(Type::Number, Type::Int) => (Type::Bool, None),
|
||||
(Type::Int, Type::Number) => (Type::Bool, None),
|
||||
(Type::Number, Type::Float) => (Type::Bool, None),
|
||||
(Type::Float, Type::Number) => (Type::Bool, None),
|
||||
(Type::Duration, Type::Duration) => (Type::Bool, None),
|
||||
(Type::Date, Type::Date) => (Type::Bool, None),
|
||||
(Type::Filesize, Type::Filesize) => (Type::Bool, None),
|
||||
|
||||
(Type::Custom(a), Type::Custom(b)) if a == b => (Type::Custom(a.to_string()), None),
|
||||
@ -539,7 +590,13 @@ pub fn math_result_type(
|
||||
(Type::Float, Type::Int) => (Type::Bool, None),
|
||||
(Type::Int, Type::Float) => (Type::Bool, None),
|
||||
(Type::Float, Type::Float) => (Type::Bool, None),
|
||||
(Type::Number, Type::Number) => (Type::Bool, None),
|
||||
(Type::Number, Type::Int) => (Type::Bool, None),
|
||||
(Type::Int, Type::Number) => (Type::Bool, None),
|
||||
(Type::Number, Type::Float) => (Type::Bool, None),
|
||||
(Type::Float, Type::Number) => (Type::Bool, None),
|
||||
(Type::Duration, Type::Duration) => (Type::Bool, None),
|
||||
(Type::Date, Type::Date) => (Type::Bool, None),
|
||||
(Type::Filesize, Type::Filesize) => (Type::Bool, None),
|
||||
|
||||
(Type::Custom(a), Type::Custom(b)) if a == b => (Type::Custom(a.to_string()), None),
|
||||
@ -731,7 +788,7 @@ pub fn math_result_type(
|
||||
},
|
||||
Operator::Comparison(Comparison::In) => match (&lhs.ty, &rhs.ty) {
|
||||
(t, Type::List(u)) if type_compatible(t, u) => (Type::Bool, None),
|
||||
(Type::Int | Type::Float, Type::Range) => (Type::Bool, None),
|
||||
(Type::Int | Type::Float | Type::Number, Type::Range) => (Type::Bool, None),
|
||||
(Type::String, Type::String) => (Type::Bool, None),
|
||||
(Type::String, Type::Record(_)) => (Type::Bool, None),
|
||||
|
||||
@ -769,7 +826,7 @@ pub fn math_result_type(
|
||||
},
|
||||
Operator::Comparison(Comparison::NotIn) => match (&lhs.ty, &rhs.ty) {
|
||||
(t, Type::List(u)) if type_compatible(t, u) => (Type::Bool, None),
|
||||
(Type::Int | Type::Float, Type::Range) => (Type::Bool, None),
|
||||
(Type::Int | Type::Float | Type::Number, Type::Range) => (Type::Bool, None),
|
||||
(Type::String, Type::String) => (Type::Bool, None),
|
||||
(Type::String, Type::Record(_)) => (Type::Bool, None),
|
||||
|
||||
|
Reference in New Issue
Block a user