| author | |
| committer | |
| log | cb715212f7aa62ec33933138c3cd47efdf95c075 |
| tree | b49c3fddb003abb8026f027057ca8d912a297788 |
| parent | c57dfa2f06e088b67ace125f84e2d97de5396c41 |
4 files changed, 116 insertions(+), 45 deletions(-)
lib/std/zig/Ast.zig+4-2| ... | @@ -139,7 +139,10 @@ pub fn deinit(tree: *Ast, gpa: Allocator) void { | ... | @@ -139,7 +139,10 @@ pub fn deinit(tree: *Ast, gpa: Allocator) void { |
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | pub const Mode = enum { zig, zon }; | 141 | pub const Mode = enum { zig, zon }; |
| 142 | pub const ParseOptions = struct { recover: bool = true, mode: Mode = .zig, use_old_precedence: bool = false }; | 142 | pub const ParseOptions = struct { |
| 143 | recover: bool = true, | ||
| 144 | mode: Mode = .zig, | ||
| 145 | }; | ||
| 143 | 146 | ||
| 144 | /// Result should be freed with tree.deinit() when there are | 147 | /// Result should be freed with tree.deinit() when there are |
| 145 | /// no more references to any of the tokens or nodes. | 148 | /// no more references to any of the tokens or nodes. |
| ... | @@ -182,7 +185,6 @@ pub fn parseTokens( | ... | @@ -182,7 +185,6 @@ pub fn parseTokens( |
| 182 | .scratch = .empty, | 185 | .scratch = .empty, |
| 183 | .tok_i = 0, | 186 | .tok_i = 0, |
| 184 | .recover = options.recover, | 187 | .recover = options.recover, |
| 185 | .use_old_precedence = options.use_old_precedence, | ||
| 186 | }; | 188 | }; |
| 187 | defer parser.errors.deinit(gpa); | 189 | defer parser.errors.deinit(gpa); |
| 188 | defer parser.nodes.deinit(gpa); | 190 | defer parser.nodes.deinit(gpa); |
lib/std/zig/Ast/Render.zig+27-35| ... | @@ -4,7 +4,6 @@ const mem = std.mem; | ... | @@ -4,7 +4,6 @@ const mem = std.mem; |
| 4 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 5 | const meta = std.meta; | 5 | const meta = std.meta; |
| 6 | const Ast = std.zig.Ast; | 6 | const Ast = std.zig.Ast; |
| 7 | const Parse = @import("../../zig/Parse.zig"); | ||
| 8 | const Token = std.zig.Token; | 7 | const Token = std.zig.Token; |
| 9 | const primitives = std.zig.primitives; | 8 | const primitives = std.zig.primitives; |
| 10 | const Writer = std.Io.Writer; | 9 | const Writer = std.Io.Writer; |
| ... | @@ -46,6 +45,8 @@ pub const Fixups = struct { | ... | @@ -46,6 +45,8 @@ pub const Fixups = struct { |
| 46 | replace_nodes_with_node: std.AutoHashMapUnmanaged(Ast.Node.Index, Ast.Node.Index) = .empty, | 45 | replace_nodes_with_node: std.AutoHashMapUnmanaged(Ast.Node.Index, Ast.Node.Index) = .empty, |
| 47 | /// Change all identifier names matching the key to be value instead. | 46 | /// Change all identifier names matching the key to be value instead. |
| 48 | rename_identifiers: std.array_hash_map.String([]const u8) = .empty, | 47 | rename_identifiers: std.array_hash_map.String([]const u8) = .empty, |
| 48 | /// Add parentheses surrounding the node. | ||
| 49 | insert_parentheses: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .empty, | ||
| 49 | 50 | ||
| 50 | /// All `@import` builtin calls which refer to a file path will be prefixed | 51 | /// All `@import` builtin calls which refer to a file path will be prefixed |
| 51 | /// with this path. | 52 | /// with this path. |
| ... | @@ -59,6 +60,7 @@ pub const Fixups = struct { | ... | @@ -59,6 +60,7 @@ pub const Fixups = struct { |
| 59 | f.append_string_after_node.count() + | 60 | f.append_string_after_node.count() + |
| 60 | f.replace_nodes_with_node.count() + | 61 | f.replace_nodes_with_node.count() + |
| 61 | f.rename_identifiers.count() + | 62 | f.rename_identifiers.count() + |
| 63 | f.insert_parentheses.count() + | ||
| 62 | @intFromBool(f.rebase_imported_paths != null); | 64 | @intFromBool(f.rebase_imported_paths != null); |
| 63 | } | 65 | } |
| 64 | 66 | ||
| ... | @@ -70,6 +72,7 @@ pub const Fixups = struct { | ... | @@ -70,6 +72,7 @@ pub const Fixups = struct { |
| 70 | f.append_string_after_node.clearRetainingCapacity(); | 72 | f.append_string_after_node.clearRetainingCapacity(); |
| 71 | f.replace_nodes_with_node.clearRetainingCapacity(); | 73 | f.replace_nodes_with_node.clearRetainingCapacity(); |
| 72 | f.rename_identifiers.clearRetainingCapacity(); | 74 | f.rename_identifiers.clearRetainingCapacity(); |
| 75 | f.insert_parentheses.clearRetainingCapacity(); | ||
| 73 | 76 | ||
| 74 | f.rebase_imported_paths = null; | 77 | f.rebase_imported_paths = null; |
| 75 | } | 78 | } |
| ... | @@ -82,6 +85,7 @@ pub const Fixups = struct { | ... | @@ -82,6 +85,7 @@ pub const Fixups = struct { |
| 82 | f.append_string_after_node.deinit(gpa); | 85 | f.append_string_after_node.deinit(gpa); |
| 83 | f.replace_nodes_with_node.deinit(gpa); | 86 | f.replace_nodes_with_node.deinit(gpa); |
| 84 | f.rename_identifiers.deinit(gpa); | 87 | f.rename_identifiers.deinit(gpa); |
| 88 | f.insert_parentheses.deinit(gpa); | ||
| 85 | f.* = undefined; | 89 | f.* = undefined; |
| 86 | } | 90 | } |
| 87 | }; | 91 | }; |
| ... | @@ -421,6 +425,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { | ... | @@ -421,6 +425,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { |
| 421 | tree.tokenTag(fallback_first) == .multiline_string_literal_line; | 425 | tree.tokenTag(fallback_first) == .multiline_string_literal_line; |
| 422 | const after_op_space: Space = if (seperate_line) .newline else .space; | 426 | const after_op_space: Space = if (seperate_line) .newline else .space; |
| 423 | 427 | ||
| 428 | if (r.fixups.insert_parentheses.contains(node)) { | ||
| 429 | try ais.writeByte('('); | ||
| 430 | } | ||
| 424 | try renderExpression(r, lhs, .space); // target | 431 | try renderExpression(r, lhs, .space); // target |
| 425 | 432 | ||
| 426 | try ais.pushIndent(.normal); | 433 | try ais.pushIndent(.normal); |
| ... | @@ -433,7 +440,13 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { | ... | @@ -433,7 +440,13 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { |
| 433 | assert(tree.tokenTag(fallback_first - 1) == .keyword_catch); | 440 | assert(tree.tokenTag(fallback_first - 1) == .keyword_catch); |
| 434 | try renderToken(r, main_token, after_op_space); // catch keyword | 441 | try renderToken(r, main_token, after_op_space); // catch keyword |
| 435 | } | 442 | } |
| 436 | try renderExpression(r, rhs, space); // fallback | 443 | if (r.fixups.insert_parentheses.contains(node)) { |
| 444 | try renderExpression(r, rhs, .none); | ||
| 445 | try ais.writeByte(')'); | ||
| 446 | try renderOnlySpace(r, space); | ||
| 447 | } else { | ||
| 448 | try renderExpression(r, rhs, space); // fallback | ||
| 449 | } | ||
| 437 | ais.popIndent(); | 450 | ais.popIndent(); |
| 438 | }, | 451 | }, |
| 439 | 452 | ||
| ... | @@ -540,43 +553,22 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { | ... | @@ -540,43 +553,22 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { |
| 540 | .@"orelse", | 553 | .@"orelse", |
| 541 | => { | 554 | => { |
| 542 | const lhs, const rhs = tree.nodeData(node).node_and_node; | 555 | const lhs, const rhs = tree.nodeData(node).node_and_node; |
| 543 | 556 | if (r.fixups.insert_parentheses.contains(node)) { | |
| 544 | const lhs_tag = tree.nodeTag(lhs); | 557 | try ais.writeByte('('); |
| 545 | const center_tag = tree.nodeTag(node); | 558 | } |
| 546 | const rhs_tag = tree.nodeTag(rhs); | 559 | try renderExpression(r, lhs, .space); |
| 547 | |||
| 548 | const lhs_prec = Parse.precedenceFromNodeTag(lhs_tag); | ||
| 549 | const center_prec = Parse.precedenceFromNodeTag(center_tag); | ||
| 550 | const rhs_prec = Parse.precedenceFromNodeTag(rhs_tag); | ||
| 551 | |||
| 552 | const left_rel = lhs_prec.cmp(center_prec); | ||
| 553 | const right_rel = rhs_prec.cmp(center_prec); | ||
| 554 | |||
| 555 | const paren_left = left_rel == null or left_rel == .lt or (left_rel == .eq and lhs_prec.group == center_prec.group and lhs_prec.assoc == .none); | ||
| 556 | const paren_right = right_rel == null or right_rel == .lt; | ||
| 557 | |||
| 558 | |||
| 559 | if (paren_left) try ais.writeByte('('); | ||
| 560 | try renderExpression(r, lhs, .none); | ||
| 561 | if (paren_left) try ais.writeByte(')'); | ||
| 562 | // Multiline string literals will eat the space so we need to skip if we thats next. | ||
| 563 | if (tree.tokenTag(tree.lastToken(lhs)) != .multiline_string_literal_line) try renderOnlySpace(r, .space); | ||
| 564 | |||
| 565 | try ais.pushIndent(.binop); | ||
| 566 | const op_token = tree.nodeMainToken(node); | 560 | const op_token = tree.nodeMainToken(node); |
| 561 | try ais.pushIndent(.binop); | ||
| 567 | const rhs_seperate_line = !tree.tokensOnSameLine(op_token, op_token + 1) or | 562 | const rhs_seperate_line = !tree.tokensOnSameLine(op_token, op_token + 1) or |
| 568 | tree.tokenTag(op_token + 1) == .multiline_string_literal_line; | 563 | tree.tokenTag(op_token + 1) == .multiline_string_literal_line; |
| 569 | try renderToken(r, op_token, if (rhs_seperate_line) .newline else .space); | 564 | try renderToken(r, op_token, if (rhs_seperate_line) .newline else .space); |
| 570 | 565 | if (r.fixups.insert_parentheses.contains(node)) { | |
| 571 | if (paren_right) try ais.writeByte('('); | 566 | try renderExpression(r, rhs, .none); |
| 572 | try renderExpression(r, rhs, .none); | 567 | try ais.writeByte(')'); |
| 573 | if (paren_right) try ais.writeByte(')'); | 568 | try renderOnlySpace(r, space); |
| 574 | 569 | } else { | |
| 575 | const most_recent = tree.lastToken(rhs); | 570 | try renderExpression(r, rhs, space); |
| 576 | const lexeme = tokenSliceForRender(tree, most_recent); | 571 | } |
| 577 | // If our space is .none, then the above code functions as normal and putting this here will duplicate comments and such | ||
| 578 | if (space != .none) try renderSpace(r, most_recent, lexeme.len, space); | ||
| 579 | |||
| 580 | ais.popIndent(); | 572 | ais.popIndent(); |
| 581 | }, | 573 | }, |
| 582 | 574 |
lib/std/zig/Parse.zig+4-6| ... | @@ -24,7 +24,6 @@ nodes: Ast.NodeList, | ... | @@ -24,7 +24,6 @@ nodes: Ast.NodeList, |
| 24 | extra_data: std.ArrayList(u32), | 24 | extra_data: std.ArrayList(u32), |
| 25 | scratch: std.ArrayList(Node.Index), | 25 | scratch: std.ArrayList(Node.Index), |
| 26 | recover: bool, | 26 | recover: bool, |
| 27 | use_old_precedence: bool = false, | ||
| 28 | 27 | ||
| 29 | fn tokenTag(p: *const Parse, token_index: TokenIndex) Token.Tag { | 28 | fn tokenTag(p: *const Parse, token_index: TokenIndex) Token.Tag { |
| 30 | return p.tokens.items(.tag)[token_index]; | 29 | return p.tokens.items(.tag)[token_index]; |
| ... | @@ -1793,8 +1792,7 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32 | ... | @@ -1793,8 +1792,7 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32 |
| 1793 | const tok_tag = p.tokenTag(p.tok_i); | 1792 | const tok_tag = p.tokenTag(p.tok_i); |
| 1794 | const info = operTable[@backingInt(tok_tag)]; | 1793 | const info = operTable[@backingInt(tok_tag)]; |
| 1795 | 1794 | ||
| 1796 | const prec_relation = if (!p.use_old_precedence) info.prec.cmp(min_exc_prec) else info.prec.v16_cmp(min_exc_prec); | 1795 | const rel = info.prec.cmp(min_exc_prec) orelse b: { |
| 1797 | const rel = prec_relation orelse b: { | ||
| 1798 | // This issues warnings if the relation is null, which it never is in 0.16.0 | 1796 | // This issues warnings if the relation is null, which it never is in 0.16.0 |
| 1799 | const prev_op_tok_tag = p.tokenTag(prev_operator_token); | 1797 | const prev_op_tok_tag = p.tokenTag(prev_operator_token); |
| 1800 | const prev_op_info = operTable[@backingInt(prev_op_tok_tag)]; | 1798 | const prev_op_info = operTable[@backingInt(prev_op_tok_tag)]; |
| ... | @@ -1831,7 +1829,8 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32 | ... | @@ -1831,7 +1829,8 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32 |
| 1831 | }, | 1829 | }, |
| 1832 | }); | 1830 | }); |
| 1833 | } | 1831 | } |
| 1834 | break :b .gt; | 1832 | // Use the old precedence rules to construct the tree to ease automatic fixups |
| 1833 | break :b info.prec.v16_cmp(min_exc_prec); | ||
| 1835 | }; | 1834 | }; |
| 1836 | 1835 | ||
| 1837 | if (rel == .lt) { | 1836 | if (rel == .lt) { |
| ... | @@ -1842,8 +1841,7 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32 | ... | @@ -1842,8 +1841,7 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32 |
| 1842 | //Special cased check to maintain the old more helpful error for chained comparisons. | 1841 | //Special cased check to maintain the old more helpful error for chained comparisons. |
| 1843 | if (info.prec.group == .comparison) { | 1842 | if (info.prec.group == .comparison) { |
| 1844 | try p.warn(.chained_comparison_operators); | 1843 | try p.warn(.chained_comparison_operators); |
| 1845 | // In 0.16.0 the only reptition prohibited was comparisons which we checked above | 1844 | } else { |
| 1846 | } else if (!p.use_old_precedence) { | ||
| 1847 | try p.warnMsg(.{ | 1845 | try p.warnMsg(.{ |
| 1848 | .tag = .illegal_chained_operators, | 1846 | .tag = .illegal_chained_operators, |
| 1849 | .is_note = false, | 1847 | .is_note = false, |
src/fmt.zig+81-2| ... | @@ -387,9 +387,88 @@ fn fmtPathFile( | ... | @@ -387,9 +387,88 @@ fn fmtPathFile( |
| 387 | break :mode .zig; | 387 | break :mode .zig; |
| 388 | }; | 388 | }; |
| 389 | 389 | ||
| 390 | var tree = try std.zig.Ast.parse(gpa, source_code, .{ .mode = mode, .use_old_precedence = fmt.fix_precedence }); | 390 | var tree = try std.zig.Ast.parse(gpa, source_code, .{ .mode = mode }); |
| 391 | defer tree.deinit(gpa); | 391 | defer tree.deinit(gpa); |
| 392 | 392 | ||
| 393 | var fixups: std.zig.Ast.Render.Fixups = .{}; | ||
| 394 | defer fixups.deinit(gpa); | ||
| 395 | |||
| 396 | fix: { | ||
| 397 | if (!fmt.fix_precedence) break :fix; | ||
| 398 | |||
| 399 | var operators_to_fix: std.AutoHashMapUnmanaged(std.zig.Ast.TokenIndex, void) = .empty; | ||
| 400 | defer operators_to_fix.deinit(gpa); | ||
| 401 | |||
| 402 | // Gather ambiguous operator precedence error tokens for quick lookup later | ||
| 403 | for (tree.errors) |err| { | ||
| 404 | switch (err.tag) { | ||
| 405 | .hint_operator_precedence_left => { | ||
| 406 | try operators_to_fix.putNoClobber(gpa, err.token - @as(u32, @intCast(err.extra.offset)), {}); | ||
| 407 | }, | ||
| 408 | .hint_operator_precedence_right => { | ||
| 409 | try operators_to_fix.putNoClobber(gpa, err.token, {}); | ||
| 410 | }, | ||
| 411 | else => continue, | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | if (operators_to_fix.count() == 0) break :fix; | ||
| 416 | |||
| 417 | // Build a new error list without the ambiguous operator precedence errors/notes | ||
| 418 | var fixed_errors: std.ArrayList(std.zig.Ast.Error) = try .initCapacity(gpa, tree.errors.len - (operators_to_fix.count() * 3)); | ||
| 419 | for (tree.errors) |err| { | ||
| 420 | switch (err.tag) { | ||
| 421 | .ambiguous_operator_precedence, | ||
| 422 | .illegal_chained_operators, | ||
| 423 | .previous_ambiguous_operator, | ||
| 424 | .hint_operator_precedence_left, | ||
| 425 | .hint_operator_precedence_right, | ||
| 426 | => continue, | ||
| 427 | else => { | ||
| 428 | fixed_errors.appendAssumeCapacity(err); | ||
| 429 | }, | ||
| 430 | } | ||
| 431 | } | ||
| 432 | gpa.free(tree.errors); | ||
| 433 | tree.errors = fixed_errors.toOwnedSliceAssert(); | ||
| 434 | |||
| 435 | // Find the nodes we need to add parens to and add them to the list | ||
| 436 | for (tree.nodes.items(.tag), 0..) |tag, i| { | ||
| 437 | switch (tag) { | ||
| 438 | .merge_error_sets, | ||
| 439 | .mul, | ||
| 440 | .div, | ||
| 441 | .mod, | ||
| 442 | .mul_wrap, | ||
| 443 | .mul_sat, | ||
| 444 | .add, | ||
| 445 | .sub, | ||
| 446 | .array_cat, | ||
| 447 | .add_wrap, | ||
| 448 | .sub_wrap, | ||
| 449 | .add_sat, | ||
| 450 | .sub_sat, | ||
| 451 | .shl, | ||
| 452 | .shl_sat, | ||
| 453 | .shr, | ||
| 454 | .bit_and, | ||
| 455 | .bit_xor, | ||
| 456 | .bit_or, | ||
| 457 | .@"orelse", | ||
| 458 | .@"catch", | ||
| 459 | .bool_and, | ||
| 460 | .bool_or, | ||
| 461 | => { | ||
| 462 | const main_tok = tree.nodes.get(i).main_token; | ||
| 463 | if (operators_to_fix.contains(main_tok)) { | ||
| 464 | try fixups.insert_parentheses.put(gpa, @fromBackingInt(@intCast(i)), {}); | ||
| 465 | } | ||
| 466 | }, | ||
| 467 | else => {}, | ||
| 468 | } | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 393 | if (tree.errors.len != 0) { | 472 | if (tree.errors.len != 0) { |
| 394 | try std.zig.printAstErrorsToStderr(gpa, io, tree, file_path, fmt.color); | 473 | try std.zig.printAstErrorsToStderr(gpa, io, tree, file_path, fmt.color); |
| 395 | fmt.any_error = true; | 474 | fmt.any_error = true; |
| ... | @@ -497,7 +576,7 @@ fn fmtPathFile( | ... | @@ -497,7 +576,7 @@ fn fmtPathFile( |
| 497 | fmt.out_buffer.clearRetainingCapacity(); | 576 | fmt.out_buffer.clearRetainingCapacity(); |
| 498 | try fmt.out_buffer.ensureTotalCapacity(source_code.len); | 577 | try fmt.out_buffer.ensureTotalCapacity(source_code.len); |
| 499 | 578 | ||
| 500 | tree.render(gpa, &fmt.out_buffer.writer, .{}) catch |err| switch (err) { | 579 | tree.render(gpa, &fmt.out_buffer.writer, fixups) catch |err| switch (err) { |
| 501 | error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, | 580 | error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, |
| 502 | }; | 581 | }; |
| 503 | if (!mem.eql(u8, fmt.out_buffer.written(), source_code)) { | 582 | if (!mem.eql(u8, fmt.out_buffer.written(), source_code)) { |