diff --git a/src/analysis.zig b/src/analysis.zig index 37b9a072c..5f966396b 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -811,28 +811,36 @@ fn findReturnStatement(tree: Ast, body: Ast.Node.Index) ?Ast.Node.Index { return findReturnStatementInternal(tree, body, &already_found); } -pub fn resolveReturnType(analyser: *Analyser, fn_decl: Ast.full.FnProto, handle: *DocumentStore.Handle, fn_body: ?Ast.Node.Index) error{OutOfMemory}!?Type { - const tree = handle.tree; - if (isTypeFunction(tree, fn_decl) and fn_body != null) { +pub fn resolveReturnType(analyser: *Analyser, func_type: Type) error{OutOfMemory}!?Type { + const func_node_handle = func_type.data.other; // this assumes that function types can only be Ast nodes + const tree = func_node_handle.handle.tree; + const func_node = func_node_handle.node; + + var buf: [1]Ast.Node.Index = undefined; + const fn_proto = tree.fullFnProto(&buf, func_node).?; + const has_body = tree.nodes.items(.tag)[func_node] == .fn_decl; + + if (isTypeFunction(tree, fn_proto) and has_body) { + const body = tree.nodes.items(.data)[func_node].rhs; // If this is a type function and it only contains a single return statement that returns // a container declaration, we will return that declaration. - const ret = findReturnStatement(tree, fn_body.?) orelse return null; + const ret = findReturnStatement(tree, body) orelse return null; const data = tree.nodes.items(.data)[ret]; if (data.lhs != 0) { - return try analyser.resolveTypeOfNodeInternal(.{ .node = data.lhs, .handle = handle }); + return try analyser.resolveTypeOfNodeInternal(.{ .node = data.lhs, .handle = func_node_handle.handle }); } return null; } - if (fn_decl.ast.return_type == 0) return null; - const return_type = fn_decl.ast.return_type; - const ret: NodeWithHandle = .{ .node = return_type, .handle = handle }; + if (fn_proto.ast.return_type == 0) return null; + const return_type = fn_proto.ast.return_type; + const ret: NodeWithHandle = .{ .node = return_type, .handle = func_node_handle.handle }; const child_type = (try analyser.resolveTypeOfNodeInternal(ret)) orelse return null; if (!child_type.is_type_val) return null; - if (ast.hasInferredError(tree, fn_decl)) { + if (ast.hasInferredError(tree, fn_proto)) { const child_type_ptr = try analyser.arena.allocator().create(Type); child_type_ptr.* = child_type; return Type{ @@ -1542,11 +1550,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e }, argument_type); } - const has_body = func_tree.nodes.items(.tag)[func_node] == .fn_decl; - const body = func_tree.nodes.items(.data)[func_node].rhs; - if (try analyser.resolveReturnType(fn_proto, func_handle, if (has_body) body else null)) |ret| { - return ret; - } + return try analyser.resolveReturnType(func_ty); }, .container_field, .container_field_init, @@ -2646,6 +2650,10 @@ pub const Type = struct { } } + pub fn isContainerType(self: Type) bool { + return self.data == .container; + } + fn isContainerKind(self: Type, container_kind_tok: std.zig.Token.Tag) bool { const scope_handle = switch (self.data) { .container => |s| s, @@ -3231,22 +3239,9 @@ pub fn getFieldAccessType( // Can't call a function type, we need a function type instance. if (current_type.?.is_type_val) return null; - // this assumes that function types can only be Ast nodes - const current_type_node_handle = ty.data.other; - const current_type_node = current_type_node_handle.node; - const current_type_handle = current_type_node_handle.handle; - - const cur_tree = current_type_handle.tree; - var buf: [1]Ast.Node.Index = undefined; - const func = cur_tree.fullFnProto(&buf, current_type_node).?; - // Check if the function has a body and if so, pass it - // so the type can be resolved if it's a generic function returning - // an anonymous struct - const has_body = cur_tree.nodes.items(.tag)[current_type_node] == .fn_decl; - const body = cur_tree.nodes.items(.data)[current_type_node].rhs; // TODO Actually bind params here when calling functions instead of just skipping args. - current_type = try analyser.resolveReturnType(func, current_type_handle, if (has_body) body else null) orelse return null; + current_type = try analyser.resolveReturnType(ty) orelse return null; if (do_unwrap_error_payload) { if (try analyser.resolveUnwrapErrorUnionType(current_type.?, .payload)) |unwrapped| current_type = unwrapped; @@ -4689,27 +4684,16 @@ pub fn resolveExpressionTypeFromAncestors( if (fn_type.is_type_val) return null; const fn_node_handle = fn_type.data.other; // this assumes that function types can only be Ast nodes - const fn_node = fn_node_handle.node; - const fn_handle = fn_node_handle.handle; - const fn_tree = fn_handle.tree; - - var fn_buf: [1]Ast.Node.Index = undefined; - const fn_proto = fn_tree.fullFnProto(&fn_buf, fn_node).?; - - var param_iter = fn_proto.iterate(&fn_tree); - if (try analyser.isInstanceCall(handle, call, fn_type)) { - _ = ast.nextFnParam(¶m_iter); - } + const param_decl: Declaration.Param = .{ + .param_index = @truncate(arg_index + @intFromBool(try analyser.hasSelfParam(fn_type))), + .func = fn_node_handle.node, + }; + const param = param_decl.get(fn_node_handle.handle.tree) orelse return null; - var param_index: usize = 0; - while (ast.nextFnParam(¶m_iter)) |param| : (param_index += 1) { - if (param_index == arg_index) { - return try analyser.resolveTypeOfNode(.{ - .node = param.type_expr, - .handle = fn_handle, - }); - } - } + return try analyser.resolveTypeOfNode(.{ + .node = param.type_expr, + .handle = fn_node_handle.handle, + }); }, .assign => { if (node == datas[ancestors[0]].rhs) { diff --git a/src/features/completions.zig b/src/features/completions.zig index b1df84edf..93c220995 100644 --- a/src/features/completions.zig +++ b/src/features/completions.zig @@ -1467,30 +1467,25 @@ fn collectVarAccessContainerNodes( const symbol_decl = try analyser.lookupSymbolGlobal(handle, handle.tree.source[loc.start..loc.end], loc.end) orelse return; const result = try symbol_decl.resolveType(analyser) orelse return; const type_expr = try analyser.resolveDerefType(result) orelse result; - if (type_expr.isFunc()) { - const fn_proto_node_handle = type_expr.data.other; // this assumes that function types can only be Ast nodes - const fn_proto_node = fn_proto_node_handle.node; - const fn_proto_handle = fn_proto_node_handle.handle; - if (dot_context.likely == .enum_comparison or dot_context.need_ret_type) { // => we need f()'s return type - var buf: [1]Ast.Node.Index = undefined; - const full_fn_proto = fn_proto_handle.tree.fullFnProto(&buf, fn_proto_node).?; - const has_body = fn_proto_handle.tree.nodes.items(.tag)[fn_proto_node] == .fn_decl; - const body = fn_proto_handle.tree.nodes.items(.data)[fn_proto_node].rhs; - var node_type = try analyser.resolveReturnType(full_fn_proto, fn_proto_handle, if (has_body) body else null) orelse return; - if (try analyser.resolveUnwrapErrorUnionType(node_type, .payload)) |unwrapped| node_type = unwrapped; - try node_type.getAllTypesWithHandlesArrayList(arena, types_with_handles); - return; - } - const fn_param_decl = Analyser.Declaration{ .function_parameter = .{ - .func = fn_proto_node, - .param_index = @intCast(dot_context.fn_arg_index), - } }; - const fn_param_decl_with_handle = Analyser.DeclWithHandle{ .decl = fn_param_decl, .handle = fn_proto_handle }; - const param_type = try fn_param_decl_with_handle.resolveType(analyser) orelse return; - try types_with_handles.append(arena, param_type); + if (!type_expr.isFunc()) { + try type_expr.getAllTypesWithHandlesArrayList(arena, types_with_handles); return; } - try type_expr.getAllTypesWithHandlesArrayList(arena, types_with_handles); + + if (dot_context.likely == .enum_comparison or dot_context.need_ret_type) { // => we need f()'s return type + var node_type = try analyser.resolveReturnType(type_expr) orelse return; + if (try analyser.resolveUnwrapErrorUnionType(node_type, .payload)) |unwrapped| node_type = unwrapped; + try node_type.getAllTypesWithHandlesArrayList(arena, types_with_handles); + return; + } + const func_node_handle = type_expr.data.other; // this assumes that function types can only be Ast nodes + const fn_param_decl: Analyser.Declaration = .{ .function_parameter = .{ + .func = func_node_handle.node, + .param_index = @intCast(dot_context.fn_arg_index), + } }; + const fn_param_decl_with_handle = Analyser.DeclWithHandle{ .decl = fn_param_decl, .handle = func_node_handle.handle }; + const param_type = try fn_param_decl_with_handle.resolveType(analyser) orelse return; + try types_with_handles.append(arena, param_type); } fn collectFieldAccessContainerNodes( @@ -1528,50 +1523,47 @@ fn collectFieldAccessContainerNodes( if (dot_context.likely == .enum_assignment or dot_context.likely == .struct_field) { if (try analyser.resolveOptionalUnwrap(node_type)) |unwrapped| node_type = unwrapped; } - if (node_type.isFunc()) { - const fn_proto_node_handle = node_type.data.other; // this assumes that function types can only be Ast nodes - const fn_proto_node = fn_proto_node_handle.node; - const fn_proto_handle = fn_proto_node_handle.handle; - var buf: [1]Ast.Node.Index = undefined; - const full_fn_proto = fn_proto_handle.tree.fullFnProto(&buf, fn_proto_node).?; - if (dot_context.need_ret_type) { // => we need f()'s return type - const has_body = fn_proto_handle.tree.nodes.items(.tag)[fn_proto_node] == .fn_decl; - const body = fn_proto_handle.tree.nodes.items(.data)[fn_proto_node].rhs; - node_type = try analyser.resolveReturnType(full_fn_proto, fn_proto_handle, if (has_body) body else null) orelse continue; - if (try analyser.resolveUnwrapErrorUnionType(node_type, .payload)) |unwrapped| node_type = unwrapped; - try node_type.getAllTypesWithHandlesArrayList(arena, types_with_handles); - continue; - } - var maybe_fn_param: ?Ast.full.FnProto.Param = undefined; - var fn_param_iter = full_fn_proto.iterate(&fn_proto_handle.tree); - // don't have the luxury of referencing an `Ast.full.Call` - // check if the first symbol is a `T` or an instance_of_T - const additional_index: usize = blk: { - // `loc` points to offsets within `handle`, not `node_type.decl.handle` - const field_access_slice = handle.tree.source[loc.start..loc.end]; - if (field_access_slice[0] == '@') break :blk 1; // assume `@import("..").some.Other{.}` - var symbol_iter = std.mem.tokenizeScalar(u8, field_access_slice, '.'); - const first_symbol = symbol_iter.next() orelse continue; - const symbol_decl = try analyser.lookupSymbolGlobal(handle, first_symbol, loc.start) orelse continue; - const symbol_type = try symbol_decl.resolveType(analyser) orelse continue; - if (!symbol_type.is_type_val) { // then => instance_of_T - if (try analyser.hasSelfParam(node_type)) break :blk 2; - } - break :blk 1; // is `T`, no SelfParam - }; - for (dot_context.fn_arg_index + additional_index) |_| maybe_fn_param = ast.nextFnParam(&fn_param_iter); - const param = maybe_fn_param orelse continue; - if (param.type_expr == 0) continue; - const param_rcts = try collectContainerNodes( - builder, - fn_proto_handle, - offsets.nodeToLoc(fn_proto_handle.tree, param.type_expr).end, - dot_context, - ); - for (param_rcts) |prct| try types_with_handles.append(arena, prct); + if (!node_type.isFunc()) { + try node_type.getAllTypesWithHandlesArrayList(arena, types_with_handles); continue; } - try node_type.getAllTypesWithHandlesArrayList(arena, types_with_handles); + + if (dot_context.need_ret_type) { // => we need f()'s return type + node_type = try analyser.resolveReturnType(node_type) orelse continue; + if (try analyser.resolveUnwrapErrorUnionType(node_type, .payload)) |unwrapped| node_type = unwrapped; + try node_type.getAllTypesWithHandlesArrayList(arena, types_with_handles); + continue; + } + // don't have the luxury of referencing an `Ast.full.Call` + // check if the first symbol is a `T` or an instance_of_T + const additional_index: usize = blk: { + // `loc` points to offsets within `handle`, not `node_type.decl.handle` + const field_access_slice = handle.tree.source[loc.start..loc.end]; + if (field_access_slice[0] == '@') break :blk 0; // assume `@import("..").some.Other{.}` + var symbol_iter = std.mem.tokenizeScalar(u8, field_access_slice, '.'); + const first_symbol = symbol_iter.next() orelse continue; + const symbol_decl = try analyser.lookupSymbolGlobal(handle, first_symbol, loc.start) orelse continue; + const symbol_type = try symbol_decl.resolveType(analyser) orelse continue; + if (!symbol_type.is_type_val) { // then => instance_of_T + if (try analyser.hasSelfParam(node_type)) break :blk 1; + } + break :blk 0; // is `T`, no SelfParam + }; + const fn_node_handle = node_type.data.other; // this assumes that function types can only be Ast nodes + const param_decl: Analyser.Declaration.Param = .{ + .param_index = @truncate(dot_context.fn_arg_index + additional_index), + .func = fn_node_handle.node, + }; + const param = param_decl.get(fn_node_handle.handle.tree) orelse continue; + + if (param.type_expr == 0) continue; + const param_rcts = try collectContainerNodes( + builder, + fn_node_handle.handle, + offsets.nodeToLoc(fn_node_handle.handle.tree, param.type_expr).end, + dot_context, + ); + for (param_rcts) |prct| try types_with_handles.append(arena, prct); } }