aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanielGavin <danielgavin5@hotmail.com>2024-02-03 13:08:38 +0100
committerGitHub <noreply@github.com>2024-02-03 13:08:38 +0100
commit1e87c5bc9732fcda326674283be75272a95f8dd3 (patch)
tree7214ad5d341c9581257d2ae1abb70c8f932be27e
parentd7531bd1ae6caf7fcd7395195865c553aa2c4ce4 (diff)
parentb7145b01decc292702f6e39e4a5e69a3d3401615 (diff)
Merge pull request #289 from DanielGavin/poly
New poly resolve system
-rw-r--r--.gitignore1
-rw-r--r--editors/vscode/package.json2
-rw-r--r--src/common/ast.odin35
-rw-r--r--src/server/analysis.odin629
-rw-r--r--src/server/check.odin2
-rw-r--r--src/server/clone.odin292
-rw-r--r--src/server/collector.odin8
-rw-r--r--src/server/generics.odin736
-rw-r--r--src/server/methods.odin11
-rw-r--r--src/server/requests.odin17
-rw-r--r--src/server/symbol.odin70
-rw-r--r--tests/completions_test.odin255
12 files changed, 1301 insertions, 757 deletions
diff --git a/.gitignore b/.gitignore
index 64acb4c..3c69cc3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,7 +6,6 @@
*.ll
*.sublime-project
*.sublime-workspace
-*.vscode
*.lib
*.exp
*.sqlite
diff --git a/editors/vscode/package.json b/editors/vscode/package.json
index b8ffe8a..b50d575 100644
--- a/editors/vscode/package.json
+++ b/editors/vscode/package.json
@@ -7,7 +7,7 @@
"type": "git",
"url": "git://github.com/DanielGavin/ols.git"
},
- "version": "0.1.16",
+ "version": "0.1.23",
"engines": {
"vscode": "^1.66.0"
},
diff --git a/src/common/ast.odin b/src/common/ast.odin
index 9adc57c..a2e5d42 100644
--- a/src/common/ast.odin
+++ b/src/common/ast.odin
@@ -163,7 +163,7 @@ get_attribute_objc_is_class_method :: proc(
return false
}
-unwrap_pointer :: proc(expr: ^ast.Expr) -> (ast.Ident, int, bool) {
+unwrap_pointer_ident :: proc(expr: ^ast.Expr) -> (ast.Ident, int, bool) {
n := 0
expr := expr
for expr != nil {
@@ -186,6 +186,25 @@ unwrap_pointer :: proc(expr: ^ast.Expr) -> (ast.Ident, int, bool) {
return {}, n, false
}
+unwrap_pointer_expr :: proc(expr: ^ast.Expr) -> (^ast.Expr, int, bool) {
+ n := 0
+ expr := expr
+ for expr != nil {
+ if pointer, ok := expr.derived.(^ast.Pointer_Type); ok {
+ expr = pointer.elem
+ n += 1
+ } else {
+ break
+ }
+ }
+
+ if expr == nil {
+ return {}, n, false
+ }
+
+ return expr, n, true
+}
+
is_expr_basic_lit :: proc(expr: ^ast.Expr) -> bool {
_, ok := expr.derived.(^ast.Basic_Lit)
return ok
@@ -198,10 +217,10 @@ collect_value_decl :: proc(
skip_private: bool,
) {
if value_decl, ok := stmt.derived.(^ast.Value_Decl); ok {
- is_deprecated := false
+ is_deprecated := false
is_private_file := false
- is_private_pkg := false
- is_builtin := false
+ is_private_pkg := false
+ is_builtin := false
for attribute in value_decl.attributes {
for elem in attribute.elems {
@@ -238,7 +257,7 @@ collect_value_decl :: proc(
if is_private_file && skip_private {
return
}
-
+
// If a private status is not explicitly set with an attribute above the declaration
// check the file comment.
if !is_private_file && !is_private_pkg && file.docs != nil {
@@ -247,7 +266,7 @@ collect_value_decl :: proc(
if strings.has_prefix(txt, "//+private") {
txt = strings.trim_prefix(txt, "//+private")
is_private_pkg = true
-
+
if strings.has_prefix(txt, " ") {
txt = strings.trim_space(txt)
if txt == "file" {
@@ -264,7 +283,7 @@ collect_value_decl :: proc(
if value_decl.type != nil {
append(
exprs,
- GlobalExpr{
+ GlobalExpr {
name = str,
name_expr = name,
expr = value_decl.type,
@@ -280,7 +299,7 @@ collect_value_decl :: proc(
if len(value_decl.values) > i {
append(
exprs,
- GlobalExpr{
+ GlobalExpr {
name = str,
name_expr = name,
expr = value_decl.values[i],
diff --git a/src/server/analysis.odin b/src/server/analysis.odin
index 07c079c..c855b22 100644
--- a/src/server/analysis.odin
+++ b/src/server/analysis.odin
@@ -87,7 +87,7 @@ AstContext :: struct {
document_package: string,
use_locals: bool,
local_id: int,
- call: ^ast.Call_Expr, //used to determene the types for generics and the correct function for overloaded functions
+ 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,
@@ -135,217 +135,6 @@ reset_ast_context :: proc(ast_context: ^AstContext) {
tokenizer_error_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) {
}
-/*
- Walk through the type expression while both the call expression and specialization type are the same
-*/
-
-resolve_poly_spec :: proc {
- resolve_poly_spec_node,
- resolve_poly_spec_array,
- resolve_poly_spec_dynamic_array,
-}
-
-resolve_poly_spec_array :: proc(
- ast_context: ^AstContext,
- call_array: $A/[]^$T,
- spec_array: $D/[]^$K,
- poly_map: ^map[string]^ast.Expr,
-) {
- if len(call_array) != len(spec_array) {
- return
- }
-
- for elem, i in call_array {
- resolve_poly_spec(ast_context, elem, spec_array[i], poly_map)
- }
-}
-
-resolve_poly_spec_dynamic_array :: proc(
- ast_context: ^AstContext,
- call_array: $A/[dynamic]^$T,
- spec_array: $D/[dynamic]^$K,
- poly_map: ^map[string]^ast.Expr,
-) {
- if len(call_array) != len(spec_array) {
- return
- }
-
- for elem, i in call_array {
- resolve_poly_spec(ast_context, elem, spec_array[i], poly_map)
- }
-}
-
-get_poly_node_to_expr :: proc(node: ^ast.Node) -> ^ast.Expr {
- using ast
-
- #partial switch v in node.derived {
- case ^Ident:
- return cast(^Expr)node
- case ^Comp_Lit:
- return v.type
- case:
- log.warnf("Unhandled poly to node kind %v", v)
- }
-
- return nil
-}
-
-resolve_poly_spec_node :: proc(
- ast_context: ^AstContext,
- call_node: ^ast.Node,
- spec_node: ^ast.Node,
- poly_map: ^map[string]^ast.Expr,
-) {
- using ast
-
- if call_node == nil || spec_node == nil {
- return
- }
-
- #partial switch m in spec_node.derived {
- case ^Bad_Expr:
- case ^Ident:
- case ^Implicit:
- case ^Undef:
- case ^Basic_Lit:
- case ^Poly_Type:
- if expr := get_poly_node_to_expr(call_node); expr != nil {
- poly_map[m.type.name] = expr
- }
- case ^Ellipsis:
- if n, ok := call_node.derived.(^Ellipsis); ok {
- resolve_poly_spec(ast_context, n.expr, m.expr, poly_map)
- }
- case ^Tag_Expr:
- if n, ok := call_node.derived.(^Tag_Expr); ok {
- resolve_poly_spec(ast_context, n.expr, m.expr, poly_map)
- }
- case ^Unary_Expr:
- if n, ok := call_node.derived.(^Unary_Expr); ok {
- resolve_poly_spec(ast_context, n.expr, m.expr, poly_map)
- }
- case ^Binary_Expr:
- if n, ok := call_node.derived.(^Binary_Expr); ok {
- resolve_poly_spec(ast_context, n.left, m.left, poly_map)
- resolve_poly_spec(ast_context, n.right, m.right, poly_map)
- }
- case ^Paren_Expr:
- if n, ok := call_node.derived.(^Paren_Expr); ok {
- resolve_poly_spec(ast_context, n.expr, m.expr, poly_map)
- }
- case ^Selector_Expr:
- if n, ok := call_node.derived.(^Selector_Expr); ok {
- resolve_poly_spec(ast_context, n.expr, m.expr, poly_map)
- resolve_poly_spec(ast_context, n.field, m.field, poly_map)
- }
- case ^Slice_Expr:
- if n, ok := call_node.derived.(^Slice_Expr); ok {
- resolve_poly_spec(ast_context, n.expr, m.expr, poly_map)
- resolve_poly_spec(ast_context, n.low, m.low, poly_map)
- resolve_poly_spec(ast_context, n.high, m.high, poly_map)
- }
- case ^Distinct_Type:
- if n, ok := call_node.derived.(^Distinct_Type); ok {
- resolve_poly_spec(ast_context, n.type, m.type, poly_map)
- }
- case ^Proc_Type:
- if n, ok := call_node.derived.(^Proc_Type); ok {
- resolve_poly_spec(ast_context, n.params, m.params, poly_map)
- resolve_poly_spec(ast_context, n.results, m.results, poly_map)
- }
- case ^Pointer_Type:
- if n, ok := call_node.derived.(^Pointer_Type); ok {
- resolve_poly_spec(ast_context, n.elem, m.elem, poly_map)
- }
- case ^Array_Type:
- if n, ok := call_node.derived.(^Array_Type); ok {
- resolve_poly_spec(ast_context, n.len, m.len, poly_map)
- resolve_poly_spec(ast_context, n.elem, m.elem, poly_map)
- }
- case ^Dynamic_Array_Type:
- if n, ok := call_node.derived.(^Dynamic_Array_Type); ok {
- resolve_poly_spec(ast_context, n.elem, m.elem, poly_map)
- }
- case ^Struct_Type:
- if n, ok := call_node.derived.(^Struct_Type); ok {
- resolve_poly_spec(
- ast_context,
- n.poly_params,
- m.poly_params,
- poly_map,
- )
- resolve_poly_spec(ast_context, n.align, m.align, poly_map)
- resolve_poly_spec(ast_context, n.fields, m.fields, poly_map)
- }
- case ^Field:
- if n, ok := call_node.derived.(^Field); ok {
- resolve_poly_spec(ast_context, n.names, m.names, poly_map)
- resolve_poly_spec(ast_context, n.type, m.type, poly_map)
- resolve_poly_spec(
- ast_context,
- n.default_value,
- m.default_value,
- poly_map,
- )
- }
- case ^Field_List:
- if n, ok := call_node.derived.(^Field_List); ok {
- resolve_poly_spec(ast_context, n.list, m.list, poly_map)
- }
- case ^Field_Value:
- if n, ok := call_node.derived.(^Field_Value); ok {
- resolve_poly_spec(ast_context, n.field, m.field, poly_map)
- resolve_poly_spec(ast_context, n.value, m.value, poly_map)
- }
- case ^Union_Type:
- if n, ok := call_node.derived.(^Union_Type); ok {
- resolve_poly_spec(
- ast_context,
- n.poly_params,
- m.poly_params,
- poly_map,
- )
- resolve_poly_spec(ast_context, n.align, m.align, poly_map)
- resolve_poly_spec(ast_context, n.variants, m.variants, poly_map)
- }
- case ^Enum_Type:
- if n, ok := call_node.derived.(^Enum_Type); ok {
- resolve_poly_spec(ast_context, n.base_type, m.base_type, poly_map)
- resolve_poly_spec(ast_context, n.fields, m.fields, poly_map)
- }
- case ^Bit_Set_Type:
- if n, ok := call_node.derived.(^Bit_Set_Type); ok {
- resolve_poly_spec(ast_context, n.elem, m.elem, poly_map)
- resolve_poly_spec(
- ast_context,
- n.underlying,
- m.underlying,
- poly_map,
- )
- }
- case ^Map_Type:
- if n, ok := call_node.derived.(^Map_Type); ok {
- resolve_poly_spec(ast_context, n.key, m.key, poly_map)
- resolve_poly_spec(ast_context, n.value, m.value, poly_map)
- }
- case ^Call_Expr:
- if n, ok := call_node.derived.(^Call_Expr); ok {
- resolve_poly_spec(ast_context, n.expr, m.expr, poly_map)
- resolve_poly_spec(ast_context, n.args, m.args, poly_map)
- }
- case ^Typeid_Type:
- if n, ok := call_node.derived.(^Typeid_Type); ok {
- resolve_poly_spec(
- ast_context,
- n.specialization,
- m.specialization,
- poly_map,
- )
- }
- case:
- log.error("Unhandled poly node kind: %T", m)
- }
-}
resolve_type_comp_literal :: proc(
ast_context: ^AstContext,
@@ -463,185 +252,6 @@ resolve_type_comp_literal :: proc(
return current_symbol, current_comp_lit, true
}
-resolve_generic_function :: proc {
- resolve_generic_function_ast,
- resolve_generic_function_symbol,
-}
-
-resolve_generic_function_symbol :: proc(
- ast_context: ^AstContext,
- params: []^ast.Field,
- results: []^ast.Field,
-) -> (
- Symbol,
- bool,
-) {
- if params == nil {
- return {}, false
- }
-
- if results == nil {
- return {}, false
- }
-
- if ast_context.call == nil {
- return {}, false
- }
-
- call_expr := ast_context.call
- poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator)
- i := 0
- count_required_params := 0
-
- for param in params {
- if param.default_value == nil {
- count_required_params += 1
- }
-
- for name in param.names {
- if len(call_expr.args) <= i {
- break
- }
-
- if poly, ok := name.derived.(^ast.Poly_Type); ok {
- poly_map[poly.type.name] = call_expr.args[i]
- }
-
- if param.type == nil {
- continue
- }
-
- if type_id, ok := param.type.derived.(^ast.Typeid_Type); ok {
- if type_id.specialization != nil &&
- !common.node_equal(
- call_expr.args[i],
- type_id.specialization,
- ) {
- return {}, false
- }
- }
-
- resolve_poly_spec_node(
- ast_context,
- call_expr.args[i],
- param.type,
- &poly_map,
- )
-
- i += 1
- }
- }
-
- if count_required_params > len(call_expr.args) ||
- count_required_params == 0 ||
- len(call_expr.args) == 0 {
- return {}, false
- }
-
- function_name := ""
- function_range: common.Range
-
- if ident, ok := call_expr.expr.derived.(^ast.Ident); ok {
- function_name = ident.name
- function_range = common.get_token_range(ident, ast_context.file.src)
- } else if selector, ok := call_expr.expr.derived.(^ast.Selector_Expr); ok {
- function_name = selector.field.name
- function_range = common.get_token_range(selector, ast_context.file.src)
- } else {
- return {}, false
- }
-
- symbol := Symbol {
- range = function_range,
- type = .Function,
- name = function_name,
- }
-
- return_types := make([dynamic]^ast.Field, ast_context.allocator)
- argument_types := make([dynamic]^ast.Field, ast_context.allocator)
-
- for result in results {
- if result.type == nil {
- continue
- }
-
- ident, wrapped, ok := common.unwrap_pointer(result.type)
-
- if ok {
- if m, ok := poly_map[ident.name]; ok {
- field := cast(^ast.Field)clone_node(
- result,
- ast_context.allocator,
- nil,
- )
- field.type = wrap_pointer(m, wrapped)
- append(&return_types, field)
- } else {
- append(&return_types, result)
- }
- } else {
- append(&return_types, result)
- }
- }
-
- for param in params {
- if len(param.names) == 0 {
- continue
- }
-
- //check the name for poly
- if poly_type, ok := param.names[0].derived.(^ast.Poly_Type);
- ok && param.type != nil {
- if m, ok := poly_map[poly_type.type.name]; ok {
- field := cast(^ast.Field)clone_node(
- param,
- ast_context.allocator,
- nil,
- )
- field.type = m
- append(&argument_types, field)
- }
- } else {
- append(&argument_types, param)
- }
- }
-
- symbol.value = SymbolProcedureValue {
- return_types = return_types[:],
- arg_types = argument_types[:],
- }
-
- return symbol, true
-}
-
-resolve_generic_function_ast :: proc(
- ast_context: ^AstContext,
- proc_lit: ast.Proc_Lit,
-) -> (
- Symbol,
- bool,
-) {
-
- using ast
-
- if proc_lit.type.params == nil {
- return Symbol{}, false
- }
-
- if proc_lit.type.results == nil {
- return Symbol{}, false
- }
-
- if ast_context.call == nil {
- return Symbol{}, false
- }
-
- return resolve_generic_function_symbol(
- ast_context,
- proc_lit.type.params.list,
- proc_lit.type.results.list,
- )
-}
is_symbol_same_typed :: proc(
ast_context: ^AstContext,
@@ -1024,6 +634,8 @@ resolve_basic_lit :: proc(
value: SymbolUntypedValue
+ value.tok = basic_lit.tok
+
if len(basic_lit.tok.text) == 0 {
return {}, false
}
@@ -1327,8 +939,13 @@ internal_resolve_type_expression :: proc(
return symbol, ok
case ^Call_Expr:
+ old_call := ast_context.call
ast_context.call = cast(^Call_Expr)node
+ defer {
+ ast_context.call = old_call
+ }
+
if ident, ok := v.expr.derived.(^ast.Ident); ok && len(v.args) >= 1 {
switch ident.name {
case "type_of":
@@ -1636,8 +1253,8 @@ internal_resolve_type_identifier :: proc(
for imp in ast_context.imports {
if strings.compare(imp.base, node.name) == 0 {
symbol := Symbol {
- type = .Package,
- pkg = imp.name,
+ type = .Package,
+ pkg = imp.name,
value = SymbolPackageValue{},
}
@@ -1688,7 +1305,7 @@ internal_resolve_type_identifier :: proc(
make_symbol_bitset_from_ast(ast_context, v^, node), true
return_symbol.name = node.name
case ^Proc_Lit:
- if !v.type.generic {
+ if !is_procedure_generic(v.type) {
return_symbol, ok =
make_symbol_procedure_from_ast(
ast_context,
@@ -1776,6 +1393,13 @@ internal_resolve_type_identifier :: proc(
v^,
)
case ^ast.Call_Expr:
+ old_call := ast_context.call
+ ast_context.call = cast(^Call_Expr)global.expr
+
+ defer {
+ ast_context.call = old_call
+ }
+
call_symbol := internal_resolve_type_expression(
ast_context,
v.expr,
@@ -1813,7 +1437,7 @@ internal_resolve_type_identifier :: proc(
make_symbol_enum_from_ast(ast_context, v^, node), true
return_symbol.name = node.name
case ^Proc_Lit:
- if !v.type.generic {
+ if !is_procedure_generic(v.type) {
return_symbol, ok =
make_symbol_procedure_from_ast(
ast_context,
@@ -1893,8 +1517,8 @@ internal_resolve_type_identifier :: proc(
//right now we replace the package ident with the absolute directory name, so it should have '/' which is not a valid ident character
if strings.contains(node.name, "/") {
symbol := Symbol {
- type = .Package,
- pkg = node.name,
+ type = .Package,
+ pkg = node.name,
value = SymbolPackageValue{},
}
@@ -1903,16 +1527,33 @@ internal_resolve_type_identifier :: proc(
return symbol, true
}
+ is_runtime := strings.contains(
+ ast_context.current_package,
+ "base/runtime",
+ )
+
+ if is_runtime {
+ if symbol, ok := lookup(node.name, "$builtin"); ok {
+ return resolve_symbol_return(ast_context, symbol)
+ }
+ }
+
//last option is to check the index
if symbol, ok := lookup(node.name, ast_context.current_package); ok {
return resolve_symbol_return(ast_context, symbol)
}
+ if !is_runtime {
+ if symbol, ok := lookup(node.name, "$builtin"); ok {
+ return resolve_symbol_return(ast_context, symbol)
+ }
+ }
+
for imp in ast_context.imports {
if strings.compare(imp.base, node.name) == 0 {
symbol := Symbol {
- type = .Package,
- pkg = imp.name,
+ type = .Package,
+ pkg = imp.name,
value = SymbolPackageValue{},
}
@@ -1922,9 +1563,6 @@ internal_resolve_type_identifier :: proc(
}
}
- if symbol, ok := lookup(node.name, "$builtin"); ok {
- return resolve_symbol_return(ast_context, symbol)
- }
for built in indexer.builtin_packages {
if symbol, ok := lookup(node.name, built); ok {
return resolve_symbol_return(ast_context, symbol)
@@ -2710,6 +2348,17 @@ make_int_ast :: proc(
return ident
}
+make_ident_ast :: proc(
+ ast_context: ^AstContext,
+ pos: tokenizer.Pos,
+ end: tokenizer.Pos,
+ name: string,
+) -> ^ast.Ident {
+ ident := new_type(ast.Ident, pos, end, ast_context.allocator)
+ ident.name = name
+ return ident
+}
+
make_int_basic_value :: proc(
ast_context: ^AstContext,
n: int,
@@ -3123,6 +2772,7 @@ make_symbol_struct_from_ast :: proc(
types = types[:],
ranges = ranges[:],
usings = usings,
+ poly = v.poly_params,
}
if _, ok := common.get_attribute_objc_class_name(attributes); ok {
@@ -3148,166 +2798,6 @@ make_symbol_struct_from_ast :: proc(
return symbol
}
-resolve_poly_union :: proc(
- ast_context: ^AstContext,
- poly_params: ^ast.Field_List,
- symbol: ^Symbol,
-) {
- if ast_context.call == nil {
- return
- }
-
- symbol_value := &symbol.value.(SymbolUnionValue)
-
- if symbol_value == nil {
- return
- }
-
- i := 0
-
- poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator)
-
- for param in poly_params.list {
- for name in param.names {
- if len(ast_context.call.args) <= i {
- break
- }
-
- if param.type == nil {
- continue
- }
-
- if poly, ok := param.type.derived.(^ast.Typeid_Type); ok {
- if ident, ok := name.derived.(^ast.Ident); ok {
- poly_map[ident.name] = ast_context.call.args[i]
- } else if poly, ok := name.derived.(^ast.Poly_Type); ok {
- if poly.type != nil {
- poly_map[poly.type.name] = ast_context.call.args[i]
- }
- }
- }
-
- i += 1
- }
- }
-
- for type, i in symbol_value.types {
- if ident, ok := type.derived.(^ast.Ident); ok {
- if expr, ok := poly_map[ident.name]; ok {
- symbol_value.types[i] = expr
- }
- } else if call_expr, ok := type.derived.(^ast.Call_Expr); ok {
- if call_expr.args == nil {
- continue
- }
-
- for arg, i in call_expr.args {
- if ident, ok := arg.derived.(^ast.Ident); ok {
- if expr, ok := poly_map[ident.name]; ok {
- symbol_value.types[i] = expr
- }
- }
- }
- }
- }
-}
-
-resolve_poly_struct :: proc(
- ast_context: ^AstContext,
- poly_params: ^ast.Field_List,
- symbol: ^Symbol,
-) {
- if ast_context.call == nil {
- return
- }
-
- symbol_value := &symbol.value.(SymbolStructValue)
-
- if symbol_value == nil {
- return
- }
-
- i := 0
-
- poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator)
-
- for param in poly_params.list {
- for name in param.names {
- if len(ast_context.call.args) <= i {
- break
- }
-
- if param.type == nil {
- continue
- }
-
- if poly, ok := param.type.derived.(^ast.Typeid_Type); ok {
- if ident, ok := name.derived.(^ast.Ident); ok {
- poly_map[ident.name] = ast_context.call.args[i]
- } else if poly, ok := name.derived.(^ast.Poly_Type); ok {
- if poly.type != nil {
- poly_map[poly.type.name] = ast_context.call.args[i]
- }
- }
- }
-
- i += 1
- }
- }
-
- Visit_Data :: struct {
- poly_map: map[string]^ast.Expr,
- symbol_value: ^SymbolStructValue,
- parent: ^ast.Node,
- i: int,
- }
-
- visit :: proc(visitor: ^ast.Visitor, node: ^ast.Node) -> ^ast.Visitor {
- if node == nil || visitor == nil {
- return nil
- }
-
- data := cast(^Visit_Data)visitor.data
-
- if ident, ok := node.derived.(^ast.Ident); ok {
- if expr, ok := data.poly_map[ident.name]; ok {
- if data.parent != nil {
- #partial switch &v in data.parent.derived {
- case ^ast.Array_Type:
- v.elem = expr
- case ^ast.Dynamic_Array_Type:
- v.elem = expr
- }
- } else {
- data.symbol_value.types[data.i] = expr
- }
- }
- }
-
- #partial switch v in node.derived {
- case ^ast.Array_Type, ^ast.Dynamic_Array_Type, ^ast.Selector_Expr:
- data.parent = node
- }
-
- return visitor
- }
-
- for type, i in symbol_value.types {
- data := Visit_Data {
- poly_map = poly_map,
- symbol_value = symbol_value,
- i = i,
- }
-
- visitor := ast.Visitor {
- data = &data,
- visit = visit,
- }
-
- ast.walk(&visitor, type)
- }
-}
-
get_globals :: proc(file: ast.File, ast_context: ^AstContext) {
exprs := common.collect_globals(file)
@@ -3331,8 +2821,13 @@ get_generic_assignment :: proc(
case ^Or_Return_Expr:
get_generic_assignment(file, v.expr, ast_context, results, calls)
case ^Call_Expr:
+ old_call := ast_context.call
ast_context.call = cast(^ast.Call_Expr)value
+ defer {
+ ast_context.call = old_call
+ }
+
if symbol, ok := resolve_type_expression(ast_context, v.expr); ok {
if procedure, ok := symbol.value.(SymbolProcedureValue); ok {
for ret in procedure.return_types {
@@ -4093,6 +3588,10 @@ resolve_entire_file :: proc(
symbols := make(map[uintptr]SymbolAndNode, 10000, allocator)
for decl in document.ast.decls {
+ if _, is_value := decl.derived.(^ast.Value_Decl); !is_value {
+ continue
+ }
+
resolve_entire_decl(
&ast_context,
document,
@@ -5035,10 +4534,10 @@ fallback_position_context_completion :: proc(
}
p := parser.Parser {
- err = common.parser_warning_handler, //empty
- warn = common.parser_warning_handler, //empty
+ err = common.parser_warning_handler, //empty
+ warn = common.parser_warning_handler, //empty
flags = {.Optional_Semicolons},
- file = &position_context.file,
+ file = &position_context.file,
}
tokenizer.init(
diff --git a/src/server/check.odin b/src/server/check.odin
index 502beb5..bd94f34 100644
--- a/src/server/check.odin
+++ b/src/server/check.odin
@@ -19,7 +19,7 @@ import "core:thread"
import "shared:common"
check :: proc(uri: common.Uri, writer: ^Writer, config: ^common.Config) {
- data := make([]byte, mem.Kilobyte * 100, context.temp_allocator)
+ data := make([]byte, mem.Kilobyte * 200, context.temp_allocator)
buffer: []byte
code: u32
diff --git a/src/server/clone.odin b/src/server/clone.odin
index e8cd090..09827d1 100644
--- a/src/server/clone.odin
+++ b/src/server/clone.odin
@@ -1,13 +1,13 @@
package server
-import "core:mem"
import "core:fmt"
-import "core:odin/tokenizer"
-import "core:odin/ast"
-import "core:strings"
-import "core:log"
import "core:intrinsics"
+import "core:log"
+import "core:mem"
+import "core:odin/ast"
+import "core:odin/tokenizer"
import "core:reflect"
+import "core:strings"
_ :: intrinsics
new_type :: proc(
@@ -143,148 +143,150 @@ clone_node :: proc(
}
if res.derived != nil do #partial switch r in res.derived {
- case ^Ident:
- n := node.derived.(^Ident)
+ case ^Ident:
+ n := node.derived.(^Ident)
- if unique_strings == nil {
- r.name = strings.clone(n.name, allocator)
- } else {
- r.name = get_index_unique_string(unique_strings, allocator, n.name)
- }
- case ^Implicit:
- n := node.derived.(^Implicit)
- if unique_strings == nil {
- r.tok.text = strings.clone(n.tok.text, allocator)
- } else {
- r.tok.text = get_index_unique_string(unique_strings, allocator, n.tok.text)
- }
- case ^Undef:
- case ^Basic_Lit:
- n := node.derived.(^Basic_Lit)
- if unique_strings == nil {
- r.tok.text = strings.clone(n.tok.text, allocator)
- } else {
- r.tok.text = get_index_unique_string(unique_strings, allocator, n.tok.text)
- }
- case ^Basic_Directive:
- n := node.derived.(^Basic_Directive)
- if unique_strings == nil {
- r.name = strings.clone(n.name, allocator)
- } else {
- r.name = get_index_unique_string(unique_strings, allocator, n.name)
- }
- case ^Ellipsis:
- r.expr = clone_type(r.expr, allocator, unique_strings)
- case ^Tag_Expr:
- r.expr = clone_type(r.expr, allocator, unique_strings)
- case ^Unary_Expr:
- r.expr = clone_type(r.expr, allocator, unique_strings)
- case ^Binary_Expr:
- n := node.derived.(^Binary_Expr)
- r.left = clone_type(r.left, allocator, unique_strings)
- r.right = clone_type(r.right, allocator, unique_strings)
- //Todo: Replace this with some constant table for opeator text
- if unique_strings == nil {
- r.op.text = strings.clone(n.op.text, allocator)
- } else {
- r.op.text = get_index_unique_string(unique_strings, allocator, n.op.text)
- }
- case ^Paren_Expr:
- r.expr = clone_type(r.expr, allocator, unique_strings)
- case ^Selector_Expr:
- r.expr = clone_type(r.expr, allocator, unique_strings)
- r.field = auto_cast clone_type(r.field, allocator, unique_strings)
- case ^Implicit_Selector_Expr:
- r.field = auto_cast clone_type(r.field, allocator, unique_strings)
- case ^Slice_Expr:
- r.expr = clone_type(r.expr, allocator, unique_strings)
- r.low = clone_type(r.low, allocator, unique_strings)
- r.high = clone_type(r.high, allocator, unique_strings)
- case ^Attribute:
- r.elems = clone_type(r.elems, allocator, unique_strings)
- case ^Distinct_Type:
- r.type = clone_type(r.type, allocator, unique_strings)
- case ^Proc_Type:
- r.params = auto_cast clone_type(r.params, allocator, unique_strings)
- r.results = auto_cast clone_type(r.results, allocator, unique_strings)
- case ^Pointer_Type:
- r.elem = clone_type(r.elem, allocator, unique_strings)
- case ^Array_Type:
- r.len = clone_type(r.len, allocator, unique_strings)
- r.elem = clone_type(r.elem, allocator, unique_strings)
- r.tag = clone_type(r.tag, allocator, unique_strings)
- case ^Dynamic_Array_Type:
- r.elem = clone_type(r.elem, allocator, unique_strings)
- r.tag = clone_type(r.tag, allocator, unique_strings)
- case ^Struct_Type:
- r.poly_params = auto_cast clone_type(r.poly_params, allocator, unique_strings)
- r.align = clone_type(r.align, allocator, unique_strings)
- r.fields = auto_cast clone_type(r.fields, allocator, unique_strings)
- r.where_clauses = clone_type(r.where_clauses, allocator, unique_strings)
- case ^Field:
- r.names = clone_type(r.names, allocator, unique_strings)
- r.type = clone_type(r.type, allocator, unique_strings)
- r.default_value = clone_type(r.default_value, allocator, unique_strings)
- case ^Field_List:
- r.list = clone_type(r.list, allocator, unique_strings)
- case ^Field_Value:
- r.field = clone_type(r.field, allocator, unique_strings)
- r.value = clone_type(r.value, allocator, unique_strings)
- case ^Union_Type:
- r.poly_params = auto_cast clone_type(r.poly_params, allocator, unique_strings)
- r.align = clone_type(r.align, allocator, unique_strings)
- r.variants = clone_type(r.variants, allocator, unique_strings)
- r.where_clauses = clone_type(r.where_clauses, allocator, unique_strings)
- case ^Enum_Type:
- r.base_type = clone_type(r.base_type, allocator, unique_strings)
- r.fields = clone_type(r.fields, allocator, unique_strings)
- case ^Bit_Set_Type:
- r.elem = clone_type(r.elem, allocator, unique_strings)
- r.underlying = clone_type(r.underlying, allocator, unique_strings)
- case ^Map_Type:
- r.key = clone_type(r.key, allocator, unique_strings)
- r.value = clone_type(r.value, allocator, unique_strings)
- case ^Call_Expr:
- r.expr = clone_type(r.expr, allocator, unique_strings)
- r.args = clone_type(r.args, allocator, unique_strings)
- case ^Typeid_Type:
- r.specialization = clone_type(r.specialization, allocator, unique_strings)
- case ^Ternary_When_Expr:
- r.x = clone_type(r.x, allocator, unique_strings)
- r.cond = clone_type(r.cond, allocator, unique_strings)
- r.y = clone_type(r.y, allocator, unique_strings)
- case ^Poly_Type:
- r.type = auto_cast clone_type(r.type, allocator, unique_strings)
- r.specialization = clone_type(r.specialization, allocator, unique_strings)
- case ^Proc_Group:
- r.args = clone_type(r.args, allocator, unique_strings)
- case ^Comp_Lit:
- r.type = clone_type(r.type, allocator, unique_strings)
- r.elems = clone_type(r.elems, allocator, unique_strings)
- case ^Proc_Lit:
- r.type = cast(^Proc_Type)clone_type(cast(^Node)r.type, allocator, unique_strings)
- r.body = nil
- r.where_clauses = nil
- case ^Helper_Type:
- r.type = clone_type(r.type, allocator, unique_strings)
- case ^Type_Cast:
- r.type = clone_type(r.type, allocator, unique_strings)
- r.expr = clone_type(r.expr, allocator, unique_strings)
- case ^Deref_Expr:
- r.expr = clone_type(r.expr, allocator, unique_strings)
- case ^Index_Expr:
- r.expr = clone_type(r.expr, allocator, unique_strings)
- r.index = clone_type(r.index, allocator, unique_strings)
- case ^Multi_Pointer_Type:
- r.elem = clone_type(r.elem, allocator, unique_strings)
- case ^Matrix_Type:
- r.elem = clone_type(r.elem, allocator, unique_strings)
- case ^Type_Assertion:
- r.expr = clone_type(r.expr, allocator, unique_strings)
- r.type = clone_type(r.type, allocator, unique_strings)
- case:
- //fmt.logf("Unhandled node kind: %T", r)
+ if unique_strings == nil {
+ r.name = strings.clone(n.name, allocator)
+ } else {
+ r.name = get_index_unique_string(unique_strings, allocator, n.name)
+ }
+ case ^Implicit:
+ n := node.derived.(^Implicit)
+ if unique_strings == nil {
+ r.tok.text = strings.clone(n.tok.text, allocator)
+ } else {
+ r.tok.text = get_index_unique_string(unique_strings, allocator, n.tok.text)
+ }
+ case ^Undef:
+ case ^Basic_Lit:
+ n := node.derived.(^Basic_Lit)
+ if unique_strings == nil {
+ r.tok.text = strings.clone(n.tok.text, allocator)
+ } else {
+ r.tok.text = get_index_unique_string(unique_strings, allocator, n.tok.text)
}
+ case ^Basic_Directive:
+ n := node.derived.(^Basic_Directive)
+ if unique_strings == nil {
+ r.name = strings.clone(n.name, allocator)
+ } else {
+ r.name = get_index_unique_string(unique_strings, allocator, n.name)
+ }
+ case ^Ellipsis:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ case ^Tag_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ case ^Unary_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ case ^Binary_Expr:
+ n := node.derived.(^Binary_Expr)
+ r.left = clone_type(r.left, allocator, unique_strings)
+ r.right = clone_type(r.right, allocator, unique_strings)
+ //Todo: Replace this with some constant table for opeator text
+ if unique_strings == nil {
+ r.op.text = strings.clone(n.op.text, allocator)
+ } else {
+ r.op.text = get_index_unique_string(unique_strings, allocator, n.op.text)
+ }
+ case ^Paren_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ case ^Selector_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ r.field = auto_cast clone_type(r.field, allocator, unique_strings)
+ case ^Implicit_Selector_Expr:
+ r.field = auto_cast clone_type(r.field, allocator, unique_strings)
+ case ^Slice_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ r.low = clone_type(r.low, allocator, unique_strings)
+ r.high = clone_type(r.high, allocator, unique_strings)
+ case ^Attribute:
+ r.elems = clone_type(r.elems, allocator, unique_strings)
+ case ^Distinct_Type:
+ r.type = clone_type(r.type, allocator, unique_strings)
+ case ^Proc_Type:
+ r.params = auto_cast clone_type(r.params, allocator, unique_strings)
+ r.results = auto_cast clone_type(r.results, allocator, unique_strings)
+ case ^Pointer_Type:
+ r.elem = clone_type(r.elem, allocator, unique_strings)
+ case ^Array_Type:
+ r.len = clone_type(r.len, allocator, unique_strings)
+ r.elem = clone_type(r.elem, allocator, unique_strings)
+ r.tag = clone_type(r.tag, allocator, unique_strings)
+ case ^Dynamic_Array_Type:
+ r.elem = clone_type(r.elem, allocator, unique_strings)
+ r.tag = clone_type(r.tag, allocator, unique_strings)
+ case ^Struct_Type:
+ r.poly_params = auto_cast clone_type(r.poly_params, allocator, unique_strings)
+ r.align = clone_type(r.align, allocator, unique_strings)
+ r.fields = auto_cast clone_type(r.fields, allocator, unique_strings)
+ r.where_clauses = clone_type(r.where_clauses, allocator, unique_strings)
+ case ^Field:
+ r.names = clone_type(r.names, allocator, unique_strings)
+ r.type = clone_type(r.type, allocator, unique_strings)
+ r.default_value = clone_type(r.default_value, allocator, unique_strings)
+ case ^Field_List:
+ r.list = clone_type(r.list, allocator, unique_strings)
+ case ^Field_Value:
+ r.field = clone_type(r.field, allocator, unique_strings)
+ r.value = clone_type(r.value, allocator, unique_strings)
+ case ^Union_Type:
+ r.poly_params = auto_cast clone_type(r.poly_params, allocator, unique_strings)
+ r.align = clone_type(r.align, allocator, unique_strings)
+ r.variants = clone_type(r.variants, allocator, unique_strings)
+ r.where_clauses = clone_type(r.where_clauses, allocator, unique_strings)
+ case ^Enum_Type:
+ r.base_type = clone_type(r.base_type, allocator, unique_strings)
+ r.fields = clone_type(r.fields, allocator, unique_strings)
+ case ^Bit_Set_Type:
+ r.elem = clone_type(r.elem, allocator, unique_strings)
+ r.underlying = clone_type(r.underlying, allocator, unique_strings)
+ case ^Map_Type:
+ r.key = clone_type(r.key, allocator, unique_strings)
+ r.value = clone_type(r.value, allocator, unique_strings)
+ case ^Call_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ r.args = clone_type(r.args, allocator, unique_strings)
+ case ^Typeid_Type:
+ r.specialization = clone_type(r.specialization, allocator, unique_strings)
+ case ^Ternary_When_Expr:
+ r.x = clone_type(r.x, allocator, unique_strings)
+ r.cond = clone_type(r.cond, allocator, unique_strings)
+ r.y = clone_type(r.y, allocator, unique_strings)
+ case ^Poly_Type:
+ r.type = auto_cast clone_type(r.type, allocator, unique_strings)
+ r.specialization = clone_type(r.specialization, allocator, unique_strings)
+ case ^Proc_Group:
+ r.args = clone_type(r.args, allocator, unique_strings)
+ case ^Comp_Lit:
+ r.type = clone_type(r.type, allocator, unique_strings)
+ r.elems = clone_type(r.elems, allocator, unique_strings)
+ case ^Proc_Lit:
+ r.type = cast(^Proc_Type)clone_type(cast(^Node)r.type, allocator, unique_strings)
+ r.body = nil
+ r.where_clauses = nil
+ case ^Helper_Type:
+ r.type = clone_type(r.type, allocator, unique_strings)
+ case ^Type_Cast:
+ r.type = clone_type(r.type, allocator, unique_strings)
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ case ^Deref_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ case ^Index_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ r.index = clone_type(r.index, allocator, unique_strings)
+ case ^Multi_Pointer_Type:
+ r.elem = clone_type(r.elem, allocator, unique_strings)
+ case ^Matrix_Type:
+ r.elem = clone_type(r.elem, allocator, unique_strings)
+ r.column_count = clone_type(r.column_count, allocator, unique_strings)
+ r.row_count = clone_type(r.row_count, allocator, unique_strings)
+ case ^Type_Assertion:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ r.type = clone_type(r.type, allocator, unique_strings)
+ case:
+ //fmt.logf("Unhandled node kind: %T", r)
+ }
return res
}
diff --git a/src/server/collector.odin b/src/server/collector.odin
index 917dea6..5d91039 100644
--- a/src/server/collector.odin
+++ b/src/server/collector.odin
@@ -75,7 +75,7 @@ make_symbol_collection :: proc(
config: ^common.Config,
) -> SymbolCollection {
return(
- SymbolCollection{
+ SymbolCollection {
allocator = allocator,
config = config,
packages = make(map[string]SymbolPackage, 16, allocator),
@@ -141,7 +141,7 @@ collect_procedure_fields :: proc(
value := SymbolProcedureValue {
return_types = returns[:],
arg_types = args[:],
- generic = proc_type.generic,
+ generic = is_procedure_generic(proc_type),
}
return value
@@ -435,7 +435,7 @@ collect_method :: proc(collection: ^SymbolCollection, symbol: Symbol) {
return
}
- expr, _, ok := common.unwrap_pointer(value.arg_types[0].type)
+ expr, _, ok := common.unwrap_pointer_ident(value.arg_types[0].type)
if !ok {
return
@@ -514,7 +514,7 @@ collect_objc :: proc(
append(
&objc_struct.functions,
- ObjcFunction{
+ ObjcFunction {
logical_name = get_index_unique_string_collection(
collection,
objc_name,
diff --git a/src/server/generics.odin b/src/server/generics.odin
new file mode 100644
index 0000000..50a477a
--- /dev/null
+++ b/src/server/generics.odin
@@ -0,0 +1,736 @@
+package server
+
+import "core:fmt"
+import "core:log"
+import "core:mem"
+import "core:odin/ast"
+import "core:odin/parser"
+import "core:odin/tokenizer"
+import "core:path/filepath"
+import path "core:path/slashpath"
+import "core:reflect"
+import "core:slice"
+import "core:sort"
+import "core:strconv"
+import "core:strings"
+import "core:unicode/utf8"
+
+import "shared:common"
+
+resolve_poly :: proc(
+ ast_context: ^AstContext,
+ call_node: ^ast.Expr,
+ call_symbol: Symbol,
+ poly_node: ^ast.Expr,
+ poly_map: ^map[string]^ast.Expr,
+) -> bool {
+ if poly_node == nil || call_node == nil {
+ return false
+ }
+
+ specialization: ^ast.Expr
+ type: ^ast.Expr
+
+ poly_node := poly_node
+ poly_node, _, _ = common.unwrap_pointer_expr(poly_node)
+
+ #partial switch v in poly_node.derived {
+ case ^ast.Typeid_Type:
+ specialization = v.specialization
+ case ^ast.Poly_Type:
+ specialization = v.specialization
+ type = v.type
+ }
+
+ if specialization == nil {
+ if type != nil {
+ if ident, ok := unwrap_ident(type); ok {
+ poly_map[ident.name] = make_ident_ast(
+ ast_context,
+ call_node.pos,
+ call_node.end,
+ call_symbol.name,
+ )
+ }
+ }
+ return true
+ } else if type != nil {
+ if ident, ok := unwrap_ident(type); ok {
+ poly_map[ident.name] = specialization
+ }
+ }
+
+ #partial switch p in specialization.derived {
+ case ^ast.Matrix_Type:
+ if call_matrix, ok := call_node.derived.(^ast.Matrix_Type); ok {
+ found := false
+ if poly_type, ok := p.row_count.derived.(^ast.Poly_Type); ok {
+ if ident, ok := unwrap_ident(poly_type.type); ok {
+ poly_map[ident.name] = call_matrix.row_count
+ }
+
+ if poly_type.specialization != nil {
+ return resolve_poly(
+ ast_context,
+ call_matrix.row_count,
+ call_symbol,
+ p.row_count,
+ poly_map,
+ )
+ }
+ found |= true
+ }
+
+ if poly_type, ok := p.column_count.derived.(^ast.Poly_Type); ok {
+ if ident, ok := unwrap_ident(poly_type.type); ok {
+ poly_map[ident.name] = call_matrix.column_count
+ }
+
+ if poly_type.specialization != nil {
+ return resolve_poly(
+ ast_context,
+ call_matrix.column_count,
+ call_symbol,
+ p.column_count,
+ poly_map,
+ )
+ }
+ found |= true
+ }
+
+ if poly_type, ok := p.elem.derived.(^ast.Poly_Type); ok {
+ if ident, ok := unwrap_ident(poly_type.type); ok {
+ poly_map[ident.name] = call_matrix.elem
+ }
+
+ if poly_type.specialization != nil {
+ return resolve_poly(
+ ast_context,
+ call_matrix.elem,
+ call_symbol,
+ p.elem,
+ poly_map,
+ )
+ }
+ found |= true
+ }
+ return found
+ }
+ case ^ast.Call_Expr:
+ if call_struct, ok := call_node.derived.(^ast.Struct_Type); ok {
+ arg_index := 0
+ struct_value := call_symbol.value.(SymbolStructValue)
+
+ for arg in p.args {
+ if poly_type, ok := arg.derived.(^ast.Poly_Type); ok {
+ if poly_type.type == nil ||
+ struct_value.poly == nil ||
+ len(struct_value.args) <= arg_index {
+ return false
+ }
+
+ poly_map[poly_type.type.name] =
+ struct_value.args[arg_index]
+
+ arg_index += 1
+ }
+ }
+ }
+ case ^ast.Struct_Type:
+ case ^ast.Dynamic_Array_Type:
+ if call_array, ok := call_node.derived.(^ast.Dynamic_Array_Type); ok {
+ if poly_type, ok := p.elem.derived.(^ast.Poly_Type); ok {
+ if ident, ok := unwrap_ident(poly_type.type); ok {
+ poly_map[ident.name] = call_array.elem
+ }
+
+ if poly_type.specialization != nil {
+ return resolve_poly(
+ ast_context,
+ call_array.elem,
+ call_symbol,
+ p.elem,
+ poly_map,
+ )
+ }
+ return true
+ }
+ }
+ case ^ast.Array_Type:
+ if call_array, ok := call_node.derived.(^ast.Array_Type); ok {
+ found := false
+ if poly_type, ok := p.elem.derived.(^ast.Poly_Type); ok {
+ if ident, ok := unwrap_ident(poly_type.type); ok {
+ poly_map[ident.name] = call_array.elem
+ }
+
+ if poly_type.specialization != nil {
+ return resolve_poly(
+ ast_context,
+ call_array.elem,
+ call_symbol,
+ p.elem,
+ poly_map,
+ )
+ }
+ found |= true
+ }
+ if p.len != nil {
+ if poly_type, ok := p.len.derived.(^ast.Poly_Type); ok {
+ if ident, ok := unwrap_ident(poly_type.type); ok {
+ poly_map[ident.name] = call_array.len
+ }
+
+ if poly_type.specialization != nil {
+ return resolve_poly(
+ ast_context,
+ call_array.len,
+ call_symbol,
+ p.len,
+ poly_map,
+ )
+ }
+ found |= true
+ }
+ }
+
+ return found
+ }
+ case ^ast.Map_Type:
+ if call_map, ok := call_node.derived.(^ast.Map_Type); ok {
+ found := false
+ if poly_type, ok := p.key.derived.(^ast.Poly_Type); ok {
+ if ident, ok := unwrap_ident(poly_type.type); ok {
+ poly_map[ident.name] = call_map.key
+ }
+
+ if poly_type.specialization != nil {
+ return resolve_poly(
+ ast_context,
+ call_map.key,
+ call_symbol,
+ p.key,
+ poly_map,
+ )
+ }
+ found |= true
+ }
+
+ if poly_type, ok := p.value.derived.(^ast.Poly_Type); ok {
+ if ident, ok := unwrap_ident(poly_type.type); ok {
+ poly_map[ident.name] = call_map.value
+ }
+
+ if poly_type.specialization != nil {
+ return resolve_poly(
+ ast_context,
+ call_map.value,
+ call_symbol,
+ p.value,
+ poly_map,
+ )
+ }
+ found |= true
+ }
+ return found
+ }
+ case ^ast.Multi_Pointer_Type:
+ if call_pointer, ok := call_node.derived.(^ast.Multi_Pointer_Type);
+ ok {
+ if poly_type, ok := p.elem.derived.(^ast.Poly_Type); ok {
+ if ident, ok := unwrap_ident(poly_type.type); ok {
+ poly_map[ident.name] = call_pointer.elem
+ }
+
+ if poly_type.specialization != nil {
+ return resolve_poly(
+ ast_context,
+ call_pointer.elem,
+ call_symbol,
+ p.elem,
+ poly_map,
+ )
+ }
+ return true
+ }
+ }
+ case ^ast.Ident:
+ if n, ok := call_node.derived.(^ast.Ident); ok {
+ return true
+ }
+ case:
+ log.panicf("Unhandled specialization %v", specialization.derived)
+ }
+
+ return false
+}
+
+find_and_replace_poly_type :: proc(
+ expr: ^ast.Expr,
+ poly_map: ^map[string]^ast.Expr,
+) {
+ is_in_poly_map :: proc(
+ node: ^ast.Node,
+ poly_map: ^map[string]^ast.Expr,
+ ) -> (
+ ^ast.Expr,
+ bool,
+ ) {
+ if node == nil {
+ return {}, false
+ }
+
+ if ident, ok := node.derived.(^ast.Ident); ok {
+ if v, ok := poly_map[ident.name]; ok {
+ return v, ok
+ }
+ }
+ if poly, ok := node.derived.(^ast.Poly_Type); ok && poly.type != nil {
+ if v, ok := poly_map[poly.type.name]; ok {
+ return v, ok
+ }
+ }
+
+ return nil, false
+ }
+
+ visit_function :: proc(
+ visitor: ^ast.Visitor,
+ node: ^ast.Node,
+ ) -> ^ast.Visitor {
+ if node == nil {
+ return nil
+ }
+
+ poly_map := cast(^map[string]^ast.Expr)visitor.data
+
+ #partial switch v in node.derived {
+ case ^ast.Matrix_Type:
+ if expr, ok := is_in_poly_map(v.elem, poly_map); ok {
+ v.elem = expr
+ }
+ if expr, ok := is_in_poly_map(v.column_count, poly_map); ok {
+ v.column_count = expr
+ }
+ if expr, ok := is_in_poly_map(v.row_count, poly_map); ok {
+ v.row_count = expr
+ }
+ case ^ast.Dynamic_Array_Type:
+ if expr, ok := is_in_poly_map(v.elem, poly_map); ok {
+ v.elem = expr
+ }
+ case ^ast.Array_Type:
+ if expr, ok := is_in_poly_map(v.elem, poly_map); ok {
+ v.elem = expr
+ }
+ if expr, ok := is_in_poly_map(v.len, poly_map); ok {
+ v.len = expr
+ }
+ case ^ast.Multi_Pointer_Type:
+ if expr, ok := is_in_poly_map(v.elem, poly_map); ok {
+ v.elem = expr
+ }
+ case ^ast.Pointer_Type:
+ if expr, ok := is_in_poly_map(v.elem, poly_map); ok {
+ v.elem = expr
+ }
+ }
+
+ return visitor
+ }
+
+ visitor := ast.Visitor {
+ data = poly_map,
+ visit = visit_function,
+ }
+
+ ast.walk(&visitor, expr)
+}
+
+resolve_generic_function :: proc {
+ resolve_generic_function_ast,
+ resolve_generic_function_symbol,
+}
+
+resolve_generic_function_ast :: proc(
+ ast_context: ^AstContext,
+ proc_lit: ast.Proc_Lit,
+) -> (
+ Symbol,
+ bool,
+) {
+
+ using ast
+
+ if proc_lit.type.params == nil {
+ return Symbol{}, false
+ }
+
+ if proc_lit.type.results == nil {
+ return Symbol{}, false
+ }
+
+ if ast_context.call == nil {
+ return Symbol{}, false
+ }
+
+ return resolve_generic_function_symbol(
+ ast_context,
+ proc_lit.type.params.list,
+ proc_lit.type.results.list,
+ )
+}
+
+
+resolve_generic_function_symbol :: proc(
+ ast_context: ^AstContext,
+ params: []^ast.Field,
+ results: []^ast.Field,
+) -> (
+ Symbol,
+ bool,
+) {
+ if params == nil {
+ return {}, false
+ }
+
+ if results == nil {
+ return {}, false
+ }
+
+ if ast_context.call == nil {
+ return {}, false
+ }
+
+ call_expr := ast_context.call
+
+ poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator)
+
+ i := 0
+ count_required_params := 0
+
+ for param in params {
+ if param.default_value == nil {
+ count_required_params += 1
+ }
+
+ for name in param.names {
+ if len(call_expr.args) <= i {
+ break
+ }
+
+ if param.type == nil {
+ continue
+ }
+
+ reset_ast_context(ast_context)
+
+ if symbol, ok := resolve_type_expression(
+ ast_context,
+ call_expr.args[i],
+ ); ok {
+ symbol_expr := symbol_to_expr(
+ symbol,
+ call_expr.args[i].pos.file,
+ context.temp_allocator,
+ )
+
+ if symbol_expr == nil {
+ return {}, false
+ }
+
+ symbol_expr = clone_expr(symbol_expr, ast_context.allocator, nil)
+ param_type := clone_expr(param.type, ast_context.allocator, nil)
+
+ if resolve_poly(
+ ast_context,
+ symbol_expr,
+ symbol,
+ param_type,
+ &poly_map,
+ ) {
+ if poly, ok := name.derived.(^ast.Poly_Type); ok {
+ poly_map[poly.type.name] = clone_expr(
+ call_expr.args[i],
+ ast_context.allocator,
+ nil,
+ )
+ }
+ }
+ }
+
+ i += 1
+ }
+ }
+
+ for k, v in poly_map {
+ find_and_replace_poly_type(v, &poly_map)
+ //fmt.println(k, v.derived, "\n")
+ }
+
+ if count_required_params > len(call_expr.args) ||
+ count_required_params == 0 ||
+ len(call_expr.args) == 0 {
+ return {}, false
+ }
+
+ function_name := ""
+ function_range: common.Range
+
+ if ident, ok := call_expr.expr.derived.(^ast.Ident); ok {
+ function_name = ident.name
+ function_range = common.get_token_range(ident, ast_context.file.src)
+ } else if selector, ok := call_expr.expr.derived.(^ast.Selector_Expr); ok {
+ function_name = selector.field.name
+ function_range = common.get_token_range(selector, ast_context.file.src)
+ } else {
+ return {}, false
+ }
+
+ symbol := Symbol {
+ range = function_range,
+ type = .Function,
+ name = function_name,
+ pkg = ast_context.current_package,
+ }
+
+ return_types := make([dynamic]^ast.Field, ast_context.allocator)
+ argument_types := make([dynamic]^ast.Field, ast_context.allocator)
+
+ for result in results {
+ if result.type == nil {
+ continue
+ }
+
+ field := cast(^ast.Field)clone_node(result, ast_context.allocator, nil)
+
+ if ident, ok := unwrap_ident(field.type); ok {
+ if expr, ok := poly_map[ident.name]; ok {
+ field.type = expr
+ }
+ }
+
+ find_and_replace_poly_type(field.type, &poly_map)
+
+ append(&return_types, field)
+ }
+
+ for param in params {
+ if len(param.names) == 0 {
+ continue
+ }
+
+ //check the name for poly
+ if poly_type, ok := param.names[0].derived.(^ast.Poly_Type);
+ ok && param.type != nil {
+ if m, ok := poly_map[poly_type.type.name]; ok {
+ field := cast(^ast.Field)clone_node(
+ param,
+ ast_context.allocator,
+ nil,
+ )
+ field.type = m
+ append(&argument_types, field)
+ }
+ } else {
+ append(&argument_types, param)
+ }
+ }
+
+ symbol.value = SymbolProcedureValue {
+ return_types = return_types[:],
+ arg_types = argument_types[:],
+ }
+
+ return symbol, true
+}
+
+is_procedure_generic :: proc(proc_type: ^ast.Proc_Type) -> bool {
+ if proc_type.generic {
+ return true
+ }
+
+ for param in proc_type.params.list {
+ if param.type == nil {
+ continue
+ }
+
+ if expr, _, ok := common.unwrap_pointer_expr(param.type); ok {
+ if _, ok := expr.derived.(^ast.Poly_Type); ok {
+ return true
+ }
+ }
+
+ }
+
+ return false
+}
+
+
+resolve_poly_struct :: proc(
+ ast_context: ^AstContext,
+ poly_params: ^ast.Field_List,
+ symbol: ^Symbol,
+) {
+ if ast_context.call == nil {
+ return
+ }
+
+ symbol_value := &symbol.value.(SymbolStructValue)
+
+ if symbol_value == nil {
+ return
+ }
+
+ i := 0
+
+ poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator)
+ args := make([dynamic]^ast.Expr, 0, context.temp_allocator)
+
+ for param in poly_params.list {
+ for name in param.names {
+ if len(ast_context.call.args) <= i {
+ break
+ }
+
+ if param.type == nil {
+ continue
+ }
+
+ if poly, ok := param.type.derived.(^ast.Typeid_Type); ok {
+ if ident, ok := name.derived.(^ast.Ident); ok {
+ poly_map[ident.name] = ast_context.call.args[i]
+ } else if poly, ok := name.derived.(^ast.Poly_Type); ok {
+ if poly.type != nil {
+ poly_map[poly.type.name] = ast_context.call.args[i]
+ }
+ }
+ }
+
+ append(&args, ast_context.call.args[i])
+
+ i += 1
+ }
+ }
+
+ Visit_Data :: struct {
+ poly_map: map[string]^ast.Expr,
+ symbol_value: ^SymbolStructValue,
+ parent: ^ast.Node,
+ i: int,
+ poly_index: int,
+ }
+
+ visit :: proc(visitor: ^ast.Visitor, node: ^ast.Node) -> ^ast.Visitor {
+ if node == nil || visitor == nil {
+ return nil
+ }
+
+ data := cast(^Visit_Data)visitor.data
+
+ if ident, ok := node.derived.(^ast.Ident); ok {
+ if expr, ok := data.poly_map[ident.name]; ok {
+ if data.parent != nil {
+ #partial switch &v in data.parent.derived {
+ case ^ast.Array_Type:
+ v.elem = expr
+ case ^ast.Dynamic_Array_Type:
+ v.elem = expr
+ }
+ } else {
+ data.symbol_value.types[data.i] = expr
+ data.poly_index += 1
+ }
+ }
+ }
+
+ #partial switch v in node.derived {
+ case ^ast.Array_Type, ^ast.Dynamic_Array_Type, ^ast.Selector_Expr:
+ data.parent = node
+ }
+
+ return visitor
+ }
+
+ for type, i in symbol_value.types {
+ data := Visit_Data {
+ poly_map = poly_map,
+ symbol_value = symbol_value,
+ i = i,
+ }
+
+ visitor := ast.Visitor {
+ data = &data,
+ visit = visit,
+ }
+
+ ast.walk(&visitor, type)
+ }
+
+ symbol_value.args = args[:]
+}
+
+
+resolve_poly_union :: proc(
+ ast_context: ^AstContext,
+ poly_params: ^ast.Field_List,
+ symbol: ^Symbol,
+) {
+ if ast_context.call == nil {
+ return
+ }
+
+ symbol_value := &symbol.value.(SymbolUnionValue)
+
+ if symbol_value == nil {
+ return
+ }
+
+ i := 0
+
+ poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator)
+
+ for param in poly_params.list {
+ for name in param.names {
+ if len(ast_context.call.args) <= i {
+ break
+ }
+
+ if param.type == nil {
+ continue
+ }
+
+ if poly, ok := param.type.derived.(^ast.Typeid_Type); ok {
+ if ident, ok := name.derived.(^ast.Ident); ok {
+ poly_map[ident.name] = ast_context.call.args[i]
+ } else if poly, ok := name.derived.(^ast.Poly_Type); ok {
+ if poly.type != nil {
+ poly_map[poly.type.name] = ast_context.call.args[i]
+ }
+ }
+ }
+
+ i += 1
+ }
+ }
+
+ for type, i in symbol_value.types {
+ if ident, ok := type.derived.(^ast.Ident); ok {
+ if expr, ok := poly_map[ident.name]; ok {
+ symbol_value.types[i] = expr
+ }
+ } else if call_expr, ok := type.derived.(^ast.Call_Expr); ok {
+ if call_expr.args == nil {
+ continue
+ }
+
+ for arg, i in call_expr.args {
+ if ident, ok := arg.derived.(^ast.Ident); ok {
+ if expr, ok := poly_map[ident.name]; ok {
+ symbol_value.types[i] = expr
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/server/methods.odin b/src/server/methods.odin
index 4d1d3f2..62d2cb4 100644
--- a/src/server/methods.odin
+++ b/src/server/methods.odin
@@ -88,6 +88,11 @@ append_method_completion :: proc(
continue
}
+ if len(value.arg_types) == 0 ||
+ value.arg_types[0].type == nil {
+ continue
+ }
+
first_arg: Symbol
first_arg, ok = resolve_type_expression(
ast_context,
@@ -109,7 +114,11 @@ append_method_completion :: proc(
if symbol.pkg != ast_context.document_package {
new_text = fmt.tprintf(
"%v.%v($0)",
- path.base(symbol.pkg, false, ast_context.allocator),
+ path.base(
+ get_symbol_pkg_name(ast_context, symbol),
+ false,
+ ast_context.allocator,
+ ),
symbol.name,
)
} else {
diff --git a/src/server/requests.odin b/src/server/requests.odin
index cdd2702..4c653ad 100644
--- a/src/server/requests.odin
+++ b/src/server/requests.odin
@@ -10,7 +10,6 @@ import "core:odin/parser"
import "core:os"
import "core:path/filepath"
import path "core:path/slashpath"
-import "core:runtime"
import "core:slice"
import "core:strconv"
import "core:strings"
@@ -20,6 +19,7 @@ import "core:time"
import "shared:common"
+import "base:runtime"
Header :: struct {
content_length: int,
@@ -551,6 +551,17 @@ read_ols_initialize_options :: proc(
allocator = context.allocator,
)
}
+
+ if "base" not_in config.collections && odin_core_env != "" {
+ forward_path, _ := filepath.to_slash(
+ odin_core_env,
+ context.temp_allocator,
+ )
+ config.collections[strings.clone("base")] = path.join(
+ elems = {forward_path, "base"},
+ allocator = context.allocator,
+ )
+ }
}
request_initialize :: proc(
@@ -1077,8 +1088,8 @@ notification_did_save :: proc(
fullpath := uri.path
p := parser.Parser {
- err = log_error_handler,
- warn = log_warning_handler,
+ err = log_error_handler,
+ warn = log_warning_handler,
flags = {.Optional_Semicolons},
}
diff --git a/src/server/symbol.odin b/src/server/symbol.odin
index d6a6aa5..57d3946 100644
--- a/src/server/symbol.odin
+++ b/src/server/symbol.odin
@@ -1,8 +1,11 @@
package server
+import "core:fmt"
import "core:hash"
+import "core:log"
import "core:mem"
import "core:odin/ast"
+import "core:odin/tokenizer"
import "core:path/filepath"
import path "core:path/slashpath"
import "core:slice"
@@ -22,6 +25,7 @@ SymbolStructValue :: struct {
types: []^ast.Expr,
usings: map[int]bool,
poly: ^ast.Field_List,
+ args: []^ast.Expr, //The arguments in the call expression for poly
}
SymbolPackageValue :: struct {}
@@ -83,6 +87,7 @@ SymbolUntypedValue :: struct {
String,
Bool,
},
+ tok: tokenizer.Token,
}
SymbolMapValue :: struct {
@@ -227,7 +232,9 @@ free_symbol :: proc(symbol: Symbol, allocator: mem.Allocator) {
case SymbolMapValue:
common.free_ast(v.key, allocator)
common.free_ast(v.value, allocator)
- case SymbolUntypedValue, SymbolPackageValue:
+ case SymbolUntypedValue:
+ delete(v.tok.text)
+ case SymbolPackageValue:
}
}
@@ -284,3 +291,64 @@ symbol_kind_to_type :: proc(type: SymbolType) -> SymbolKind {
return .Null
}
}
+
+symbol_to_expr :: proc(
+ symbol: Symbol,
+ file: string,
+ allocator := context.temp_allocator,
+) -> ^ast.Expr {
+
+ pos := tokenizer.Pos {
+ file = file,
+ }
+
+ end := tokenizer.Pos {
+ file = file,
+ }
+
+ #partial switch v in symbol.value {
+ case SymbolDynamicArrayValue:
+ type := new_type(ast.Dynamic_Array_Type, pos, end, allocator)
+ type.elem = v.expr
+ return type
+ case SymbolFixedArrayValue:
+ type := new_type(ast.Array_Type, pos, end, allocator)
+ type.elem = v.expr
+ type.len = v.len
+ return type
+ case SymbolMapValue:
+ type := new_type(ast.Map_Type, pos, end, allocator)
+ type.key = v.key
+ type.value = v.value
+ return type
+ case SymbolBasicValue:
+ return v.ident
+ case SymbolSliceValue:
+ type := new_type(ast.Array_Type, pos, end, allocator)
+ type.elem = v.expr
+ return type
+ case SymbolStructValue:
+ type := new_type(ast.Struct_Type, pos, end, allocator)
+ return type
+ case SymbolUntypedValue:
+ type := new_type(ast.Basic_Lit, pos, end, allocator)
+ return type
+ case SymbolMatrixValue:
+ type := new_type(ast.Matrix_Type, pos, end, allocator)
+ type.row_count = v.x
+ type.column_count = v.y
+ type.elem = v.expr
+ return type
+ case SymbolProcedureValue:
+ type := new_type(ast.Proc_Type, pos, end, allocator)
+ type.results = new_type(ast.Field_List, pos, end, allocator)
+ type.results.list = v.return_types
+ type.params = new_type(ast.Field_List, pos, end, allocator)
+ type.params.list = v.arg_types
+ return type
+ case:
+ return nil
+ }
+
+ return nil
+}
diff --git a/tests/completions_test.odin b/tests/completions_test.odin
index c3496e4..e5a9460 100644
--- a/tests/completions_test.odin
+++ b/tests/completions_test.odin
@@ -789,33 +789,6 @@ ast_struct_for_in_switch_stmt_completion :: proc(t: ^testing.T) {
test.expect_completion_details(t, &source, ".", {"Window.height: int"})
}
-/*
-@(test)
-ast_overload_with_autocast_completion :: proc(t: ^testing.T) {
- source := test.Source {
- main = `package test
-
- my_group :: proc{
- with_autocast,
- with_bool,
- };
- with_autocast :: proc(#any_int a: int) -> bool {
- }
- with_bool :: proc(a: bool) -> int {
- }
-
- main :: proc() {
- my_uint: uint = 0;
- my_value := my_group(my_uint);
- my_val{*}
- }
- `,
- packages = {},
- }
-
- test.expect_completion_details(t, &source, "", {"test.my_value: bool"})
-}
-*/
@(test)
ast_overload_with_any_int_completion :: proc(t: ^testing.T) {
@@ -2407,3 +2380,231 @@ ast_assign_to_global_function :: proc(t: ^testing.T) {
test.expect_completion_details(t, &source, "", {"test.global_foo: string"})
}
+
+@(test)
+ast_poly_dynamic_type :: proc(t: ^testing.T) {
+ packages := make([dynamic]test.Package)
+
+ source := test.Source {
+ main = `package test
+ import "my_package"
+
+ _raw_data_dynamic :: proc(data: $T/[dynamic]$E) -> [^]E {
+ return {}
+ }
+
+ main :: proc() {
+ my_dynamic: [dynamic]int
+ ret_dynamic := _raw_data_dynamic(my_dynamic)
+ ret_dy{*}
+ }
+
+ `,
+ packages = packages[:],
+ }
+
+ test.expect_completion_details(
+ t,
+ &source,
+ "",
+ {"test.ret_dynamic: [^]int"},
+ )
+}
+
+@(test)
+ast_poly_array_type :: proc(t: ^testing.T) {
+ packages := make([dynamic]test.Package)
+
+ source := test.Source {
+ main = `package test
+ import "my_package"
+
+ _raw_data_array :: proc(data: $T/[]$E) -> [^]E {
+ return {}
+ }
+
+ main :: proc() {
+ my_array: []int
+ ret_array := _raw_data_array(my_array)
+ ret_arr{*}
+ }
+
+ `,
+ packages = packages[:],
+ }
+
+ test.expect_completion_details(t, &source, "", {"test.ret_array: [^]int"})
+}
+
+@(test)
+ast_poly_struct_with_poly :: proc(t: ^testing.T) {
+ packages := make([dynamic]test.Package)
+
+ source := test.Source {
+ main = `package test
+ Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
+ data: [N]T,
+ len: int,
+ }
+
+ Animal :: struct {
+ happy: bool,
+ sad: bool,
+ fluffy: bool,
+ }
+
+ get_ptr :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> ^T {
+ return &a.data[index]
+ }
+
+ main :: proc() {
+ animals := Small_Array(5, Animal){}
+ first := get_ptr(&animals, 0)
+ fir{*}
+ }
+ `,
+ packages = packages[:],
+ }
+
+
+ test.expect_completion_details(t, &source, "", {"test.first: ^Animal"})
+}
+
+@(test)
+ast_poly_proc_array_constant :: proc(t: ^testing.T) {
+ packages := make([dynamic]test.Package)
+
+ source := test.Source {
+ main = `package test
+ make_f32_array :: proc($N: int, $val: f32) -> (res: [N]f32) {
+ for _, i in res {
+ res[i] = val*val
+ }
+ return
+ }
+
+ main :: proc() {
+ array := make_f32_array(3, 2)
+ arr{*}
+ }
+ `,
+ packages = packages[:],
+ }
+
+
+ test.expect_completion_details(t, &source, "", {"test.array: [3]f32"})
+}
+
+@(test)
+ast_poly_proc_matrix_type :: proc(t: ^testing.T) {
+ packages := make([dynamic]test.Package)
+
+ source := test.Source {
+ main = `package test
+
+ matrix_to_ptr :: proc "contextless" (m: ^$A/matrix[$I, $J]$E) -> ^E {
+ return &m[0, 0]
+ }
+
+
+ main :: proc() {
+ my_matrix: matrix[2, 2]f32
+ ptr := matrix_to_ptr(&my_matrix)
+ pt{*}
+ }
+
+ `,
+ packages = packages[:],
+ }
+
+ test.expect_completion_details(t, &source, "", {"test.ptr: ^f32"})
+}
+
+@(test)
+ast_poly_proc_matrix_constant_array :: proc(t: ^testing.T) {
+ packages := make([dynamic]test.Package)
+
+ source := test.Source {
+ main = `package test
+
+ matrix_to_ptr :: proc "contextless" (m: ^$A/matrix[$I, $J]$E) -> [J]E {
+ return {}
+ }
+
+ main :: proc() {
+ my_matrix: matrix[4, 3]f32
+
+ ptr := matrix_to_ptr(&my_matrix)
+ pt{*}
+ }
+ `,
+ packages = packages[:],
+ }
+
+ test.expect_completion_details(t, &source, "", {"test.ptr: [3]f32"})
+}
+
+@(test)
+ast_poly_proc_matrix_constant_array_2 :: proc(t: ^testing.T) {
+ packages := make([dynamic]test.Package)
+
+ source := test.Source {
+ main = `package test
+ array_cast :: proc "contextless" (
+ v: $A/[$N]$T,
+ $Elem_Type: typeid,
+ ) -> (
+ w: [N]Elem_Type,
+ ) {
+ for i in 0 ..< N {
+ w[i] = Elem_Type(v[i])
+ }
+ return
+ }
+ main :: proc() {
+ my_vector: [10]int
+ myss := array_cast(my_vector, f32)
+ mys{*}
+ }
+ `,
+ packages = packages[:],
+ }
+
+ test.expect_completion_details(t, &source, "", {"test.myss: [10]f32"})
+}
+
+@(test)
+ast_poly_proc_matrix_whole :: proc(t: ^testing.T) {
+ packages := make([dynamic]test.Package)
+
+ source := test.Source {
+ main = `package test
+
+ @(require_results)
+ matrix_mul :: proc "contextless" (
+ a, b: $M/matrix[$N, N]$E,
+ ) -> (
+ c: M,
+ ) where !IS_ARRAY(E),
+ IS_NUMERIC(E) #no_bounds_check {
+ return a * b
+ }
+
+ matrix4_from_trs_f16 :: proc "contextless" () -> matrix[4, 4]f32 {
+ translation: matrix[4, 4]f32
+ rotation: matrix[4, 4]f32
+ dsszz := matrix_mul(scale, translation)
+ dssz{*}
+ }
+ `,
+ packages = {},
+ }
+
+ test.expect_completion_details(
+ t,
+ &source,
+ "",
+ {"test.dsszz: matrix[4,4]f32"},
+ )
+
+}