Added .?, .*, [] support
This commit is contained in:
parent
54f3590b6d
commit
7602f34bb4
192
src/analysis.zig
192
src/analysis.zig
@ -284,6 +284,92 @@ fn resolveReturnType(analysis_ctx: *AnalysisContext, fn_decl: *ast.Node.FnProto)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves the child type of an optional type
|
||||||
|
fn resolveUnwrapOptionalType(analysis_ctx: *AnalysisContext, opt: *ast.Node) ?*ast.Node {
|
||||||
|
if (opt.cast(ast.Node.PrefixOp)) |prefix_op| {
|
||||||
|
if (prefix_op.op == .OptionalType) {
|
||||||
|
return resolveTypeOfNode(analysis_ctx, prefix_op.rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves the child type of a defer type
|
||||||
|
fn resolveDerefType(analysis_ctx: *AnalysisContext, deref: *ast.Node) ?*ast.Node {
|
||||||
|
if (deref.cast(ast.Node.PrefixOp)) |pop| {
|
||||||
|
if (pop.op == .PtrType) {
|
||||||
|
const op_token_id = analysis_ctx.tree().token_ids[pop.op_token];
|
||||||
|
switch (op_token_id) {
|
||||||
|
.Asterisk => return resolveTypeOfNode(analysis_ctx, pop.rhs),
|
||||||
|
.LBracket, .AsteriskAsterisk => return null,
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn makeSliceType(analysis_ctx: *AnalysisContext, child_type: *ast.Node) ?*ast.Node {
|
||||||
|
// TODO: Better values for fields, better way to do this?
|
||||||
|
var slice_type = analysis_ctx.arena.allocator.create(ast.Node.PrefixOp) catch return null;
|
||||||
|
slice_type.* = .{
|
||||||
|
.op_token = child_type.firstToken(),
|
||||||
|
.op = .{
|
||||||
|
.SliceType = .{
|
||||||
|
.allowzero_token = null,
|
||||||
|
.align_info = null,
|
||||||
|
.const_token = null,
|
||||||
|
.volatile_token = null,
|
||||||
|
.sentinel = null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.rhs = child_type,
|
||||||
|
};
|
||||||
|
|
||||||
|
return &slice_type.base;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves bracket access type (both slicing and array access)
|
||||||
|
fn resolveBracketAccessType(
|
||||||
|
analysis_ctx: *AnalysisContext,
|
||||||
|
lhs: *ast.Node,
|
||||||
|
rhs: enum { Single, Range },
|
||||||
|
) ?*ast.Node {
|
||||||
|
if (lhs.cast(ast.Node.PrefixOp)) |pop| {
|
||||||
|
switch (pop.op) {
|
||||||
|
.SliceType => {
|
||||||
|
if (rhs == .Single) return resolveTypeOfNode(analysis_ctx, pop.rhs);
|
||||||
|
return lhs;
|
||||||
|
},
|
||||||
|
.ArrayType => {
|
||||||
|
if (rhs == .Single) return resolveTypeOfNode(analysis_ctx, pop.rhs);
|
||||||
|
return makeSliceType(analysis_ctx, pop.rhs);
|
||||||
|
},
|
||||||
|
.PtrType => {
|
||||||
|
if (pop.rhs.cast(std.zig.ast.Node.PrefixOp)) |child_pop| {
|
||||||
|
switch (child_pop.op) {
|
||||||
|
.ArrayType => {
|
||||||
|
if (rhs == .Single) {
|
||||||
|
return resolveTypeOfNode(analysis_ctx, child_pop.rhs);
|
||||||
|
}
|
||||||
|
return makeSliceType(analysis_ctx, child_pop.rhs);
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called to remove one level of pointerness before a field access
|
||||||
|
fn resolveFieldAccessLhsType(analysis_ctx: *AnalysisContext, lhs: *ast.Node) *ast.Node {
|
||||||
|
return resolveDerefType(analysis_ctx, lhs) orelse lhs;
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolves the type of a node
|
/// Resolves the type of a node
|
||||||
pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast.Node {
|
pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast.Node {
|
||||||
switch (node.id) {
|
switch (node.id) {
|
||||||
@ -318,6 +404,28 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast.
|
|||||||
else => decl,
|
else => decl,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
.SuffixOp => {
|
||||||
|
const suffix_op = node.cast(ast.Node.SuffixOp).?;
|
||||||
|
switch (suffix_op.op) {
|
||||||
|
.UnwrapOptional => {
|
||||||
|
const left_type = resolveTypeOfNode(analysis_ctx, suffix_op.lhs) orelse return null;
|
||||||
|
return resolveUnwrapOptionalType(analysis_ctx, left_type);
|
||||||
|
},
|
||||||
|
.Deref => {
|
||||||
|
const left_type = resolveTypeOfNode(analysis_ctx, suffix_op.lhs) orelse return null;
|
||||||
|
return resolveDerefType(analysis_ctx, left_type);
|
||||||
|
},
|
||||||
|
.ArrayAccess => {
|
||||||
|
const left_type = resolveTypeOfNode(analysis_ctx, suffix_op.lhs) orelse return null;
|
||||||
|
return resolveBracketAccessType(analysis_ctx, left_type, .Single);
|
||||||
|
},
|
||||||
|
.Slice => {
|
||||||
|
const left_type = resolveTypeOfNode(analysis_ctx, suffix_op.lhs) orelse return null;
|
||||||
|
return resolveBracketAccessType(analysis_ctx, left_type, .Range);
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
.InfixOp => {
|
.InfixOp => {
|
||||||
const infix_op = node.cast(ast.Node.InfixOp).?;
|
const infix_op = node.cast(ast.Node.InfixOp).?;
|
||||||
switch (infix_op.op) {
|
switch (infix_op.op) {
|
||||||
@ -327,25 +435,31 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast.
|
|||||||
var rhs_str = nodeToString(analysis_ctx.tree(), infix_op.rhs) orelse return null;
|
var rhs_str = nodeToString(analysis_ctx.tree(), infix_op.rhs) orelse return null;
|
||||||
// Use the analysis context temporary arena to store the rhs string.
|
// Use the analysis context temporary arena to store the rhs string.
|
||||||
rhs_str = std.mem.dupe(&analysis_ctx.arena.allocator, u8, rhs_str) catch return null;
|
rhs_str = std.mem.dupe(&analysis_ctx.arena.allocator, u8, rhs_str) catch return null;
|
||||||
const left = resolveTypeOfNode(analysis_ctx, infix_op.lhs) orelse return null;
|
|
||||||
const child = getChild(analysis_ctx.tree(), left, rhs_str) orelse return null;
|
// If we are accessing a pointer type, remove one pointerness level :)
|
||||||
|
const left_type = resolveFieldAccessLhsType(
|
||||||
|
analysis_ctx,
|
||||||
|
resolveTypeOfNode(analysis_ctx, infix_op.lhs) orelse return null,
|
||||||
|
);
|
||||||
|
|
||||||
|
const child = getChild(analysis_ctx.tree(), left_type, rhs_str) orelse return null;
|
||||||
return resolveTypeOfNode(analysis_ctx, child);
|
return resolveTypeOfNode(analysis_ctx, child);
|
||||||
},
|
},
|
||||||
|
.UnwrapOptional => {
|
||||||
|
const left_type = resolveTypeOfNode(analysis_ctx, infix_op.lhs) orelse return null;
|
||||||
|
return resolveUnwrapOptionalType(analysis_ctx, left_type);
|
||||||
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.PrefixOp => {
|
.PrefixOp => {
|
||||||
const prefix_op = node.cast(ast.Node.PrefixOp).?;
|
const prefix_op = node.cast(ast.Node.PrefixOp).?;
|
||||||
switch (prefix_op.op) {
|
switch (prefix_op.op) {
|
||||||
.SliceType, .ArrayType => return node,
|
.SliceType,
|
||||||
.PtrType => {
|
.ArrayType,
|
||||||
const op_token_id = analysis_ctx.tree().token_ids[prefix_op.op_token];
|
.OptionalType,
|
||||||
switch (op_token_id) {
|
.PtrType,
|
||||||
.Asterisk => return resolveTypeOfNode(analysis_ctx, prefix_op.rhs),
|
=> return node,
|
||||||
.LBracket, .AsteriskAsterisk => return null,
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.Try => {
|
.Try => {
|
||||||
const rhs_type = resolveTypeOfNode(analysis_ctx, prefix_op.rhs) orelse return null;
|
const rhs_type = resolveTypeOfNode(analysis_ctx, prefix_op.rhs) orelse return null;
|
||||||
switch (rhs_type.id) {
|
switch (rhs_type.id) {
|
||||||
@ -435,26 +549,26 @@ pub fn getFieldAccessTypeNode(
|
|||||||
tokenizer: *std.zig.Tokenizer,
|
tokenizer: *std.zig.Tokenizer,
|
||||||
) ?*ast.Node {
|
) ?*ast.Node {
|
||||||
var current_node = analysis_ctx.in_container;
|
var current_node = analysis_ctx.in_container;
|
||||||
var current_container = analysis_ctx.in_container;
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const tok = tokenizer.next();
|
const tok = tokenizer.next();
|
||||||
switch (tok.id) {
|
switch (tok.id) {
|
||||||
.Eof => return current_node,
|
.Eof => return resolveFieldAccessLhsType(analysis_ctx, current_node),
|
||||||
.Identifier => {
|
.Identifier => {
|
||||||
if (getChildOfSlice(analysis_ctx.tree(), analysis_ctx.scope_nodes, tokenizer.buffer[tok.loc.start..tok.loc.end])) |child| {
|
if (getChildOfSlice(analysis_ctx.tree(), analysis_ctx.scope_nodes, tokenizer.buffer[tok.loc.start..tok.loc.end])) |child| {
|
||||||
if (resolveTypeOfNode(analysis_ctx, child)) |node_type| {
|
if (resolveTypeOfNode(analysis_ctx, child)) |child_type| {
|
||||||
current_node = node_type;
|
current_node = child_type;
|
||||||
} else return null;
|
} else return null;
|
||||||
} else return null;
|
} else return null;
|
||||||
},
|
},
|
||||||
.Period => {
|
.Period => {
|
||||||
const after_period = tokenizer.next();
|
const after_period = tokenizer.next();
|
||||||
switch (after_period.id) {
|
switch (after_period.id) {
|
||||||
.Eof => return current_node,
|
.Eof => return resolveFieldAccessLhsType(analysis_ctx, current_node),
|
||||||
.Identifier => {
|
.Identifier => {
|
||||||
// TODO: This works for now, maybe we should filter based on the partial identifier ourselves?
|
if (after_period.loc.end == tokenizer.buffer.len) return resolveFieldAccessLhsType(analysis_ctx, current_node);
|
||||||
if (after_period.loc.end == tokenizer.buffer.len) return current_node;
|
|
||||||
|
current_node = resolveFieldAccessLhsType(analysis_ctx, current_node);
|
||||||
if (getChild(analysis_ctx.tree(), current_node, tokenizer.buffer[after_period.loc.start..after_period.loc.end])) |child| {
|
if (getChild(analysis_ctx.tree(), current_node, tokenizer.buffer[after_period.loc.start..after_period.loc.end])) |child| {
|
||||||
if (resolveTypeOfNode(analysis_ctx, child)) |child_type| {
|
if (resolveTypeOfNode(analysis_ctx, child)) |child_type| {
|
||||||
current_node = child_type;
|
current_node = child_type;
|
||||||
@ -462,8 +576,9 @@ pub fn getFieldAccessTypeNode(
|
|||||||
} else return null;
|
} else return null;
|
||||||
},
|
},
|
||||||
.QuestionMark => {
|
.QuestionMark => {
|
||||||
std.debug.warn("TODO: .? support\n", .{});
|
if (resolveUnwrapOptionalType(analysis_ctx, current_node)) |child_type| {
|
||||||
return null;
|
current_node = child_type;
|
||||||
|
} else return null;
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
std.debug.warn("Unrecognized token {} after period.\n", .{after_period.id});
|
std.debug.warn("Unrecognized token {} after period.\n", .{after_period.id});
|
||||||
@ -472,8 +587,9 @@ pub fn getFieldAccessTypeNode(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
.PeriodAsterisk => {
|
.PeriodAsterisk => {
|
||||||
std.debug.warn("TODO: .* support\n", .{});
|
if (resolveDerefType(analysis_ctx, current_node)) |child_type| {
|
||||||
return null;
|
current_node = child_type;
|
||||||
|
} else return null;
|
||||||
},
|
},
|
||||||
.LParen => {
|
.LParen => {
|
||||||
switch (current_node.id) {
|
switch (current_node.id) {
|
||||||
@ -498,20 +614,40 @@ pub fn getFieldAccessTypeNode(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
.LBracket => {
|
.LBracket => {
|
||||||
std.debug.warn("TODO: Implement [] support\n", .{});
|
var brack_count: usize = 1;
|
||||||
return null;
|
var next = tokenizer.next();
|
||||||
|
var is_range = false;
|
||||||
|
while (next.id != .Eof) : (next = tokenizer.next()) {
|
||||||
|
if (next.id == .RBracket) {
|
||||||
|
brack_count -= 1;
|
||||||
|
if (brack_count == 0) break;
|
||||||
|
} else if (next.id == .LBracket) {
|
||||||
|
brack_count += 1;
|
||||||
|
} else if (next.id == .Ellipsis2 and brack_count == 1) {
|
||||||
|
is_range = true;
|
||||||
|
}
|
||||||
|
} else return null;
|
||||||
|
|
||||||
|
if (resolveBracketAccessType(
|
||||||
|
analysis_ctx,
|
||||||
|
current_node,
|
||||||
|
if (is_range) .Range else .Single,
|
||||||
|
)) |child_type| {
|
||||||
|
current_node = child_type;
|
||||||
|
} else return null;
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
std.debug.warn("Unimplemented token: {}\n", .{tok.id});
|
std.debug.warn("Unimplemented token: {}\n", .{tok.id});
|
||||||
return null;
|
return null;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (current_node.id == .ContainerDecl or current_node.id == .Root) {
|
if (current_node.cast(ast.Node.ContainerDecl)) |container_decl| {
|
||||||
current_container = current_node;
|
analysis_ctx.onContainer(container_decl) catch return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return current_node;
|
return resolveFieldAccessLhsType(analysis_ctx, current_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isNodePublic(tree: *ast.Tree, node: *ast.Node) bool {
|
pub fn isNodePublic(tree: *ast.Tree, node: *ast.Node) bool {
|
||||||
|
14
src/main.zig
14
src/main.zig
@ -273,6 +273,20 @@ fn nodeToCompletion(
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
.PrefixOp => {
|
.PrefixOp => {
|
||||||
|
const prefix_op = node.cast(std.zig.ast.Node.PrefixOp).?;
|
||||||
|
switch (prefix_op.op) {
|
||||||
|
.ArrayType, .SliceType => {},
|
||||||
|
.PtrType => {
|
||||||
|
if (prefix_op.rhs.cast(std.zig.ast.Node.PrefixOp)) |child_pop| {
|
||||||
|
switch (child_pop.op) {
|
||||||
|
.ArrayType => {},
|
||||||
|
else => return,
|
||||||
|
}
|
||||||
|
} else return;
|
||||||
|
},
|
||||||
|
else => return,
|
||||||
|
}
|
||||||
|
|
||||||
try list.append(.{
|
try list.append(.{
|
||||||
.label = "len",
|
.label = "len",
|
||||||
.kind = .Field,
|
.kind = .Field,
|
||||||
|
Loading…
Reference in New Issue
Block a user