From 9a24e99d35a4bdce56a0460e4dea7094dd34070c Mon Sep 17 00:00:00 2001 From: Bagaev Dmitry Date: Tue, 15 Sep 2026 14:45:14 +0200 Subject: [PATCH] fix!: reject keyword arguments in `@model` signatures instead of warning Keyword arguments in a `@model` function signature were parsed and then silently dropped, with only a `@warn`. The body never saw them, so a user writing `@model function m(x; a = 1)` got no `a` and a later `UndefVarError` as the only real signal, and the warning is easy to miss in a noisy REPL. Raise an error at macro expansion instead, consistent with unsupported positional arguments at the call site, and name the offending keyword arguments plus the supported form. BREAKING: `@model` definitions carrying keyword arguments now fail to expand rather than silently ignoring them. Nothing in this repository relied on the old behaviour: no test or doc asserted the warning, and no `@model` signature with keyword arguments exists in `src/`, `test/`, `docs/` or `README.md`. Downstream packages that defined such signatures will need to drop them, but their keyword arguments were never in effect to begin with. Closes #307. Co-Authored-By: Claude Opus 5 (1M context) --- src/model_macro.jl | 8 +++++++- test/model_macro_tests.jl | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/model_macro.jl b/src/model_macro.jl index 5a1388f8..3a30e3ab 100644 --- a/src/model_macro.jl +++ b/src/model_macro.jl @@ -957,7 +957,13 @@ function model_macro_interior(backend_type, model_specification) num_interfaces = Base.length(ms_args) if !isnothing(ms_kwargs) && length(ms_kwargs) > 0 - @warn("Model specification language does not support keyword arguments. Ignoring $(length(ms_kwargs)) keyword arguments.") + # Keyword arguments in a model signature were previously parsed and then silently dropped, with only + # a warning. The body never saw them, so the only real signal was a later `UndefVarError`. Fail here + # instead, consistent with how unsupported positional arguments are rejected at the call site. + kwargs_names = map(kwarg -> (kwarg isa Expr && kwarg.head === :kw) ? kwarg.args[1] : kwarg, ms_kwargs) + error( + "The `$(ms_name)` model macro does not support keyword arguments in the model signature, but got $(length(ms_kwargs)): $(join(kwargs_names, ", ")). Declare all model interfaces as positional arguments, `$(ms_name)($(join(ms_args, ", ")))`, they are passed by name at the call site." + ) end boilerplate_functions = GraphPPL.get_boilerplate_functions(backend_type, ms_name, ms_args, num_interfaces) diff --git a/test/model_macro_tests.jl b/test/model_macro_tests.jl index 30d53bd3..d629fd37 100644 --- a/test/model_macro_tests.jl +++ b/test/model_macro_tests.jl @@ -2070,3 +2070,28 @@ end somemodel() ) end + +@testitem "`@model` should reject keyword arguments in the model signature" begin + import GraphPPL: model_macro_interior + + include("testutils.jl") + + # Keyword arguments used to be parsed and then silently dropped with only a warning, so the body + # never saw them and the user's only signal was a later `UndefVarError` + kwargs_spec = :(function model_with_kwargs(x; a = 1, b = 2) + y ~ Normal(x, a) + end) + + @test_throws "does not support keyword arguments in the model signature" model_macro_interior( + TestUtils.TestGraphPPLBackend, kwargs_spec + ) + # the offending keyword arguments are named, and the supported form is shown + @test_throws "but got 2: a, b" model_macro_interior(TestUtils.TestGraphPPLBackend, kwargs_spec) + @test_throws "model_with_kwargs(x)" model_macro_interior(TestUtils.TestGraphPPLBackend, kwargs_spec) + + # positional-only signatures are unaffected + positional_spec = :(function model_without_kwargs(x, a) + y ~ Normal(x, a) + end) + @test model_macro_interior(TestUtils.TestGraphPPLBackend, positional_spec) isa Expr +end