diff --git a/crates/analysis/src/alpha050/stmnts.rs b/crates/analysis/src/alpha050/stmnts.rs index f09ed44..886f4ea 100644 --- a/crates/analysis/src/alpha050/stmnts.rs +++ b/crates/analysis/src/alpha050/stmnts.rs @@ -887,6 +887,124 @@ pub fn analyze_stmnt( return_ty: exp_analysis.return_ty, } } + Statement::ArrayIndexSet((var, var_span), index_exp, value_exp) => { + let var_ty = match get_symbol_definition_info(files, var, &file, var_span.start) { + Some(info) => { + match info.symbol_type { + SymbolType::Function(_) => { + files.report_error(&file, "Cannot assign to a function", *var_span); + } + SymbolType::Variable(var) if var.is_const => { + files.report_error(&file, "Cannot assign to a constant", *var_span); + } + _ => {} + } + + info.data_type + } + None => DataType::Any, + }; + + let element_ty = match &var_ty { + DataType::Array(inner) => *inner.clone(), + DataType::Generic(id) => { + match scoped_generic_types.get_recursive(*id) { + DataType::Array(inner) => match *inner { + DataType::Any => { + // Generic array with unknown inner type; create a + // parametric inner type so it can be resolved later. + let inner_id = scoped_generic_types.new_generic_id(); + scoped_generic_types.constrain_generic_type( + *id, + DataType::Array(Box::new(DataType::Generic(inner_id))), + ); + DataType::Generic(inner_id) + } + other => other, + }, + DataType::Any => { + // Unconstrained generic; we now know it is used as an array. + let inner_id = scoped_generic_types.new_generic_id(); + scoped_generic_types.constrain_generic_type( + *id, + DataType::Array(Box::new(DataType::Generic(inner_id))), + ); + DataType::Generic(inner_id) + } + _ => { + files.report_error( + &file, + &format!( + "Cannot index into value of type {}", + var_ty.to_string(scoped_generic_types) + ), + *var_span, + ); + DataType::Any + } + } + } + DataType::Any => DataType::Any, + _ => { + files.report_error( + &file, + &format!( + "Cannot index into value of type {}", + var_ty.to_string(scoped_generic_types) + ), + *var_span, + ); + DataType::Any + } + }; + + let index_analysis = analyze_exp( + file_id, + file_version, + index_exp, + DataType::Int, + files, + scoped_generic_types, + contexts, + ); + + let exp_analysis = analyze_exp( + file_id, + file_version, + value_exp, + element_ty.clone(), + files, + scoped_generic_types, + contexts, + ); + + // Constrain the generic element type if we learned a concrete type. + if let DataType::Generic(id) = &element_ty { + scoped_generic_types.constrain_generic_type(*id, exp_analysis.exp_ty); + } + + insert_symbol_reference( + var, + files, + &SymbolLocation { + file, + start: var_span.start, + end: var_span.end, + }, + scoped_generic_types, + contexts, + ); + + StmntAnalysisResult { + is_propagating_failure: exp_analysis.is_propagating_failure + || index_analysis.is_propagating_failure, + return_ty: match (index_analysis.return_ty, exp_analysis.return_ty) { + (Some(a), Some(b)) => Some(make_union_type(vec![a, b])), + (Some(a), None) | (None, Some(a)) => Some(a), + (None, None) => None, + }, + } + } Statement::Break => { if !contexts.iter().any(|c| matches!(c, Context::Loop)) { files.report_error(&file, "Break statement outside of loop", *span); diff --git a/crates/grammar/src/alpha050/mod.rs b/crates/grammar/src/alpha050/mod.rs index 270e772..f0e39a7 100644 --- a/crates/grammar/src/alpha050/mod.rs +++ b/crates/grammar/src/alpha050/mod.rs @@ -193,6 +193,11 @@ pub enum Statement { VariableInit(Spanned, Spanned, Spanned), ConstInit(Spanned, Spanned, Box>), VariableSet(Spanned, Box>), + ArrayIndexSet( + Spanned, + Box>, + Box>, + ), IfCondition( Spanned, Spanned, diff --git a/crates/grammar/src/alpha050/semantic_tokens.rs b/crates/grammar/src/alpha050/semantic_tokens.rs index 2e5a87c..f6f05dc 100644 --- a/crates/grammar/src/alpha050/semantic_tokens.rs +++ b/crates/grammar/src/alpha050/semantic_tokens.rs @@ -567,6 +567,17 @@ fn semantic_tokens_from_stmnts(stmnts: &[Spanned]) -> Vec { + let mut tokens = vec![( + hash_semantic_token_type(SemanticTokenType::VARIABLE), + *var_span, + )]; + + tokens.extend(semantic_tokens_from_expr(index)); + tokens.extend(semantic_tokens_from_expr(value)); + + tokens + } Statement::Error => vec![], }) .collect() diff --git a/crates/grammar/src/alpha050/statements/array_index_set.rs b/crates/grammar/src/alpha050/statements/array_index_set.rs new file mode 100644 index 0000000..9468cec --- /dev/null +++ b/crates/grammar/src/alpha050/statements/array_index_set.rs @@ -0,0 +1,48 @@ +use chumsky::prelude::*; + +use crate::alpha050::expressions::parse_expr; +use crate::alpha050::parser::{ + default_recovery, + ident, +}; +use crate::alpha050::{ + AmberParser, + Expression, + Spanned, + Statement, +}; +use crate::T; + +pub fn array_index_set_parser<'a>( + stmnts: impl AmberParser<'a, Spanned>, +) -> impl AmberParser<'a, Spanned> { + ident("variable".to_string()) + .map_with(|name, e| (name, e.span())) + .then_ignore(just(T!['['])) + .then( + parse_expr(stmnts.clone()).recover_with(via_parser( + default_recovery() + .or_not() + .map_with(|_, e| (Expression::Error, e.span())), + )), + ) + .then_ignore( + just(T![']']).recover_with(via_parser(default_recovery().or_not().map(|_| T![']']))), + ) + .then_ignore(just(T!["="])) + .then( + parse_expr(stmnts).recover_with(via_parser( + default_recovery() + .or_not() + .map_with(|_, e| (Expression::Error, e.span())), + )), + ) + .map_with(|((name, index), value), e| { + ( + Statement::ArrayIndexSet(name, Box::new(index), Box::new(value)), + e.span(), + ) + }) + .boxed() + .labelled("array index set") +} diff --git a/crates/grammar/src/alpha050/statements/mod.rs b/crates/grammar/src/alpha050/statements/mod.rs index 7f77b95..06e3c7b 100644 --- a/crates/grammar/src/alpha050/statements/mod.rs +++ b/crates/grammar/src/alpha050/statements/mod.rs @@ -9,6 +9,7 @@ use super::{ Statement, }; +pub mod array_index_set; pub mod block; pub mod comment; pub mod const_init; @@ -29,6 +30,7 @@ pub fn statement_parser<'a>() -> impl AmberParser<'a, Spanned> { comment::comment_parser().map_with(|com, e| (Statement::Comment(com), e.span())), shebang::shebang_parser(), var_init::var_init_parser(stmnt.clone()), + array_index_set::array_index_set_parser(stmnt.clone()), var_set::var_set_parser(stmnt.clone()), block::block_parser_statement(stmnt.clone()), if_cond::if_chain_parser(stmnt.clone()), diff --git a/crates/server/tests/analysis/alpha050.rs b/crates/server/tests/analysis/alpha050.rs index 3e5c4c9..6cd122c 100644 --- a/crates/server/tests/analysis/alpha050.rs +++ b/crates/server/tests/analysis/alpha050.rs @@ -438,3 +438,257 @@ $$ failed(code) { .collect::>()); assert_debug_snapshot!(backend.files.errors); } + +#[test] +async fn test_array_index_set() { + let (service, _) = tower_lsp_server::LspService::new(|client| { + Backend::new( + client, + AmberVersion::Alpha050, + Some(Arc::new(MemoryFS::new())), + ) + }); + + let backend = service.inner(); + let vfs = &backend.files.fs; + + let file = { + #[cfg(windows)] + { + Path::new("C:\\main.ab") + } + #[cfg(unix)] + { + Path::new("/main.ab") + } + }; + let uri = Uri::from_file_path(file).unwrap(); + + vfs.write( + &uri.to_file_path().unwrap(), + r#" +let arr = [1, 2, 3] +arr[0] = 10 +"#, + ) + .await + .unwrap(); + + let file_id = backend.open_document(&uri).await.unwrap(); + + let symbol_table = backend.files.symbol_table.get(&file_id).unwrap(); + let generic_types = backend.files.generic_types.clone(); + + assert_debug_snapshot!(symbol_table.symbols); + assert_debug_snapshot!(symbol_table + .symbols + .iter() + .map(|(_, symbol_info)| symbol_info.to_string(&generic_types)) + .collect::>()); + assert_debug_snapshot!(backend.files.errors); +} + +#[test] +async fn test_array_index_set_const_error() { + let (service, _) = tower_lsp_server::LspService::new(|client| { + Backend::new( + client, + AmberVersion::Alpha050, + Some(Arc::new(MemoryFS::new())), + ) + }); + + let backend = service.inner(); + let vfs = &backend.files.fs; + + let file = { + #[cfg(windows)] + { + Path::new("C:\\main.ab") + } + #[cfg(unix)] + { + Path::new("/main.ab") + } + }; + let uri = Uri::from_file_path(file).unwrap(); + + vfs.write( + &uri.to_file_path().unwrap(), + r#" +const arr = [1, 2, 3] +arr[0] = 10 +"#, + ) + .await + .unwrap(); + + let file_id = backend.open_document(&uri).await.unwrap(); + + let symbol_table = backend.files.symbol_table.get(&file_id).unwrap(); + let generic_types = backend.files.generic_types.clone(); + + assert_debug_snapshot!(symbol_table.symbols); + assert_debug_snapshot!(symbol_table + .symbols + .iter() + .map(|(_, symbol_info)| symbol_info.to_string(&generic_types)) + .collect::>()); + // Should contain an error about assigning to a constant + assert_debug_snapshot!(backend.files.errors); +} + +#[test] +async fn test_array_index_set_non_array_error() { + let (service, _) = tower_lsp_server::LspService::new(|client| { + Backend::new( + client, + AmberVersion::Alpha050, + Some(Arc::new(MemoryFS::new())), + ) + }); + + let backend = service.inner(); + let vfs = &backend.files.fs; + + let file = { + #[cfg(windows)] + { + Path::new("C:\\main.ab") + } + #[cfg(unix)] + { + Path::new("/main.ab") + } + }; + let uri = Uri::from_file_path(file).unwrap(); + + vfs.write( + &uri.to_file_path().unwrap(), + r#" +let x = 5 +x[0] = 10 +"#, + ) + .await + .unwrap(); + + let file_id = backend.open_document(&uri).await.unwrap(); + + let symbol_table = backend.files.symbol_table.get(&file_id).unwrap(); + let generic_types = backend.files.generic_types.clone(); + + assert_debug_snapshot!(symbol_table.symbols); + assert_debug_snapshot!(symbol_table + .symbols + .iter() + .map(|(_, symbol_info)| symbol_info.to_string(&generic_types)) + .collect::>()); + // Should contain an error about indexing into a non-array type + assert_debug_snapshot!(backend.files.errors); +} + +#[test] +async fn test_array_index_set_type_mismatch_error() { + let (service, _) = tower_lsp_server::LspService::new(|client| { + Backend::new( + client, + AmberVersion::Alpha050, + Some(Arc::new(MemoryFS::new())), + ) + }); + + let backend = service.inner(); + let vfs = &backend.files.fs; + + let file = { + #[cfg(windows)] + { + Path::new("C:\\main.ab") + } + #[cfg(unix)] + { + Path::new("/main.ab") + } + }; + let uri = Uri::from_file_path(file).unwrap(); + + vfs.write( + &uri.to_file_path().unwrap(), + r#" +let arr = [1, 2, 3] +arr[0] = "hello" +"#, + ) + .await + .unwrap(); + + let file_id = backend.open_document(&uri).await.unwrap(); + + let symbol_table = backend.files.symbol_table.get(&file_id).unwrap(); + let generic_types = backend.files.generic_types.clone(); + + assert_debug_snapshot!(symbol_table.symbols); + assert_debug_snapshot!(symbol_table + .symbols + .iter() + .map(|(_, symbol_info)| symbol_info.to_string(&generic_types)) + .collect::>()); + // Should contain a type mismatch error + assert_debug_snapshot!(backend.files.errors); +} + +#[test] +async fn test_array_index_set_generic() { + let (service, _) = tower_lsp_server::LspService::new(|client| { + Backend::new( + client, + AmberVersion::Alpha050, + Some(Arc::new(MemoryFS::new())), + ) + }); + + let backend = service.inner(); + let vfs = &backend.files.fs; + + let file = { + #[cfg(windows)] + { + Path::new("C:\\main.ab") + } + #[cfg(unix)] + { + Path::new("/main.ab") + } + }; + let uri = Uri::from_file_path(file).unwrap(); + + vfs.write( + &uri.to_file_path().unwrap(), + r#" +fun set_first(arr, value) { + arr[0] = value +} + +set_first([1, 2, 3], 42) +"#, + ) + .await + .unwrap(); + + let file_id = backend.open_document(&uri).await.unwrap(); + + let symbol_table = backend.files.symbol_table.get(&file_id).unwrap(); + let generic_types = backend.files.generic_types.clone(); + + // In symbol_table.symbols the definition of set_first shows generic + // params 'arr: [Any]' and 'value: Any', but at the call site + // set_first([1, 2, 3], 42) they are constrained to [Int] and Int. + assert_debug_snapshot!(symbol_table.symbols); + assert_debug_snapshot!(symbol_table + .symbols + .iter() + .map(|(_, symbol_info)| symbol_info.to_string(&generic_types)) + .collect::>()); + assert_debug_snapshot!(backend.files.errors); +} diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set-2.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set-2.snap new file mode 100644 index 0000000..b5dc670 --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set-2.snap @@ -0,0 +1,8 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: "symbol_table.symbols.iter().map(|(_, symbol_info)|\nsymbol_info.to_string(&generic_types)).collect::>()" +--- +[ + "arr: [Int]", + "arr: [Int]", +] diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set-3.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set-3.snap new file mode 100644 index 0000000..2d46bbb --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set-3.snap @@ -0,0 +1,14 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: backend.files.errors +--- +{ + ( + FileId( + 0, + ), + FileVersion( + 1, + ), + ): [], +} diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set.snap new file mode 100644 index 0000000..49dcaf9 --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set.snap @@ -0,0 +1,32 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: symbol_table.symbols +--- +{ + 5..=8: SymbolInfo { + name: "arr", + symbol_type: Variable( + VariableSymbol { + is_const: false, + }, + ), + data_type: [Int], + is_definition: true, + undefined: false, + span: 5..8, + contexts: [], + }, + 21..=24: SymbolInfo { + name: "arr", + symbol_type: Variable( + VariableSymbol { + is_const: false, + }, + ), + data_type: [Int], + is_definition: false, + undefined: false, + span: 21..24, + contexts: [], + }, +} diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_const_error-2.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_const_error-2.snap new file mode 100644 index 0000000..b5dc670 --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_const_error-2.snap @@ -0,0 +1,8 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: "symbol_table.symbols.iter().map(|(_, symbol_info)|\nsymbol_info.to_string(&generic_types)).collect::>()" +--- +[ + "arr: [Int]", + "arr: [Int]", +] diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_const_error-3.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_const_error-3.snap new file mode 100644 index 0000000..1ecd455 --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_const_error-3.snap @@ -0,0 +1,19 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: backend.files.errors +--- +{ + ( + FileId( + 0, + ), + FileVersion( + 1, + ), + ): [ + ( + "Cannot assign to a constant", + 23..26, + ), + ], +} diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_const_error.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_const_error.snap new file mode 100644 index 0000000..10311fd --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_const_error.snap @@ -0,0 +1,32 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: symbol_table.symbols +--- +{ + 7..=10: SymbolInfo { + name: "arr", + symbol_type: Variable( + VariableSymbol { + is_const: true, + }, + ), + data_type: [Int], + is_definition: true, + undefined: false, + span: 7..10, + contexts: [], + }, + 23..=26: SymbolInfo { + name: "arr", + symbol_type: Variable( + VariableSymbol { + is_const: true, + }, + ), + data_type: [Int], + is_definition: false, + undefined: false, + span: 23..26, + contexts: [], + }, +} diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_generic-2.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_generic-2.snap new file mode 100644 index 0000000..c67b13a --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_generic-2.snap @@ -0,0 +1,12 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: "symbol_table.symbols.iter().map(|(_, symbol_info)|\nsymbol_info.to_string(&generic_types)).collect::>()" +--- +[ + "fun set_first(arr: [Any], value: Any): Null", + "arr: [Any]", + "value: Any", + "arr: [Any]", + "value: Any", + "fun set_first(arr: [Int], value: Int): Null", +] diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_generic-3.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_generic-3.snap new file mode 100644 index 0000000..2d46bbb --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_generic-3.snap @@ -0,0 +1,14 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: backend.files.errors +--- +{ + ( + FileId( + 0, + ), + FileVersion( + 1, + ), + ): [], +} diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_generic.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_generic.snap new file mode 100644 index 0000000..214096a --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_generic.snap @@ -0,0 +1,144 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: symbol_table.symbols +--- +{ + 5..=14: SymbolInfo { + name: "set_first", + symbol_type: Function( + FunctionSymbol { + arguments: [ + ( + FunctionArgument { + name: "arr", + data_type: Any, + is_optional: false, + default_value_type: None, + is_ref: false, + }, + 15..18, + ), + ( + FunctionArgument { + name: "value", + data_type: Any, + is_optional: false, + default_value_type: None, + is_ref: false, + }, + 20..25, + ), + ], + is_public: false, + compiler_flags: [], + docs: None, + }, + ), + data_type: Null, + is_definition: true, + undefined: false, + span: 5..14, + contexts: [], + }, + 15..=18: SymbolInfo { + name: "arr", + symbol_type: Variable( + VariableSymbol { + is_const: false, + }, + ), + data_type: Any, + is_definition: true, + undefined: false, + span: 15..18, + contexts: [], + }, + 20..=25: SymbolInfo { + name: "value", + symbol_type: Variable( + VariableSymbol { + is_const: false, + }, + ), + data_type: Any, + is_definition: true, + undefined: false, + span: 20..25, + contexts: [], + }, + 33..=36: SymbolInfo { + name: "arr", + symbol_type: Variable( + VariableSymbol { + is_const: false, + }, + ), + data_type: Any, + is_definition: false, + undefined: false, + span: 33..36, + contexts: [ + Function( + FunctionContext { + compiler_flags: [], + }, + ), + ], + }, + 42..=47: SymbolInfo { + name: "value", + symbol_type: Variable( + VariableSymbol { + is_const: false, + }, + ), + data_type: Any, + is_definition: false, + undefined: false, + span: 42..47, + contexts: [ + Function( + FunctionContext { + compiler_flags: [], + }, + ), + ], + }, + 51..=60: SymbolInfo { + name: "set_first", + symbol_type: Function( + FunctionSymbol { + arguments: [ + ( + FunctionArgument { + name: "arr", + data_type: [Int], + is_optional: false, + default_value_type: None, + is_ref: false, + }, + 61..70, + ), + ( + FunctionArgument { + name: "value", + data_type: Int, + is_optional: false, + default_value_type: None, + is_ref: false, + }, + 72..74, + ), + ], + is_public: false, + compiler_flags: [], + docs: None, + }, + ), + data_type: Null, + is_definition: false, + undefined: false, + span: 51..60, + contexts: [], + }, +} diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_non_array_error-2.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_non_array_error-2.snap new file mode 100644 index 0000000..e0e318b --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_non_array_error-2.snap @@ -0,0 +1,8 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: "symbol_table.symbols.iter().map(|(_, symbol_info)|\nsymbol_info.to_string(&generic_types)).collect::>()" +--- +[ + "x: Int", + "x: Int", +] diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_non_array_error-3.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_non_array_error-3.snap new file mode 100644 index 0000000..c05ff00 --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_non_array_error-3.snap @@ -0,0 +1,19 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: backend.files.errors +--- +{ + ( + FileId( + 0, + ), + FileVersion( + 1, + ), + ): [ + ( + "Cannot index into value of type Int", + 11..12, + ), + ], +} diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_non_array_error.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_non_array_error.snap new file mode 100644 index 0000000..9b8a83a --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_non_array_error.snap @@ -0,0 +1,32 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: symbol_table.symbols +--- +{ + 5..=6: SymbolInfo { + name: "x", + symbol_type: Variable( + VariableSymbol { + is_const: false, + }, + ), + data_type: Int, + is_definition: true, + undefined: false, + span: 5..6, + contexts: [], + }, + 11..=12: SymbolInfo { + name: "x", + symbol_type: Variable( + VariableSymbol { + is_const: false, + }, + ), + data_type: Int, + is_definition: false, + undefined: false, + span: 11..12, + contexts: [], + }, +} diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_type_mismatch_error-2.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_type_mismatch_error-2.snap new file mode 100644 index 0000000..b5dc670 --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_type_mismatch_error-2.snap @@ -0,0 +1,8 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: "symbol_table.symbols.iter().map(|(_, symbol_info)|\nsymbol_info.to_string(&generic_types)).collect::>()" +--- +[ + "arr: [Int]", + "arr: [Int]", +] diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_type_mismatch_error-3.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_type_mismatch_error-3.snap new file mode 100644 index 0000000..08ce206 --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_type_mismatch_error-3.snap @@ -0,0 +1,19 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: backend.files.errors +--- +{ + ( + FileId( + 0, + ), + FileVersion( + 1, + ), + ): [ + ( + "Expected type `Int`, found type `Text`", + 30..37, + ), + ], +} diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_type_mismatch_error.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_type_mismatch_error.snap new file mode 100644 index 0000000..49dcaf9 --- /dev/null +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__array_index_set_type_mismatch_error.snap @@ -0,0 +1,32 @@ +--- +source: crates/server/tests/analysis/alpha050.rs +expression: symbol_table.symbols +--- +{ + 5..=8: SymbolInfo { + name: "arr", + symbol_type: Variable( + VariableSymbol { + is_const: false, + }, + ), + data_type: [Int], + is_definition: true, + undefined: false, + span: 5..8, + contexts: [], + }, + 21..=24: SymbolInfo { + name: "arr", + symbol_type: Variable( + VariableSymbol { + is_const: false, + }, + ), + data_type: [Int], + is_definition: false, + undefined: false, + span: 21..24, + contexts: [], + }, +} diff --git a/crates/server/tests/grammar/alpha050.rs b/crates/server/tests/grammar/alpha050.rs index 9d21283..cec8849 100644 --- a/crates/server/tests/grammar/alpha050.rs +++ b/crates/server/tests/grammar/alpha050.rs @@ -357,3 +357,24 @@ fn test_modifiers() { assert_debug_snapshot!(parse(&tokenize(input))); } + +#[test] +fn test_array_index_set() { + let input = r#" + let arr = [1, 2, 3] + arr[0] = 10 +"#; + + assert_debug_snapshot!(parse_unwrap(&tokenize(input))); +} + +#[test] +fn test_array_index_set_expression_index() { + let input = r#" + let arr = [1, 2, 3] + let i = 0 + arr[i + 1] = 42 +"#; + + assert_debug_snapshot!(parse_unwrap(&tokenize(input))); +} diff --git a/crates/server/tests/grammar/snapshots/r#mod__grammar__alpha050__array_index_set.snap b/crates/server/tests/grammar/snapshots/r#mod__grammar__alpha050__array_index_set.snap new file mode 100644 index 0000000..a049adf --- /dev/null +++ b/crates/server/tests/grammar/snapshots/r#mod__grammar__alpha050__array_index_set.snap @@ -0,0 +1,95 @@ +--- +source: crates/server/tests/grammar/alpha050.rs +expression: parse_unwrap(&tokenize(input)) +--- +[ + ( + Statement( + ( + VariableInit( + ( + "let", + 5..8, + ), + ( + "arr", + 9..12, + ), + ( + Expression( + ( + Array( + [ + ( + Int( + ( + 1, + 16..17, + ), + ), + 16..17, + ), + ( + Int( + ( + 2, + 19..20, + ), + ), + 19..20, + ), + ( + Int( + ( + 3, + 22..23, + ), + ), + 22..23, + ), + ], + ), + 15..24, + ), + ), + 15..24, + ), + ), + 5..24, + ), + ), + 5..24, + ), + ( + Statement( + ( + ArrayIndexSet( + ( + "arr", + 29..32, + ), + ( + Int( + ( + 0, + 33..34, + ), + ), + 33..34, + ), + ( + Int( + ( + 10, + 38..40, + ), + ), + 38..40, + ), + ), + 29..40, + ), + ), + 29..40, + ), +] diff --git a/crates/server/tests/grammar/snapshots/r#mod__grammar__alpha050__array_index_set_expression_index.snap b/crates/server/tests/grammar/snapshots/r#mod__grammar__alpha050__array_index_set_expression_index.snap new file mode 100644 index 0000000..65434dc --- /dev/null +++ b/crates/server/tests/grammar/snapshots/r#mod__grammar__alpha050__array_index_set_expression_index.snap @@ -0,0 +1,141 @@ +--- +source: crates/server/tests/grammar/alpha050.rs +expression: parse_unwrap(&tokenize(input)) +--- +[ + ( + Statement( + ( + VariableInit( + ( + "let", + 5..8, + ), + ( + "arr", + 9..12, + ), + ( + Expression( + ( + Array( + [ + ( + Int( + ( + 1, + 16..17, + ), + ), + 16..17, + ), + ( + Int( + ( + 2, + 19..20, + ), + ), + 19..20, + ), + ( + Int( + ( + 3, + 22..23, + ), + ), + 22..23, + ), + ], + ), + 15..24, + ), + ), + 15..24, + ), + ), + 5..24, + ), + ), + 5..24, + ), + ( + Statement( + ( + VariableInit( + ( + "let", + 29..32, + ), + ( + "i", + 33..34, + ), + ( + Expression( + ( + Int( + ( + 0, + 37..38, + ), + ), + 37..38, + ), + ), + 37..38, + ), + ), + 29..38, + ), + ), + 29..38, + ), + ( + Statement( + ( + ArrayIndexSet( + ( + "arr", + 43..46, + ), + ( + Add( + ( + Var( + ( + "i", + 47..48, + ), + ), + 47..48, + ), + ( + Int( + ( + 1, + 51..52, + ), + ), + 51..52, + ), + ), + 47..52, + ), + ( + Int( + ( + 42, + 56..58, + ), + ), + 56..58, + ), + ), + 43..58, + ), + ), + 43..58, + ), +] diff --git a/crates/types/src/data_type.rs b/crates/types/src/data_type.rs index e01e931..42bf9c1 100644 --- a/crates/types/src/data_type.rs +++ b/crates/types/src/data_type.rs @@ -323,7 +323,6 @@ pub fn matches_type(expected: &DataType, given: &DataType, generics_map: &Generi (DataType::Error, _) | (_, DataType::Error) => false, (expected, DataType::Failable(given)) => matches_type(expected, given, generics_map), (DataType::Failable(expected), given) => matches_type(expected, given, generics_map), - (DataType::Number, DataType::Int) => true, (t1, t2) => *t1 == *t2, } }