From 4ea09138429425ca675c2534110a369f9ae5ab45 Mon Sep 17 00:00:00 2001 From: sofia Date: Sun, 3 Aug 2025 00:13:53 +0300 Subject: [PATCH] Add autocomplete for associated functions and struct fields --- examples/array_structs.reid | 4 +- reid-lsp/client/src/extension.ts | 1 + reid-lsp/src/analysis.rs | 95 +++++++++++++++++++------ reid/src/ast/parse.rs | 41 +++++++++-- reid/src/ast/token_stream.rs | 5 +- reid/src/lib.rs | 2 +- reid/src/mir/typecheck/typecheck.rs | 2 +- reid/src/mir/typecheck/typeinference.rs | 6 +- reid/src/mir/typecheck/typerefs.rs | 19 ++++- 9 files changed, 138 insertions(+), 37 deletions(-) diff --git a/examples/array_structs.reid b/examples/array_structs.reid index 5daea58..05fe5bd 100644 --- a/examples/array_structs.reid +++ b/examples/array_structs.reid @@ -21,7 +21,5 @@ fn main() -> u32 { let mut a = &mut value; - *a.second[2] = 5; - - return *a.second[2]; + return *a.second[0]; } diff --git a/reid-lsp/client/src/extension.ts b/reid-lsp/client/src/extension.ts index b737317..d3b91a4 100644 --- a/reid-lsp/client/src/extension.ts +++ b/reid-lsp/client/src/extension.ts @@ -36,6 +36,7 @@ export function activate(context: ExtensionContext) { env: { ...process.env, RUST_LOG: "debug", + RUST_BACKTRACE: 1, } } }; diff --git a/reid-lsp/src/analysis.rs b/reid-lsp/src/analysis.rs index 74c28b4..a7facbc 100644 --- a/reid-lsp/src/analysis.rs +++ b/reid-lsp/src/analysis.rs @@ -1,11 +1,12 @@ use std::{collections::HashMap, fmt::format, path::PathBuf}; use reid::{ - ast::{self, FunctionDefinition, lexer::FullToken}, + ast::{self, FunctionDefinition, lexer::FullToken, token_stream::TokenRange}, compile_module, error_raporting::{ErrorModules, ReidError}, mir::{ self, Context, FunctionCall, FunctionParam, IfExpression, SourceModuleId, StructType, TypeKind, WhileStatement, + typecheck::typerefs::TypeRefs, }, perform_all_passes, }; @@ -34,6 +35,7 @@ pub struct Autocomplete { #[derive(Debug, Clone)] pub enum AutocompleteKind { Type, + Field(TypeKind), Function(Vec, TypeKind), } @@ -48,6 +50,7 @@ impl ToString for AutocompleteKind { .collect::>(); format!("({}) -> {}", params.join(", "), ret_ty) } + AutocompleteKind::Field(type_kind) => format!("{}", type_kind), } } } @@ -97,19 +100,17 @@ pub fn init_types(map: &mut TokenAnalysisMap, meta: &mir::Metadata, ty: Option) { - for token in meta.range.start..=meta.range.end { - if let Some(token) = map.get_mut(&token) { - token.autocomplete = autocomplete.clone(); - } else { - map.insert( - token, - SemanticAnalysis { - ty: None, - autocomplete: autocomplete.clone(), - }, - ); - } +pub fn set_autocomplete(map: &mut TokenAnalysisMap, token_idx: usize, autocomplete: Vec) { + if let Some(token) = map.get_mut(&token_idx) { + token.autocomplete = autocomplete.clone(); + } else { + map.insert( + token_idx, + SemanticAnalysis { + ty: None, + autocomplete: autocomplete.clone(), + }, + ); } } @@ -152,8 +153,7 @@ pub fn analyze_context(context: &mir::Context, module: &mir::Module, error: Opti } } } - dbg!(import_meta, &autocompletes); - set_autocomplete(&mut map, &import_meta, autocompletes); + set_autocomplete(&mut map, import_meta.range.end, autocompletes); } } @@ -219,7 +219,7 @@ pub fn analyze_block( map, &named_variable_ref.2, expression - .return_type(&Default::default(), source_module.module_id) + .return_type(&TypeRefs::unknown(), source_module.module_id) .ok() .map(|(_, ty)| ty), ); @@ -254,7 +254,7 @@ pub fn analyze_expr( init_types( map, &expr.1, - expr.return_type(&Default::default(), source_module.module_id) + expr.return_type(&TypeRefs::unknown(), source_module.module_id) .ok() .map(|(_, t)| t), ); @@ -265,8 +265,48 @@ pub fn analyze_expr( analyze_expr(context, source_module, &value, map); analyze_expr(context, source_module, &index_expr, map); } - mir::ExprKind::Accessed(expression, ..) => { + mir::ExprKind::Accessed(expression, _, name, meta) => { analyze_expr(context, source_module, &expression, map); + + let accessed_type = expression.return_type(&TypeRefs::unknown(), source_module.module_id); + + let mut autocompletes = Vec::new(); + match accessed_type { + Ok((_, accessed_type)) => { + autocompletes.extend( + source_module + .associated_functions + .iter() + .filter(|(t, fun)| *t == accessed_type && fun.name.starts_with(name)) + .map(|(_, fun)| Autocomplete { + text: fun.name.clone(), + kind: AutocompleteKind::Function(fun.parameters.clone(), fun.return_type.clone()), + }), + ); + match accessed_type { + TypeKind::CustomType(ty_key) => { + let typedef = source_module + .typedefs + .iter() + .find(|t| t.name == ty_key.0 && t.source_module == ty_key.1); + if let Some(typedef) = typedef { + autocompletes.extend(match &typedef.kind { + mir::TypeDefinitionKind::Struct(StructType(fields)) => { + fields.iter().filter(|f| f.0.starts_with(name)).map(|f| Autocomplete { + text: f.0.clone(), + kind: AutocompleteKind::Field(f.1.clone()), + }) + } + }); + } + } + _ => {} + } + } + _ => {} + } + + set_autocomplete(map, meta.range.end - 1, autocompletes); } mir::ExprKind::Array(expressions) => { for expr in expressions { @@ -288,10 +328,25 @@ pub fn analyze_expr( analyze_expr(context, source_module, expr, map); } } - mir::ExprKind::AssociatedFunctionCall(_, FunctionCall { parameters, .. }) => { + mir::ExprKind::AssociatedFunctionCall( + ty, + FunctionCall { + parameters, name, meta, .. + }, + ) => { for expr in parameters { analyze_expr(context, source_module, expr, map); } + let function_autocomplete = source_module + .associated_functions + .iter() + .filter(|(t, fun)| t == ty && fun.name.starts_with(name)) + .map(|(_, fun)| Autocomplete { + text: fun.name.clone(), + kind: AutocompleteKind::Function(fun.parameters.clone(), fun.return_type.clone()), + }) + .collect::>(); + set_autocomplete(map, meta.range.end, function_autocomplete); } mir::ExprKind::If(IfExpression(cond, then_e, else_e)) => { analyze_expr(context, source_module, &cond, map); diff --git a/reid/src/ast/parse.rs b/reid/src/ast/parse.rs index 7f0f5a4..c53c996 100644 --- a/reid/src/ast/parse.rs +++ b/reid/src/ast/parse.rs @@ -175,7 +175,36 @@ impl Parse for AssociatedFunctionCall { let ty = stream.parse()?; stream.expect(Token::Colon)?; stream.expect(Token::Colon)?; - Ok(AssociatedFunctionCall(ty, stream.parse()?)) + match stream.parse() { + Ok(fn_call) => Ok(AssociatedFunctionCall(ty, fn_call)), + _ => { + if let Some(Token::Identifier(fn_name)) = stream.peek() { + stream.next(); + stream.expected_err_nonfatal("associated function call"); + + Ok(AssociatedFunctionCall( + ty, + FunctionCallExpression { + name: fn_name, + params: Vec::new(), + range: stream.get_range_prev_single().unwrap(), + is_macro: false, + }, + )) + } else { + stream.expected_err_nonfatal("associated function name"); + Ok(AssociatedFunctionCall( + ty, + FunctionCallExpression { + name: String::new(), + params: Vec::new(), + range: stream.get_range_prev_single().unwrap(), + is_macro: false, + }, + )) + } + } + } } } @@ -610,7 +639,7 @@ impl Parse for LetStatement { stream.expect(Token::Equals)?; let expression = stream.parse()?; - stream.expect_nonfatal(Token::Semi); + stream.expect_nonfatal(Token::Semi).ok(); Ok(LetStatement { name: variable, ty, @@ -645,7 +674,7 @@ impl Parse for ImportStatement { Err(stream.expected_err("identifier")?)? } - stream.expect_nonfatal(Token::Semi); + stream.expect_nonfatal(Token::Semi).ok(); Ok(ImportStatement(import_list, stream.get_range().unwrap())) } @@ -934,7 +963,7 @@ impl Parse for BlockLevelStatement { Some(Token::ReturnKeyword) => { stream.next(); let exp = stream.parse().ok(); - stream.expect(Token::Semi)?; + stream.expect_nonfatal(Token::Semi).ok(); Stmt::Return(ReturnType::Hard, exp) } Some(Token::For) => { @@ -999,7 +1028,7 @@ impl Parse for SetStatement { let var_ref = stream.parse()?; stream.expect(Token::Equals)?; let expr = stream.parse()?; - stream.expect_nonfatal(Token::Semi); + stream.expect_nonfatal(Token::Semi).ok(); Ok(SetStatement(var_ref, expr, stream.get_range().unwrap())) } } @@ -1040,7 +1069,7 @@ impl Parse for TopLevelStatement { stream.next(); // Consume Extern stream.expect(Token::FnKeyword)?; let extern_fn = Stmt::ExternFunction(stream.parse()?); - stream.expect_nonfatal(Token::Semi); + stream.expect_nonfatal(Token::Semi).ok(); extern_fn } Some(Token::FnKeyword) | Some(Token::PubKeyword) => Stmt::FunctionDefinition(stream.parse()?), diff --git a/reid/src/ast/token_stream.rs b/reid/src/ast/token_stream.rs index a6b6f57..a6e5539 100644 --- a/reid/src/ast/token_stream.rs +++ b/reid/src/ast/token_stream.rs @@ -87,15 +87,18 @@ impl<'a, 'b> TokenStream<'a, 'b> { } } - pub fn expect_nonfatal(&mut self, token: Token) { + pub fn expect_nonfatal(&mut self, token: Token) -> Result<(), ()> { if let (pos, Some(peeked)) = self.next_token(self.position) { if token == peeked.token { self.position = pos + 1; + Ok(()) } else { self.expecting_err_nonfatal(token); + Err(()) } } else { self.expecting_err_nonfatal(token); + Err(()) } } diff --git a/reid/src/lib.rs b/reid/src/lib.rs index f43ffbd..66e566b 100644 --- a/reid/src/lib.rs +++ b/reid/src/lib.rs @@ -130,7 +130,7 @@ pub fn compile_module<'map>( }; if errors.len() > 0 { - dbg!(&ast_module); + // dbg!(&ast_module); return Ok(Err(( ast_module, ReidError::from_kind( diff --git a/reid/src/mir/typecheck/typecheck.rs b/reid/src/mir/typecheck/typecheck.rs index f468a1f..c67f22b 100644 --- a/reid/src/mir/typecheck/typecheck.rs +++ b/reid/src/mir/typecheck/typecheck.rs @@ -621,7 +621,7 @@ impl Expression { // Update possibly resolved type Ok(true_ty) } else { - Err(ErrorKind::NoSuchField(field_name.clone())) + Err(ErrorKind::NoSuchField(key.0.clone())) } } else { Err(ErrorKind::TriedAccessingNonStruct(expr_ty)) diff --git a/reid/src/mir/typecheck/typeinference.rs b/reid/src/mir/typecheck/typeinference.rs index 73e2c7b..2068131 100644 --- a/reid/src/mir/typecheck/typeinference.rs +++ b/reid/src/mir/typecheck/typeinference.rs @@ -14,7 +14,7 @@ use crate::{ mir::{ pass::{AssociatedFunctionKey, ScopeVariable}, BinopDefinition, Block, CustomTypeKey, ExprKind, Expression, FunctionDefinition, FunctionDefinitionKind, - IfExpression, Module, ReturnKind, StmtKind, TypeKind, WhileStatement, + IfExpression, Module, ReturnKind, StmtKind, TypeKind, VagueType, WhileStatement, }, util::try_all, }; @@ -546,10 +546,10 @@ impl Expression { *type_kind = elem_ty.as_type().clone(); Ok(elem_ty) } - None => Err(ErrorKind::NoSuchField(field_name.clone())), + None => Ok(type_refs.from_type(&TypeKind::Vague(VagueType::Unknown)).unwrap()), } } - _ => Err(ErrorKind::TriedAccessingNonStruct(kind)), + _ => Ok(type_refs.from_type(&TypeKind::Vague(VagueType::Unknown)).unwrap()), } } ExprKind::Struct(struct_name, fields) => { diff --git a/reid/src/mir/typecheck/typerefs.rs b/reid/src/mir/typecheck/typerefs.rs index bf79821..dc6b6d0 100644 --- a/reid/src/mir/typecheck/typerefs.rs +++ b/reid/src/mir/typecheck/typerefs.rs @@ -97,6 +97,9 @@ pub struct TypeRefs { /// Indirect ID-references, referring to hints-vec pub(super) type_refs: RefCell>, pub(super) binop_types: BinopMap, + /// Used when the real typerefs are not available, and any TypeRefs need to + /// be resolved as Unknown. + pub unknown_typerefs: bool, } impl std::fmt::Display for TypeRefs { @@ -122,6 +125,14 @@ impl TypeRefs { hints: Default::default(), type_refs: Default::default(), binop_types: binops, + unknown_typerefs: false, + } + } + + pub fn unknown() -> TypeRefs { + TypeRefs { + unknown_typerefs: true, + ..Default::default() } } @@ -177,8 +188,12 @@ impl TypeRefs { } pub fn retrieve_typeref(&self, idx: usize) -> Option { - let inner_idx = unsafe { *self.recurse_type_ref(idx).borrow() }; - self.hints.borrow().get(inner_idx).cloned() + if !self.unknown_typerefs { + let inner_idx = unsafe { *self.recurse_type_ref(idx).borrow() }; + self.hints.borrow().get(inner_idx).cloned() + } else { + Some(TypeRefKind::Direct(TypeKind::Vague(VagueType::Unknown))) + } } pub fn retrieve_wide_type(&self, idx: usize, seen: &mut HashSet) -> Option {