fix #801, IOOB in foldingRanges (#802)

* Add smoke tests for folding ranges

* fix index out of bounds in foldingRanges

closes #801

For invalid syntax trees, zig's parser seems to return bogus data where
startToken > endToken, which then causes everything else to crash.

This seems like a deeper issue, which needs to be fixed "properly", but
let's just paper over it here.
This commit is contained in:
Alex Kladov 2022-12-03 15:23:13 +00:00 committed by GitHub
parent 3ab859a304
commit cfb0b023ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 3 deletions

View File

@ -2454,7 +2454,7 @@ fn foldingRangeHandler(server: *Server, writer: anytype, id: types.RequestId, re
end: Ast.TokenIndex, end: Ast.TokenIndex,
end_reach: Inclusivity, end_reach: Inclusivity,
) std.mem.Allocator.Error!bool { ) std.mem.Allocator.Error!bool {
const can_add = !tree.tokensOnSameLine(start, end); const can_add = start < end and !tree.tokensOnSameLine(start, end);
if (can_add) { if (can_add) {
try addTokRange(p_ranges, tree, start, end, end_reach); try addTokRange(p_ranges, tree, start, end, end_reach);
} }

View File

@ -0,0 +1,66 @@
const std = @import("std");
const zls = @import("zls");
const builtin = @import("builtin");
const Context = @import("../context.zig").Context;
const types = zls.types;
const requests = zls.requests;
const allocator: std.mem.Allocator = std.testing.allocator;
test "foldingRange - empty" {
try testFoldingRange("", "[]");
}
test "foldingRange - smoke" {
try testFoldingRange(
\\fn main() u32 {
\\ return 1 + 1;
\\}
,
\\[{"startLine":0,"endLine":1}]
);
}
test "foldingRange - #801" {
try testFoldingRange(
\\fn score(c: u8) !u32 {
\\ return switch(c) {
\\ 'a'...'z' => c - 'a',
\\ 'A'...'Z' => c - 'A',
\\ _ => error
\\ };
\\}
,
\\[]
);
}
fn testFoldingRange(source: []const u8, expect: []const u8) !void {
var ctx = try Context.init();
defer ctx.deinit();
const test_uri: []const u8 = switch (builtin.os.tag) {
.windows => "file:///C:\\test.zig",
else => "file:///test.zig",
};
try ctx.requestDidOpen(test_uri, source);
const request = requests.FoldingRange{ .params = .{ .textDocument = .{ .uri = test_uri } } };
const response = try ctx.requestGetResponse(?[]types.FoldingRange, "textDocument/foldingRange", request);
defer response.deinit();
var actual = std.ArrayList(u8).init(allocator);
defer actual.deinit();
try std.json.stringify(response.result, .{}, actual.writer());
try expectEqualJson(expect, actual.items);
}
fn expectEqualJson(expect: []const u8, actual: []const u8) !void {
// TODO: Actually compare strings as JSON values.
return std.testing.expectEqualStrings(expect, actual);
}

View File

@ -10,11 +10,12 @@ comptime {
// TODO Document Synchronization // TODO Document Synchronization
// LSP features // LSP features
_ = @import("lsp_features/semantic_tokens.zig"); _ = @import("lsp_features/completion.zig");
_ = @import("lsp_features/folding_range.zig");
_ = @import("lsp_features/inlay_hints.zig"); _ = @import("lsp_features/inlay_hints.zig");
_ = @import("lsp_features/references.zig"); _ = @import("lsp_features/references.zig");
_ = @import("lsp_features/completion.zig");
_ = @import("lsp_features/selection_range.zig"); _ = @import("lsp_features/selection_range.zig");
_ = @import("lsp_features/semantic_tokens.zig");
// Language features // Language features
_ = @import("language_features/cimport.zig"); _ = @import("language_features/cimport.zig");