Skip to content

Commit 44397c7

Browse files
authored
Improve error messages in the macros (#4182)
1 parent ab719c8 commit 44397c7

21 files changed

Lines changed: 1792 additions & 671 deletions

docs/src/manual/constraints.md

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -178,25 +178,20 @@ julia> @variable(model, y[1:2, 1:2], Symmetric);
178178
179179
julia> @constraint(model, x >= y)
180180
ERROR: At none:1: `@constraint(model, x >= y)`:
181-
The syntax `x >= y` is ambiguous for matrices because we cannot tell if
182-
you intend a positive semidefinite constraint or an elementwise
183-
inequality.
184181
185-
To create a positive semidefinite constraint, pass `PSDCone()` or
186-
`HermitianPSDCone()`:
182+
The syntax `x >= y` is ambiguous for matrices because JuMP cannot determine if you intend a positive semidefinite constraint or an elementwise inequality.
187183
184+
To create a positive semidefinite constraint, pass `PSDCone()` or `HermitianPSDCone()`:
188185
```julia
189186
@constraint(model, x >= y, PSDCone())
190187
```
191-
192-
To create an element-wise inequality, pass `Nonnegatives()`, or use
193-
broadcasting:
194-
188+
To create an elementwise inequality, pass `Nonnegatives()` or use broadcasting:
195189
```julia
196190
@constraint(model, x >= y, Nonnegatives())
197191
# or
198192
@constraint(model, x .>= y)
199193
```
194+
200195
Stacktrace:
201196
[...]
202197
````

docs/src/manual/variables.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,9 +1148,14 @@ julia> type = :PSD
11481148
:PSD
11491149
11501150
julia> @variable(model, x[1:2, 1:2], type)
1151-
ERROR: At none:1: `@variable(model, x[1:2, 1:2], type)`: Unrecognized positional arguments: (:PSD,). (You may have passed it as a positional argument, or as a keyword value to `variable_type`.)
1151+
ERROR: At none:1: `@variable(model, x[1:2, 1:2], type)`:
1152+
1153+
Unrecognized positional arguments: (:PSD,).
1154+
1155+
You may have passed an unrecognized type or a keyword value to `variable_type`.
11521156
11531157
If you're trying to create a JuMP extension, you need to implement `build_variable`. Read the docstring for more details.
1158+
11541159
Stacktrace:
11551160
[...]
11561161
```
@@ -1212,9 +1217,14 @@ julia> type = :Symmetric
12121217
:Symmetric
12131218
12141219
julia> @variable(model, x[1:2, 1:2], type)
1215-
ERROR: At none:1: `@variable(model, x[1:2, 1:2], type)`: Unrecognized positional arguments: (:Symmetric,). (You may have passed it as a positional argument, or as a keyword value to `variable_type`.)
1220+
ERROR: At none:1: `@variable(model, x[1:2, 1:2], type)`:
1221+
1222+
Unrecognized positional arguments: (:Symmetric,).
1223+
1224+
You may have passed an unrecognized type or a keyword value to `variable_type`.
12161225
12171226
If you're trying to create a JuMP extension, you need to implement `build_variable`. Read the docstring for more details.
1227+
12181228
Stacktrace:
12191229
[...]
12201230
```

src/Containers/macro.jl

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,27 @@ function parse_macro_arguments(
4949
if Meta.isexpr(arg, :(=), 2)
5050
if haskey(kwargs, arg.args[1])
5151
error_fn(
52-
"the keyword argument `$(arg.args[1])` was given " *
53-
"multiple times.",
52+
"""
53+
The keyword argument `$(arg.args[1])` was given multiple times.
54+
""",
5455
)
5556
elseif valid_kwargs !== nothing && !(arg.args[1] in valid_kwargs)
56-
error_fn("unsupported keyword argument `$(arg.args[1])`.")
57+
error_fn(
58+
"""
59+
Unsupported keyword argument `$(arg.args[1])`.
60+
61+
If you are trying to construct an equality constraint, use `==` instead of `=`.
62+
""",
63+
)
5764
elseif !(arg.args[1] isa Symbol)
5865
error_fn(
59-
"Invalid keyword argument detected. If you are trying to " *
60-
"construct an equality constraint, use `==` instead of `=`.",
66+
"""
67+
Invalid keyword argument detected.
68+
69+
The left-hand side of the `=` operator was `$(arg.args[1])`.
70+
71+
If you are trying to construct an equality constraint, use `==` instead of `=`.
72+
""",
6173
)
6274
end
6375
kwargs[arg.args[1]] = arg.args[2]
@@ -69,14 +81,18 @@ function parse_macro_arguments(
6981
n = length(pos_args)
7082
if n != num_positional_args
7183
error_fn(
72-
"expected $num_positional_args positional arguments, got $n.",
84+
"""
85+
Expected $num_positional_args positional arguments, got $n.
86+
""",
7387
)
7488
end
7589
elseif num_positional_args isa UnitRange{Int}
7690
if !(length(pos_args) in num_positional_args)
7791
a, b = num_positional_args.start, num_positional_args.stop
7892
error_fn(
79-
"expected $a to $b positional arguments, got $(length(pos_args)).",
93+
"""
94+
Expected $a to $b positional arguments, got $(length(pos_args)).
95+
""",
8096
)
8197
end
8298
end
@@ -96,8 +112,9 @@ function _explicit_oneto(error_fn, index_set)
96112
$index_set
97113
catch
98114
$error_fn(
99-
"unexpected error parsing reference set: ",
115+
"Unexpected error parsing reference set: ",
100116
$(Meta.quot(_drop_esc(index_set))),
117+
"\n\nSee the \"caused by\" stacktrace below for the underlying error.\n",
101118
)
102119
end
103120
end
@@ -178,8 +195,11 @@ function _parse_ref_sets(error_fn::Function, expr::Expr)
178195
name = popfirst!(c.args)
179196
if !(name isa Symbol)
180197
error_fn(
181-
"Unsupported syntax: the expression `$name` cannot be used " *
182-
"as a name.",
198+
"""
199+
The expression `$name` cannot be used as a name.
200+
201+
Names must be symbols, not expressions, and they cannot contain spaces.
202+
""",
183203
)
184204
end
185205
end
@@ -188,9 +208,12 @@ function _parse_ref_sets(error_fn::Function, expr::Expr)
188208
# the second argument.
189209
if length(c.args) > 2
190210
error_fn(
191-
"Unsupported syntax $c: There can be at most one filtering " *
192-
"condition, which is separated from the indices by a single " *
193-
"`;`.",
211+
"""
212+
Unsupported syntax `$c`.
213+
214+
There can be at most one filtering condition, which is separated \
215+
from the indices by a single `;`.
216+
""",
194217
)
195218
elseif length(c.args) == 2
196219
condition = pop!(c.args)
@@ -247,7 +270,13 @@ function _has_dependent_sets(index_vars::Vector{Any}, index_sets::Vector{Any})
247270
end
248271

249272
function _container_name(error_fn::Function, x)
250-
return error_fn("Expression `$x::$(typeof(x))` cannot be used as a name.")
273+
return error_fn(
274+
"""
275+
Expression `$x::$(typeof(x))` cannot be used as a name.
276+
277+
Names must be symbols, not expressions, and they cannot contain spaces.
278+
""",
279+
)
251280
end
252281

253282
_container_name(::Function, expr::Union{Symbol,Nothing}) = expr
@@ -258,7 +287,13 @@ function _container_name(error_fn::Function, expr::Expr)
258287
elseif Meta.isexpr(expr, (:ref, :typed_vcat))
259288
return _container_name(error_fn, expr.args[1])
260289
end
261-
return error_fn("Expression $expr cannot be used as a name.")
290+
return error_fn(
291+
"""
292+
Expression `$expr` cannot be used as a name.
293+
294+
Names must be symbols, not expressions, and they cannot contain spaces.
295+
""",
296+
)
262297
end
263298

264299
"""
@@ -305,8 +340,11 @@ function parse_ref_sets(
305340
for name in invalid_index_variables
306341
if name in index_vars
307342
error_fn(
308-
"the index name `$name` conflicts with another variable in " *
309-
"this scope. Use a different name for the index.",
343+
"""
344+
The index name `$name` conflicts with another variable in this scope.
345+
346+
Use a different name for the index.
347+
""",
310348
)
311349
end
312350
end
@@ -342,7 +380,9 @@ additionally prints the macro from which it was called.
342380
"""
343381
function build_error_fn(macro_name, args, source)
344382
msg = build_macro_expression_string(macro_name, args)
345-
error_fn(str...) = error("At $(source.file):$(source.line): $msg: ", str...)
383+
function error_fn(str...)
384+
return error("At $(source.file):$(source.line): $msg:\n\n", str...)
385+
end
346386
return error_fn
347387
end
348388

@@ -357,8 +397,11 @@ function build_ref_sets(error_fn::Function, expr)
357397
index_vars, index_sets, condition = _parse_ref_sets(error_fn, expr)
358398
if any(_expr_is_splat, index_sets)
359399
error_fn(
360-
"cannot use splatting operator `...` in the definition of an " *
361-
"index set.",
400+
"""
401+
Cannot use the splatting operator `...` in the definition of an index set.
402+
403+
Each index must be explicitly separated by a comma.
404+
""",
362405
)
363406
end
364407
if !_has_dependent_sets(index_vars, index_sets) && condition == :()
@@ -377,8 +420,9 @@ function build_ref_sets(error_fn::Function, expr)
377420
$(index_sets[i])
378421
catch
379422
$error_fn(
380-
"unexpected error parsing reference set: ",
423+
"Unexpected error parsing reference set: ",
381424
$(Meta.quot(_drop_esc(index_sets[i]))),
425+
"\n\nSee the \"caused by\" stacktrace below for the underlying error.\n",
382426
)
383427
end
384428
end,
@@ -390,8 +434,9 @@ function build_ref_sets(error_fn::Function, expr)
390434
$(esc(condition))
391435
catch
392436
$error_fn(
393-
"unexpected error parsing condition: ",
437+
"Unexpected error parsing condition: ",
394438
$(Meta.quot(condition)),
439+
"\n\nSee the \"caused by\" stacktrace below for the underlying error.\n",
395440
)
396441
end
397442
end

src/complement.jl

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,25 @@
1010
# @constraint(model, 2x - 1 ⟂ x)
1111

1212
function _build_complements_constraint(
13-
errorf::Function,
13+
error_fn::Function,
1414
F::AbstractArray{<:Union{Real,AbstractJuMPScalar}},
1515
x::AbstractArray{<:AbstractVariableRef},
1616
)
1717
if size(F) != size(x)
18-
errorf(
19-
"size of mapping does not match size of variables: " *
20-
"$(size(F)) != $(size(x)).",
18+
error_fn(
19+
"""
20+
The number of elements in the left-hand side $(size(F)) does not match the right-hand side $(size(x)).
21+
22+
There must be a one-to-one mapping between the left- and right-hand \
23+
sides of a complementarity constraint.
24+
""",
2125
)
2226
end
2327
return VectorConstraint([F; x], MOI.Complements(2 * length(F)))
2428
end
2529

2630
function _build_complements_constraint(
27-
errorf::Function,
31+
error_fn::Function,
2832
F::Containers.SparseAxisArray{<:Union{Real,AbstractJuMPScalar}},
2933
x::Containers.SparseAxisArray{<:AbstractVariableRef},
3034
)
@@ -33,18 +37,31 @@ function _build_complements_constraint(
3337
if haskey(x, i)
3438
push!(elements, x[i])
3539
else
36-
errorf("keys of the SparseAxisArray's do not match.")
40+
error_fn(
41+
"""
42+
Keys of the SparseAxisArray's do not match.
43+
44+
The left-hand side has key `$i`, but the right-hand side does not.
45+
""",
46+
)
3747
end
3848
end
3949
return VectorConstraint(elements, MOI.Complements(length(elements)))
4050
end
4151

4252
function _build_complements_constraint(
43-
errorf::Function,
53+
error_fn::Function,
4454
::AbstractArray{<:Union{Real,AbstractJuMPScalar}},
45-
::AbstractArray{<:AbstractJuMPScalar},
55+
x::AbstractArray{<:AbstractJuMPScalar},
4656
)
47-
return errorf("second term must be an array of variables.")
57+
return error_fn(
58+
"""
59+
The right-hand side term in a complementarity constraint must be a \
60+
variable or an array of variables.
61+
62+
Currently, it is a `$(typeof(x))`.
63+
""",
64+
)
4865
end
4966

5067
function _build_complements_constraint(
@@ -56,20 +73,26 @@ function _build_complements_constraint(
5673
end
5774

5875
function _build_complements_constraint(
59-
errorf::Function,
76+
error_fn::Function,
6077
::Union{Real,AbstractJuMPScalar},
61-
::AbstractJuMPScalar,
78+
x::AbstractJuMPScalar,
6279
)
63-
return errorf("second term must be a variable.")
80+
return error_fn(
81+
"""
82+
The right-hand side term in a complementarity constraint must be a variable.
83+
84+
Currently, it is a `$(typeof(x))`.
85+
""",
86+
)
6487
end
6588

6689
function parse_constraint_call(
67-
errorf::Function,
90+
error_fn::Function,
6891
::Bool,
6992
::Union{Val{:complements},Val{:⟂}},
7093
F,
7194
x,
7295
)
73-
f, parse_code = _rewrite_expression(F)
74-
return parse_code, :(_build_complements_constraint($errorf, $f, $(esc(x))))
96+
f, parse = _rewrite_expression(F)
97+
return parse, :(_build_complements_constraint($error_fn, $f, $(esc(x))))
7598
end

0 commit comments

Comments
 (0)