const tests = @import("tests.zig");
const std = @import("std");
const CrossTarget = std.zig.CrossTarget;

pub fn addCases(cases: *tests.TranslateCContext) void {
    cases.add("pointer to opaque demoted struct",
        \\typedef struct {
        \\    _Atomic int foo;
        \\} Foo;
        \\
        \\typedef struct {
        \\    Foo *bar;
        \\} Bar;
    , &[_][]const u8{
        \\const struct_unnamed_1 = //
        ,
        \\warning: unsupported type: 'Atomic'
        \\    opaque {}; //
        ,
        \\pub const Foo = struct_unnamed_1;
        \\const struct_unnamed_2 = extern struct {
        \\    bar: ?*Foo,
        \\};
    });

    cases.add("macro expressions respect C operator precedence",
        \\#define FOO *((foo) + 2)
        \\#define VALUE  (1 + 2 * 3 + 4 * 5 + 6 << 7 | 8 == 9)
        \\#define _AL_READ3BYTES(p)   ((*(unsigned char *)(p))            \
        \\                             | (*((unsigned char *)(p) + 1) << 8)  \
        \\                             | (*((unsigned char *)(p) + 2) << 16))
    , &[_][]const u8{
        \\pub const FOO = (foo + 2).*;
        ,
        \\pub const VALUE = ((((1 + (2 * 3)) + (4 * 5)) + 6) << 7) | @boolToInt(8 == 9);
        ,
        \\pub inline fn _AL_READ3BYTES(p: anytype) @TypeOf(((@import("std").meta.cast([*c]u8, p)).* | (((@import("std").meta.cast([*c]u8, p)) + 1).* << 8)) | (((@import("std").meta.cast([*c]u8, p)) + 2).* << 16)) {
        \\    return ((@import("std").meta.cast([*c]u8, p)).* | (((@import("std").meta.cast([*c]u8, p)) + 1).* << 8)) | (((@import("std").meta.cast([*c]u8, p)) + 2).* << 16);
        \\}
    });

    cases.add("extern variable in block scope",
        \\float bar;
        \\int foo() {
        \\    _Thread_local static int bar = 2;
        \\}
    , &[_][]const u8{
        \\pub export var bar: f32 = @import("std").mem.zeroes(f32);
        \\threadlocal var bar_1: c_int = 2;
        \\pub export fn foo() c_int {
        \\    _ = bar_1;
        \\    return 0;
        \\}
    });

    cases.add("missing return stmt",
        \\int foo() {}
        \\int bar() {
        \\    int a = 2;
        \\}
        \\int baz() {
        \\    return 0;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() c_int {
        \\    return 0;
        \\}
        \\pub export fn bar() c_int {
        \\    var a: c_int = 2;
        \\    return 0;
        \\}
        \\pub export fn baz() c_int {
        \\    return 0;
        \\}
    });

    cases.add("alignof",
        \\void main() {
        \\    int a = _Alignof(int);
        \\}
    , &[_][]const u8{
        \\pub export fn main() void {
        \\    var a: c_int = @bitCast(c_int, @truncate(c_uint, @alignOf(c_int)));
        \\}
    });

    cases.add("initializer list macro",
        \\typedef struct Color {
        \\    unsigned char r;
        \\    unsigned char g;
        \\    unsigned char b;
        \\    unsigned char a;
        \\} Color;
        \\#define CLITERAL(type)      (type)
        \\#define LIGHTGRAY  CLITERAL(Color){ 200, 200, 200, 255 }   // Light Gray
    , &[_][]const u8{ // TODO properly translate this
        \\pub const struct_Color = extern struct {
        \\    r: u8,
        \\    g: u8,
        \\    b: u8,
        \\    a: u8,
        \\};
        \\pub const Color = struct_Color;
        ,
        \\pub inline fn CLITERAL(type_1: anytype) @TypeOf(type_1) {
        \\    return type_1;
        \\}
        ,
        \\pub const LIGHTGRAY = @import("std").mem.zeroInit(CLITERAL(Color), .{ 200, 200, 200, 255 });
    });

    cases.add("complex switch",
        \\int main() {
        \\    int i = 2;
        \\    switch (i) {
        \\        case 0: {
        \\            case 2:{
        \\                i += 2;}
        \\            i += 1;
        \\        }
        \\    }
        \\}
    , &[_][]const u8{ // TODO properly translate this
        \\pub const main = @compileError("unable to translate function");
    });

    cases.add("correct semicolon after infixop",
        \\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0)
    , &[_][]const u8{
        \\pub inline fn __ferror_unlocked_body(_fp: anytype) @TypeOf(((_fp.*._flags) & _IO_ERR_SEEN) != 0) {
        \\    return ((_fp.*._flags) & _IO_ERR_SEEN) != 0;
        \\}
    });

    cases.add("c booleans are just ints",
        \\#define FOO(x) ((x >= 0) + (x >= 0))
        \\#define BAR 1 && 2 > 4
    , &[_][]const u8{
        \\pub inline fn FOO(x: anytype) @TypeOf(@boolToInt(x >= 0) + @boolToInt(x >= 0)) {
        \\    return @boolToInt(x >= 0) + @boolToInt(x >= 0);
        \\}
        ,
        \\pub const BAR = (1 != 0) and (2 > 4);
    });

    cases.add("struct with aligned fields",
        \\struct foo {
        \\    __attribute__((aligned(1))) short bar;
        \\};
    , &[_][]const u8{
        \\pub const struct_foo = extern struct {
        \\    bar: c_short align(1),
        \\};
    });

    cases.add("structs with VLAs are rejected",
        \\struct foo { int x; int y[]; };
        \\struct bar { int x; int y[0]; };
    , &[_][]const u8{
        \\pub const struct_foo = opaque {};
        ,
        \\pub const struct_bar = opaque {};
    });

    cases.add("nested loops without blocks",
        \\void foo() {
        \\    while (0) while (0) {}
        \\    for (;;) while (0);
        \\    for (;;) do {} while (0);
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    while (false) while (false) {};
        \\    while (true) while (false) {};
        \\    while (true) while (true) {
        \\        if (!false) break;
        \\    };
        \\}
    });

    cases.add("macro comma operator",
        \\#define foo (foo, bar)
        \\#define bar(x) (&x, +3, 4 == 4, 5 * 6, baz(1, 2), 2 % 2, baz(1,2))
    , &[_][]const u8{
        \\pub const foo = blk: {
        \\    _ = foo;
        \\    break :blk bar;
        \\};
        ,
        \\pub inline fn bar(x: anytype) @TypeOf(baz(1, 2)) {
        \\    return blk: {
        \\        _ = &x;
        \\        _ = 3;
        \\        _ = 4 == 4;
        \\        _ = 5 * 6;
        \\        _ = baz(1, 2);
        \\        _ = 2 % 2;
        \\        break :blk baz(1, 2);
        \\    };
        \\}
    });

    cases.add("macro keyword define",
        \\#define foo 1
        \\#define inline 2
    , &[_][]const u8{
        \\pub const foo = 1;
        ,
        \\pub const @"inline" = 2;
    });

    cases.add("macro line continuation",
        \\#define FOO -\
        \\BAR
    , &[_][]const u8{
        \\pub const FOO = -BAR;
    });

    cases.add("struct with atomic field",
        \\struct arcan_shmif_cont {
        \\        struct arcan_shmif_page* addr;
        \\};
        \\struct arcan_shmif_page {
        \\        volatile _Atomic int abufused[12];
        \\};
    , &[_][]const u8{
        \\pub const struct_arcan_shmif_page = //
        ,
        \\warning: unsupported type: 'Atomic'
        \\    opaque {}; //
        ,
        \\ warning: struct demoted to opaque type - unable to translate type of field abufused
        , // TODO should be `addr: *struct_arcan_shmif_page`
        \\pub const struct_arcan_shmif_cont = extern struct {
        \\    addr: [*c]struct_arcan_shmif_page,
        \\};
    });

    cases.add("function prototype translated as optional",
        \\typedef void (*fnptr_ty)(void);
        \\typedef __attribute__((cdecl)) void (*fnptr_attr_ty)(void);
        \\struct foo {
        \\    __attribute__((cdecl)) void (*foo)(void);
        \\    void (*bar)(void);
        \\    fnptr_ty baz;
        \\    fnptr_attr_ty qux;
        \\};
    , &[_][]const u8{
        \\pub const fnptr_ty = ?fn () callconv(.C) void;
        \\pub const fnptr_attr_ty = ?fn () callconv(.C) void;
        \\pub const struct_foo = extern struct {
        \\    foo: ?fn () callconv(.C) void,
        \\    bar: ?fn () callconv(.C) void,
        \\    baz: fnptr_ty,
        \\    qux: fnptr_attr_ty,
        \\};
    });

    cases.add("function prototype with parenthesis",
        \\void (f0) (void *L);
        \\void ((f1)) (void *L);
        \\void (((f2))) (void *L);
    , &[_][]const u8{
        \\pub extern fn f0(L: ?*c_void) void;
        \\pub extern fn f1(L: ?*c_void) void;
        \\pub extern fn f2(L: ?*c_void) void;
    });

    cases.add("array initializer w/ typedef",
        \\typedef unsigned char uuid_t[16];
        \\static const uuid_t UUID_NULL __attribute__ ((unused)) = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
    , &[_][]const u8{
        \\pub const uuid_t = [16]u8;
        \\pub const UUID_NULL: uuid_t = [16]u8{
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\    @bitCast(u8, @truncate(i8, @as(c_int, 0))),
        \\};
    });

    cases.add("empty declaration",
        \\;
    , &[_][]const u8{""});

    cases.add("#define hex literal with capital X",
        \\#define VAL 0XF00D
    , &[_][]const u8{
        \\pub const VAL = 0xF00D;
    });

    cases.add("anonymous struct & unions",
        \\typedef struct {
        \\    union {
        \\        char x;
        \\        struct { int y; };
        \\    };
        \\} outer;
        \\void foo(outer *x) { x->y = x->x; }
    , &[_][]const u8{
        \\const struct_unnamed_3 = extern struct {
        \\    y: c_int,
        \\};
        \\const union_unnamed_2 = extern union {
        \\    x: u8,
        \\    unnamed_0: struct_unnamed_3,
        \\};
        \\const struct_unnamed_1 = extern struct {
        \\    unnamed_0: union_unnamed_2,
        \\};
        \\pub const outer = struct_unnamed_1;
        \\pub export fn foo(arg_x: [*c]outer) void {
        \\    var x = arg_x;
        \\    x.*.unnamed_0.unnamed_0.y = @bitCast(c_int, @as(c_uint, x.*.unnamed_0.x));
        \\}
    });

    cases.add("union initializer",
        \\union { int x; char c[4]; }
        \\  ua = {1},
        \\  ub = {.c={'a','b','b','a'}};
    , &[_][]const u8{
        \\const union_unnamed_1 = extern union {
        \\    x: c_int,
        \\    c: [4]u8,
        \\};
        \\pub export var ua: union_unnamed_1 = union_unnamed_1{
        \\    .x = @as(c_int, 1),
        \\};
        \\pub export var ub: union_unnamed_1 = union_unnamed_1{
        \\    .c = [4]u8{
        \\        @bitCast(u8, @truncate(i8, @as(c_int, 'a'))),
        \\        @bitCast(u8, @truncate(i8, @as(c_int, 'b'))),
        \\        @bitCast(u8, @truncate(i8, @as(c_int, 'b'))),
        \\        @bitCast(u8, @truncate(i8, @as(c_int, 'a'))),
        \\    },
        \\};
    });

    cases.add("struct initializer - simple",
        \\typedef struct { int x; } foo;
        \\struct {double x,y,z;} s0 = {1.2, 1.3};
        \\struct {int sec,min,hour,day,mon,year;} s1 = {.day=31,12,2014,.sec=30,15,17};
        \\struct {int x,y;} s2 = {.y = 2, .x=1};
        \\foo s3 = { 123 };
    , &[_][]const u8{
        \\const struct_unnamed_1 = extern struct {
        \\    x: c_int,
        \\};
        \\pub const foo = struct_unnamed_1;
        \\const struct_unnamed_2 = extern struct {
        \\    x: f64,
        \\    y: f64,
        \\    z: f64,
        \\};
        \\pub export var s0: struct_unnamed_2 = struct_unnamed_2{
        \\    .x = 1.2,
        \\    .y = 1.3,
        \\    .z = 0,
        \\};
        \\const struct_unnamed_3 = extern struct {
        \\    sec: c_int,
        \\    min: c_int,
        \\    hour: c_int,
        \\    day: c_int,
        \\    mon: c_int,
        \\    year: c_int,
        \\};
        \\pub export var s1: struct_unnamed_3 = struct_unnamed_3{
        \\    .sec = @as(c_int, 30),
        \\    .min = @as(c_int, 15),
        \\    .hour = @as(c_int, 17),
        \\    .day = @as(c_int, 31),
        \\    .mon = @as(c_int, 12),
        \\    .year = @as(c_int, 2014),
        \\};
        \\const struct_unnamed_4 = extern struct {
        \\    x: c_int,
        \\    y: c_int,
        \\};
        \\pub export var s2: struct_unnamed_4 = struct_unnamed_4{
        \\    .x = @as(c_int, 1),
        \\    .y = @as(c_int, 2),
        \\};
        \\pub export var s3: foo = foo{
        \\    .x = @as(c_int, 123),
        \\};
    });

    cases.add("simple ptrCast for casts between opaque types",
        \\struct opaque;
        \\struct opaque_2;
        \\void function(struct opaque *opaque) {
        \\    struct opaque_2 *cast = (struct opaque_2 *)opaque;
        \\}
    , &[_][]const u8{
        \\pub const struct_opaque = opaque {};
        \\pub const struct_opaque_2 = opaque {};
        \\pub export fn function(arg_opaque_1: ?*struct_opaque) void {
        \\    var opaque_1 = arg_opaque_1;
        \\    var cast: ?*struct_opaque_2 = @ptrCast(?*struct_opaque_2, opaque_1);
        \\}
    });

    cases.add("struct initializer - packed",
        \\struct {int x,y,z;} __attribute__((packed)) s0 = {1, 2};
    , &[_][]const u8{
        \\const struct_unnamed_1 = packed struct {
        \\    x: c_int,
        \\    y: c_int,
        \\    z: c_int,
        \\};
        \\pub export var s0: struct_unnamed_1 = struct_unnamed_1{
        \\    .x = @as(c_int, 1),
        \\    .y = @as(c_int, 2),
        \\    .z = 0,
        \\};
    });

    cases.add("align() attribute",
        \\__attribute__ ((aligned(128)))
        \\extern char my_array[16];
        \\__attribute__ ((aligned(128)))
        \\void my_fn(void) { }
    , &[_][]const u8{
        \\pub extern var my_array: [16]u8 align(128);
        \\pub export fn my_fn() align(128) void {}
    });

    cases.add("linksection() attribute",
        \\// Use the "segment,section" format to make this test pass when
        \\// targeting the mach-o binary format
        \\__attribute__ ((__section__("NEAR,.data")))
        \\extern char my_array[16];
        \\__attribute__ ((__section__("NEAR,.data")))
        \\void my_fn(void) { }
    , &[_][]const u8{
        \\pub extern var my_array: [16]u8 linksection("NEAR,.data");
        \\pub export fn my_fn() linksection("NEAR,.data") void {}
    });

    cases.add("simple function prototypes",
        \\void __attribute__((noreturn)) foo(void);
        \\int bar(void);
    , &[_][]const u8{
        \\pub extern fn foo() noreturn;
        \\pub extern fn bar() c_int;
    });

    cases.add("simple var decls",
        \\void foo(void) {
        \\    int a;
        \\    char b = 123;
        \\    const int c;
        \\    const unsigned d = 440;
        \\    int e = 10;
        \\    unsigned int f = 10u;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    var a: c_int = undefined;
        \\    var b: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 123)));
        \\    const c: c_int = undefined;
        \\    const d: c_uint = @bitCast(c_uint, @as(c_int, 440));
        \\    var e: c_int = 10;
        \\    var f: c_uint = 10;
        \\}
    });

    cases.add("ignore result, explicit function arguments",
        \\void foo(void) {
        \\    int a;
        \\    1;
        \\    "hey";
        \\    1 + 1;
        \\    1 - 1;
        \\    a = 1;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    var a: c_int = undefined;
        \\    _ = @as(c_int, 1);
        \\    _ = "hey";
        \\    _ = (@as(c_int, 1) + @as(c_int, 1));
        \\    _ = (@as(c_int, 1) - @as(c_int, 1));
        \\    a = 1;
        \\}
    });

    cases.add("function with no prototype",
        \\int foo() {
        \\    return 5;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() c_int {
        \\    return 5;
        \\}
    });

    cases.add("variables",
        \\extern int extern_var;
        \\static const int int_var = 13;
        \\int foo;
    , &[_][]const u8{
        \\pub extern var extern_var: c_int;
        \\pub const int_var: c_int = 13;
        \\pub export var foo: c_int = @import("std").mem.zeroes(c_int);
    });

    cases.add("const ptr initializer",
        \\static const char *v0 = "0.0.0";
    , &[_][]const u8{
        \\pub var v0: [*c]const u8 = "0.0.0";
    });

    cases.add("static incomplete array inside function",
        \\void foo(void) {
        \\    static const char v2[] = "2.2.2";
        \\}
    , &[_][]const u8{
        \\const v2: [*c]const u8 = "2.2.2";
        \\pub export fn foo() void {
        \\    _ = v2;
        \\}
    });

    cases.add("simple function definition",
        \\void foo(void) {}
        \\static void bar(void) {}
    , &[_][]const u8{
        \\pub export fn foo() void {}
        \\pub fn bar() callconv(.C) void {}
    });

    cases.add("typedef void",
        \\typedef void Foo;
        \\Foo fun(Foo *a);
    , &[_][]const u8{
        \\pub const Foo = c_void;
        ,
        \\pub extern fn fun(a: ?*Foo) Foo;
    });

    cases.add("duplicate typedef",
        \\typedef long foo;
        \\typedef int bar;
        \\typedef long foo;
        \\typedef int baz;
    , &[_][]const u8{
        \\pub const foo = c_long;
        \\pub const bar = c_int;
        \\pub const baz = c_int;
    });

    cases.add("casting pointers to ints and ints to pointers",
        \\void foo(void);
        \\void bar(void) {
        \\    void *func_ptr = foo;
        \\    void (*typed_func_ptr)(void) = (void (*)(void)) (unsigned long) func_ptr;
        \\}
    , &[_][]const u8{
        \\pub extern fn foo() void;
        \\pub export fn bar() void {
        \\    var func_ptr: ?*c_void = @ptrCast(?*c_void, foo);
        \\    var typed_func_ptr: ?fn () callconv(.C) void = @intToPtr(?fn () callconv(.C) void, @intCast(c_ulong, @ptrToInt(func_ptr)));
        \\}
    });

    cases.add("noreturn attribute",
        \\void foo(void) __attribute__((noreturn));
    , &[_][]const u8{
        \\pub extern fn foo() noreturn;
    });

    cases.add("add, sub, mul, div, rem",
        \\int s() {
        \\    int a, b, c;
        \\    c = a + b;
        \\    c = a - b;
        \\    c = a * b;
        \\    c = a / b;
        \\    c = a % b;
        \\}
        \\unsigned u() {
        \\    unsigned a, b, c;
        \\    c = a + b;
        \\    c = a - b;
        \\    c = a * b;
        \\    c = a / b;
        \\    c = a % b;
        \\}
    , &[_][]const u8{
        \\pub export fn s() c_int {
        \\    var a: c_int = undefined;
        \\    var b: c_int = undefined;
        \\    var c: c_int = undefined;
        \\    c = (a + b);
        \\    c = (a - b);
        \\    c = (a * b);
        \\    c = @divTrunc(a, b);
        \\    c = @rem(a, b);
        \\    return 0;
        \\}
        \\pub export fn u() c_uint {
        \\    var a: c_uint = undefined;
        \\    var b: c_uint = undefined;
        \\    var c: c_uint = undefined;
        \\    c = (a +% b);
        \\    c = (a -% b);
        \\    c = (a *% b);
        \\    c = (a / b);
        \\    c = (a % b);
        \\    return 0;
        \\}
    });

    cases.add("typedef of function in struct field",
        \\typedef void lws_callback_function(void);
        \\struct Foo {
        \\    void (*func)(void);
        \\    lws_callback_function *callback_http;
        \\};
    , &[_][]const u8{
        \\pub const lws_callback_function = fn () callconv(.C) void;
        \\pub const struct_Foo = extern struct {
        \\    func: ?fn () callconv(.C) void,
        \\    callback_http: ?lws_callback_function,
        \\};
    });

    cases.add("pointer to struct demoted to opaque due to bit fields",
        \\struct Foo {
        \\    unsigned int: 1;
        \\};
        \\struct Bar {
        \\    struct Foo *foo;
        \\};
    , &[_][]const u8{
        \\pub const struct_Foo = opaque {};
        ,
        \\pub const struct_Bar = extern struct {
        \\    foo: ?*struct_Foo,
        \\};
    });

    cases.add("macro with left shift",
        \\#define REDISMODULE_READ (1<<0)
    , &[_][]const u8{
        \\pub const REDISMODULE_READ = 1 << 0;
    });

    cases.add("macro with right shift",
        \\#define FLASH_SIZE         0x200000UL          /* 2 MB   */
        \\#define FLASH_BANK_SIZE    (FLASH_SIZE >> 1)   /* 1 MB   */
    , &[_][]const u8{
        \\pub const FLASH_SIZE = @as(c_ulong, 0x200000);
        ,
        \\pub const FLASH_BANK_SIZE = FLASH_SIZE >> 1;
    });

    cases.add("double define struct",
        \\typedef struct Bar Bar;
        \\typedef struct Foo Foo;
        \\
        \\struct Foo {
        \\    Foo *a;
        \\};
        \\
        \\struct Bar {
        \\    Foo *a;
        \\};
    , &[_][]const u8{
        \\pub const struct_Foo = extern struct {
        \\    a: [*c]Foo,
        \\};
        ,
        \\pub const Foo = struct_Foo;
        ,
        \\pub const struct_Bar = extern struct {
        \\    a: [*c]Foo,
        \\};
        ,
        \\pub const Bar = struct_Bar;
    });

    cases.add("simple struct",
        \\struct Foo {
        \\    int x;
        \\    char *y;
        \\};
    , &[_][]const u8{
        \\const struct_Foo = extern struct {
        \\    x: c_int,
        \\    y: [*c]u8,
        \\};
        ,
        \\pub const Foo = struct_Foo;
    });

    cases.add("self referential struct with function pointer",
        \\struct Foo {
        \\    void (*derp)(struct Foo *foo);
        \\};
    , &[_][]const u8{
        \\pub const struct_Foo = extern struct {
        \\    derp: ?fn ([*c]struct_Foo) callconv(.C) void,
        \\};
        ,
        \\pub const Foo = struct_Foo;
    });

    cases.add("struct prototype used in func",
        \\struct Foo;
        \\struct Foo *some_func(struct Foo *foo, int x);
    , &[_][]const u8{
        \\pub const struct_Foo = opaque {};
        ,
        \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo;
        ,
        \\pub const Foo = struct_Foo;
    });

    cases.add("#define an unsigned integer literal",
        \\#define CHANNEL_COUNT 24
    , &[_][]const u8{
        \\pub const CHANNEL_COUNT = 24;
    });

    cases.add("#define referencing another #define",
        \\#define THING2 THING1
        \\#define THING1 1234
    , &[_][]const u8{
        \\pub const THING1 = 1234;
        ,
        \\pub const THING2 = THING1;
    });

    cases.add("circular struct definitions",
        \\struct Bar;
        \\
        \\struct Foo {
        \\    struct Bar *next;
        \\};
        \\
        \\struct Bar {
        \\    struct Foo *next;
        \\};
    , &[_][]const u8{
        \\pub const struct_Bar = extern struct {
        \\    next: [*c]struct_Foo,
        \\};
        ,
        \\pub const struct_Foo = extern struct {
        \\    next: [*c]struct_Bar,
        \\};
    });

    cases.add("#define string",
        \\#define  foo  "a string"
    , &[_][]const u8{
        \\pub const foo = "a string";
    });

    cases.add("zig keywords in C code",
        \\struct comptime {
        \\    int defer;
        \\};
    , &[_][]const u8{
        \\pub const struct_comptime = extern struct {
        \\    @"defer": c_int,
        \\};
        ,
        \\pub const @"comptime" = struct_comptime;
    });

    cases.add("macro with parens around negative number",
        \\#define LUA_GLOBALSINDEX        (-10002)
    , &[_][]const u8{
        \\pub const LUA_GLOBALSINDEX = -10002;
    });

    cases.add(
        "u integer suffix after 0 (zero) in macro definition",
        "#define ZERO 0U",
        &[_][]const u8{
            "pub const ZERO = @as(c_uint, 0);",
        },
    );

    cases.add(
        "l integer suffix after 0 (zero) in macro definition",
        "#define ZERO 0L",
        &[_][]const u8{
            "pub const ZERO = @as(c_long, 0);",
        },
    );

    cases.add(
        "ul integer suffix after 0 (zero) in macro definition",
        "#define ZERO 0UL",
        &[_][]const u8{
            "pub const ZERO = @as(c_ulong, 0);",
        },
    );

    cases.add(
        "lu integer suffix after 0 (zero) in macro definition",
        "#define ZERO 0LU",
        &[_][]const u8{
            "pub const ZERO = @as(c_ulong, 0);",
        },
    );

    cases.add(
        "ll integer suffix after 0 (zero) in macro definition",
        "#define ZERO 0LL",
        &[_][]const u8{
            "pub const ZERO = @as(c_longlong, 0);",
        },
    );

    cases.add(
        "ull integer suffix after 0 (zero) in macro definition",
        "#define ZERO 0ULL",
        &[_][]const u8{
            "pub const ZERO = @as(c_ulonglong, 0);",
        },
    );

    cases.add(
        "llu integer suffix after 0 (zero) in macro definition",
        "#define ZERO 0LLU",
        &[_][]const u8{
            "pub const ZERO = @as(c_ulonglong, 0);",
        },
    );

    cases.add(
        "bitwise not on u-suffixed 0 (zero) in macro definition",
        "#define NOT_ZERO (~0U)",
        &[_][]const u8{
            "pub const NOT_ZERO = ~@as(c_uint, 0);",
        },
    );

    cases.add("float suffixes",
        \\#define foo 3.14f
        \\#define bar 16.e-2l
        \\#define FOO 0.12345
        \\#define BAR .12345
    , &[_][]const u8{
        "pub const foo = @as(f32, 3.14);",
        "pub const bar = @as(c_longdouble, 16.e-2);",
        "pub const FOO = 0.12345;",
        "pub const BAR = 0.12345;",
    });

    cases.add("comments",
        \\#define foo 1 //foo
        \\#define bar /* bar */ 2
    , &[_][]const u8{
        "pub const foo = 1;",
        "pub const bar = 2;",
    });

    cases.add("string prefix",
        \\#define foo L"hello"
    , &[_][]const u8{
        "pub const foo = \"hello\";",
    });

    cases.add("null statements",
        \\void foo(void) {
        \\    ;;;;;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    {}
        \\    {}
        \\    {}
        \\    {}
        \\    {}
        \\}
    });

    if (std.Target.current.os.tag != .windows) {
        // Windows treats this as an enum with type c_int
        cases.add("big negative enum init values when C ABI supports long long enums",
            \\enum EnumWithInits {
            \\    VAL01 = 0,
            \\    VAL02 = 1,
            \\    VAL03 = 2,
            \\    VAL04 = 3,
            \\    VAL05 = -1,
            \\    VAL06 = -2,
            \\    VAL07 = -3,
            \\    VAL08 = -4,
            \\    VAL09 = VAL02 + VAL08,
            \\    VAL10 = -1000012000,
            \\    VAL11 = -1000161000,
            \\    VAL12 = -1000174001,
            \\    VAL13 = VAL09,
            \\    VAL14 = VAL10,
            \\    VAL15 = VAL11,
            \\    VAL16 = VAL13,
            \\    VAL17 = (VAL16 - VAL10 + 1),
            \\    VAL18 = 0x1000000000000000L,
            \\    VAL19 = VAL18 + VAL18 + VAL18 - 1,
            \\    VAL20 = VAL19 + VAL19,
            \\    VAL21 = VAL20 + 0xFFFFFFFFFFFFFFFF,
            \\    VAL22 = 0xFFFFFFFFFFFFFFFF + 1,
            \\    VAL23 = 0xFFFFFFFFFFFFFFFF,
            \\};
        , &[_][]const u8{
            \\pub const enum_EnumWithInits = extern enum(c_longlong) {
            \\    VAL01 = 0,
            \\    VAL02 = 1,
            \\    VAL03 = 2,
            \\    VAL04 = 3,
            \\    VAL05 = -1,
            \\    VAL06 = -2,
            \\    VAL07 = -3,
            \\    VAL08 = -4,
            \\    VAL09 = -3,
            \\    VAL10 = -1000012000,
            \\    VAL11 = -1000161000,
            \\    VAL12 = -1000174001,
            \\    VAL13 = -3,
            \\    VAL14 = -1000012000,
            \\    VAL15 = -1000161000,
            \\    VAL16 = -3,
            \\    VAL17 = 1000011998,
            \\    VAL18 = 1152921504606846976,
            \\    VAL19 = 3458764513820540927,
            \\    VAL20 = 6917529027641081854,
            \\    VAL21 = 6917529027641081853,
            \\    VAL22 = 0,
            \\    VAL23 = -1,
            \\    _,
            \\};
        });
    }

    cases.add("predefined expressions",
        \\void foo(void) {
        \\    __func__;
        \\    __FUNCTION__;
        \\    __PRETTY_FUNCTION__;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    _ = "foo";
        \\    _ = "foo";
        \\    _ = "void foo(void)";
        \\}
    });

    cases.add("constant size array",
        \\void func(int array[20]);
    , &[_][]const u8{
        \\pub extern fn func(array: [*c]c_int) void;
    });

    cases.add("__cdecl doesn't mess up function pointers",
        \\void foo(void (__cdecl *fn_ptr)(void));
    , &[_][]const u8{
        \\pub extern fn foo(fn_ptr: ?fn () callconv(.C) void) void;
    });

    cases.add("void cast",
        \\void foo() {
        \\    int a;
        \\    (void) a;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    var a: c_int = undefined;
        \\    _ = a;
        \\}
    });

    cases.add("implicit cast to void *",
        \\void *foo() {
        \\    unsigned short *x;
        \\    return x;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() ?*c_void {
        \\    var x: [*c]c_ushort = undefined;
        \\    return @ptrCast(?*c_void, x);
        \\}
    });

    cases.add("null pointer implicit cast",
        \\int* foo(void) {
        \\    return 0;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() [*c]c_int {
        \\    return null;
        \\}
    });

    cases.add("simple union",
        \\union Foo {
        \\    int x;
        \\    double y;
        \\};
    , &[_][]const u8{
        \\pub const union_Foo = extern union {
        \\    x: c_int,
        \\    y: f64,
        \\};
        ,
        \\pub const Foo = union_Foo;
    });

    cases.add("string literal",
        \\const char *foo(void) {
        \\    return "bar";
        \\}
    , &[_][]const u8{
        \\pub export fn foo() [*c]const u8 {
        \\    return "bar";
        \\}
    });

    cases.add("return void",
        \\void foo(void) {
        \\    return;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    return;
        \\}
    });

    cases.add("for loop",
        \\void foo(void) {
        \\    for (int i = 0; i; i++) { }
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    {
        \\        var i: c_int = 0;
        \\        while (i != 0) : (i += 1) {}
        \\    }
        \\}
    });

    cases.add("empty for loop",
        \\void foo(void) {
        \\    for (;;) { }
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    while (true) {}
        \\}
    });

    cases.add("for loop with simple init expression",
        \\void foo(void) {
        \\    int i;
        \\    for (i = 3; i; i--) { }
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    var i: c_int = undefined;
        \\    {
        \\        i = 3;
        \\        while (i != 0) : (i -= 1) {}
        \\    }
        \\}
    });

    cases.add("break statement",
        \\void foo(void) {
        \\    for (;;) {
        \\        break;
        \\    }
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    while (true) {
        \\        break;
        \\    }
        \\}
    });

    cases.add("continue statement",
        \\void foo(void) {
        \\    for (;;) {
        \\        continue;
        \\    }
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    while (true) {
        \\        continue;
        \\    }
        \\}
    });

    cases.add("pointer casting",
        \\float *ptrcast() {
        \\    int *a;
        \\    return (float *)a;
        \\}
    , &[_][]const u8{
        \\pub export fn ptrcast() [*c]f32 {
        \\    var a: [*c]c_int = undefined;
        \\    return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a));
        \\}
    });

    cases.add("pointer conversion with different alignment",
        \\void test_ptr_cast() {
        \\    void *p;
        \\    {
        \\        char *to_char = (char *)p;
        \\        short *to_short = (short *)p;
        \\        int *to_int = (int *)p;
        \\        long long *to_longlong = (long long *)p;
        \\    }
        \\    {
        \\        char *to_char = p;
        \\        short *to_short = p;
        \\        int *to_int = p;
        \\        long long *to_longlong = p;
        \\    }
        \\}
    , &[_][]const u8{
        \\pub export fn test_ptr_cast() void {
        \\    var p: ?*c_void = undefined;
        \\    {
        \\        var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
        \\        var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
        \\        var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
        \\        var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
        \\    }
        \\    {
        \\        var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
        \\        var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
        \\        var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
        \\        var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
        \\    }
        \\}
    });

    cases.add("while on non-bool",
        \\int while_none_bool() {
        \\    int a;
        \\    float b;
        \\    void *c;
        \\    while (a) return 0;
        \\    while (b) return 1;
        \\    while (c) return 2;
        \\    return 3;
        \\}
    , &[_][]const u8{
        \\pub export fn while_none_bool() c_int {
        \\    var a: c_int = undefined;
        \\    var b: f32 = undefined;
        \\    var c: ?*c_void = undefined;
        \\    while (a != 0) return 0;
        \\    while (b != 0) return 1;
        \\    while (c != null) return 2;
        \\    return 3;
        \\}
    });

    cases.add("for on non-bool",
        \\int for_none_bool() {
        \\    int a;
        \\    float b;
        \\    void *c;
        \\    for (;a;) return 0;
        \\    for (;b;) return 1;
        \\    for (;c;) return 2;
        \\    return 3;
        \\}
    , &[_][]const u8{
        \\pub export fn for_none_bool() c_int {
        \\    var a: c_int = undefined;
        \\    var b: f32 = undefined;
        \\    var c: ?*c_void = undefined;
        \\    while (a != 0) return 0;
        \\    while (b != 0) return 1;
        \\    while (c != null) return 2;
        \\    return 3;
        \\}
    });

    cases.add("bitshift",
        \\int foo(void) {
        \\    return (1 << 2) >> 1;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() c_int {
        \\    return (@as(c_int, 1) << @intCast(@import("std").math.Log2Int(c_int), 2)) >> @intCast(@import("std").math.Log2Int(c_int), 1);
        \\}
    });

    cases.add("sizeof",
        \\#include <stddef.h>
        \\size_t size_of(void) {
        \\        return sizeof(int);
        \\}
    , &[_][]const u8{
        \\pub export fn size_of() usize {
        \\    return @sizeOf(c_int);
        \\}
    });

    cases.add("normal deref",
        \\void foo() {
        \\    int *x;
        \\    *x = 1;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    var x: [*c]c_int = undefined;
        \\    x.?.* = 1;
        \\}
    });

    cases.add("address of operator",
        \\int foo(void) {
        \\    int x = 1234;
        \\    int *ptr = &x;
        \\    return *ptr;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() c_int {
        \\    var x: c_int = 1234;
        \\    var ptr: [*c]c_int = &x;
        \\    return ptr.?.*;
        \\}
    });

    cases.add("bin not",
        \\int foo() {
        \\    int x;
        \\    return ~x;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() c_int {
        \\    var x: c_int = undefined;
        \\    return ~x;
        \\}
    });

    cases.add("bool not",
        \\int foo() {
        \\    int a;
        \\    float b;
        \\    void *c;
        \\    return !(a == 0);
        \\    return !a;
        \\    return !b;
        \\    return !c;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() c_int {
        \\    var a: c_int = undefined;
        \\    var b: f32 = undefined;
        \\    var c: ?*c_void = undefined;
        \\    return !(a == @as(c_int, 0));
        \\    return !(a != 0);
        \\    return !(b != 0);
        \\    return !(c != null);
        \\}
    });

    cases.add("__extension__ cast",
        \\int foo(void) {
        \\    return __extension__ 1;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() c_int {
        \\    return 1;
        \\}
    });

    if (std.Target.current.os.tag != .windows) {
        // sysv_abi not currently supported on windows
        cases.add("Macro qualified functions",
            \\void __attribute__((sysv_abi)) foo(void);
        , &[_][]const u8{
            \\pub extern fn foo() void;
        });
    }

    cases.add("Forward-declared enum",
        \\extern enum enum_ty my_enum;
        \\enum enum_ty { FOO };
    , &[_][]const u8{
        \\pub const FOO = @enumToInt(enum_enum_ty.FOO);
        \\pub const enum_enum_ty = extern enum(c_int) {
        \\    FOO,
        \\    _,
        \\};
        \\pub extern var my_enum: enum_enum_ty;
    });

    cases.add("Parameterless function pointers",
        \\typedef void (*fn0)();
        \\typedef void (*fn1)(char);
    , &[_][]const u8{
        \\pub const fn0 = ?fn (...) callconv(.C) void;
        \\pub const fn1 = ?fn (u8) callconv(.C) void;
    });

    cases.addWithTarget("Calling convention", .{
        .cpu_arch = .i386,
        .os_tag = .linux,
        .abi = .none,
    },
        \\void __attribute__((fastcall)) foo1(float *a);
        \\void __attribute__((stdcall)) foo2(float *a);
        \\void __attribute__((vectorcall)) foo3(float *a);
        \\void __attribute__((cdecl)) foo4(float *a);
        \\void __attribute__((thiscall)) foo5(float *a);
    , &[_][]const u8{
        \\pub extern fn foo1(a: [*c]f32) callconv(.Fastcall) void;
        \\pub extern fn foo2(a: [*c]f32) callconv(.Stdcall) void;
        \\pub extern fn foo3(a: [*c]f32) callconv(.Vectorcall) void;
        \\pub extern fn foo4(a: [*c]f32) void;
        \\pub extern fn foo5(a: [*c]f32) callconv(.Thiscall) void;
    });

    cases.addWithTarget("Calling convention", CrossTarget.parse(.{
        .arch_os_abi = "arm-linux-none",
        .cpu_features = "generic+v8_5a",
    }) catch unreachable,
        \\void __attribute__((pcs("aapcs"))) foo1(float *a);
        \\void __attribute__((pcs("aapcs-vfp"))) foo2(float *a);
    , &[_][]const u8{
        \\pub extern fn foo1(a: [*c]f32) callconv(.AAPCS) void;
        \\pub extern fn foo2(a: [*c]f32) callconv(.AAPCSVFP) void;
    });

    cases.addWithTarget("Calling convention", CrossTarget.parse(.{
        .arch_os_abi = "aarch64-linux-none",
        .cpu_features = "generic+v8_5a",
    }) catch unreachable,
        \\void __attribute__((aarch64_vector_pcs)) foo1(float *a);
    , &[_][]const u8{
        \\pub extern fn foo1(a: [*c]f32) callconv(.Vectorcall) void;
    });

    cases.add("Parameterless function prototypes",
        \\void a() {}
        \\void b(void) {}
        \\void c();
        \\void d(void);
    , &[_][]const u8{
        \\pub export fn a() void {}
        \\pub export fn b() void {}
        \\pub extern fn c(...) void;
        \\pub extern fn d() void;
    });

    cases.add("variable declarations",
        \\extern char arr0[] = "hello";
        \\static char arr1[] = "hello";
        \\char arr2[] = "hello";
    , &[_][]const u8{
        \\pub export var arr0: [*c]u8 = "hello";
        \\pub var arr1: [*c]u8 = "hello";
        \\pub export var arr2: [*c]u8 = "hello";
    });

    cases.add("array initializer expr",
        \\static void foo(void){
        \\    char arr[10] ={1};
        \\    char *arr1[10] ={0};
        \\}
    , &[_][]const u8{
        \\pub fn foo() callconv(.C) void {
        \\    var arr: [10]u8 = [1]u8{
        \\        @bitCast(u8, @truncate(i8, @as(c_int, 1))),
        \\    } ++ [1]u8{0} ** 9;
        \\    var arr1: [10][*c]u8 = [1][*c]u8{
        \\        null,
        \\    } ++ [1][*c]u8{null} ** 9;
        \\}
    });

    cases.add("enums",
        \\typedef enum {
        \\    a,
        \\    b,
        \\    c,
        \\} d;
        \\enum {
        \\    e,
        \\    f = 4,
        \\    g,
        \\} h = e;
        \\struct Baz {
        \\    enum {
        \\        i,
        \\        j,
        \\        k,
        \\    } l;
        \\    d m;
        \\};
        \\enum i {
        \\    n,
        \\    o,
        \\    p,
        \\};
    , &[_][]const u8{
        \\pub const a = @enumToInt(enum_unnamed_1.a);
        \\pub const b = @enumToInt(enum_unnamed_1.b);
        \\pub const c = @enumToInt(enum_unnamed_1.c);
        \\const enum_unnamed_1 = extern enum(c_int) {
        \\    a,
        \\    b,
        \\    c,
        \\    _,
        \\};
        \\pub const d = enum_unnamed_1;
        \\pub const e = @enumToInt(enum_unnamed_2.e);
        \\pub const f = @enumToInt(enum_unnamed_2.f);
        \\pub const g = @enumToInt(enum_unnamed_2.g);
        \\const enum_unnamed_2 = extern enum(c_int) {
        \\    e = 0,
        \\    f = 4,
        \\    g = 5,
        \\    _,
        \\};
        \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e);
        \\pub const i = @enumToInt(enum_unnamed_3.i);
        \\pub const j = @enumToInt(enum_unnamed_3.j);
        \\pub const k = @enumToInt(enum_unnamed_3.k);
        \\const enum_unnamed_3 = extern enum(c_int) {
        \\    i,
        \\    j,
        \\    k,
        \\    _,
        \\};
        \\pub const struct_Baz = extern struct {
        \\    l: enum_unnamed_3,
        \\    m: d,
        \\};
        \\pub const n = @enumToInt(enum_i.n);
        \\pub const o = @enumToInt(enum_i.o);
        \\pub const p = @enumToInt(enum_i.p);
        \\pub const enum_i = extern enum(c_int) {
        \\    n,
        \\    o,
        \\    p,
        \\    _,
        \\};
        ,
        \\pub const Baz = struct_Baz;
    });

    cases.add("#define a char literal",
        \\#define A_CHAR  'a'
    , &[_][]const u8{
        \\pub const A_CHAR = 'a';
    });

    cases.add("comment after integer literal",
        \\#define SDL_INIT_VIDEO 0x00000020  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
    , &[_][]const u8{
        \\pub const SDL_INIT_VIDEO = 0x00000020;
    });

    cases.add("u integer suffix after hex literal",
        \\#define SDL_INIT_VIDEO 0x00000020u  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
    , &[_][]const u8{
        \\pub const SDL_INIT_VIDEO = @as(c_uint, 0x00000020);
    });

    cases.add("l integer suffix after hex literal",
        \\#define SDL_INIT_VIDEO 0x00000020l  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
    , &[_][]const u8{
        \\pub const SDL_INIT_VIDEO = @as(c_long, 0x00000020);
    });

    cases.add("ul integer suffix after hex literal",
        \\#define SDL_INIT_VIDEO 0x00000020ul  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
    , &[_][]const u8{
        \\pub const SDL_INIT_VIDEO = @as(c_ulong, 0x00000020);
    });

    cases.add("lu integer suffix after hex literal",
        \\#define SDL_INIT_VIDEO 0x00000020lu  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
    , &[_][]const u8{
        \\pub const SDL_INIT_VIDEO = @as(c_ulong, 0x00000020);
    });

    cases.add("ll integer suffix after hex literal",
        \\#define SDL_INIT_VIDEO 0x00000020ll  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
    , &[_][]const u8{
        \\pub const SDL_INIT_VIDEO = @as(c_longlong, 0x00000020);
    });

    cases.add("ull integer suffix after hex literal",
        \\#define SDL_INIT_VIDEO 0x00000020ull  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
    , &[_][]const u8{
        \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020);
    });

    cases.add("llu integer suffix after hex literal",
        \\#define SDL_INIT_VIDEO 0x00000020llu  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
    , &[_][]const u8{
        \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020);
    });

    cases.add("generate inline func for #define global extern fn",
        \\extern void (*fn_ptr)(void);
        \\#define foo fn_ptr
        \\
        \\extern char (*fn_ptr2)(int, float);
        \\#define bar fn_ptr2
    , &[_][]const u8{
        \\pub extern var fn_ptr: ?fn () callconv(.C) void;
        ,
        \\pub inline fn foo() void {
        \\    return fn_ptr.?();
        \\}
        ,
        \\pub extern var fn_ptr2: ?fn (c_int, f32) callconv(.C) u8;
        ,
        \\pub inline fn bar(arg_1: c_int, arg_2: f32) u8 {
        \\    return fn_ptr2.?(arg_1, arg_2);
        \\}
    });

    cases.add("macros with field targets",
        \\typedef unsigned int GLbitfield;
        \\typedef void (*PFNGLCLEARPROC) (GLbitfield mask);
        \\typedef void(*OpenGLProc)(void);
        \\union OpenGLProcs {
        \\    OpenGLProc ptr[1];
        \\    struct {
        \\        PFNGLCLEARPROC Clear;
        \\    } gl;
        \\};
        \\extern union OpenGLProcs glProcs;
        \\#define glClearUnion glProcs.gl.Clear
        \\#define glClearPFN PFNGLCLEARPROC
    , &[_][]const u8{
        \\pub const GLbitfield = c_uint;
        \\pub const PFNGLCLEARPROC = ?fn (GLbitfield) callconv(.C) void;
        \\pub const OpenGLProc = ?fn () callconv(.C) void;
        \\const struct_unnamed_1 = extern struct {
        \\    Clear: PFNGLCLEARPROC,
        \\};
        \\pub const union_OpenGLProcs = extern union {
        \\    ptr: [1]OpenGLProc,
        \\    gl: struct_unnamed_1,
        \\};
        \\pub extern var glProcs: union_OpenGLProcs;
        ,
        \\pub const glClearPFN = PFNGLCLEARPROC;
        ,
        \\pub inline fn glClearUnion(arg_2: GLbitfield) void {
        \\    return glProcs.gl.Clear.?(arg_2);
        \\}
        ,
        \\pub const OpenGLProcs = union_OpenGLProcs;
    });

    cases.add("macro pointer cast",
        \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
    , &[_][]const u8{
        \\pub const NRF_GPIO = (@import("std").meta.cast([*c]NRF_GPIO_Type, NRF_GPIO_BASE));
    });

    cases.add("basic macro function",
        \\extern int c;
        \\#define BASIC(c) (c*2)
        \\#define FOO(L,b) (L + b)
        \\#define BAR() (c*c)
    , &[_][]const u8{
        \\pub extern var c: c_int;
        ,
        \\pub inline fn BASIC(c_1: anytype) @TypeOf(c_1 * 2) {
        \\    return c_1 * 2;
        \\}
        ,
        \\pub inline fn FOO(L: anytype, b: anytype) @TypeOf(L + b) {
        \\    return L + b;
        \\}
        ,
        \\pub inline fn BAR() @TypeOf(c * c) {
        \\    return c * c;
        \\}
    });

    cases.add("macro defines string literal with hex",
        \\#define FOO "aoeu\xab derp"
        \\#define FOO2 "aoeu\x0007a derp"
        \\#define FOO_CHAR '\xfF'
    , &[_][]const u8{
        \\pub const FOO = "aoeu\xab derp";
        ,
        \\pub const FOO2 = "aoeu\x7a derp";
        ,
        \\pub const FOO_CHAR = '\xff';
    });

    cases.add("macro add",
        \\#define PERIPH_BASE               (0x40000000UL) /*!< Base address of : AHB/APB Peripherals                                                   */
        \\#define D3_APB1PERIPH_BASE       (PERIPH_BASE + 0x18000000UL)
        \\#define RCC_BASE              (D3_AHB1PERIPH_BASE + 0x4400UL)
    , &[_][]const u8{
        \\pub const PERIPH_BASE = @as(c_ulong, 0x40000000);
        ,
        \\pub const D3_APB1PERIPH_BASE = PERIPH_BASE + @as(c_ulong, 0x18000000);
        ,
        \\pub const RCC_BASE = D3_AHB1PERIPH_BASE + @as(c_ulong, 0x4400);
    });

    cases.add("variable aliasing",
        \\static long a = 2;
        \\static long b = 2;
        \\static int c = 4;
        \\void foo(char c) {
        \\    int a;
        \\    char b = 123;
        \\    b = (char) a;
        \\    {
        \\        int d = 5;
        \\    }
        \\    unsigned d = 440;
        \\}
    , &[_][]const u8{
        \\pub var a: c_long = @bitCast(c_long, @as(c_long, @as(c_int, 2)));
        \\pub var b: c_long = @bitCast(c_long, @as(c_long, @as(c_int, 2)));
        \\pub var c: c_int = 4;
        \\pub export fn foo(arg_c_1: u8) void {
        \\    var c_1 = arg_c_1;
        \\    var a_2: c_int = undefined;
        \\    var b_3: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 123)));
        \\    b_3 = @bitCast(u8, @truncate(i8, a_2));
        \\    {
        \\        var d: c_int = 5;
        \\    }
        \\    var d: c_uint = @bitCast(c_uint, @as(c_int, 440));
        \\}
    });

    cases.add("comma operator",
        \\int foo() {
        \\    2, 4;
        \\    return 2, 4, 6;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() c_int {
        \\    _ = @as(c_int, 2);
        \\    _ = @as(c_int, 4);
        \\    _ = @as(c_int, 2);
        \\    _ = @as(c_int, 4);
        \\    return @as(c_int, 6);
        \\}
    });

    cases.add("worst-case assign",
        \\void foo() {
        \\    int a;
        \\    int b;
        \\    a = b = 2;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    var a: c_int = undefined;
        \\    var b: c_int = undefined;
        \\    a = blk: {
        \\        const tmp = @as(c_int, 2);
        \\        b = tmp;
        \\        break :blk tmp;
        \\    };
        \\}
    });

    cases.add("while loops",
        \\int foo() {
        \\    int a = 5;
        \\    while (2)
        \\        a = 2;
        \\    while (4) {
        \\        int a = 4;
        \\        a = 9;
        \\        return 6, a;
        \\    }
        \\    do {
        \\        int a = 2;
        \\        a = 12;
        \\    } while (4);
        \\    do
        \\        a = 7;
        \\    while (4);
        \\}
    , &[_][]const u8{
        \\pub export fn foo() c_int {
        \\    var a: c_int = 5;
        \\    while (true) a = 2;
        \\    while (true) {
        \\        var a_1: c_int = 4;
        \\        a_1 = 9;
        \\        _ = @as(c_int, 6);
        \\        return a_1;
        \\    }
        \\    while (true) {
        \\        var a_1: c_int = 2;
        \\        a_1 = 12;
        \\        if (!true) break;
        \\    }
        \\    while (true) {
        \\        a = 7;
        \\        if (!true) break;
        \\    }
        \\    return 0;
        \\}
    });

    cases.add("for loops",
        \\void foo() {
        \\    for (int i = 2, b = 4; i + 2; i = 2) {
        \\        int a = 2;
        \\        a = 6, 5, 7;
        \\    }
        \\    char i = 2;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    {
        \\        var i: c_int = 2;
        \\        var b: c_int = 4;
        \\        while ((i + @as(c_int, 2)) != 0) : (i = 2) {
        \\            var a: c_int = 2;
        \\            a = 6;
        \\            _ = @as(c_int, 5);
        \\            _ = @as(c_int, 7);
        \\        }
        \\    }
        \\    var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 2)));
        \\}
    });

    cases.add("shadowing primitive types",
        \\unsigned anyerror = 2;
        \\#define noreturn _Noreturn
    , &[_][]const u8{
        \\pub export var anyerror_1: c_uint = @bitCast(c_uint, @as(c_int, 2));
        ,
        \\pub const noreturn_2 = @compileError("unable to translate C expr: unexpected token .Keyword_noreturn");
    });

    cases.add("floats",
        \\float a = 3.1415;
        \\double b = 3.1415;
        \\int c = 3.1415;
        \\double d = 3;
    , &[_][]const u8{
        \\pub export var a: f32 = @floatCast(f32, 3.1415);
        \\pub export var b: f64 = 3.1415;
        \\pub export var c: c_int = @floatToInt(c_int, 3.1415);
        \\pub export var d: f64 = @intToFloat(f64, @as(c_int, 3));
    });

    cases.add("conditional operator",
        \\int bar(void) {
        \\    if (2 ? 5 : 5 ? 4 : 6) 2;
        \\    return  2 ? 5 : 5 ? 4 : 6;
        \\}
    , &[_][]const u8{
        \\pub export fn bar() c_int {
        \\    if ((if (true) @as(c_int, 5) else (if (true) @as(c_int, 4) else @as(c_int, 6))) != 0) _ = @as(c_int, 2);
        \\    return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6);
        \\}
    });

    cases.add("switch on int",
        \\void switch_fn(int i) {
        \\    int res = 0;
        \\    switch (i) {
        \\        case 0:
        \\            res = 1;
        \\        case 1 ... 3:
        \\            res = 2;
        \\        default:
        \\            res = 3 * i;
        \\            break;
        \\        case 4:
        \\            res = 5;
        \\    }
        \\}
    , &[_][]const u8{
        \\pub export fn switch_fn(arg_i: c_int) void {
        \\    var i = arg_i;
        \\    var res: c_int = 0;
        \\    @"switch": {
        \\        case_2: {
        \\            default: {
        \\                case_1: {
        \\                    case: {
        \\                        switch (i) {
        \\                            @as(c_int, 0) => break :case,
        \\                            @as(c_int, 1)...@as(c_int, 3) => break :case_1,
        \\                            else => break :default,
        \\                            @as(c_int, 4) => break :case_2,
        \\                        }
        \\                    }
        \\                    res = 1;
        \\                }
        \\                res = 2;
        \\            }
        \\            res = (@as(c_int, 3) * i);
        \\            break :@"switch";
        \\        }
        \\        res = 5;
        \\    }
        \\}
    });

    if (std.Target.current.os.tag != .windows) {
        // When clang uses the <arch>-windows-none triple it behaves as MSVC and
        // interprets the inner `struct Bar` as an anonymous structure
        cases.add("type referenced struct",
            \\struct Foo {
            \\    struct Bar{
            \\        int b;
            \\    };
            \\    struct Bar c;
            \\};
        , &[_][]const u8{
            \\pub const struct_Bar = extern struct {
            \\    b: c_int,
            \\};
            \\pub const struct_Foo = extern struct {
            \\    c: struct_Bar,
            \\};
        });
    }

    cases.add("undefined array global",
        \\int array[100] = {};
    , &[_][]const u8{
        \\pub export var array: [100]c_int = [1]c_int{0} ** 100;
    });

    cases.add("restrict -> noalias",
        \\void foo(void *restrict bar, void *restrict);
    , &[_][]const u8{
        \\pub extern fn foo(noalias bar: ?*c_void, noalias ?*c_void) void;
    });

    cases.add("assign",
        \\void max(int a) {
        \\    int tmp;
        \\    tmp = a;
        \\    a = tmp;
        \\}
    , &[_][]const u8{
        \\pub export fn max(arg_a: c_int) void {
        \\    var a = arg_a;
        \\    var tmp: c_int = undefined;
        \\    tmp = a;
        \\    a = tmp;
        \\}
    });

    cases.add("chaining assign",
        \\void max(int a) {
        \\    int b, c;
        \\    c = b = a;
        \\}
    , &[_][]const u8{
        \\pub export fn max(arg_a: c_int) void {
        \\    var a = arg_a;
        \\    var b: c_int = undefined;
        \\    var c: c_int = undefined;
        \\    c = blk: {
        \\        const tmp = a;
        \\        b = tmp;
        \\        break :blk tmp;
        \\    };
        \\}
    });

    cases.add("anonymous enum",
        \\enum {
        \\    One,
        \\    Two,
        \\};
    , &[_][]const u8{
        \\pub const One = @enumToInt(enum_unnamed_1.One);
        \\pub const Two = @enumToInt(enum_unnamed_1.Two);
        \\const enum_unnamed_1 = extern enum(c_int) {
        \\    One,
        \\    Two,
        \\    _,
        \\};
    });

    cases.add("c style cast",
        \\int float_to_int(float a) {
        \\    return (int)a;
        \\}
    , &[_][]const u8{
        \\pub export fn float_to_int(arg_a: f32) c_int {
        \\    var a = arg_a;
        \\    return @floatToInt(c_int, a);
        \\}
    });

    // TODO translate-c should in theory be able to figure out to drop all these casts
    cases.add("escape sequences",
        \\const char *escapes() {
        \\char a = '\'',
        \\    b = '\\',
        \\    c = '\a',
        \\    d = '\b',
        \\    e = '\f',
        \\    f = '\n',
        \\    g = '\r',
        \\    h = '\t',
        \\    i = '\v',
        \\    j = '\0',
        \\    k = '\"';
        \\    return "\'\\\a\b\f\n\r\t\v\0\"";
        \\}
        \\
    , &[_][]const u8{
        \\pub export fn escapes() [*c]const u8 {
        \\    var a: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\'')));
        \\    var b: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\\')));
        \\    var c: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x07')));
        \\    var d: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x08')));
        \\    var e: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x0c')));
        \\    var f: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\n')));
        \\    var g: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\r')));
        \\    var h: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\t')));
        \\    var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x0b')));
        \\    var j: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x00')));
        \\    var k: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\"')));
        \\    return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\"";
        \\}
    });

    cases.add("do loop",
        \\void foo(void) {
        \\    int a = 2;
        \\    do {
        \\        a = a - 1;
        \\    } while (a);
        \\
        \\    int b = 2;
        \\    do
        \\        b = b -1;
        \\    while (b);
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    var a: c_int = 2;
        \\    while (true) {
        \\        a = (a - @as(c_int, 1));
        \\        if (!(a != 0)) break;
        \\    }
        \\    var b: c_int = 2;
        \\    while (true) {
        \\        b = (b - @as(c_int, 1));
        \\        if (!(b != 0)) break;
        \\    }
        \\}
    });

    cases.add("logical and, logical or, on non-bool values, extra parens",
        \\enum Foo {
        \\    FooA,
        \\    FooB,
        \\    FooC,
        \\};
        \\typedef int SomeTypedef;
        \\int and_or_non_bool(int a, float b, void *c) {
        \\    enum Foo d = FooA;
        \\    int e = (a && b);
        \\    int f = (b && c);
        \\    int g = (a && c);
        \\    int h = (a || b);
        \\    int i = (b || c);
        \\    int j = (a || c);
        \\    int k = (a || d);
        \\    int l = (d && b);
        \\    int m = (c || d);
        \\    SomeTypedef td = 44;
        \\    int o = (td || b);
        \\    int p = (c && td);
        \\    return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
        \\}
    , &[_][]const u8{
        \\pub const enum_Foo = extern enum(c_int) {
        \\    A,
        \\    B,
        \\    C,
        \\    _,
        \\};
        \\pub const SomeTypedef = c_int;
        \\pub export fn and_or_non_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void) c_int {
        \\    var a = arg_a;
        \\    var b = arg_b;
        \\    var c = arg_c;
        \\    var d: enum_Foo = @intToEnum(enum_Foo, FooA);
        \\    var e: c_int = @boolToInt(((a != 0) and (b != 0)));
        \\    var f: c_int = @boolToInt(((b != 0) and (c != null)));
        \\    var g: c_int = @boolToInt(((a != 0) and (c != null)));
        \\    var h: c_int = @boolToInt(((a != 0) or (b != 0)));
        \\    var i: c_int = @boolToInt(((b != 0) or (c != null)));
        \\    var j: c_int = @boolToInt(((a != 0) or (c != null)));
        \\    var k: c_int = @boolToInt(((a != 0) or (@enumToInt(d) != 0)));
        \\    var l: c_int = @boolToInt(((@enumToInt(d) != 0) and (b != 0)));
        \\    var m: c_int = @boolToInt(((c != null) or (@enumToInt(d) != 0)));
        \\    var td: SomeTypedef = 44;
        \\    var o: c_int = @boolToInt(((td != 0) or (b != 0)));
        \\    var p: c_int = @boolToInt(((c != null) and (td != 0)));
        \\    return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
        \\}
        ,
        \\pub const Foo = enum_Foo;
    });

    cases.add("qualified struct and enum",
        \\struct Foo {
        \\    int x;
        \\    int y;
        \\};
        \\enum Bar {
        \\    BarA,
        \\    BarB,
        \\};
        \\void func(struct Foo *a, enum Bar **b);
    , &[_][]const u8{
        \\pub const struct_Foo = extern struct {
        \\    x: c_int,
        \\    y: c_int,
        \\};
        ,
        \\pub const enum_Bar = extern enum(c_int) {
        \\    A,
        \\    B,
        \\    _,
        \\};
        \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
        ,
        \\pub const Foo = struct_Foo;
        \\pub const Bar = enum_Bar;
    });

    cases.add("bitwise binary operators, simpler parens",
        \\int max(int a, int b) {
        \\    return (a & b) ^ (a | b);
        \\}
    , &[_][]const u8{
        \\pub export fn max(arg_a: c_int, arg_b: c_int) c_int {
        \\    var a = arg_a;
        \\    var b = arg_b;
        \\    return ((a & b) ^ (a | b));
        \\}
    });

    cases.add("comparison operators (no if)", // TODO Come up with less contrived tests? Make sure to cover all these comparisons.
        \\int test_comparisons(int a, int b) {
        \\    int c = (a < b);
        \\    int d = (a > b);
        \\    int e = (a <= b);
        \\    int f = (a >= b);
        \\    int g = (c < d);
        \\    int h = (e < f);
        \\    int i = (g < h);
        \\    return i;
        \\}
    , &[_][]const u8{
        \\pub export fn test_comparisons(arg_a: c_int, arg_b: c_int) c_int {
        \\    var a = arg_a;
        \\    var b = arg_b;
        \\    var c: c_int = @boolToInt((a < b));
        \\    var d: c_int = @boolToInt((a > b));
        \\    var e: c_int = @boolToInt((a <= b));
        \\    var f: c_int = @boolToInt((a >= b));
        \\    var g: c_int = @boolToInt((c < d));
        \\    var h: c_int = @boolToInt((e < f));
        \\    var i: c_int = @boolToInt((g < h));
        \\    return i;
        \\}
    });

    cases.add("==, !=",
        \\int max(int a, int b) {
        \\    if (a == b)
        \\        return a;
        \\    if (a != b)
        \\        return b;
        \\    return a;
        \\}
    , &[_][]const u8{
        \\pub export fn max(arg_a: c_int, arg_b: c_int) c_int {
        \\    var a = arg_a;
        \\    var b = arg_b;
        \\    if (a == b) return a;
        \\    if (a != b) return b;
        \\    return a;
        \\}
    });

    cases.add("typedeffed bool expression",
        \\typedef char* yes;
        \\void foo(void) {
        \\    yes a;
        \\    if (a) 2;
        \\}
    , &[_][]const u8{
        \\pub const yes = [*c]u8;
        \\pub export fn foo() void {
        \\    var a: yes = undefined;
        \\    if (a != null) _ = @as(c_int, 2);
        \\}
    });

    cases.add("statement expression",
        \\int foo(void) {
        \\    return ({
        \\        int a = 1;
        \\        a;
        \\        a;
        \\    });
        \\}
    , &[_][]const u8{
        \\pub export fn foo() c_int {
        \\    return (blk: {
        \\        var a: c_int = 1;
        \\        _ = a;
        \\        break :blk a;
        \\    });
        \\}
    });

    cases.add("field access expression",
        \\#define ARROW a->b
        \\#define DOT a.b
        \\extern struct Foo {
        \\    int b;
        \\}a;
        \\float b = 2.0f;
        \\void foo(void) {
        \\    struct Foo *c;
        \\    a.b;
        \\    c->b;
        \\}
    , &[_][]const u8{
        \\pub const struct_Foo = extern struct {
        \\    b: c_int,
        \\};
        \\pub extern var a: struct_Foo;
        \\pub export var b: f32 = 2;
        \\pub export fn foo() void {
        \\    var c: [*c]struct_Foo = undefined;
        \\    _ = a.b;
        \\    _ = c.*.b;
        \\}
        ,
        \\pub const DOT = a.b;
        ,
        \\pub const ARROW = a.*.b;
    });

    cases.add("array access",
        \\#define ACCESS array[2]
        \\int array[100] = {};
        \\int foo(int index) {
        \\    return array[index];
        \\}
    , &[_][]const u8{
        \\pub export var array: [100]c_int = [1]c_int{0} ** 100;
        \\pub export fn foo(arg_index: c_int) c_int {
        \\    var index = arg_index;
        \\    return array[@intCast(c_uint, index)];
        \\}
        ,
        \\pub const ACCESS = array[2];
    });

    cases.add("cast signed array index to unsigned",
        \\void foo() {
        \\  int a[10], i = 0;
        \\  a[i] = 0;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    var a: [10]c_int = undefined;
        \\    var i: c_int = 0;
        \\    a[@intCast(c_uint, i)] = 0;
        \\}
    });

    cases.add("long long array index cast to usize",
        \\void foo() {
        \\  long long a[10], i = 0;
        \\  a[i] = 0;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    var a: [10]c_longlong = undefined;
        \\    var i: c_longlong = @bitCast(c_longlong, @as(c_longlong, @as(c_int, 0)));
        \\    a[@intCast(usize, i)] = @bitCast(c_longlong, @as(c_longlong, @as(c_int, 0)));
        \\}
    });

    cases.add("unsigned array index skips cast",
        \\void foo() {
        \\  unsigned int a[10], i = 0;
        \\  a[i] = 0;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    var a: [10]c_uint = undefined;
        \\    var i: c_uint = @bitCast(c_uint, @as(c_int, 0));
        \\    a[i] = @bitCast(c_uint, @as(c_int, 0));
        \\}
    });

    cases.add("macro call",
        \\#define CALL(arg) bar(arg)
    , &[_][]const u8{
        \\pub inline fn CALL(arg: anytype) @TypeOf(bar(arg)) {
        \\    return bar(arg);
        \\}
    });

    cases.add("logical and, logical or",
        \\int max(int a, int b) {
        \\    if (a < b || a == b)
        \\        return b;
        \\    if (a >= b && a == b)
        \\        return a;
        \\    return a;
        \\}
    , &[_][]const u8{
        \\pub export fn max(arg_a: c_int, arg_b: c_int) c_int {
        \\    var a = arg_a;
        \\    var b = arg_b;
        \\    if ((a < b) or (a == b)) return b;
        \\    if ((a >= b) and (a == b)) return a;
        \\    return a;
        \\}
    });

    cases.add("simple if statement",
        \\int max(int a, int b) {
        \\    if (a < b)
        \\        return b;
        \\
        \\    if (a < b)
        \\        return b;
        \\    else
        \\        return a;
        \\
        \\    if (a < b) ; else ;
        \\}
    , &[_][]const u8{
        \\pub export fn max(arg_a: c_int, arg_b: c_int) c_int {
        \\    var a = arg_a;
        \\    var b = arg_b;
        \\    if (a < b) return b;
        \\    if (a < b) return b else return a;
        \\    if (a < b) {} else {}
        \\    return 0;
        \\}
    });

    cases.add("if statements",
        \\void foo() {
        \\    if (2) {
        \\        int a = 2;
        \\    }
        \\    if (2, 5) {
        \\        int a = 2;
        \\    }
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    if (true) {
        \\        var a: c_int = 2;
        \\    }
        \\    if ((blk: {
        \\        _ = @as(c_int, 2);
        \\        break :blk @as(c_int, 5);
        \\    }) != 0) {
        \\        var a: c_int = 2;
        \\    }
        \\}
    });

    cases.add("if on non-bool",
        \\enum SomeEnum { A, B, C };
        \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) {
        \\    if (a) return 0;
        \\    if (b) return 1;
        \\    if (c) return 2;
        \\    if (d) return 3;
        \\    return 4;
        \\}
    , &[_][]const u8{
        \\pub const enum_SomeEnum = extern enum(c_int) {
        \\    A,
        \\    B,
        \\    C,
        \\    _,
        \\};
        \\pub export fn if_none_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void, arg_d: enum_SomeEnum) c_int {
        \\    var a = arg_a;
        \\    var b = arg_b;
        \\    var c = arg_c;
        \\    var d = arg_d;
        \\    if (a != 0) return 0;
        \\    if (b != 0) return 1;
        \\    if (c != null) return 2;
        \\    if (d != 0) return 3;
        \\    return 4;
        \\}
    });

    cases.add("simple data types",
        \\#include <stdint.h>
        \\int foo(char a, unsigned char b, signed char c);
        \\int foo(char a, unsigned char b, signed char c); // test a duplicate prototype
        \\void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d);
        \\void baz(int8_t a, int16_t b, int32_t c, int64_t d);
    , &[_][]const u8{
        \\pub extern fn foo(a: u8, b: u8, c: i8) c_int;
        \\pub extern fn bar(a: u8, b: u16, c: u32, d: u64) void;
        \\pub extern fn baz(a: i8, b: i16, c: i32, d: i64) void;
    });

    cases.add("simple function",
        \\int abs(int a) {
        \\    return a < 0 ? -a : a;
        \\}
    , &[_][]const u8{
        \\pub export fn abs(arg_a: c_int) c_int {
        \\    var a = arg_a;
        \\    return if (a < @as(c_int, 0)) -a else a;
        \\}
    });

    cases.add("post increment",
        \\unsigned foo1(unsigned a) {
        \\    a++;
        \\    return a;
        \\}
        \\int foo2(int a) {
        \\    a++;
        \\    return a;
        \\}
        \\int *foo3(int *a) {
        \\    a++;
        \\    return a;
        \\}
    , &[_][]const u8{
        \\pub export fn foo1(arg_a: c_uint) c_uint {
        \\    var a = arg_a;
        \\    a +%= 1;
        \\    return a;
        \\}
        \\pub export fn foo2(arg_a: c_int) c_int {
        \\    var a = arg_a;
        \\    a += 1;
        \\    return a;
        \\}
        \\pub export fn foo3(arg_a: [*c]c_int) [*c]c_int {
        \\    var a = arg_a;
        \\    a += 1;
        \\    return a;
        \\}
    });

    cases.add("deref function pointer",
        \\void foo(void) {}
        \\int baz(void) { return 0; }
        \\void bar(void) {
        \\    void(*f)(void) = foo;
        \\    int(*b)(void) = baz;
        \\    f();
        \\    (*(f))();
        \\    foo();
        \\    b();
        \\    (*(b))();
        \\    baz();
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {}
        \\pub export fn baz() c_int {
        \\    return 0;
        \\}
        \\pub export fn bar() void {
        \\    var f: ?fn () callconv(.C) void = foo;
        \\    var b: ?fn () callconv(.C) c_int = baz;
        \\    f.?();
        \\    (f).?();
        \\    foo();
        \\    _ = b.?();
        \\    _ = (b).?();
        \\    _ = baz();
        \\}
    });

    cases.add("pre increment/decrement",
        \\void foo(void) {
        \\    int i = 0;
        \\    unsigned u = 0;
        \\    ++i;
        \\    --i;
        \\    ++u;
        \\    --u;
        \\    i = ++i;
        \\    i = --i;
        \\    u = ++u;
        \\    u = --u;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    var i: c_int = 0;
        \\    var u: c_uint = @bitCast(c_uint, @as(c_int, 0));
        \\    i += 1;
        \\    i -= 1;
        \\    u +%= 1;
        \\    u -%= 1;
        \\    i = (blk: {
        \\        const ref = &i;
        \\        ref.* += 1;
        \\        break :blk ref.*;
        \\    });
        \\    i = (blk: {
        \\        const ref = &i;
        \\        ref.* -= 1;
        \\        break :blk ref.*;
        \\    });
        \\    u = (blk: {
        \\        const ref = &u;
        \\        ref.* +%= 1;
        \\        break :blk ref.*;
        \\    });
        \\    u = (blk: {
        \\        const ref = &u;
        \\        ref.* -%= 1;
        \\        break :blk ref.*;
        \\    });
        \\}
    });

    cases.add("shift right assign",
        \\int log2(unsigned a) {
        \\    int i = 0;
        \\    while (a > 0) {
        \\        a >>= 1;
        \\    }
        \\    return i;
        \\}
    , &[_][]const u8{
        \\pub export fn log2(arg_a: c_uint) c_int {
        \\    var a = arg_a;
        \\    var i: c_int = 0;
        \\    while (a > @bitCast(c_uint, @as(c_int, 0))) {
        \\        a >>= @intCast(@import("std").math.Log2Int(c_int), 1);
        \\    }
        \\    return i;
        \\}
    });

    cases.add("shift right assign with a fixed size type",
        \\#include <stdint.h>
        \\int log2(uint32_t a) {
        \\    int i = 0;
        \\    while (a > 0) {
        \\        a >>= 1;
        \\    }
        \\    return i;
        \\}
    , &[_][]const u8{
        \\pub export fn log2(arg_a: u32) c_int {
        \\    var a = arg_a;
        \\    var i: c_int = 0;
        \\    while (a > @bitCast(c_uint, @as(c_int, 0))) {
        \\        a >>= @intCast(@import("std").math.Log2Int(c_int), 1);
        \\    }
        \\    return i;
        \\}
    });

    cases.add("compound assignment operators",
        \\void foo(void) {
        \\    int a = 0;
        \\    unsigned b = 0;
        \\    a += (a += 1);
        \\    a -= (a -= 1);
        \\    a *= (a *= 1);
        \\    a &= (a &= 1);
        \\    a |= (a |= 1);
        \\    a ^= (a ^= 1);
        \\    a >>= (a >>= 1);
        \\    a <<= (a <<= 1);
        \\    a /= (a /= 1);
        \\    a %= (a %= 1);
        \\    b /= (b /= 1);
        \\    b %= (b %= 1);
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    var a: c_int = 0;
        \\    var b: c_uint = @bitCast(c_uint, @as(c_int, 0));
        \\    a += (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* + @as(c_int, 1);
        \\        break :blk ref.*;
        \\    });
        \\    a -= (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* - @as(c_int, 1);
        \\        break :blk ref.*;
        \\    });
        \\    a *= (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* * @as(c_int, 1);
        \\        break :blk ref.*;
        \\    });
        \\    a &= (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* & @as(c_int, 1);
        \\        break :blk ref.*;
        \\    });
        \\    a |= (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* | @as(c_int, 1);
        \\        break :blk ref.*;
        \\    });
        \\    a ^= (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* ^ @as(c_int, 1);
        \\        break :blk ref.*;
        \\    });
        \\    a >>= @intCast(@import("std").math.Log2Int(c_int), (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* >> @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
        \\        break :blk ref.*;
        \\    }));
        \\    a <<= @intCast(@import("std").math.Log2Int(c_int), (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* << @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
        \\        break :blk ref.*;
        \\    }));
        \\    a = @divTrunc(a, (blk: {
        \\        const ref = &a;
        \\        ref.* = @divTrunc(ref.*, @as(c_int, 1));
        \\        break :blk ref.*;
        \\    }));
        \\    a = @rem(a, (blk: {
        \\        const ref = &a;
        \\        ref.* = @rem(ref.*, @as(c_int, 1));
        \\        break :blk ref.*;
        \\    }));
        \\    b /= (blk: {
        \\        const ref = &b;
        \\        ref.* = ref.* / @bitCast(c_uint, @as(c_int, 1));
        \\        break :blk ref.*;
        \\    });
        \\    b %= (blk: {
        \\        const ref = &b;
        \\        ref.* = ref.* % @bitCast(c_uint, @as(c_int, 1));
        \\        break :blk ref.*;
        \\    });
        \\}
    });

    cases.add("compound assignment operators unsigned",
        \\void foo(void) {
        \\    unsigned a = 0;
        \\    a += (a += 1);
        \\    a -= (a -= 1);
        \\    a *= (a *= 1);
        \\    a &= (a &= 1);
        \\    a |= (a |= 1);
        \\    a ^= (a ^= 1);
        \\    a >>= (a >>= 1);
        \\    a <<= (a <<= 1);
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    var a: c_uint = @bitCast(c_uint, @as(c_int, 0));
        \\    a +%= (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* +% @bitCast(c_uint, @as(c_int, 1));
        \\        break :blk ref.*;
        \\    });
        \\    a -%= (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* -% @bitCast(c_uint, @as(c_int, 1));
        \\        break :blk ref.*;
        \\    });
        \\    a *%= (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* *% @bitCast(c_uint, @as(c_int, 1));
        \\        break :blk ref.*;
        \\    });
        \\    a &= (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* & @bitCast(c_uint, @as(c_int, 1));
        \\        break :blk ref.*;
        \\    });
        \\    a |= (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* | @bitCast(c_uint, @as(c_int, 1));
        \\        break :blk ref.*;
        \\    });
        \\    a ^= (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* ^ @bitCast(c_uint, @as(c_int, 1));
        \\        break :blk ref.*;
        \\    });
        \\    a >>= @intCast(@import("std").math.Log2Int(c_uint), (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* >> @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
        \\        break :blk ref.*;
        \\    }));
        \\    a <<= @intCast(@import("std").math.Log2Int(c_uint), (blk: {
        \\        const ref = &a;
        \\        ref.* = ref.* << @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
        \\        break :blk ref.*;
        \\    }));
        \\}
    });

    cases.add("post increment/decrement",
        \\void foo(void) {
        \\    int i = 0;
        \\    unsigned u = 0;
        \\    i++;
        \\    i--;
        \\    u++;
        \\    u--;
        \\    i = i++;
        \\    i = i--;
        \\    u = u++;
        \\    u = u--;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() void {
        \\    var i: c_int = 0;
        \\    var u: c_uint = @bitCast(c_uint, @as(c_int, 0));
        \\    i += 1;
        \\    i -= 1;
        \\    u +%= 1;
        \\    u -%= 1;
        \\    i = (blk: {
        \\        const ref = &i;
        \\        const tmp = ref.*;
        \\        ref.* += 1;
        \\        break :blk tmp;
        \\    });
        \\    i = (blk: {
        \\        const ref = &i;
        \\        const tmp = ref.*;
        \\        ref.* -= 1;
        \\        break :blk tmp;
        \\    });
        \\    u = (blk: {
        \\        const ref = &u;
        \\        const tmp = ref.*;
        \\        ref.* +%= 1;
        \\        break :blk tmp;
        \\    });
        \\    u = (blk: {
        \\        const ref = &u;
        \\        const tmp = ref.*;
        \\        ref.* -%= 1;
        \\        break :blk tmp;
        \\    });
        \\}
    });

    cases.add("implicit casts",
        \\#include <stdbool.h>
        \\
        \\void fn_int(int x);
        \\void fn_f32(float x);
        \\void fn_f64(double x);
        \\void fn_char(char x);
        \\void fn_bool(bool x);
        \\void fn_ptr(void *x);
        \\
        \\void call() {
        \\    fn_int(3.0f);
        \\    fn_int(3.0);
        \\    fn_int(3.0L);
        \\    fn_int('ABCD');
        \\    fn_f32(3);
        \\    fn_f64(3);
        \\    fn_char('3');
        \\    fn_char('\x1');
        \\    fn_char(0);
        \\    fn_f32(3.0f);
        \\    fn_f64(3.0);
        \\    fn_bool(123);
        \\    fn_bool(0);
        \\    fn_bool(&fn_int);
        \\    fn_int(&fn_int);
        \\    fn_ptr(42);
        \\}
    , &[_][]const u8{
        \\pub extern fn fn_int(x: c_int) void;
        \\pub extern fn fn_f32(x: f32) void;
        \\pub extern fn fn_f64(x: f64) void;
        \\pub extern fn fn_char(x: u8) void;
        \\pub extern fn fn_bool(x: bool) void;
        \\pub extern fn fn_ptr(x: ?*c_void) void;
        \\pub export fn call() void {
        \\    fn_int(@floatToInt(c_int, 3));
        \\    fn_int(@floatToInt(c_int, 3));
        \\    fn_int(@floatToInt(c_int, 3));
        \\    fn_int(@as(c_int, 1094861636));
        \\    fn_f32(@intToFloat(f32, @as(c_int, 3)));
        \\    fn_f64(@intToFloat(f64, @as(c_int, 3)));
        \\    fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '3'))));
        \\    fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '\x01'))));
        \\    fn_char(@bitCast(u8, @truncate(i8, @as(c_int, 0))));
        \\    fn_f32(3);
        \\    fn_f64(3);
        \\    fn_bool(@as(c_int, 123) != 0);
        \\    fn_bool(@as(c_int, 0) != 0);
        \\    fn_bool(@ptrToInt(&fn_int) != 0);
        \\    fn_int(@intCast(c_int, @ptrToInt(&fn_int)));
        \\    fn_ptr(@intToPtr(?*c_void, @as(c_int, 42)));
        \\}
    });

    cases.add("function call",
        \\static void bar(void) { }
        \\void foo(int *(baz)(void)) {
        \\    bar();
        \\    baz();
        \\}
    , &[_][]const u8{
        \\pub fn bar() callconv(.C) void {}
        \\pub export fn foo(arg_baz: ?fn () callconv(.C) [*c]c_int) void {
        \\    var baz = arg_baz;
        \\    bar();
        \\    _ = baz.?();
        \\}
    });

    cases.add("macro defines string literal with octal",
        \\#define FOO "aoeu\023 derp"
        \\#define FOO2 "aoeu\0234 derp"
        \\#define FOO_CHAR '\077'
    , &[_][]const u8{
        \\pub const FOO = "aoeu\x13 derp";
        ,
        \\pub const FOO2 = "aoeu\x134 derp";
        ,
        \\pub const FOO_CHAR = '\x3f';
    });

    cases.add("enums",
        \\enum Foo {
        \\    FooA = 2,
        \\    FooB = 5,
        \\    Foo1,
        \\};
    , &[_][]const u8{
        \\pub const FooA = @enumToInt(enum_Foo.A);
        \\pub const FooB = @enumToInt(enum_Foo.B);
        \\pub const Foo1 = @enumToInt(enum_Foo.@"1");
        \\pub const enum_Foo = extern enum(c_int) {
        \\    A = 2,
        \\    B = 5,
        \\    @"1" = 6,
        \\    _,
        \\};
        ,
        \\pub const Foo = enum_Foo;
    });

    cases.add("macro cast",
        \\#define FOO(bar) baz((void *)(baz))
        \\#define BAR (void*) a
        \\#define BAZ (uint32_t)(2)
    , &[_][]const u8{
        \\pub inline fn FOO(bar: anytype) @TypeOf(baz((@import("std").meta.cast(?*c_void, baz)))) {
        \\    return baz((@import("std").meta.cast(?*c_void, baz)));
        \\}
        ,
        \\pub const BAR = (@import("std").meta.cast(?*c_void, a));
        ,
        \\pub const BAZ = (@import("std").meta.cast(u32, 2));
    });

    cases.add("macro with cast to unsigned short, long, and long long",
        \\#define CURLAUTH_BASIC_BUT_USHORT ((unsigned short) 1)
        \\#define CURLAUTH_BASIC ((unsigned long) 1)
        \\#define CURLAUTH_BASIC_BUT_ULONGLONG ((unsigned long long) 1)
    , &[_][]const u8{
        \\pub const CURLAUTH_BASIC_BUT_USHORT = (@import("std").meta.cast(c_ushort, 1));
        \\pub const CURLAUTH_BASIC = (@import("std").meta.cast(c_ulong, 1));
        \\pub const CURLAUTH_BASIC_BUT_ULONGLONG = (@import("std").meta.cast(c_ulonglong, 1));
    });

    cases.add("macro conditional operator",
        \\#define FOO a ? b : c
    , &[_][]const u8{
        \\pub const FOO = if (a) b else c;
    });

    cases.add("do while as expr",
        \\static void foo(void) {
        \\    if (1)
        \\        do {} while (0);
        \\}
    , &[_][]const u8{
        \\pub fn foo() callconv(.C) void {
        \\    if (true) while (true) {
        \\        if (!false) break;
        \\    };
        \\}
    });

    cases.add("macro comparisions",
        \\#define MIN(a, b) ((b) < (a) ? (b) : (a))
        \\#define MAX(a, b) ((b) > (a) ? (b) : (a))
    , &[_][]const u8{
        \\pub inline fn MIN(a: anytype, b: anytype) @TypeOf(if (b < a) b else a) {
        \\    return if (b < a) b else a;
        \\}
        ,
        \\pub inline fn MAX(a: anytype, b: anytype) @TypeOf(if (b > a) b else a) {
        \\    return if (b > a) b else a;
        \\}
    });

    // TODO: detect to use different block labels here
    cases.add("nested assignment",
        \\int foo(int *p, int x) {
        \\    return *p++ = x;
        \\}
    , &[_][]const u8{
        \\pub export fn foo(arg_p: [*c]c_int, arg_x: c_int) c_int {
        \\    var p = arg_p;
        \\    var x = arg_x;
        \\    return blk: {
        \\        const tmp = x;
        \\        (blk_1: {
        \\            const ref = &p;
        \\            const tmp_2 = ref.*;
        \\            ref.* += 1;
        \\            break :blk_1 tmp_2;
        \\        }).?.* = tmp;
        \\        break :blk tmp;
        \\    };
        \\}
    });

    cases.add("widening and truncating integer casting to different signedness",
        \\unsigned long foo(void) {
        \\    return -1;
        \\}
        \\unsigned short bar(long x) {
        \\    return x;
        \\}
    , &[_][]const u8{
        \\pub export fn foo() c_ulong {
        \\    return @bitCast(c_ulong, @as(c_long, -@as(c_int, 1)));
        \\}
        \\pub export fn bar(arg_x: c_long) c_ushort {
        \\    var x = arg_x;
        \\    return @bitCast(c_ushort, @truncate(c_short, x));
        \\}
    });

    cases.add("arg name aliasing decl which comes after",
        \\void foo(int bar) {
        \\    bar = 2;
        \\}
        \\int bar = 4;
    , &[_][]const u8{
        \\pub export fn foo(arg_bar_1: c_int) void {
        \\    var bar_1 = arg_bar_1;
        \\    bar_1 = 2;
        \\}
        \\pub export var bar: c_int = 4;
    });

    cases.add("arg name aliasing macro which comes after",
        \\void foo(int bar) {
        \\    bar = 2;
        \\}
        \\#define bar 4
    , &[_][]const u8{
        \\pub export fn foo(arg_bar_1: c_int) void {
        \\    var bar_1 = arg_bar_1;
        \\    bar_1 = 2;
        \\}
        ,
        \\pub const bar = 4;
    });

    cases.add("don't export inline functions",
        \\inline void a(void) {}
        \\static void b(void) {}
        \\void c(void) {}
        \\static void foo() {}
    , &[_][]const u8{
        \\pub fn a() callconv(.C) void {}
        \\pub fn b() callconv(.C) void {}
        \\pub export fn c() void {}
        \\pub fn foo(...) callconv(.C) void {}
    });

    cases.add("casting away const and volatile",
        \\void foo(int *a) {}
        \\void bar(const int *a) {
        \\    foo((int *)a);
        \\}
        \\void baz(volatile int *a) {
        \\    foo((int *)a);
        \\}
    , &[_][]const u8{
        \\pub export fn foo(arg_a: [*c]c_int) void {
        \\    var a = arg_a;
        \\}
        \\pub export fn bar(arg_a: [*c]const c_int) void {
        \\    var a = arg_a;
        \\    foo(@intToPtr([*c]c_int, @ptrToInt(a)));
        \\}
        \\pub export fn baz(arg_a: [*c]volatile c_int) void {
        \\    var a = arg_a;
        \\    foo(@intToPtr([*c]c_int, @ptrToInt(a)));
        \\}
    });

    cases.add("handling of _Bool type",
        \\_Bool foo(_Bool x) {
        \\    _Bool a = x != 1;
        \\    _Bool b = a != 0;
        \\    _Bool c = foo;
        \\    return foo(c != b);
        \\}
    , &[_][]const u8{
        \\pub export fn foo(arg_x: bool) bool {
        \\    var x = arg_x;
        \\    var a: bool = (@intCast(c_int, @bitCast(i1, @intCast(u1, @boolToInt(x)))) != @as(c_int, 1));
        \\    var b: bool = (@intCast(c_int, @bitCast(i1, @intCast(u1, @boolToInt(a)))) != @as(c_int, 0));
        \\    var c: bool = @ptrToInt(foo) != 0;
        \\    return foo((@intCast(c_int, @bitCast(i1, @intCast(u1, @boolToInt(c)))) != @intCast(c_int, @bitCast(i1, @intCast(u1, @boolToInt(b))))));
        \\}
    });

    cases.add("Don't make const parameters mutable",
        \\int max(const int x, int y) {
        \\    return (x > y) ? x : y;
        \\}
    , &[_][]const u8{
        \\pub export fn max(x: c_int, arg_y: c_int) c_int {
        \\    var y = arg_y;
        \\    return if (x > y) x else y;
        \\}
    });

    cases.add("string concatenation in macros",
        \\#define FOO "hello"
        \\#define BAR FOO " world"
        \\#define BAZ "oh, " FOO
    , &[_][]const u8{
        \\pub const FOO = "hello";
        ,
        \\pub const BAR = FOO ++ " world";
        ,
        \\pub const BAZ = "oh, " ++ FOO;
    });

    cases.add("string concatenation in macros: two defines",
        \\#define FOO "hello"
        \\#define BAZ " world"
        \\#define BAR FOO BAZ
    , &[_][]const u8{
        \\pub const FOO = "hello";
        ,
        \\pub const BAZ = " world";
        ,
        \\pub const BAR = FOO ++ BAZ;
    });

    cases.add("string concatenation in macros: two strings",
        \\#define FOO "a" "b"
        \\#define BAR FOO "c"
    , &[_][]const u8{
        \\pub const FOO = "a" ++ "b";
        ,
        \\pub const BAR = FOO ++ "c";
    });

    cases.add("string concatenation in macros: three strings",
        \\#define FOO "a" "b" "c"
    , &[_][]const u8{
        \\pub const FOO = "a" ++ "b" ++ "c";
    });

    cases.add("multibyte character literals",
        \\#define FOO 'abcd'
    , &[_][]const u8{
        \\pub const FOO = 0x61626364;
    });

    cases.add("Make sure casts are grouped",
        \\typedef struct
        \\{
        \\    int i;
        \\}
        \\*_XPrivDisplay;
        \\typedef struct _XDisplay Display;
        \\#define DefaultScreen(dpy) (((_XPrivDisplay)(dpy))->default_screen)
        \\
    , &[_][]const u8{
        \\pub inline fn DefaultScreen(dpy: anytype) @TypeOf((@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen) {
        \\    return (@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen;
        \\}
    });

    cases.add("macro integer literal casts",
        \\#define NULL ((void*)0)
        \\#define FOO ((int)0x8000)
    , &[_][]const u8{
        \\pub const NULL = (@import("std").meta.cast(?*c_void, 0));
        ,
        \\pub const FOO = (@import("std").meta.cast(c_int, 0x8000));
    });

    if (std.Target.current.abi == .msvc) {
        cases.add("nameless struct fields",
            \\typedef struct NAMED
            \\{
            \\    long name;
            \\} NAMED;
            \\
            \\typedef struct ONENAMEWITHSTRUCT
            \\{
            \\    NAMED;
            \\    long b;
            \\} ONENAMEWITHSTRUCT;
        , &[_][]const u8{
            \\pub const struct_NAMED = extern struct {
            \\    name: c_long,
            \\};
            \\pub const NAMED = struct_NAMED;
            \\pub const struct_ONENAMEWITHSTRUCT = extern struct {
            \\    unnamed_0: struct_NAMED,
            \\    b: c_long,
            \\};
        });
    } else {
        cases.add("nameless struct fields",
            \\typedef struct NAMED
            \\{
            \\    long name;
            \\} NAMED;
            \\
            \\typedef struct ONENAMEWITHSTRUCT
            \\{
            \\    NAMED;
            \\    long b;
            \\} ONENAMEWITHSTRUCT;
        , &[_][]const u8{
            \\pub const struct_NAMED = extern struct {
            \\    name: c_long,
            \\};
            \\pub const NAMED = struct_NAMED;
            \\pub const struct_ONENAMEWITHSTRUCT = extern struct {
            \\    b: c_long,
            \\};
        });
    }

    cases.add("unnamed fields have predictabile names",
        \\struct a {
        \\    struct {};
        \\};
        \\struct b {
        \\    struct {};
        \\};
    , &[_][]const u8{
        \\const struct_unnamed_1 = extern struct {};
        \\pub const struct_a = extern struct {
        \\    unnamed_0: struct_unnamed_1,
        \\};
        \\const struct_unnamed_2 = extern struct {};
        \\pub const struct_b = extern struct {
        \\    unnamed_0: struct_unnamed_2,
        \\};
    });
}
