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