Commit c98b020ce2

Matthew Borkowski <matthew.h.borkowski@gmail.com>
2021-12-02 09:50:23
parse.zig: make chained comparison operators a parse error
1 parent fb9fcf5
Changed files (4)
lib/std/zig/Ast.zig
@@ -136,6 +136,9 @@ pub fn renderError(tree: Tree, parse_error: Error, stream: anytype) !void {
             // location would point to the `*` after the `.*`.
             return stream.writeAll("'.*' cannot be followed by '*'. Are you missing a space?");
         },
+        .chained_comparison_operators => {
+            return stream.writeAll("comparison operators cannot be chained");
+        },
         .decl_between_fields => {
             return stream.writeAll("declarations are not allowed between container fields");
         },
@@ -2424,6 +2427,7 @@ pub const Error = struct {
 
     pub const Tag = enum {
         asterisk_after_ptr_deref,
+        chained_comparison_operators,
         decl_between_fields,
         expected_block,
         expected_block_or_assignment,
lib/std/zig/parse.zig
@@ -1374,6 +1374,7 @@ const Parser = struct {
     });
 
     fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index {
+        assert(min_prec >= 0);
         var node = try p.parsePrefixExpr();
         if (node == 0) {
             return null_node;
@@ -1384,9 +1385,12 @@ const Parser = struct {
         while (true) {
             const tok_tag = p.token_tags[p.tok_i];
             const info = operTable[@intCast(usize, @enumToInt(tok_tag))];
-            if (info.prec < min_prec or info.prec == banned_prec) {
+            if (info.prec < min_prec) {
                 break;
             }
+            if (info.prec == banned_prec) {
+                return p.fail(.chained_comparison_operators);
+            }
             const oper_token = p.nextToken();
             // Special-case handling for "catch" and "&&".
             switch (tok_tag) {
lib/std/zig/parser_test.zig
@@ -5056,8 +5056,8 @@ test "recovery: non-associative operators" {
         \\const x = a == b == c;
         \\const x = a == b != c;
     , &[_]Error{
-        .expected_token,
-        .expected_token,
+        .chained_comparison_operators,
+        .chained_comparison_operators,
     });
 }
 
test/compile_errors.zig
@@ -5033,18 +5033,21 @@ pub fn addCases(ctx: *TestContext) !void {
         "tmp.zig:2:5: note: control flow is diverted here",
     });
 
-    ctx.objErrStage1("unreachable code - multiple things",
+    ctx.objErrStage1("unreachable code - nested returns",
         \\export fn a() i32 {
         \\    return return 1;
         \\}
-        \\export fn b(value: u32) bool {
-        \\    return 1 < value < 1000;
-        \\}
     , &[_][]const u8{
         "tmp.zig:2:5: error: unreachable code",
         "tmp.zig:2:12: note: control flow is diverted here",
-        "tmp.zig:5:22: error: unreachable code",
-        "tmp.zig:5:5: note: control flow is diverted here",
+    });
+
+    ctx.objErrStage1("chained comparison operators",
+        \\export fn a(value: u32) bool {
+        \\    return 1 < value < 1000;
+        \\}
+    , &[_][]const u8{
+        "tmp.zig:2:22: error: comparison operators cannot be chained",
     });
 
     ctx.objErrStage1("bad import",