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

Report mismatched parentheses before more specific errors #20764

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
49 changes: 49 additions & 0 deletions lib/std/zig/AstGen.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Ingests an AST and produces ZIR code.
const AstGen = @This();

const Parse = @import("Parse.zig");

const std = @import("std");
const Ast = std.zig.Ast;
const mem = std.mem;
Expand Down Expand Up @@ -13819,6 +13821,53 @@ fn lowerAstErrors(astgen: *AstGen) !void {

const gpa = astgen.gpa;
const parse_err = tree.errors[0];
const err_tok = parse_err.token + @intFromBool(parse_err.token_is_prev);

const token_tags = tree.tokens.items(.tag);

if (try Parse.findUnmatchedParen(astgen.gpa, token_tags)) |tok| {
const text: []const u8 = switch (token_tags[tok]) {
.l_paren => "unclosed parenthesis",
.l_brace => "unclosed curly brace",
.l_bracket => "unclosed bracket",
.r_paren => "unmatched parenthesis",
.r_brace => "unmatched curly brace",
.r_bracket => "unmatched bracket",
else => unreachable,
};
try astgen.appendErrorTok(tok, "{s}", .{text});
// Unmatched parentheses are often an underlying cause of
// otherwise more obscure errors, so we only report the parse
// error if it probably wasn't caused by this.
switch (parse_err.tag) {
.asterisk_after_ptr_deref,
.chained_comparison_operators,
.expected_inlinable,
.expected_labelable,
.expected_prefix_expr,
.expected_return_type,
.extern_fn_body,
.extra_addrspace_qualifier,
.extra_align_qualifier,
.extra_allowzero_qualifier,
.extra_const_qualifier,
.extra_volatile_qualifier,
.ptr_mod_on_array_child_type,
.invalid_bit_range,
.same_line_doc_comment,
.test_doc_comment,
.comptime_doc_comment,
.varargs_nonfinal,
.expected_continue_expr,
.mismatched_binary_op_whitespace,
.invalid_ampersand_ampersand,
.extra_for_capture,
.for_input_not_captured,
=> {},
.expected_token => if (token_tags[err_tok] != .invalid) return,
else => return,
}
}

var msg: std.ArrayListUnmanaged(u8) = .{};
defer msg.deinit(gpa);
Expand Down
34 changes: 34 additions & 0 deletions lib/std/zig/Parse.zig
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,40 @@ pub fn parseZon(p: *Parse) !void {
};
}

pub fn findUnmatchedParen(gpa: Allocator, token_tags: []const Token.Tag) !?TokenIndex {
var stack = std.ArrayList(struct {
tag: Token.Tag,
idx: TokenIndex,
}).init(gpa);
defer stack.deinit();

for (token_tags, 0..) |t, i| {
switch (t) {
.l_paren, .l_brace, .l_bracket => try stack.append(.{ .tag = t, .idx = @intCast(i) }),
.r_paren, .r_brace, .r_bracket => {
if (stack.items.len == 0)
return @intCast(i);
const opening = stack.pop();
if (t != closingParen(opening.tag))
return opening.idx;
},
else => {},
}
}
if (stack.items.len > 0)
return stack.pop().idx;
return null;
}

fn closingParen(a: Token.Tag) Token.Tag {
return switch (a) {
.l_paren => .r_paren,
.l_brace => .r_brace,
.l_bracket => .r_bracket,
else => unreachable,
};
}

/// ContainerMembers <- ContainerDeclaration* (ContainerField COMMA)* (ContainerField / ContainerDeclaration*)
///
/// ContainerDeclaration <- TestDecl / ComptimeDecl / doc_comment? KEYWORD_pub? Decl
Expand Down
2 changes: 1 addition & 1 deletion test/cases/compile_errors/invalid_unicode_escape.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export fn entry() void {
const a = '\u{12z34}';
const a = '\u{12z34';
}

// error
Expand Down
46 changes: 43 additions & 3 deletions test/compile_errors.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,53 @@ pub fn addCases(ctx: *Cases, b: *std.Build) !void {
\\ );
\\}
, &[_][]const u8{
\\:2:5: error:
\\:2:5: error:
\\ hello!
\\ I'm a multiline error message.
\\ I hope to be very useful!
\\
\\
\\ also I will leave this trailing newline here if you don't mind
\\
\\
});
}

{
const case = ctx.obj("unmatched parentheses", b.graph.host);

case.addError(
\\export fn a() void {
\\
, &[_][]const u8{
":1:20: error: unclosed curly brace",
});
}

{
const case = ctx.obj("unmatched parentheses #2", b.graph.host);

case.addError(
\\const c = [
\\)
\\];
, &[_][]const u8{
":1:11: error: unclosed bracket",
});
}

{
const case = ctx.obj("unmatched parentheses #3", b.graph.host);

case.addError(
\\pub fn bar() void {
\\ // Oops...
\\ }
\\
\\ if (true) {
\\ return;
\\ }
\\}
, &[_][]const u8{
":8:1: error: unmatched curly brace",
});
}

Expand Down
Loading