From ccda4509f972ceb683d7d6c1fd6e55497bde1634 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Sat, 4 Oct 2025 07:42:01 -0400 Subject: Correctly provide completions when calling a proc immediately after a proc overload call --- src/server/analysis.odin | 80 +++++++++++++++++++++++++++------------------ tests/completions_test.odin | 49 +++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 32 deletions(-) diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 2278e00..abddab6 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -25,27 +25,32 @@ UsingStatement :: struct { } AstContext :: struct { - locals: [dynamic]LocalGroup, //locals all the way to the document position - globals: map[string]GlobalExpr, - recursion_map: map[rawptr]struct{}, - usings: [dynamic]UsingStatement, - file: ast.File, - allocator: mem.Allocator, - imports: []Package, //imports for the current document - current_package: string, - document_package: string, - deferred_package: [DeferredDepth]string, //When a package change happens when resolving - deferred_count: int, - use_locals: bool, - call: ^ast.Call_Expr, //used to determine the types for generics and the correct function for overloaded functions - value_decl: ^ast.Value_Decl, - field_name: ast.Ident, - uri: string, - fullpath: string, - non_mutable_only: bool, //Only store local value declarations that are non mutable. - overloading: bool, - position_hint: DocumentPositionContextHint, - resolving_locals: bool, + locals: [dynamic]LocalGroup, //locals all the way to the document position + globals: map[string]GlobalExpr, + recursion_map: map[rawptr]struct{}, + usings: [dynamic]UsingStatement, + file: ast.File, + allocator: mem.Allocator, + imports: []Package, //imports for the current document + current_package: string, + document_package: string, + deferred_package: [DeferredDepth]string, //When a package change happens when resolving + deferred_count: int, + use_locals: bool, + call: ^ast.Call_Expr, //used to determine the types for generics and the correct function for overloaded functions + value_decl: ^ast.Value_Decl, + field_name: ast.Ident, + uri: string, + fullpath: string, + non_mutable_only: bool, //Only store local value declarations that are non mutable. + overloading: bool, + position_hint: DocumentPositionContextHint, + resolving_locals: bool, + // Explicitly set whether to only resolve the correct overload, rather than have it be inferred by + // whether we're resolving locals and the position hint + // + // We should probably rework how this is handled in the future + resolve_specific_overload: bool, } make_ast_context :: proc( @@ -669,6 +674,20 @@ get_top_candiate :: proc(candidates: []Candidate) -> (Candidate, bool) { return top, true } + +should_resolve_all_proc_overload_possibilities :: proc(ast_context: ^AstContext, call_expr: ^ast.Call_Expr) -> bool { + // TODO: We need a better way to handle this + if ast_context.resolve_specific_overload { + return false + } + + if ast_context.resolving_locals { + return false + } + + return ast_context.position_hint == .Completion || ast_context.position_hint == .SignatureHelp || call_expr == nil +} + /* Figure out which function the call expression is using out of the list from proc group */ @@ -686,15 +705,7 @@ resolve_function_overload :: proc(ast_context: ^AstContext, group: ast.Proc_Grou ast_context.overloading = false } - resolve_all_possibilities: bool - if ast_context.resolving_locals { - resolve_all_possibilities = false - } else { - resolve_all_possibilities = - ast_context.position_hint == .Completion || ast_context.position_hint == .SignatureHelp || call_expr == nil - } - - + resolve_all_possibilities := should_resolve_all_proc_overload_possibilities(ast_context, call_expr) call_unnamed_arg_count := 0 if call_expr != nil { call_unnamed_arg_count = get_unnamed_arg_count(call_expr.args) @@ -1230,6 +1241,8 @@ internal_resolve_type_expression :: proc(ast_context: ^AstContext, node: ^ast.Ex resolve_call_expr :: proc(ast_context: ^AstContext, v: ^ast.Call_Expr) -> (Symbol, bool) { symbol := Symbol{} + // The function being called may be a local variable + ast_context.use_locals = true if ident, ok := v.expr.derived.(^ast.Ident); ok && len(v.args) >= 1 { switch ident.name { case "type_of": @@ -1239,6 +1252,9 @@ resolve_call_expr :: proc(ast_context: ^AstContext, v: ^ast.Call_Expr) -> (Symbo } } else if call, ok := v.expr.derived.(^ast.Call_Expr); ok { // handle the case where we immediately call a proc returned by another proc + // in this case we don't want to resolve all possibilities for a proc overload + ast_context.resolve_specific_overload = true + defer ast_context.resolve_specific_overload = false if ok := internal_resolve_type_expression(ast_context, v.expr, &symbol); ok { if value, ok := symbol.value.(SymbolProcedureValue); ok { if len(value.return_types) == 1 { @@ -1298,9 +1314,9 @@ resolve_index_expr :: proc(ast_context: ^AstContext, v: ^ast.Index_Expr) -> (Sym } return {}, false case SymbolMatrixValue: - value := SymbolFixedArrayValue{ + value := SymbolFixedArrayValue { expr = v2.expr, - len = v2.x, + len = v2.x, } indexed.value = value return indexed, true diff --git a/tests/completions_test.odin b/tests/completions_test.odin index a97ff0f..cb05518 100644 --- a/tests/completions_test.odin +++ b/tests/completions_test.odin @@ -4738,3 +4738,52 @@ ast_completion_handle_matching_field_with_unary :: proc(t: ^testing.T) { } test.expect_completion_insert_text(t, &source, "", {"foo"}) } + +@(test) +ast_completion_overload_proc_returning_proc_complete_comp_lit_arg_local :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + bar: int, + bazz: string, + } + + foo_none :: proc() -> proc(config: Foo) -> bool {} + foo_string :: proc(s: string) -> proc(config: Foo) -> bool {} + + foo :: proc{foo_none, foo_string} + + main :: proc() { + result := foo() + result({ + {*} + }) + } + `, + } + test.expect_completion_docs(t, &source, "", {"Foo.bar: int", "Foo.bazz: string"}) +} + +@(test) +ast_completion_overload_proc_returning_proc_complete_comp_lit_arg_direct :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + bar: int, + bazz: string, + } + + foo_none :: proc() -> proc(config: Foo) -> bool {} + foo_string :: proc(s: string) -> proc(config: Foo) -> bool {} + + foo :: proc{foo_none, foo_string} + + main :: proc() { + foo()({ + {*} + }) + } + `, + } + test.expect_completion_docs(t, &source, "", {"Foo.bar: int", "Foo.bazz: string"}) +} -- cgit v1.2.3