@@ -7,7 +7,7 @@ expand(Meta, Args, E, RequireSize) ->
7
7
case ? key (E , context ) of
8
8
match ->
9
9
{EArgs , Alignment , EE } =
10
- expand (Meta , fun elixir_expand :expand /2 , Args , [], E , 0 , RequireSize ),
10
+ expand (Meta , fun elixir_expand :expand /2 , Args , [], E , E , 0 , RequireSize ),
11
11
12
12
case find_match (EArgs ) of
13
13
false ->
@@ -17,13 +17,13 @@ expand(Meta, Args, E, RequireSize) ->
17
17
end ;
18
18
_ ->
19
19
{EArgs , Alignment , {_EC , EV }} =
20
- expand (Meta , fun elixir_expand :expand_arg /2 , Args , [], {E , E }, 0 , RequireSize ),
20
+ expand (Meta , fun elixir_expand :expand_arg /2 , Args , [], {E , E }, E , 0 , RequireSize ),
21
21
{{'<<>>' , [{alignment , Alignment } | Meta ], EArgs }, EV }
22
22
end .
23
23
24
- expand (_BitstrMeta , _Fun , [], Acc , E , Alignment , _RequireSize ) ->
24
+ expand (_BitstrMeta , _Fun , [], Acc , E , _OriginalE , Alignment , _RequireSize ) ->
25
25
{lists :reverse (Acc ), Alignment , E };
26
- expand (BitstrMeta , Fun , [{'::' , Meta , [Left , Right ]} | T ], Acc , E , Alignment , RequireSize ) ->
26
+ expand (BitstrMeta , Fun , [{'::' , Meta , [Left , Right ]} | T ], Acc , E , OriginalE , Alignment , RequireSize ) ->
27
27
{ELeft , EL } = expand_expr (Meta , Left , Fun , E ),
28
28
29
29
% % Variables defined outside the binary can be accounted
@@ -38,7 +38,7 @@ expand(BitstrMeta, Fun, [{'::', Meta, [Left, Right]} | T], Acc, E, Alignment, Re
38
38
end ,
39
39
40
40
EType = expr_type (ELeft ),
41
- {ERight , EAlignment , ES } = expand_specs (EType , Meta , Right , EM , RequireSize or MatchSize ),
41
+ {ERight , EAlignment , ES } = expand_specs (EType , Meta , Right , EM , OriginalE , RequireSize or MatchSize ),
42
42
43
43
EE =
44
44
case EL of
@@ -65,15 +65,15 @@ expand(BitstrMeta, Fun, [{'::', Meta, [Left, Right]} | T], Acc, E, Alignment, Re
65
65
prepend_unless_bitstring_in_match (EType , Meta , ELeft , ERight , Acc , E )
66
66
end ,
67
67
68
- expand (BitstrMeta , Fun , T , EAcc , EE , alignment (Alignment , EAlignment ), RequireSize );
69
- expand (BitstrMeta , Fun , [{_ , Meta , _ } = H | T ], Acc , E , Alignment , RequireSize ) ->
68
+ expand (BitstrMeta , Fun , T , EAcc , EE , OriginalE , alignment (Alignment , EAlignment ), RequireSize );
69
+ expand (BitstrMeta , Fun , [{_ , Meta , _ } = H | T ], Acc , E , OriginalE , Alignment , RequireSize ) ->
70
70
{Expr , ES } = expand_expr (Meta , H , Fun , E ),
71
71
{EAcc , EAlignment } = wrap_expr (Expr , Acc ),
72
- expand (BitstrMeta , Fun , T , EAcc , ES , alignment (Alignment , EAlignment ), RequireSize );
73
- expand (Meta , Fun , [H | T ], Acc , E , Alignment , RequireSize ) ->
72
+ expand (BitstrMeta , Fun , T , EAcc , ES , OriginalE , alignment (Alignment , EAlignment ), RequireSize );
73
+ expand (Meta , Fun , [H | T ], Acc , E , OriginalE , Alignment , RequireSize ) ->
74
74
{Expr , ES } = expand_expr (Meta , H , Fun , E ),
75
75
{EAcc , EAlignment } = wrap_expr (Expr , Acc ),
76
- expand (Meta , Fun , T , EAcc , ES , alignment (Alignment , EAlignment ), RequireSize ).
76
+ expand (Meta , Fun , T , EAcc , ES , OriginalE , alignment (Alignment , EAlignment ), RequireSize ).
77
77
78
78
prepend_unless_bitstring_in_match (Type , Meta , Left , Right , Acc , E ) ->
79
79
Expr = {'::' , Meta , [Left , Right ]},
@@ -157,15 +157,15 @@ env_for_error(E) -> E.
157
157
158
158
% % Expands and normalizes types of a bitstring.
159
159
160
- expand_specs (ExprType , Meta , Info , E , RequireSize ) ->
160
+ expand_specs (ExprType , Meta , Info , E , OriginalE , RequireSize ) ->
161
161
Default =
162
162
#{size => default ,
163
163
unit => default ,
164
164
sign => default ,
165
165
type => default ,
166
166
endianness => default },
167
167
{#{size := Size , unit := Unit , type := Type , endianness := Endianness , sign := Sign }, ES } =
168
- expand_each_spec (Meta , unpack_specs (Info , []), Default , E ),
168
+ expand_each_spec (Meta , unpack_specs (Info , []), Default , E , OriginalE ),
169
169
MergedType = type (Meta , ExprType , Type , E ),
170
170
validate_size_required (Meta , RequireSize , ExprType , MergedType , Size , ES ),
171
171
SizeAndUnit = size_and_unit (Meta , ExprType , Size , Unit , ES ),
@@ -190,30 +190,30 @@ type(_, default, Type, _) ->
190
190
type (Meta , Other , Value , E ) ->
191
191
form_error (Meta , ? key (E , file ), ? MODULE , {bittype_mismatch , Value , Other , type }).
192
192
193
- expand_each_spec (Meta , [{Expr , _ , Args } = H | T ], Map , E ) when is_atom (Expr ) ->
193
+ expand_each_spec (Meta , [{Expr , _ , Args } = H | T ], Map , E , OriginalE ) when is_atom (Expr ) ->
194
194
case validate_spec (Expr , Args ) of
195
195
{Key , Arg } ->
196
196
{Value , EE } = expand_spec_arg (Arg , E ),
197
- validate_spec_arg (Meta , Key , Value , EE ),
197
+ validate_spec_arg (Meta , Key , Value , EE , OriginalE ),
198
198
199
199
case maps :get (Key , Map ) of
200
200
default -> ok ;
201
201
Value -> ok ;
202
202
Other -> form_error (Meta , ? key (E , file ), ? MODULE , {bittype_mismatch , Value , Other , Key })
203
203
end ,
204
204
205
- expand_each_spec (Meta , T , maps :put (Key , Value , Map ), EE );
205
+ expand_each_spec (Meta , T , maps :put (Key , Value , Map ), EE , OriginalE );
206
206
none ->
207
207
case 'Elixir.Macro' :expand (H , elixir_env :linify ({? line (Meta ), E })) of
208
208
H ->
209
209
form_error (Meta , ? key (E , file ), ? MODULE , {undefined_bittype , H });
210
210
NewTypes ->
211
- expand_each_spec (Meta , unpack_specs (NewTypes , []) ++ T , Map , E )
211
+ expand_each_spec (Meta , unpack_specs (NewTypes , []) ++ T , Map , E , OriginalE )
212
212
end
213
213
end ;
214
- expand_each_spec (Meta , [Expr | _ ], _Map , E ) ->
214
+ expand_each_spec (Meta , [Expr | _ ], _Map , E , _OriginalE ) ->
215
215
form_error (Meta , ? key (E , file ), ? MODULE , {undefined_bittype , Expr });
216
- expand_each_spec (_Meta , [], Map , E ) ->
216
+ expand_each_spec (_Meta , [], Map , E , _OriginalE ) ->
217
217
{Map , E }.
218
218
219
219
unpack_specs ({'-' , _ , [H , T ]}, Acc ) ->
@@ -251,17 +251,33 @@ validate_spec(_, _) -> none.
251
251
expand_spec_arg (Expr , E ) when is_atom (Expr ); is_integer (Expr ) -> {Expr , E };
252
252
expand_spec_arg (Expr , E ) -> elixir_expand :expand (Expr , E ).
253
253
254
- validate_spec_arg (Meta , size , Value , E ) ->
254
+ validate_spec_arg (Meta , size , Value , E , OriginalE ) ->
255
255
case Value of
256
- {Var , _ , Context } when is_atom (Var ) and is_atom (Context ) -> ok ;
257
- _ when is_integer (Value ) -> ok ;
258
- _ -> form_error (Meta , ? key (E , file ), ? MODULE , {bad_size_argument , Value })
256
+ {Var , _ , Context } when is_atom (Var ) and is_atom (Context ) ->
257
+ case is_valid_spec_arg_var ({Var , Context }, E , OriginalE ) of
258
+ true -> ok ;
259
+ false -> form_error (Meta , ? key (E , file ), ? MODULE , {undefined_var_in_spec , Value })
260
+ end ;
261
+ _ when is_integer (Value ) ->
262
+ ok ;
263
+ _ ->
264
+ form_error (Meta , ? key (E , file ), ? MODULE , {bad_size_argument , Value })
259
265
end ;
260
- validate_spec_arg (Meta , unit , Value , E ) when not is_integer (Value ) ->
266
+ validate_spec_arg (Meta , unit , Value , E , _OriginalE ) when not is_integer (Value ) ->
261
267
form_error (Meta , ? key (E , file ), ? MODULE , {bad_unit_argument , Value });
262
- validate_spec_arg (_Meta , _Key , _Value , _E ) ->
268
+ validate_spec_arg (_Meta , _Key , _Value , _E , _OriginalE ) ->
263
269
ok .
264
270
271
+ is_valid_spec_arg_var (Var , E , #{context := match } = OriginalE ) ->
272
+ case ? key (OriginalE , prematch_vars ) of
273
+ #{Var := _ } ->
274
+ true ;
275
+ _ ->
276
+ maps :is_key (Var , ? key (E , current_vars )) andalso
277
+ not maps :is_key (Var , ? key (OriginalE , current_vars ))
278
+ end ;
279
+ is_valid_spec_arg_var (_Var , _E , _OriginalE ) -> true .
280
+
265
281
validate_size_required (Meta , true , default , Type , default , E ) when Type == binary ; Type == bitstring ->
266
282
form_error (Meta , ? key (E , file ), ? MODULE , unsized_binary );
267
283
validate_size_required (_ , _ , _ , _ , _ , _ ) ->
@@ -375,4 +391,10 @@ format_error({nested_match, Expr}) ->
375
391
Message =
376
392
" cannot pattern match inside a bitstring "
377
393
" that is already in match, got: ~ts " ,
378
- io_lib :format (Message , ['Elixir.Macro' :to_string (Expr )]).
394
+ io_lib :format (Message , ['Elixir.Macro' :to_string (Expr )]);
395
+ format_error ({undefined_var_in_spec , Var }) ->
396
+ Message =
397
+ " undefined variable \" ~ts \" in bitstring segment. If the size of the binary is a "
398
+ " variable, the variable must be defined prior to its use in the binary/bitstring match "
399
+ " itself, or outside the pattern match" ,
400
+ io_lib :format (Message , ['Elixir.Macro' :to_string (Var )]).
0 commit comments