Commit 1e52b7d690

Vexu <git@vexu.eu>
2019-12-28 22:38:07
translate-c-2 improve macro errors
1 parent aea46dc
Changed files (2)
src-self-hosted/c_tokenizer.zig
@@ -1,5 +1,8 @@
 const std = @import("std");
 const expect = std.testing.expect;
+const ZigClangSourceLocation = @import("clang.zig").ZigClangSourceLocation;
+const Context = @import("translate_c.zig").Context;
+const failDecl = @import("translate_c.zig").failDecl;
 
 pub const TokenList = std.SegmentedList(CToken, 32);
 
@@ -46,13 +49,13 @@ pub const CToken = struct {
     };
 };
 
-pub fn tokenizeCMacro(tl: *TokenList, chars: [*:0]const u8) !void {
+pub fn tokenizeCMacro(ctx: *Context, loc: ZigClangSourceLocation, name: []const u8, tl: *TokenList, chars: [*:0]const u8) !void {
     var index: usize = 0;
     var first = true;
     while (true) {
-        const tok = try next(chars, &index);
+        const tok = try next(ctx, loc, name, chars, &index);
         if (tok.id == .StrLit or tok.id == .CharLit)
-            try tl.push(try zigifyEscapeSequences(tl.allocator, tok))
+            try tl.push(try zigifyEscapeSequences(ctx, loc, name, tl.allocator, tok))
         else
             try tl.push(tok);
         if (tok.id == .Eof)
@@ -70,7 +73,7 @@ pub fn tokenizeCMacro(tl: *TokenList, chars: [*:0]const u8) !void {
     }
 }
 
-fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken {
+fn zigifyEscapeSequences(ctx: *Context, loc: ZigClangSourceLocation, name: []const u8, allocator: *std.mem.Allocator, tok: CToken) !CToken {
     for (tok.bytes) |c| {
         if (c == '\\') {
             break;
@@ -136,11 +139,11 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken {
                         bytes[i] = '?';
                     },
                     'u', 'U' => {
-                        // TODO unicode escape sequences
+                        try failDecl(ctx, loc, name, "macro tokenizing failed: TODO unicode escape sequences", .{});
                         return error.TokenizingFailed;
                     },
                     else => {
-                        // unknown escape sequence
+                        try failDecl(ctx, loc, name, "macro tokenizing failed: unknown escape sequence", .{});
                         return error.TokenizingFailed;
                     },
                 }
@@ -158,19 +161,28 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken {
             .Hex => {
                 switch (c) {
                     '0'...'9' => {
-                        num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed;
+                        num = std.math.mul(u8, num, 16) catch {
+                            try failDecl(ctx, loc, name, "macro tokenizing failed: hex literal overflowed", .{});
+                            return error.TokenizingFailed;
+                        };
                         num += c - '0';
                     },
                     'a'...'f' => {
-                        num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed;
+                        num = std.math.mul(u8, num, 16) catch {
+                            try failDecl(ctx, loc, name, "macro tokenizing failed: hex literal overflowed", .{});
+                            return error.TokenizingFailed;
+                        };
                         num += c - 'a' + 10;
                     },
                     'A'...'F' => {
-                        num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed;
+                        num = std.math.mul(u8, num, 16) catch {
+                            try failDecl(ctx, loc, name, "macro tokenizing failed: hex literal overflowed", .{});
+                            return error.TokenizingFailed;
+                        };
                         num += c - 'A' + 10;
                     },
                     else => {
-                        i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2});
+                        i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
                         num = 0;
                         if (c == '\\')
                             state = .Escape
@@ -185,14 +197,17 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken {
                 switch (c) {
                     '0'...'7' => {
                         count += 1;
-                        num = std.math.mul(u8, num, 8) catch return error.TokenizingFailed;
+                        num = std.math.mul(u8, num, 8) catch {
+                            try failDecl(ctx, loc, name, "macro tokenizing failed: octal literal overflowed", .{});
+                            return error.TokenizingFailed;
+                        };
                         num += c - '0';
                         if (count < 3)
                             continue;
                     },
                     else => {},
                 }
-                i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2});
+                i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
                 state = .Start;
                 count = 0;
                 num = 0;
@@ -200,14 +215,14 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken {
         }
     }
     if (state == .Hex or state == .Octal)
-        i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2});
+        i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
     return CToken{
         .id = tok.id,
         .bytes = bytes[0..i],
     };
 }
 
-fn next(chars: [*:0]const u8, i: *usize) !CToken {
+fn next(ctx: *Context, loc: ZigClangSourceLocation, name: []const u8, chars: [*:0]const u8, i: *usize) !CToken {
     var state: enum {
         Start,
         GotLt,
@@ -278,7 +293,10 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                 .String,
                 .ExpSign,
                 .FloatExpFirst,
-                => return error.TokenizingFailed,
+                => {
+                    try failDecl(ctx, loc, name, "macro tokenizing failed: unexpected EOF", .{});
+                    return error.TokenizingFailed;
+                },
             }
         }
         switch (state) {
@@ -375,7 +393,10 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                         result.id = .Colon;
                         state = .Done;
                     },
-                    else => return error.TokenizingFailed,
+                    else => {
+                        try failDecl(ctx, loc, name, "macro tokenizing failed: unexpected character '{c}'", .{c});
+                        return error.TokenizingFailed;
+                    },
                 }
             },
             .Done => return result,
@@ -433,7 +454,10 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                     '0'...'9' => {
                         state = .FloatExp;
                     },
-                    else => return error.TokenizingFailed,
+                    else =>  {
+                        try failDecl(ctx, loc, name, "macro tokenizing failed: expected a digit or '+' or '-'", .{});
+                        return error.TokenizingFailed;
+                    },
                 }
             },
             .FloatExpFirst => {
@@ -441,7 +465,10 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                     '0'...'9' => {
                         state = .FloatExp;
                     },
-                    else => return error.TokenizingFailed,
+                    else => {
+                        try failDecl(ctx, loc, name, "macro tokenizing failed: expected a digit", .{});
+                        return error.TokenizingFailed;
+                    },
                 }
             },
             .FloatExp => {
@@ -518,7 +545,10 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
             .Octal => {
                 switch (c) {
                     '0'...'7' => {},
-                    '8', '9' => return error.TokenizingFailed,
+                    '8', '9' => {
+                        try failDecl(ctx, loc, name, "macro tokenizing failed: invalid digit '{c}' in octal number", .{c});
+                        return error.TokenizingFailed;
+                    },
                     else => {
                         result.bytes = chars[begin_index..i.*];
                         return result;
@@ -549,7 +579,10 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
             .Bin => {
                 switch (c) {
                     '0'...'1' => {},
-                    '2'...'9' => return error.TokenizingFailed,
+                    '2'...'9' => {
+                        try failDecl(ctx, loc, name, "macro tokenizing failed: invalid digit '{c}' in binary number", .{c});
+                        return error.TokenizingFailed;
+                    },
                     'u', 'U' => {
                         // marks the number literal as unsigned
                         state = .NumLitIntSuffixU;
@@ -681,7 +714,10 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                     '\n', '\r' => {
                         state = .Start;
                     },
-                    else => return error.TokenizingFailed,
+                    else => {
+                        try failDecl(ctx, loc, name, "macro tokenizing failed: expected whitespace", .{});
+                        return error.TokenizingFailed;
+                    },
                 }
             },
         }
src-self-hosted/translate_c.zig
@@ -182,7 +182,7 @@ const Scope = struct {
     }
 };
 
-const Context = struct {
+pub const Context = struct {
     tree: *ast.Tree,
     source_buffer: *std.Buffer,
     err: Error,
@@ -3921,7 +3921,7 @@ fn emitWarning(c: *Context, loc: ZigClangSourceLocation, comptime format: []cons
     _ = try appendTokenFmt(c, .LineComment, "// {}: warning: " ++ format, args_prefix ++ args);
 }
 
-fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: var) !void {
+pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: var) !void {
     // const name = @compileError(msg);
     const const_tok = try appendToken(c, .Keyword_const, "const");
     const name_tok = try appendIdentifier(c, name);
@@ -4091,10 +4091,9 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
                     continue;
                 }
                 const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc);
-                ctok.tokenizeCMacro(&tok_list, begin_c) catch |err| switch (err) {
+                ctok.tokenizeCMacro(c, begin_loc, checked_name, &tok_list, begin_c) catch |err| switch (err) {
                     error.OutOfMemory => |e| return e,
                     else => {
-                        try failDecl(c, begin_loc, checked_name, "unable to tokenize macro definition", .{});
                         continue;
                     },
                 };
@@ -4127,9 +4126,7 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
                     transMacroFnDefine(c, &tok_it, checked_name, begin_loc)
                 else
                     transMacroDefine(c, &tok_it, checked_name, begin_loc)) catch |err| switch (err) {
-                    error.UnsupportedTranslation,
-                    error.ParseError,
-                    => try failDecl(c, begin_loc, checked_name, "unable to translate macro", .{}),
+                    error.ParseError => continue,
                     error.OutOfMemory => |e| return e,
                 };
             },
@@ -4139,20 +4136,19 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
 }
 
 fn transMacroDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {
-    const rp = makeRestorePoint(c);
     const scope = &c.global_scope.base;
 
     const node = try transCreateNodeVarDecl(c, true, true, name);
     node.eq_token = try appendToken(c, .Equal, "=");
 
-    node.init_node = try parseCExpr(rp, it, source_loc, scope);
+    node.init_node = try parseCExpr(c, it, source_loc, scope);
     const last = it.next().?;
     if (last.id != .Eof)
-        return revertAndWarn(
-            rp,
-            error.UnsupportedTranslation,
+        return failDecl(
+            c,
             source_loc,
-            "unable to translate C expr, unexpected token: {}",
+            name,
+            "unable to translate C expr: unexpected token {}",
             .{last.id},
         );
 
@@ -4161,7 +4157,6 @@ fn transMacroDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8,
 }
 
 fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {
-    const rp = makeRestorePoint(c);
     const block_scope = try Scope.Block.init(c, &c.global_scope.base, null);
     const scope = &block_scope.base;
 
@@ -4172,13 +4167,26 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u
     _ = try appendToken(c, .LParen, "(");
 
     if (it.next().?.id != .LParen) {
-        return error.ParseError;
+        return failDecl(
+            c,
+            source_loc,
+            name,
+            "unable to translate C expr: expected '('",
+            .{},
+        );
     }
     var fn_params = ast.Node.FnProto.ParamList.init(c.a());
     while (true) {
         const param_tok = it.next().?;
-        if (param_tok.id != .Identifier)
-            return error.ParseError;
+        if (param_tok.id != .Identifier) {
+            return failDecl(
+                c,
+                source_loc,
+                name,
+                "unable to translate C expr: expected identifier",
+                .{},
+            );
+        }
 
         const checked_name = if (try scope.createAlias(c, param_tok.bytes)) |alias| blk: {
             try block_scope.variables.push(.{ .name = param_tok.bytes, .alias = alias });
@@ -4213,7 +4221,13 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u
     }
 
     if (it.next().?.id != .RParen) {
-        return error.ParseError;
+        return failDecl(
+            c,
+            source_loc,
+            name,
+            "unable to translate C expr: expected ')'",
+            .{},
+        );
     }
 
     _ = try appendToken(c, .RParen, ")");
@@ -4241,14 +4255,14 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u
     const block = try transCreateNodeBlock(c, null);
 
     const return_expr = try transCreateNodeReturnExpr(c);
-    const expr = try parseCExpr(rp, it, source_loc, scope);
+    const expr = try parseCExpr(c, it, source_loc, scope);
     const last = it.next().?;
     if (last.id != .Eof)
-        return revertAndWarn(
-            rp,
-            error.UnsupportedTranslation,
+        return failDecl(
+            c,
             source_loc,
-            "unable to translate C expr, unexpected token: {}",
+            name,
+            "unable to translate C expr: unexpected token {}",
             .{last.id},
         );
     _ = try appendToken(c, .Semicolon, ";");
@@ -4261,31 +4275,28 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u
     _ = try c.global_scope.macro_table.put(name, &fn_proto.base);
 }
 
-const ParseError = Error || error{
-    ParseError,
-    UnsupportedTranslation,
-};
+const ParseError = Error || error{ParseError};
 
-fn parseCExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
-    return parseCPrefixOpExpr(rp, it, source_loc, scope);
+fn parseCExpr(c: *Context, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
+    return parseCPrefixOpExpr(c, it, source_loc, scope);
 }
 
-fn parseCNumLit(rp: RestorePoint, tok: *CToken, source_loc: ZigClangSourceLocation) ParseError!*ast.Node {
+fn parseCNumLit(c: *Context, tok: *CToken, source_loc: ZigClangSourceLocation) ParseError!*ast.Node {
     if (tok.id == .NumLitInt) {
         if (tok.num_lit_suffix == .None) {
             if (tok.bytes.len > 2 and tok.bytes[0] == '0') {
                 switch (tok.bytes[1]) {
                     '0'...'7' => {
                         // octal
-                        return transCreateNodeInt(rp.c, try std.fmt.allocPrint(rp.c.a(), "0o{}", .{tok.bytes}));
+                        return transCreateNodeInt(c, try std.fmt.allocPrint(c.a(), "0o{}", .{tok.bytes}));
                     },
                     else => {},
                 }
             }
-            return transCreateNodeInt(rp.c, tok.bytes);
+            return transCreateNodeInt(c, tok.bytes);
         }
-        const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
-        try cast_node.params.push(try transCreateNodeIdentifier(rp.c, switch (tok.num_lit_suffix) {
+        const cast_node = try transCreateNodeBuiltinFnCall(c, "@as");
+        try cast_node.params.push(try transCreateNodeIdentifier(c, switch (tok.num_lit_suffix) {
             .U => "c_uint",
             .L => "c_long",
             .LU => "c_ulong",
@@ -4293,62 +4304,55 @@ fn parseCNumLit(rp: RestorePoint, tok: *CToken, source_loc: ZigClangSourceLocati
             .LLU => "c_ulonglong",
             else => unreachable,
         }));
-        _ = try appendToken(rp.c, .Comma, ",");
-        try cast_node.params.push(try transCreateNodeInt(rp.c, tok.bytes));
-        cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+        _ = try appendToken(c, .Comma, ",");
+        try cast_node.params.push(try transCreateNodeInt(c, tok.bytes));
+        cast_node.rparen_token = try appendToken(c, .RParen, ")");
         return &cast_node.base;
     } else if (tok.id == .NumLitFloat) {
         if (tok.num_lit_suffix == .None) {
-            return transCreateNodeFloat(rp.c, tok.bytes);
+            return transCreateNodeFloat(c, tok.bytes);
         }
-        const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
-        try cast_node.params.push(try transCreateNodeIdentifier(rp.c, switch (tok.num_lit_suffix) {
+        const cast_node = try transCreateNodeBuiltinFnCall(c, "@as");
+        try cast_node.params.push(try transCreateNodeIdentifier(c, switch (tok.num_lit_suffix) {
             .F => "f32",
             .L => "f64",
             else => unreachable,
         }));
-        _ = try appendToken(rp.c, .Comma, ",");
-        try cast_node.params.push(try transCreateNodeFloat(rp.c, tok.bytes));
-        cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+        _ = try appendToken(c, .Comma, ",");
+        try cast_node.params.push(try transCreateNodeFloat(c, tok.bytes));
+        cast_node.rparen_token = try appendToken(c, .RParen, ")");
         return &cast_node.base;
-    } else
-        return revertAndWarn(
-        rp,
-        error.ParseError,
-        source_loc,
-        "expected number literal",
-        .{},
-    );
+    } else unreachable;
 }
 
-fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
+fn parseCPrimaryExpr(c: *Context, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
     const tok = it.next().?;
     switch (tok.id) {
         .CharLit => {
-            const token = try appendToken(rp.c, .CharLiteral, tok.bytes);
-            const node = try rp.c.a().create(ast.Node.CharLiteral);
+            const token = try appendToken(c, .CharLiteral, tok.bytes);
+            const node = try c.a().create(ast.Node.CharLiteral);
             node.* = ast.Node.CharLiteral{
                 .token = token,
             };
             return &node.base;
         },
         .StrLit => {
-            const token = try appendToken(rp.c, .StringLiteral, tok.bytes);
-            const node = try rp.c.a().create(ast.Node.StringLiteral);
+            const token = try appendToken(c, .StringLiteral, tok.bytes);
+            const node = try c.a().create(ast.Node.StringLiteral);
             node.* = ast.Node.StringLiteral{
                 .token = token,
             };
             return &node.base;
         },
         .NumLitInt, .NumLitFloat => {
-            return parseCNumLit(rp, tok, source_loc);
+            return parseCNumLit(c, tok, source_loc);
         },
         .Identifier => {
             const name = if (scope.getAlias(tok.bytes)) |a| a else tok.bytes;
-            return transCreateNodeIdentifier(rp.c, name);
+            return transCreateNodeIdentifier(c, name);
         },
         .LParen => {
-            const inner_node = try parseCExpr(rp, it, source_loc, scope);
+            const inner_node = try parseCExpr(c, it, source_loc, scope);
 
             if (it.peek().?.id == .RParen) {
                 _ = it.next();
@@ -4359,18 +4363,19 @@ fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc:
             }
 
             // hack to get zig fmt to render a comma in builtin calls
-            _ = try appendToken(rp.c, .Comma, ",");
+            _ = try appendToken(c, .Comma, ",");
 
-            const node_to_cast = try parseCExpr(rp, it, source_loc, scope);
+            const node_to_cast = try parseCExpr(c, it, source_loc, scope);
 
             if (it.next().?.id != .RParen) {
-                return revertAndWarn(
-                    rp,
-                    error.ParseError,
+                try failDecl(
+                    c,
                     source_loc,
-                    "unable to translate C expr",
+                    it.list.at(0).*.bytes,
+                    "unable to translate C expr: expected ')''",
                     .{},
                 );
+                return error.ParseError;
             }
 
             //if (@typeId(@TypeOf(x)) == .Pointer)
@@ -4380,114 +4385,133 @@ fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc:
             //else
             //    @as(dest, x)
 
-            const if_1 = try transCreateNodeIf(rp.c);
-            const type_id_1 = try transCreateNodeBuiltinFnCall(rp.c, "@typeId");
-            const type_of_1 = try transCreateNodeBuiltinFnCall(rp.c, "@TypeOf");
+            const if_1 = try transCreateNodeIf(c);
+            const type_id_1 = try transCreateNodeBuiltinFnCall(c, "@typeId");
+            const type_of_1 = try transCreateNodeBuiltinFnCall(c, "@TypeOf");
             try type_id_1.params.push(&type_of_1.base);
             try type_of_1.params.push(node_to_cast);
-            type_of_1.rparen_token = try appendToken(rp.c, .LParen, ")");
-            type_id_1.rparen_token = try appendToken(rp.c, .LParen, ")");
+            type_of_1.rparen_token = try appendToken(c, .LParen, ")");
+            type_id_1.rparen_token = try appendToken(c, .LParen, ")");
 
-            const cmp_1 = try rp.c.a().create(ast.Node.InfixOp);
+            const cmp_1 = try c.a().create(ast.Node.InfixOp);
             cmp_1.* = .{
-                .op_token = try appendToken(rp.c, .EqualEqual, "=="),
+                .op_token = try appendToken(c, .EqualEqual, "=="),
                 .lhs = &type_id_1.base,
                 .op = .EqualEqual,
-                .rhs = try transCreateNodeEnumLiteral(rp.c, "Pointer"),
+                .rhs = try transCreateNodeEnumLiteral(c, "Pointer"),
             };
             if_1.condition = &cmp_1.base;
-            _ = try appendToken(rp.c, .LParen, ")");
+            _ = try appendToken(c, .LParen, ")");
 
-            const ptr_cast = try transCreateNodeBuiltinFnCall(rp.c, "@ptrCast");
+            const ptr_cast = try transCreateNodeBuiltinFnCall(c, "@ptrCast");
             try ptr_cast.params.push(inner_node);
             try ptr_cast.params.push(node_to_cast);
-            ptr_cast.rparen_token = try appendToken(rp.c, .LParen, ")");
+            ptr_cast.rparen_token = try appendToken(c, .LParen, ")");
             if_1.body = &ptr_cast.base;
 
-            const else_1 = try transCreateNodeElse(rp.c);
+            const else_1 = try transCreateNodeElse(c);
             if_1.@"else" = else_1;
 
-            const if_2 = try transCreateNodeIf(rp.c);
-            const type_id_2 = try transCreateNodeBuiltinFnCall(rp.c, "@typeId");
-            const type_of_2 = try transCreateNodeBuiltinFnCall(rp.c, "@TypeOf");
+            const if_2 = try transCreateNodeIf(c);
+            const type_id_2 = try transCreateNodeBuiltinFnCall(c, "@typeId");
+            const type_of_2 = try transCreateNodeBuiltinFnCall(c, "@TypeOf");
             try type_id_2.params.push(&type_of_2.base);
             try type_of_2.params.push(node_to_cast);
-            type_of_2.rparen_token = try appendToken(rp.c, .LParen, ")");
-            type_id_2.rparen_token = try appendToken(rp.c, .LParen, ")");
+            type_of_2.rparen_token = try appendToken(c, .LParen, ")");
+            type_id_2.rparen_token = try appendToken(c, .LParen, ")");
 
-            const cmp_2 = try rp.c.a().create(ast.Node.InfixOp);
+            const cmp_2 = try c.a().create(ast.Node.InfixOp);
             cmp_2.* = .{
-                .op_token = try appendToken(rp.c, .EqualEqual, "=="),
+                .op_token = try appendToken(c, .EqualEqual, "=="),
                 .lhs = &type_id_2.base,
                 .op = .EqualEqual,
-                .rhs = try transCreateNodeEnumLiteral(rp.c, "Int"),
+                .rhs = try transCreateNodeEnumLiteral(c, "Int"),
             };
             if_2.condition = &cmp_2.base;
             else_1.body = &if_2.base;
-            _ = try appendToken(rp.c, .LParen, ")");
+            _ = try appendToken(c, .LParen, ")");
 
-            const int_to_ptr = try transCreateNodeBuiltinFnCall(rp.c, "@intToPtr");
+            const int_to_ptr = try transCreateNodeBuiltinFnCall(c, "@intToPtr");
             try int_to_ptr.params.push(inner_node);
             try int_to_ptr.params.push(node_to_cast);
-            int_to_ptr.rparen_token = try appendToken(rp.c, .LParen, ")");
+            int_to_ptr.rparen_token = try appendToken(c, .LParen, ")");
             if_2.body = &int_to_ptr.base;
 
-            const else_2 = try transCreateNodeElse(rp.c);
+            const else_2 = try transCreateNodeElse(c);
             if_2.@"else" = else_2;
 
-            const as = try transCreateNodeBuiltinFnCall(rp.c, "@as");
+            const as = try transCreateNodeBuiltinFnCall(c, "@as");
             try as.params.push(inner_node);
             try as.params.push(node_to_cast);
-            as.rparen_token = try appendToken(rp.c, .LParen, ")");
+            as.rparen_token = try appendToken(c, .LParen, ")");
             else_2.body = &as.base;
 
             return &if_1.base;
         },
-        else => return revertAndWarn(
-            rp,
-            error.UnsupportedTranslation,
-            source_loc,
-            "unable to translate C expr, unexpected token: {}",
-            .{tok.id},
-        ),
+        else => {
+            try failDecl(
+                c,
+                source_loc,
+                it.list.at(0).*.bytes,
+                "unable to translate C expr: unexpected token {}",
+                .{tok.id},
+            );
+            return error.ParseError;
+        },
     }
 }
 
-fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
-    var node = try parseCPrimaryExpr(rp, it, source_loc, scope);
+fn parseCSuffixOpExpr(c: *Context, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
+    var node = try parseCPrimaryExpr(c, it, source_loc, scope);
     while (true) {
         const tok = it.next().?;
         switch (tok.id) {
             .Dot => {
                 const name_tok = it.next().?;
-                if (name_tok.id != .Identifier)
+                if (name_tok.id != .Identifier) {
+                    try failDecl(
+                        c,
+                        source_loc,
+                        it.list.at(0).*.bytes,
+                        "unable to translate C expr: expected identifier",
+                        .{},
+                    );
                     return error.ParseError;
+                }
 
-                node = try transCreateNodeFieldAccess(rp.c, node, name_tok.bytes);
+                node = try transCreateNodeFieldAccess(c, node, name_tok.bytes);
             },
             .Arrow => {
                 const name_tok = it.next().?;
-                if (name_tok.id != .Identifier)
+                if (name_tok.id != .Identifier) {
+                    try failDecl(
+                        c,
+                        source_loc,
+                        it.list.at(0).*.bytes,
+                        "unable to translate C expr: expected identifier",
+                        .{},
+                    );
                     return error.ParseError;
+                }
 
-                const deref = try transCreateNodePtrDeref(rp.c, node);
-                node = try transCreateNodeFieldAccess(rp.c, deref, name_tok.bytes);
+                const deref = try transCreateNodePtrDeref(c, node);
+                node = try transCreateNodeFieldAccess(c, deref, name_tok.bytes);
             },
             .Asterisk => {
                 if (it.peek().?.id == .RParen) {
                     // type *)
 
                     // hack to get zig fmt to render a comma in builtin calls
-                    _ = try appendToken(rp.c, .Comma, ",");
+                    _ = try appendToken(c, .Comma, ",");
 
-                    const ptr = try transCreateNodePtrType(rp.c, false, false, .Identifier);
+                    const ptr = try transCreateNodePtrType(c, false, false, .Identifier);
                     ptr.rhs = node;
                     return &ptr.base;
                 } else {
                     // expr * expr
-                    const op_token = try appendToken(rp.c, .Asterisk, "*");
-                    const rhs = try parseCPrimaryExpr(rp, it, source_loc, scope);
-                    const mul_node = try rp.c.a().create(ast.Node.InfixOp);
+                    const op_token = try appendToken(c, .Asterisk, "*");
+                    const rhs = try parseCPrimaryExpr(c, it, source_loc, scope);
+                    const mul_node = try c.a().create(ast.Node.InfixOp);
                     mul_node.* = .{
                         .op_token = op_token,
                         .lhs = node,
@@ -4498,9 +4522,9 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
                 }
             },
             .Shl => {
-                const op_token = try appendToken(rp.c, .AngleBracketAngleBracketLeft, "<<");
-                const rhs = try parseCExpr(rp, it, source_loc, scope);
-                const bitshift_node = try rp.c.a().create(ast.Node.InfixOp);
+                const op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<");
+                const rhs = try parseCExpr(c, it, source_loc, scope);
+                const bitshift_node = try c.a().create(ast.Node.InfixOp);
                 bitshift_node.* = .{
                     .op_token = op_token,
                     .lhs = node,
@@ -4510,9 +4534,9 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
                 node = &bitshift_node.base;
             },
             .Pipe => {
-                const op_token = try appendToken(rp.c, .Pipe, "|");
-                const rhs = try parseCExpr(rp, it, source_loc, scope);
-                const or_node = try rp.c.a().create(ast.Node.InfixOp);
+                const op_token = try appendToken(c, .Pipe, "|");
+                const rhs = try parseCExpr(c, it, source_loc, scope);
+                const or_node = try c.a().create(ast.Node.InfixOp);
                 or_node.* = .{
                     .op_token = op_token,
                     .lhs = node,
@@ -4522,39 +4546,63 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
                 node = &or_node.base;
             },
             .LBrace => {
-                const arr_node = try transCreateNodeArrayAccess(rp.c, node);
-                arr_node.op.ArrayAccess = try parseCExpr(rp, it, source_loc, scope);
-                arr_node.rtoken = try appendToken(rp.c, .RBrace, "]");
+                const arr_node = try transCreateNodeArrayAccess(c, node);
+                arr_node.op.ArrayAccess = try parseCExpr(c, it, source_loc, scope);
+                arr_node.rtoken = try appendToken(c, .RBrace, "]");
                 node = &arr_node.base;
-                if (it.next().?.id != .RBrace)
+                if (it.next().?.id != .RBrace) {
+                    try failDecl(
+                        c,
+                        source_loc,
+                        it.list.at(0).*.bytes,
+                        "unable to translate C expr: expected ']'",
+                        .{},
+                    );
                     return error.ParseError;
+                }
             },
             .LParen => {
-                const call_node = try transCreateNodeFnCall(rp.c, node);
+                const call_node = try transCreateNodeFnCall(c, node);
                 while (true) {
-                    const arg = try parseCExpr(rp, it, source_loc, scope);
+                    const arg = try parseCExpr(c, it, source_loc, scope);
                     try call_node.op.Call.params.push(arg);
                     const next = it.next().?;
                     if (next.id == .Comma)
-                        _ = try appendToken(rp.c, .Comma, ",")
+                        _ = try appendToken(c, .Comma, ",")
                     else if (next.id == .RParen)
                         break
-                    else
+                    else {
+                        try failDecl(
+                            c,
+                            source_loc,
+                            it.list.at(0).*.bytes,
+                            "unable to translate C expr: expected ',' or ')'",
+                            .{},
+                        );
                         return error.ParseError;
+                    }
                 }
-                call_node.rtoken = try appendToken(rp.c, .RParen, ")");
+                call_node.rtoken = try appendToken(c, .RParen, ")");
                 node = &call_node.base;
             },
             .QuestionMark => {
                 // must come immediately after expr
-                _ = try appendToken(rp.c, .RParen, ")");
-                const if_node = try transCreateNodeIf(rp.c);
+                _ = try appendToken(c, .RParen, ")");
+                const if_node = try transCreateNodeIf(c);
                 if_node.condition = node;
-                if_node.body = try parseCPrimaryExpr(rp, it, source_loc, scope);
-                if (it.next().?.id != .Colon)
+                if_node.body = try parseCPrimaryExpr(c, it, source_loc, scope);
+                if (it.next().?.id != .Colon) {
+                    try failDecl(
+                        c,
+                        source_loc,
+                        it.list.at(0).*.bytes,
+                        "unable to translate C expr: expected ':'",
+                        .{},
+                    );
                     return error.ParseError;
-                if_node.@"else" = try transCreateNodeElse(rp.c);
-                if_node.@"else".?.body = try parseCPrimaryExpr(rp, it, source_loc, scope);
+                }
+                if_node.@"else" = try transCreateNodeElse(c);
+                if_node.@"else".?.body = try parseCPrimaryExpr(c, it, source_loc, scope);
                 node = &if_node.base;
             },
             else => {
@@ -4565,32 +4613,32 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
     }
 }
 
-fn parseCPrefixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
+fn parseCPrefixOpExpr(c: *Context, it: *ctok.TokenList.Iterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
     const op_tok = it.next().?;
 
     switch (op_tok.id) {
         .Bang => {
-            const node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
-            node.rhs = try parseCPrefixOpExpr(rp, it, source_loc, scope);
+            const node = try transCreateNodePrefixOp(c, .BoolNot, .Bang, "!");
+            node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
             return &node.base;
         },
         .Minus => {
-            const node = try transCreateNodePrefixOp(rp.c, .Negation, .Minus, "-");
-            node.rhs = try parseCPrefixOpExpr(rp, it, source_loc, scope);
+            const node = try transCreateNodePrefixOp(c, .Negation, .Minus, "-");
+            node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
             return &node.base;
         },
         .Tilde => {
-            const node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~");
-            node.rhs = try parseCPrefixOpExpr(rp, it, source_loc, scope);
+            const node = try transCreateNodePrefixOp(c, .BitNot, .Tilde, "~");
+            node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
             return &node.base;
         },
         .Asterisk => {
-            const prefix_op_expr = try parseCPrefixOpExpr(rp, it, source_loc, scope);
-            return try transCreateNodePtrDeref(rp.c, prefix_op_expr);
+            const prefix_op_expr = try parseCPrefixOpExpr(c, it, source_loc, scope);
+            return try transCreateNodePtrDeref(c, prefix_op_expr);
         },
         else => {
             _ = it.prev();
-            return try parseCSuffixOpExpr(rp, it, source_loc, scope);
+            return try parseCSuffixOpExpr(c, it, source_loc, scope);
         },
     }
 }