serc-cli 0.1.2

An experimental CPython bytecode compiler, written in Rust.
mod common;

use serc::PythonVersion;

#[test]
fn repeated_call_keywords_follow_native_lowering_boundaries() {
    let cases = [
        (
            "*a\nf(x=1, x=2)\n",
            Some("can't use starred expression here"),
        ),
        (
            "f(x=1, x=2)\nclass C(meta=1, meta=2): pass\n",
            Some("keyword argument repeated: x"),
        ),
        ("f(x=1, x=2)\n", Some("keyword argument repeated: x")),
        (
            "f(x=1, **values, x=2)\n",
            Some("keyword argument repeated: x"),
        ),
        (
            "f(x=1, y=1, y=2, x=2)\n",
            Some("keyword argument repeated: x"),
        ),
        ("f(\u{212a}=1, K=2)\n", Some("keyword argument repeated: K")),
        (
            "if False:\n    f(x=1, x=2)\n",
            Some("keyword argument repeated: x"),
        ),
        (
            "def f():\n    return\n    g(x=1, x=2)\n",
            Some("keyword argument repeated: x"),
        ),
        (
            "value = True or f(x=1, x=2)\n",
            Some("keyword argument repeated: x"),
        ),
        (
            "value = lambda: f(x=1, x=2)\n",
            Some("keyword argument repeated: x"),
        ),
        (
            "value = [f(x=1, x=2) for x in ()]\n",
            Some("keyword argument repeated: x"),
        ),
        (
            "@f(x=1, x=2)\ndef g(): pass\n",
            Some("keyword argument repeated: x"),
        ),
        (
            "def g(v=f(x=1, x=2)): pass\n",
            Some("keyword argument repeated: x"),
        ),
        ("value: f(x=1, x=2)\n", Some("keyword argument repeated: x")),
        (
            "f(x=1, x=2)\nx=1\nglobal x\n",
            Some("name 'x' is assigned to before global declaration"),
        ),
        (
            "f(x=1, x=2)\nfrom __future__ import annotations\n",
            Some("from __future__ imports must occur at the beginning of the file"),
        ),
        ("f(x=1, y=2)\n", None),
        ("f(x=1, **{'x': 2})\n", None),
        ("f(**{'x': 1}, **{'x': 2})\n", None),
        (
            "from __future__ import annotations\nvalue: f(x=1, x=2)\n",
            None,
        ),
        (
            "from __future__ import annotations\ndef f(x: g(x=1, x=2)) -> g(x=1, x=2): pass\n",
            None,
        ),
        ("def f():\n    value: g(x=1, x=2)\n", None),
        ("def f():\n    value: (lambda: g(x=1, x=2))\n", None),
        (
            "from __future__ import annotations\nvalue: (lambda: g(x=1, x=2))\n",
            None,
        ),
        (
            "class C:\n    value: f(x=1, x=2)\ng(y=1, y=2)\n",
            Some("keyword argument repeated: x"),
        ),
        (
            "first: f(x=1, x=2)\nsecond: g(y=1, y=2)\n",
            Some("keyword argument repeated: x"),
        ),
        (
            "f(x=1, x=2).attr: g(y=1, y=2)\n",
            Some("keyword argument repeated: x"),
        ),
        (
            "f(x=1, x=2).attr: g(y=1, y=2) = 0\n",
            Some("keyword argument repeated: x"),
        ),
        (
            "f(x=1, x=2)[0]: g(y=1, y=2)\n",
            Some("keyword argument repeated: x"),
        ),
        (
            "f(x=1, x=2).attr: g(y=1, y=2) = h(z=1, z=2)\n",
            Some("keyword argument repeated: z"),
        ),
        (
            "from __future__ import annotations\nvalue: f(x=1, x=2)\ng(y=1, y=2)\n",
            Some("keyword argument repeated: y"),
        ),
    ];
    let annotation_cases = [
        (
            "value: f(x=1, x=2)\ng(y=1, y=2)\n",
            Some("keyword argument repeated: x"),
            Some("keyword argument repeated: y"),
        ),
        (
            "class C:\n    value: f(x=1, x=2)\n    g(y=1, y=2)\n",
            Some("keyword argument repeated: x"),
            Some("keyword argument repeated: y"),
        ),
        (
            "if flag:\n    value: f(x=1, x=2)\n    g(y=1, y=2)\n",
            Some("keyword argument repeated: x"),
            Some("keyword argument repeated: y"),
        ),
        (
            "value: f(x=1, x=2)\nclass C:\n    g(y=1, y=2)\n",
            Some("keyword argument repeated: x"),
            Some("keyword argument repeated: y"),
        ),
        (
            "obj.attr: f(x=1, x=2)\n",
            Some("keyword argument repeated: x"),
            None,
        ),
    ];
    let directory = tempfile::tempdir().unwrap();
    let path = directory.path().join("repeated_keywords.py");
    for version in common::PYTHON_VERSIONS {
        let modern = matches!(version, PythonVersion::Py314 | PythonVersion::Py315);
        let annotations = annotation_cases
            .iter()
            .map(|&(source, legacy, deferred)| (source, if modern { deferred } else { legacy }));
        for (source, mut expected) in cases.into_iter().chain(annotations) {
            if source.starts_with("f(x=1, x=2)\nfrom __future__")
                && matches!(version, PythonVersion::Py312 | PythonVersion::Py313)
            {
                expected = Some("keyword argument repeated: x");
            }
            common::diagnostics::assert_compilation(source, &path, version, expected);
        }
    }
}