Skip to content

zig fmt bugs with nested containers #23071

@dweiller

Description

@dweiller

Zig Version

0.14.0-dev.3451+d8d2aa9af

Steps to Reproduce and Observed Behavior

Run these (posix) shell commands to create the repro case files (or otherwise copy the files contents to some files). I haven't investigated all container types/syntaxes, but the initial issue I saw was the one from fmt_repro2.zig, but with the decls being array decls like const data = [_]T{...}, so I assume similar issues will be present for various different container types/syntaxes.

cat > fmt_repro1.zig << EOF
// These examples show cases where formatting does not get turned back on. Moving the 'off'
// directives inside the inner structs, or the 'on' directives above the inner structs makes the
// examples behave as would be expected.

const data1 = .{
    // zig fmt: off
    .{
    },
    // zig fmt: on
};

    const a = 0; // formatting is off

// zig fmt: on

    const b = 0; // formatting is on

const data2 = .{
    // zig fmt: off
    .{
    // zig fmt: on
        }, // formatting is off
};

    const c = 0; // formatting is off

// indentation of the following 'off' directives is just to show formatting gets turned on correctly

// zig fmt: on
    // zig fmt: off
const data3 = .{
    .{
    },
    // zig fmt: on
};

    const d = 0; // formatting is off

// zig fmt: on
    // zig fmt: off
const data4 = .{
    .{
    // zig fmt: on
    },
};

    const e = 0; // formatting is off
EOF

cat > fmt_repro2.zig << EOF
// These examples will indent the 'zig fmt: off' lines one level more (this is expected) but will
// also indent the field and closing brace, as well as the 'zig fmt: on' line. Subsequent use of
// `zig fmt` will then continue to indent the lines between formatting directives (including the
// 'on' directives, but not the 'off' directives): `zig fmt` is not idempotent on these examples.

const data1 = .{
    .{
    // zig fmt: off
    .{},
    },
    // zig fmt: on
};

// the inclusion of '.a = ' is meaningful here; removing the '.a = ' from this example has different
// behaviour, see `fmt_repro3.zig`
const data2 = .{
    .{
    // zig fmt: off
        .a = .{},
    // zig fmt: on
    },
};
EOF

cat > fmt_repro3.zig << EOF
// running `zig fmt` on this file will indent the 'off' directives to the expected place, but
// deleted everything this between the 'off' directive and the closing brace of `data` (exclusive).
// `data2` gets formatted correctly.

const data = .{
    .{
    // zig fmt: off
        .{
        },
    // zig fmt: on
    },
};

    const a = 0; // formatting is on, as expected

const data2 = .{
    .{
    // zig fmt: off
        .{
        },
    },
};

    const b = 0; // formatting is on, despite no 'on' directive
// Since formatting is on here, the fact that's it's on for line 14 may be a fluke

const data3 = .{
    .{
    // zig fmt: off
        .a = .{
        },
    // unlike the cases above (without the named field), placing an 'on' directive here makes this
    // into a case from 'fmt_repro2.zig' rather than deleting lines
    },
};

    const c = 0; // formatting is on, despite no 'on' directive
EOF

Format the files using zig fmt - the easiest way to understand what happens would be to do so in your editor, especially with the second file, which shows non-idempotent behaviour. If you'd prefer CLI commands instead of playing with the examples in an editor, run:

zig fmt fmt_repro1.zig
zig fmt fmt_repro2.zig
zig fmt fmt_repro2.zig
zig fmt fmt_repro2.zig
zig fmt fmt_repro3.zig

Expected Behavior

  • for fmt_repro1.zig I would expect formatting directives to be observed regardless of how they interleave with nested containers
  • for fmt_repro2.zig formatting should be idempotent, and shouldn't indent the lines between the directives, though I guess it's consistent/reasonable for it the change the indenting of the directives. Note that the 'off' directives just get indented one level more each time, rather than indented to the correct level, unlike the 'on' directives
  • for fmt_repro3.zig formatting should not be deleting non-blank lines of code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugObserved behavior contradicts documented or intended behaviorzig fmt

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions