diff --git a/src/stage2/Parse.zig b/src/stage2/Parse.zig index 879978f3f..8c61a2fd6 100644 --- a/src/stage2/Parse.zig +++ b/src/stage2/Parse.zig @@ -873,7 +873,7 @@ fn parseGlobalVarDecl(p: *Parse) !Node.Index { p.nodes.items(.data)[var_decl].rhs = init_node; - try p.expectSemicolon(.expected_semi_after_decl, false); + try p.expectSemicolon(.expected_semi_after_decl, true); return var_decl; } @@ -2432,6 +2432,11 @@ fn parseCurlySuffixExpr(p: *Parse) !Node.Index { if (lhs == 0) return null_node; const lbrace = p.eatToken(.l_brace) orelse return lhs; + if (p.token_tags[p.tok_i] == .period and p.token_tags[p.tok_i + 1] == .period) { // zls + try p.warn(.expected_initializer); + p.tok_i += 1; + } + // If there are 0 or 1 items, we can use ArrayInitOne/StructInitOne; // otherwise we use the full ArrayInit/StructInit. @@ -2451,6 +2456,12 @@ fn parseCurlySuffixExpr(p: *Parse) !Node.Index { // Likely just a missing comma; give error but continue parsing. else => try p.warn(.expected_comma_after_initializer), } + if (p.token_tags[p.tok_i] == .period) { // zls + if (p.token_tags[p.tok_i + 1] == .period or p.token_tags[p.tok_i + 1] == .r_brace) { + try p.warn(.expected_initializer); + p.tok_i += 1; + } + } if (p.eatToken(.r_brace)) |_| break; const next = try p.expectFieldInit(); try p.scratch.append(p.gpa, next); @@ -2479,6 +2490,10 @@ fn parseCurlySuffixExpr(p: *Parse) !Node.Index { } while (true) { + if (p.token_tags[p.tok_i] == .period and p.token_tags[p.tok_i + 1] == .r_brace) { // zls + try p.warn(.expected_expr); + p.tok_i += 1; + } if (p.eatToken(.r_brace)) |_| break; const elem_init = try p.expectExpr(); try p.scratch.append(p.gpa, elem_init); @@ -2851,6 +2866,11 @@ fn parsePrimaryTypeExpr(p: *Parse) !Node.Index { const lbrace = p.tok_i + 1; p.tok_i = lbrace + 1; + if (p.token_tags[p.tok_i] == .period and p.token_tags[p.tok_i + 1] == .period) { // zls + try p.warn(.expected_initializer); + p.tok_i += 1; + } + // If there are 0, 1, or 2 items, we can use ArrayInitDotTwo/StructInitDotTwo; // otherwise we use the full ArrayInitDot/StructInitDot. @@ -2870,6 +2890,12 @@ fn parsePrimaryTypeExpr(p: *Parse) !Node.Index { // Likely just a missing comma; give error but continue parsing. else => try p.warn(.expected_comma_after_initializer), } + if (p.token_tags[p.tok_i] == .period) { // zls + if (p.token_tags[p.tok_i + 1] == .period or p.token_tags[p.tok_i + 1] == .r_brace) { + try p.warn(.expected_initializer); + p.tok_i += 1; + } + } if (p.eatToken(.r_brace)) |_| break; const next = try p.expectFieldInit(); try p.scratch.append(p.gpa, next); @@ -2909,6 +2935,10 @@ fn parsePrimaryTypeExpr(p: *Parse) !Node.Index { } while (true) { + if (p.token_tags[p.tok_i] == .period and p.token_tags[p.tok_i + 1] == .r_brace) { // zls + try p.warn(.expected_expr); + p.tok_i += 1; + } if (p.eatToken(.r_brace)) |_| break; const elem_init = try p.expectExpr(); try p.scratch.append(p.gpa, elem_init); @@ -3130,6 +3160,10 @@ fn expectSwitchExpr(p: *Parse) !Node.Index { _ = try p.expectToken(.l_brace); const cases = try p.parseSwitchProngList(); const trailing_comma = p.token_tags[p.tok_i - 1] == .comma; + if (p.token_tags[p.tok_i] == .period and p.token_tags[p.tok_i + 1] == .r_brace) { // zls + try p.warn(.expected_expr); + p.tok_i += 1; + } _ = try p.expectToken(.r_brace); return p.addNode(.{ @@ -3439,6 +3473,10 @@ fn parseSwitchProng(p: *Parse) !Node.Index { if (p.eatToken(.keyword_else) == null) { while (true) { + if (p.token_tags[p.tok_i] == .period and p.token_tags[p.tok_i + 1] == .period) { // zls + try p.warn(.expected_expr); + p.tok_i += 1; + } const item = try p.parseSwitchItem(); if (item == 0) break; try p.scratch.append(p.gpa, item); diff --git a/tests/lsp_features/completion.zig b/tests/lsp_features/completion.zig index 33abedc66..5204a3df1 100644 --- a/tests/lsp_features/completion.zig +++ b/tests/lsp_features/completion.zig @@ -241,16 +241,16 @@ test "completion - generic function" { try testCompletion( \\const S = struct { alpha: u32 }; \\fn foo(comptime T: type) T {} - \\const s = foo(S); - \\const foo = s. + \\const S1 = foo(S); + \\const S2 = S1. , &.{ .{ .label = "alpha", .kind = .Field, .detail = "u32" }, }); try testCompletion( \\const S = struct { alpha: u32 }; \\fn foo(any: anytype, comptime T: type) T {} - \\const s = foo(null, S); - \\const foo = s. + \\const S1 = foo(null, S); + \\const S2 = S1. , &.{ .{ .label = "alpha", .kind = .Field, .detail = "u32" }, }); @@ -1797,7 +1797,9 @@ test "completion - struct init" { \\ brefa: A, \\ this_is_b: []const u8, \\}; - \\ref(.{ .arefb = .{ .brefa = .{.} } }); + \\test { + \\ ref(.{ .arefb = .{ .brefa = .{.} } }); + \\} , &.{ .{ .label = "arefb", .kind = .Field, .detail = "B = 8" }, .{ .label = "this_is_a", .kind = .Field, .detail = "u32 = 9" }, @@ -1836,7 +1838,9 @@ test "completion - struct init" { \\ const Self = @This(); \\ pub fn s3(self: *Self, p0: es, p1: S2) void {} \\}; - \\S3.s3(null, .{ .mye = .{} }, .{ .ref1 = .{ .ref3 = .{ .ref2 = .{ .ref1 = .{.} } } } }); + \\test { + \\ S3.s3(null, .{ .mye = .{} }, .{ .ref1 = .{ .ref3 = .{ .ref2 = .{ .ref1 = .{.} } } } }); + \\} , &.{ .{ .label = "s1f1", .kind = .Field, .detail = "u8" }, .{ .label = "s1f2", .kind = .Field, .detail = "u32 = 1" }, @@ -1862,8 +1866,10 @@ test "completion - struct init" { \\ const Self = @This(); \\ pub fn s3(self: Self, p0: es, p1: S1) void {} \\}; - \\const iofs3 = S3{}; - \\iofs3.s3(.{.}); + \\test { + \\ const iofs3 = S3{}; + \\ iofs3.s3(.{.}); + \\} , &.{ .{ .label = "s1f1", .kind = .Field, .detail = "u8" }, .{ .label = "s1f2", .kind = .Field, .detail = "u32 = 1" },