use super::*;

const P0009_NOTES: &[&str] =
    &["`new(...)` accepts up to three optional arguments: `outer`, `name`, and `flags`."];

pub(super) const P0001_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0001_01.uc",
        source: "c && ( /*lol*/ ** calc_it())",
    },
    Fixture {
        label: "files/P0001_02.uc",
        source: "\r\na + (\n//AAA\n//BBB\n//CCC\n//DDD\n//EEE\n//FFF\n      ]",
    },
    Fixture {
        label: "files/P0001_03.uc",
        source: "(\n// nothing here, bucko",
    },
];

pub(super) const P0002_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0002_01.uc",
        source: "a + [",
    },
    Fixture {
        label: "files/P0002_02.uc",
        source: "a * \n//some\n//empty lines\n *",
    },
    Fixture {
        label: "files/P0002_03.uc",
        source: "a &&",
    },
    Fixture {
        label: "files/P0002_04.uc",
        source: "a * * *",
    },
    Fixture {
        label: "files/P0002_05.uc",
        source: "new(Outer, Name, 7 +) SomeClass"
    }
];

pub(super) const P0003_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0003_01.uc",
        source: "(a + b && c / d ^ e @ f",
    },
    Fixture {
        label: "files/P0003_02.uc",
        source: "(a]",
    },
    Fixture {
        label: "files/P0003_03.uc",
        source: "(a\n;",
    },
];

pub(super) const P0004_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0004_01.uc",
        source: "class<>",
    },
    Fixture {
        label: "files/P0004_02.uc",
        source: "(class<>)",
    },
    Fixture {
        label: "files/P0004_03.uc",
        source: "new class<\n\n\n\n\n//>WOah there!\r\n    >",
    },
];

pub(super) const P0005_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0005_01.uc",
        source: "class<Foo.>",
    },
    Fixture {
        label: "files/P0005_02.uc",
        source: "class<Foo.",
    },
    Fixture {
        label: "files/P0005_03.uc",
        source: "class<Foo..Bar>",
    },
    Fixture {
        label: "files/P0005_04.uc",
        source: "class<Foo.*>",
    },
    Fixture {
        label: "files/P0005_05.uc",
        source: "class<Foo.Bar.>",
    },
    Fixture {
        label: "files/P0005_06.uc",
        source: "new class<Foo\n\n\n\n.\n// still waiting\r\n    >",
    },
    Fixture {
        label: "files/P0005_07.uc",
        source: "new class<Foo.\n// still waiting\r\n    >",
    },
];

pub(super) const P0006_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0006_01.uc",
        source: "class<[",
    },
    Fixture {
        label: "files/P0006_02.uc",
        source: "class\n<*>",
    },
    Fixture {
        label: "files/P0006_03.uc",
        source: "class<",
    },
    Fixture {
        label: "files/P0006_04.uc",
        source: "new class<\n// nothing valid here\n    ]",
    },
];

pub(super) const P0007_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0007_01.uc",
        source: "class<Foo",
    },
    Fixture {
        label: "files/P0007_02.uc",
        source: "class<Foo.Bar\n]",
    },
    Fixture {
        label: "files/P0007_03.uc",
        source: "new class<Foo\n// still waiting\n    ;",
    },
    Fixture {
        label: "files/P0007_04.uc",
        source: "new class\n<\r\nFoo.Bar\n// more stuff\n    ",
    },
];

pub(super) const P0008_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0008_01.uc",
        source: "new",
    },
    Fixture {
        label: "files/P0008_02.uc",
        source: "new\n// no class specifier here\n;",
    },
    Fixture {
        label: "files/P0008_03.uc",
        source: "new(\n    Outer,\n    Name,\n    7\n)\n]",
    },
    Fixture {
        label: "files/P0008_04.uc",
        source: "new\n(Outer,\n    Name,\n    7\n)",
    },
];

pub(super) const P0009_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0009_01.uc",
        source: "new(Outer, Name, 7, Extra) SomeClass",
    },
    Fixture {
        label: "files/P0009_02.uc",
        source: "new(\n    Outer,\n    Name,\n    7,\n    Extra\n) SomeClass",
    },
    Fixture {
        label: "files/P0009_03.uc",
        source: "new(\n    Outer,\n    Name,\n    7,\n    // fourth argument is not allowed\n    Extra\n) class'Foo'",
    },
    Fixture {
        label: "files/P0009_04.uc",
        source: "new(Outer, Name, 7,) SomeClass",
    },
    Fixture {
        label: "files/P0009_05.uc",
        source: "new\n(\n    Outer,\n    Name,\n    7,\n    Extra\n) SomeClass",
    },
    Fixture {
        label: "files/P0009_06.uc",
        source: "new(Outer, Name, , Extra) SomeClass",
    },
    Fixture {
        label: "files/P0009_07.uc",
        source: "new(Outer, , 7, Extra) SomeClass",
    },
    Fixture {
        label: "files/P0009_08.uc",
        source: "new(Outer, Name, 7, Extra1, Extra2) SomeClass",
    },
    Fixture {
        label: "files/P0009_09.uc",
        source: "new(Outer, Name, 7, Foo.Bar(Baz)) SomeClass",
    },
];

pub(super) const P0010_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0010_01.uc",
        source: "new(Outer, Name, 7 SomeClass",
    },
    Fixture {
        label: "files/P0010_02.uc",
        source: "new\n(\n    Outer,\n    Name,\n    7\n    // missing `)` before the class specifier\n    SomeClass",
    },
    Fixture {
        label: "files/P0010_03.uc",
        source: "new(\n    Outer,\n    Name,\n    Foo.Bar(Baz)\n    class'Foo'",
    },
    Fixture {
        label: "files/P0010_04.uc",
        source: "new(\n    Outer,\n    Name,\n    (A + B)\n",
    },
    Fixture {
        label: "files/P0010_05.uc",
        source: "new\n(\n    Foo.Bar(Baz)\n    ],\n    Flags\n) class'Pkg.Type'",
    },
];

pub(super) const P0011_FIXTURES: &[Fixture] = &[
    Fixture {
        label: "files/P0011_01.uc",
        source: "new(Outer Name, 7) SomeClass",
    },
    Fixture {
        label: "files/P0011_02.uc",
        source: "new(\n    ,\n    Name\n    7\n) SomeClass",
    },
    Fixture {
        label: "files/P0011_03.uc",
        source: "new\n(\n    Foo.Bar(Baz)\n    Name,\n    Flags\n) class'Pkg.Type'",
    },
    Fixture {
        label: "files/P0011_04.uc",
        source: "new(\n    Outer,\n    (A + B)\n    SomeFlags\n) class<Weapon>",
    },
];

#[test]
fn check_p0001_fixtures() {
    let runs = run_fixtures(P0001_FIXTURES);

    assert_eq!(runs.get("files/P0001_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0001_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0001_03.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0001_01.uc"),
        &ExpectedDiagnostic {
            headline: "expected expression inside parentheses, found `**`",
            severity: Severity::Error,
            code: Some("P0001"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(8),
                    end: TokenPosition(8),
                },
                message: "unexpected `**`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0001_02.uc"),
        &ExpectedDiagnostic {
            headline: "expected expression inside parentheses, found `]`",
            severity: Severity::Error,
            code: Some("P0001"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(5),
                    end: TokenPosition(20),
                },
                message: "unexpected `]`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(5),
                    end: TokenPosition(5),
                },
                message: "parenthesized expression starts here",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0001_03.uc"),
        &ExpectedDiagnostic {
            headline: "expected expression, found end of file",
            severity: Severity::Error,
            code: Some("P0001"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(0),
                    end: TokenPosition(3),
                },
                message: "reached end of file here",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(0),
                    end: TokenPosition(0),
                },
                message: "parenthesized expression starts here",
            }],
            help: None,
            notes: &[],
        },
    );
}

#[test]
fn check_p0002_fixtures() {
    let runs = run_fixtures(P0002_FIXTURES);

    assert_eq!(runs.get("files/P0002_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0002_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0002_03.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0002_04.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0002_05.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0002_01.uc"),
        &ExpectedDiagnostic {
            headline: "expected expression after `+`, found `[`",
            severity: Severity::Error,
            code: Some("P0002"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(4),
                    end: TokenPosition(4),
                },
                message: "unexpected `[`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0002_02.uc"),
        &ExpectedDiagnostic {
            headline: "expected expression after `*`, found `*`",
            severity: Severity::Error,
            code: Some("P0002"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(10),
                    end: TokenPosition(10),
                },
                message: "unexpected `*`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(2),
                    end: TokenPosition(2),
                },
                message: "after this `*`, an expression was expected",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0002_03.uc"),
        &ExpectedDiagnostic {
            headline: "expected expression after `&&`, found end of file",
            severity: Severity::Error,
            code: Some("P0002"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(3),
                    end: TokenPosition(3),
                },
                message: "reached end of file here",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0002_04.uc"),
        &ExpectedDiagnostic {
            headline: "expected expression after `*`, found `*`",
            severity: Severity::Error,
            code: Some("P0002"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(4),
                    end: TokenPosition(4),
                },
                message: "unexpected `*`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0002_05.uc"),
        &ExpectedDiagnostic {
            headline: "expected expression after `+`, found `)`",
            severity: Severity::Error,
            code: Some("P0002"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(11),
                    end: TokenPosition(11),
                },
                message: "unexpected `)`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );
}

#[test]
fn check_p0003_fixtures() {
    let runs = run_fixtures(P0003_FIXTURES);

    assert_eq!(runs.get("files/P0003_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0003_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0003_03.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0003_01.uc"),
        &ExpectedDiagnostic {
            headline: "missing `)` to close parenthesized expression",
            severity: Severity::Error,
            code: Some("P0003"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(22),
                    end: TokenPosition(22),
                },
                message: "expected `)` before end of file",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0003_02.uc"),
        &ExpectedDiagnostic {
            headline: "missing `)` to close parenthesized expression",
            severity: Severity::Error,
            code: Some("P0003"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(2),
                    end: TokenPosition(2),
                },
                message: "expected `)` before `]`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0003_03.uc"),
        &ExpectedDiagnostic {
            headline: "missing `)` to close parenthesized expression",
            severity: Severity::Error,
            code: Some("P0003"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(0),
                    end: TokenPosition(3),
                },
                message: "expected `)` before `;`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(0),
                    end: TokenPosition(0),
                },
                message: "parenthesized expression starts here",
            }],
            help: None,
            notes: &[],
        },
    );
}

#[test]
fn check_p0004_fixtures() {
    let runs = run_fixtures(P0004_FIXTURES);

    assert_eq!(runs.get("files/P0004_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0004_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0004_03.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0004_01.uc"),
        &ExpectedDiagnostic {
            headline: "missing type argument in `class<...>`",
            severity: Severity::Error,
            code: Some("P0004"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(2),
                    end: TokenPosition(2),
                },
                message: "expected a type name before `>`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0004_02.uc"),
        &ExpectedDiagnostic {
            headline: "missing type argument in `class<...>`",
            severity: Severity::Error,
            code: Some("P0004"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(3),
                    end: TokenPosition(3),
                },
                message: "expected a type name before `>`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0004_03.uc"),
        &ExpectedDiagnostic {
            headline: "missing type argument in `class<...>`",
            severity: Severity::Error,
            code: Some("P0004"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(3),
                    end: TokenPosition(12),
                },
                message: "expected a type name before `>`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(3),
                    end: TokenPosition(3),
                },
                message: "type argument starts here",
            }],
            help: None,
            notes: &[],
        },
    );
}

#[test]
fn check_p0005_fixtures() {
    let runs = run_fixtures(P0005_FIXTURES);

    assert_eq!(runs.get("files/P0005_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0005_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0005_03.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0005_04.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0005_05.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0005_06.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0005_07.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0005_01.uc"),
        &ExpectedDiagnostic {
            headline: "expected another type segment after `.`, found `>`",
            severity: Severity::Error,
            code: Some("P0005"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(4),
                    end: TokenPosition(4),
                },
                message: "unexpected `>`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0005_02.uc"),
        &ExpectedDiagnostic {
            headline: "expected another type segment after `.`, found end of file",
            severity: Severity::Error,
            code: Some("P0005"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(4),
                    end: TokenPosition(4),
                },
                message: "reached end of file here",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0005_03.uc"),
        &ExpectedDiagnostic {
            headline: "expected another type segment after `.`, found `.`",
            severity: Severity::Error,
            code: Some("P0005"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(4),
                    end: TokenPosition(4),
                },
                message: "unexpected `.`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0005_04.uc"),
        &ExpectedDiagnostic {
            headline: "expected another type segment after `.`, found `*`",
            severity: Severity::Error,
            code: Some("P0005"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(4),
                    end: TokenPosition(4),
                },
                message: "unexpected `*`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0005_05.uc"),
        &ExpectedDiagnostic {
            headline: "expected another type segment after `.`, found `>`",
            severity: Severity::Error,
            code: Some("P0005"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(6),
                    end: TokenPosition(6),
                },
                message: "unexpected `>`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0005_06.uc"),
        &ExpectedDiagnostic {
            headline: "expected another type segment after `.`, found `>`",
            severity: Severity::Error,
            code: Some("P0005"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(14),
                    end: TokenPosition(14),
                },
                message: "unexpected `>`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(9),
                        end: TokenPosition(9),
                    },
                    message: "after this `.`, another type segment was expected",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(2),
                        end: TokenPosition(2),
                    },
                    message: "while parsing this `class<...>` type expression",
                },
            ],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0005_07.uc"),
        &ExpectedDiagnostic {
            headline: "expected another type segment after `.`, found `>`",
            severity: Severity::Error,
            code: Some("P0005"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(10),
                    end: TokenPosition(10),
                },
                message: "unexpected `>`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(5),
                    end: TokenPosition(5),
                },
                message: "after this `.`, another type segment was expected",
            }],
            help: None,
            notes: &[],
        },
    );
}

#[test]
fn check_p0006_fixtures() {
    let runs = run_fixtures(P0006_FIXTURES);

    assert_eq!(runs.get("files/P0006_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0006_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0006_03.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0006_04.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0006_01.uc"),
        &ExpectedDiagnostic {
            headline: "expected a type argument after `<` in `class<...>`, found `[`",
            severity: Severity::Error,
            code: Some("P0006"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(2),
                    end: TokenPosition(2),
                },
                message: "unexpected `[`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0006_02.uc"),
        &ExpectedDiagnostic {
            headline: "expected a type argument after `<` in `class<...>`, found `*`",
            severity: Severity::Error,
            code: Some("P0006"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(3),
                    end: TokenPosition(3),
                },
                message: "unexpected `*`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(0),
                    end: TokenPosition(0),
                },
                message: "while parsing this `class<...>` type expression",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0006_03.uc"),
        &ExpectedDiagnostic {
            headline: "expected a type argument after `<` in `class<...>`, found end of file",
            severity: Severity::Error,
            code: Some("P0006"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(2),
                    end: TokenPosition(2),
                },
                message: "reached end of file here",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0006_04.uc"),
        &ExpectedDiagnostic {
            headline: "expected a type argument after `<` in `class<...>`, found `]`",
            severity: Severity::Error,
            code: Some("P0006"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(8),
                    end: TokenPosition(8),
                },
                message: "unexpected `]`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(3),
                    end: TokenPosition(3),
                },
                message: "type argument starts here",
            }],
            help: None,
            notes: &[],
        },
    );
}

#[test]
fn check_p0007_fixtures() {
    let runs = run_fixtures(P0007_FIXTURES);

    assert_eq!(runs.get("files/P0007_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0007_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0007_03.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0007_04.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0007_01.uc"),
        &ExpectedDiagnostic {
            headline: "missing `>` to close `class<...>`",
            severity: Severity::Error,
            code: Some("P0007"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(3),
                    end: TokenPosition(3),
                },
                message: "expected `>` before end of file",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0007_02.uc"),
        &ExpectedDiagnostic {
            headline: "missing `>` to close `class<...>`",
            severity: Severity::Error,
            code: Some("P0007"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(6),
                    end: TokenPosition(6),
                },
                message: "expected `>` before `]`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(1),
                    end: TokenPosition(1),
                },
                message: "type argument starts here",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0007_03.uc"),
        &ExpectedDiagnostic {
            headline: "missing `>` to close `class<...>`",
            severity: Severity::Error,
            code: Some("P0007"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(9),
                    end: TokenPosition(9),
                },
                message: "expected `>` before `;`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(3),
                    end: TokenPosition(3),
                },
                message: "type argument starts here",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0007_04.uc"),
        &ExpectedDiagnostic {
            headline: "missing `>` to close `class<...>`",
            severity: Severity::Error,
            code: Some("P0007"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(13),
                    end: TokenPosition(13),
                },
                message: "expected `>` before end of file",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(4),
                        end: TokenPosition(4),
                    },
                    message: "type argument starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(2),
                        end: TokenPosition(2),
                    },
                    message: "while parsing this `class<...>` type expression",
                },
            ],
            help: None,
            notes: &[],
        },
    );
}

#[test]
fn check_p0008_fixtures() {
    let runs = run_fixtures(P0008_FIXTURES);

    assert_eq!(runs.get("files/P0008_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0008_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0008_03.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0008_04.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0008_01.uc"),
        &ExpectedDiagnostic {
            headline: "expected class expression after `new`, found end of file",
            severity: Severity::Error,
            code: Some("P0008"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(1),
                    end: TokenPosition(1),
                },
                message: "reached end of file here",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0008_02.uc"),
        &ExpectedDiagnostic {
            headline: "expected class expression after `new`, found `;`",
            severity: Severity::Error,
            code: Some("P0008"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(4),
                    end: TokenPosition(4),
                },
                message: "unexpected `;`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(0),
                    end: TokenPosition(0),
                },
                message: "`new` expression starts here",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0008_03.uc"),
        &ExpectedDiagnostic {
            headline: "expected class expression after `new(...)`, found `]`",
            severity: Severity::Error,
            code: Some("P0008"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(14),
                    end: TokenPosition(16),
                },
                message: "unexpected `]`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(0),
                        end: TokenPosition(0),
                    },
                    message: "`new` expression starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(14),
                        end: TokenPosition(14),
                    },
                    message: "optional `new(...)` arguments end here",
                },
            ],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0008_04.uc"),
        &ExpectedDiagnostic {
            headline: "expected class expression after `new(...)`, found end of file",
            severity: Severity::Error,
            code: Some("P0008"),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(0),
                    end: TokenPosition(0),
                },
                message: "`new` expression starts here",
            }],
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(14),
                    end: TokenPosition(14),
                },
                message: "reached end of file here",
            }),
            help: None,
            notes: &[],
        },
    );
}

#[test]
fn check_p0009_fixtures() {
    let runs = run_fixtures(P0009_FIXTURES);

    assert_eq!(runs.get("files/P0009_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0009_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0009_03.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0009_04.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0009_05.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0009_06.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0009_07.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0009_08.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0009_09.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0009_01.uc"),
        &ExpectedDiagnostic {
            headline: "too many arguments in `new(...)`",
            severity: Severity::Error,
            code: Some("P0009"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(11),
                    end: TokenPosition(11),
                },
                message: "a fourth argument is not allowed in `new(...)`",
            }),
            secondary_labels: &[],
            help: None,
            notes: P0009_NOTES,
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0009_02.uc"),
        &ExpectedDiagnostic {
            headline: "too many arguments in `new(...)`",
            severity: Severity::Error,
            code: Some("P0009"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(12),
                    end: TokenPosition(16),
                },
                message: "a fourth argument is not allowed in `new(...)`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(1),
                        end: TokenPosition(1),
                    },
                    message: "`new(...)` argument list starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(12),
                        end: TokenPosition(12),
                    },
                    message: "the third allowed argument ends here",
                },
            ],
            help: None,
            notes: P0009_NOTES,
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0009_03.uc"),
        &ExpectedDiagnostic {
            headline: "too many arguments in `new(...)`",
            severity: Severity::Error,
            code: Some("P0009"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(12),
                    end: TokenPosition(19),
                },
                message: "a fourth argument is not allowed in `new(...)`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(1),
                        end: TokenPosition(1),
                    },
                    message: "`new(...)` argument list starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(12),
                        end: TokenPosition(12),
                    },
                    message: "the third allowed argument ends here",
                },
            ],
            help: None,
            notes: P0009_NOTES,
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0009_04.uc"),
        &ExpectedDiagnostic {
            headline: "too many arguments in `new(...)`",
            severity: Severity::Error,
            code: Some("P0009"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(9),
                    end: TokenPosition(9),
                },
                message: "this `,` starts a fourth argument, which is not allowed here",
            }),
            secondary_labels: &[],
            help: None,
            notes: P0009_NOTES,
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0009_05.uc"),
        &ExpectedDiagnostic {
            headline: "too many arguments in `new(...)`",
            severity: Severity::Error,
            code: Some("P0009"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(13),
                    end: TokenPosition(17),
                },
                message: "a fourth argument is not allowed in `new(...)`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(0),
                        end: TokenPosition(0),
                    },
                    message: "`new` expression starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(2),
                        end: TokenPosition(2),
                    },
                    message: "`new(...)` argument list starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(13),
                        end: TokenPosition(13),
                    },
                    message: "the third allowed argument ends here",
                },
            ],
            help: None,
            notes: P0009_NOTES,
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0009_06.uc"),
        &ExpectedDiagnostic {
            headline: "too many arguments in `new(...)`",
            severity: Severity::Error,
            code: Some("P0009"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(10),
                    end: TokenPosition(10),
                },
                message: "a fourth argument is not allowed in `new(...)`",
            }),
            secondary_labels: &[],
            help: None,
            notes: P0009_NOTES,
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0009_07.uc"),
        &ExpectedDiagnostic {
            headline: "too many arguments in `new(...)`",
            severity: Severity::Error,
            code: Some("P0009"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(10),
                    end: TokenPosition(10),
                },
                message: "a fourth argument is not allowed in `new(...)`",
            }),
            secondary_labels: &[],
            help: None,
            notes: P0009_NOTES,
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0009_08.uc"),
        &ExpectedDiagnostic {
            headline: "too many arguments in `new(...)`",
            severity: Severity::Error,
            code: Some("P0009"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(11),
                    end: TokenPosition(11),
                },
                message: "a fourth argument is not allowed in `new(...)`",
            }),
            secondary_labels: &[],
            help: None,
            notes: P0009_NOTES,
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0009_09.uc"),
        &ExpectedDiagnostic {
            headline: "too many arguments in `new(...)`",
            severity: Severity::Error,
            code: Some("P0009"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(11),
                    end: TokenPosition(16),
                },
                message: "a fourth argument is not allowed in `new(...)`",
            }),
            secondary_labels: &[],
            help: None,
            notes: P0009_NOTES,
        },
    );
}

#[test]
fn check_p0010_fixtures() {
    let runs = run_fixtures(P0010_FIXTURES);

    assert_eq!(runs.get("files/P0010_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0010_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0010_03.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0010_04.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0010_05.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0010_01.uc"),
        &ExpectedDiagnostic {
            headline: "missing `)` to close `new(...)` argument list",
            severity: Severity::Error,
            code: Some("P0010"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(10),
                    end: TokenPosition(10),
                },
                message: "expected `)` before `SomeClass`",
            }),
            secondary_labels: &[],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0010_02.uc"),
        &ExpectedDiagnostic {
            headline: "missing `)` to close `new(...)` argument list",
            severity: Severity::Error,
            code: Some("P0010"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(19),
                    end: TokenPosition(19),
                },
                message: "expected `)` before `SomeClass`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(0),
                        end: TokenPosition(0),
                    },
                    message: "`new` expression starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(2),
                        end: TokenPosition(2),
                    },
                    message: "`new(...)` argument list starts here",
                },
            ],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0010_03.uc"),
        &ExpectedDiagnostic {
            headline: "missing `)` to close `new(...)` argument list",
            severity: Severity::Error,
            code: Some("P0010"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(20),
                    end: TokenPosition(20),
                },
                message: "expected `)` before `class`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(1),
                    end: TokenPosition(1),
                },
                message: "`new(...)` argument list starts here",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0010_04.uc"),
        &ExpectedDiagnostic {
            headline: "missing `)` to close `new(...)` argument list",
            severity: Severity::Error,
            code: Some("P0010"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(20),
                    end: TokenPosition(20),
                },
                message: "expected `)` before end of file",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(1),
                    end: TokenPosition(1),
                },
                message: "`new(...)` argument list starts here",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0010_05.uc"),
        &ExpectedDiagnostic {
            headline: "missing `)` to close `new(...)` argument list",
            severity: Severity::Error,
            code: Some("P0010"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(13),
                    end: TokenPosition(13),
                },
                message: "expected `)` before `]`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(0),
                        end: TokenPosition(0),
                    },
                    message: "`new` expression starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(2),
                        end: TokenPosition(2),
                    },
                    message: "`new(...)` argument list starts here",
                },
            ],
            help: None,
            notes: &[],
        },
    );
}

#[test]
fn check_p0011_fixtures() {
    let runs = run_fixtures(P0011_FIXTURES);

    assert_eq!(runs.get("files/P0011_01.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0011_02.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0011_03.uc").unwrap().len(), 1);
    assert_eq!(runs.get("files/P0011_04.uc").unwrap().len(), 1);

    assert_diagnostic(
        &runs.get_any("files/P0011_01.uc"),
        &ExpectedDiagnostic {
            headline: "missing `,` between `new(...)` arguments",
            severity: Severity::Error,
            code: Some("P0011"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(4),
                    end: TokenPosition(4),
                },
                message: "expected `,` before `Name`",
            }),
            secondary_labels: &[ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(2),
                    end: TokenPosition(2),
                },
                message: "previous argument ends here",
            }],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0011_02.uc"),
        &ExpectedDiagnostic {
            headline: "missing `,` between `new(...)` arguments",
            severity: Severity::Error,
            code: Some("P0011"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(10),
                    end: TokenPosition(10),
                },
                message: "expected `,` before `7`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(1),
                        end: TokenPosition(1),
                    },
                    message: "`new(...)` argument list starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(7),
                        end: TokenPosition(7),
                    },
                    message: "previous argument ends here",
                },
            ],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0011_03.uc"),
        &ExpectedDiagnostic {
            headline: "missing `,` between `new(...)` arguments",
            severity: Severity::Error,
            code: Some("P0011"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(13),
                    end: TokenPosition(13),
                },
                message: "expected `,` before `Name`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(0),
                        end: TokenPosition(0),
                    },
                    message: "`new` expression starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(2),
                        end: TokenPosition(2),
                    },
                    message: "`new(...)` argument list starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(5),
                        end: TokenPosition(10),
                    },
                    message: "previous argument ends here",
                },
            ],
            help: None,
            notes: &[],
        },
    );

    assert_diagnostic(
        &runs.get_any("files/P0011_04.uc"),
        &ExpectedDiagnostic {
            headline: "missing `,` between `new(...)` arguments",
            severity: Severity::Error,
            code: Some("P0011"),
            primary_label: Some(ExpectedLabel {
                span: TokenSpan {
                    start: TokenPosition(17),
                    end: TokenPosition(17),
                },
                message: "expected `,` before `SomeFlags`",
            }),
            secondary_labels: &[
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(1),
                        end: TokenPosition(1),
                    },
                    message: "`new(...)` argument list starts here",
                },
                ExpectedLabel {
                    span: TokenSpan {
                        start: TokenPosition(8),
                        end: TokenPosition(14),
                    },
                    message: "previous argument ends here",
                },
            ],
            help: None,
            notes: &[],
        },
    );
}
