Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

completions: add result type based completions for builtin arguments #1720

Merged
merged 2 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 55 additions & 19 deletions src/analysis.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1762,19 +1762,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
}

if (std.mem.eql(u8, call_name, "@typeInfo")) {
const zig_lib_path = analyser.store.config.zig_lib_path orelse return null;
const builtin_path = try std.fs.path.join(analyser.arena.allocator(), &.{ zig_lib_path, "std", "builtin.zig" });
const builtin_uri = try URI.fromPath(analyser.arena.allocator(), builtin_path);

const builtin_handle = analyser.store.getOrLoadHandle(builtin_uri) orelse return null;
const builtin_root_struct_type: Type = .{
.data = .{ .container = .{ .handle = builtin_handle, .scope = Scope.Index.root } },
.is_type_val = true,
};

const builtin_type_decl = try builtin_root_struct_type.lookupSymbol(analyser, "Type") orelse return null;
const builtin_type = try builtin_type_decl.resolveType(analyser) orelse return null;
return try builtin_type.instanceTypeVal(analyser);
return analyser.instanceStdBuiltinType("Type");
}

if (std.mem.eql(u8, call_name, "@import")) {
Expand Down Expand Up @@ -1804,7 +1792,9 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
},
.is_type_val = true,
};
} else if (std.mem.eql(u8, call_name, "@cImport")) {
}

if (std.mem.eql(u8, call_name, "@cImport")) {
const cimport_uri = (try analyser.store.resolveCImport(handle, node)) orelse return null;

const new_handle = analyser.store.getOrLoadHandle(cimport_uri) orelse return null;
Expand All @@ -1819,6 +1809,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
.is_type_val = true,
};
}

if (std.mem.eql(u8, call_name, "@field")) {
if (params.len < 2) return null;
var field_name_node: NodeWithHandle = .{ .node = params[1], .handle = handle };
Expand Down Expand Up @@ -1854,7 +1845,11 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
.handle = handle,
})) orelse return null;

return try resolveFieldAccess(analyser, lhs, field_name[1 .. field_name.len - 1]);
return try analyser.resolveFieldAccess(lhs, field_name[1 .. field_name.len - 1]);
}

if (std.mem.eql(u8, call_name, "@src")) {
return analyser.instanceStdBuiltinType("SourceLocation");
}
if (std.mem.eql(u8, call_name, "@compileError")) {
return Type{ .data = .{ .compile_error = node_handle }, .is_type_val = false };
Expand Down Expand Up @@ -2989,6 +2984,24 @@ pub const ScopeWithHandle = struct {
}
};

/// Look up `type_name` in 'zig_lib_path/std/builtin.zig' and return it as an instance
/// Useful for functionality related to builtin fns
pub fn instanceStdBuiltinType(analyser: *Analyser, type_name: []const u8) error{OutOfMemory}!?Type {
const zig_lib_path = analyser.store.config.zig_lib_path orelse return null;
const builtin_path = try std.fs.path.join(analyser.arena.allocator(), &.{ zig_lib_path, "std", "builtin.zig" });
const builtin_uri = try URI.fromPath(analyser.arena.allocator(), builtin_path);

const builtin_handle = analyser.store.getOrLoadHandle(builtin_uri) orelse return null;
const builtin_root_struct_type: Type = .{
.data = .{ .container = .{ .handle = builtin_handle, .scope = Scope.Index.root } },
.is_type_val = true,
};

const builtin_type_decl = try builtin_root_struct_type.lookupSymbol(analyser, type_name) orelse return null;
const builtin_type = try builtin_type_decl.resolveType(analyser) orelse return null;
return try builtin_type.instanceTypeVal(analyser);
}

/// Collects all `@import`'s we can find into a slice of import paths (without quotes).
pub fn collectImports(allocator: std.mem.Allocator, tree: Ast) error{OutOfMemory}!std.ArrayListUnmanaged([]const u8) {
var imports = std.ArrayListUnmanaged([]const u8){};
Expand Down Expand Up @@ -3234,7 +3247,9 @@ pub fn getFieldAccessType(
current_type = (try analyser.resolveBracketAccessType(current_type orelse return null, kind)) orelse return null;
},
.builtin => {
if (std.mem.eql(u8, tokenizer.buffer[tok.loc.start..tok.loc.end], "@import")) {
const binfn_name = tokenizer.buffer[tok.loc.start..tok.loc.end];

if (std.mem.eql(u8, binfn_name, "@import")) {
if (tokenizer.next().tag != .l_paren) return null;
const import_str_tok = tokenizer.next(); // should be the .string_literal
if (import_str_tok.tag != .string_literal) return null;
Expand All @@ -3255,10 +3270,27 @@ pub fn getFieldAccessType(
.is_type_val = true,
};
_ = tokenizer.next(); // eat the .r_paren
} else {
log.debug("Unhandled builtin: {s}", .{offsets.locToSlice(tokenizer.buffer, tok.loc)});
return null;
continue; // Outermost `while`
}

if (std.mem.eql(u8, binfn_name, "@typeInfo")) {
current_type = try analyser.instanceStdBuiltinType("Type") orelse return null;
// Skip to the right paren
var paren_count: usize = 0;
var next = tokenizer.next();
while (next.tag != .eof) : (next = tokenizer.next()) {
if (next.tag == .r_paren) {
paren_count -= 1;
if (paren_count == 0) break;
} else if (next.tag == .l_paren) {
paren_count += 1;
}
} else return null;
continue; // Outermost `while`
}

log.debug("Unhandled builtin: {s}", .{offsets.locToSlice(tokenizer.buffer, tok.loc)});
return null;
},
// only hit when `(try foo())` otherwise getPositionContext never includes the `try` keyword
.keyword_try => do_unwrap_error_payload = true,
Expand Down Expand Up @@ -3287,6 +3319,7 @@ pub const PositionContext = union(enum) {
char_literal: offsets.Loc,
/// XXX: Internal use only, currently points to the loc of the first l_paren
parens_expr: offsets.Loc,
keyword: std.zig.Token.Tag,
pre_label,
label: bool,
other,
Expand All @@ -3306,6 +3339,7 @@ pub const PositionContext = union(enum) {
.number_literal => |r| r,
.char_literal => |r| r,
.parens_expr => |r| r,
.keyword => null,
.pre_label => null,
.label => null,
.other => null,
Expand Down Expand Up @@ -3499,6 +3533,7 @@ pub fn getPositionContext(
.empty, .pre_label => curr_ctx.ctx = .{ .enum_literal = tok.loc },
.enum_literal => curr_ctx.ctx = .empty,
.field_access => {},
.keyword => return .other, // no keyword can be `.`/`.*` accessed
.other => {},
.global_error_set => {},
.label => |filled| if (filled) {
Expand Down Expand Up @@ -3548,6 +3583,7 @@ pub fn getPositionContext(
return PositionContext{ .char_literal = tok.loc };
}
},
.keyword_callconv, .keyword_addrspace => curr_ctx.ctx = .{ .keyword = tok.tag },
else => curr_ctx.ctx = .empty,
}

Expand Down
Loading