@@ -711,6 +711,9 @@ module Make (Context : C) (FlowAPIUtils : F with type cx = Context.t) :
711711 (* Track parameter binding def_locs currently being processed, so that we can
712712 error when these appear in the corresponding annotation. *)
713713 current_bindings : string L.LMap .t ;
714+ (* Track when we're visiting a parameter default expression, so we can
715+ produce the appropriate error message for self-references. *)
716+ in_param_default : bool ;
714717 }
715718
716719 type pattern_write_kind =
@@ -1110,6 +1113,7 @@ module Make (Context : C) (FlowAPIUtils : F with type cx = Context.t) :
11101113 jsx_base_name;
11111114 pred_func_map = L.LMap. empty;
11121115 current_bindings = L.LMap. empty;
1116+ in_param_default = false ;
11131117 }
11141118
11151119 method jsx_base_name = env_state.jsx_base_name
@@ -2259,6 +2263,31 @@ module Make (Context : C) (FlowAPIUtils : F with type cx = Context.t) :
22592263 | _ -> () );
22602264 env_state < - { env_state with write_entries }
22612265
2266+ (* Override pattern_object_property to detect SELF-references in pattern-inline defaults.
2267+ For {a = a}, check if a's default references a itself (self-reference) - ERROR.
2268+ For {a, b = a}, b's default references a, not b - OK (not a self-reference).
2269+ For {a = b, b}, a's default references b - handled by reference-before-declaration.
2270+ We only check for self-references here; forward references are caught elsewhere. *)
2271+ method! pattern_object_property ?kind prop =
2272+ let open Ast.Pattern.Object.Property in
2273+ let (loc, { key; pattern; default; shorthand }) = prop in
2274+ ignore @@ this#pattern_object_property_key ?kind key;
2275+ ignore @@ this#pattern_object_property_pattern ?kind pattern;
2276+ Base.Option. iter default ~f: (fun default_expr ->
2277+ this#visit_default_with_pattern_bindings pattern default_expr
2278+ );
2279+ (loc, { key; pattern; default; shorthand })
2280+
2281+ method! function_param param =
2282+ let open Ast.Function.Param in
2283+ let (loc, { argument; default }) = param in
2284+ this#visit_function_or_component_param_pattern ~is_rest: false argument;
2285+ ignore @@ super#function_param_pattern argument;
2286+ Base.Option. iter default ~f: (fun default_expr ->
2287+ this#visit_default_with_pattern_bindings argument default_expr
2288+ );
2289+ (loc, { argument; default })
2290+
22622291 (* This method is called during every read of an identifier. We need to ensure that
22632292 * if the identifier is refined that we record the refiner as the write that reaches
22642293 * this read
@@ -2308,6 +2337,18 @@ module Make (Context : C) (FlowAPIUtils : F with type cx = Context.t) :
23082337 env_state < - { env_state with current_bindings = old_val }
23092338 )
23102339
2340+ method private with_in_param_default ~f =
2341+ let old_in_param_default = env_state.in_param_default in
2342+ env_state < - { env_state with in_param_default = true };
2343+ Exception. protect ~f ~finally: (fun () ->
2344+ env_state < - { env_state with in_param_default = old_in_param_default }
2345+ )
2346+
2347+ method private visit_default_with_pattern_bindings pattern default_expr =
2348+ this#with_current_pattern_bindings pattern ~f: (fun () ->
2349+ this#with_in_param_default ~f: (fun () -> ignore @@ this#expression default_expr)
2350+ )
2351+
23112352 (* Override the object type constuctor to disable the EReferenceInAnnotation check
23122353 * since this is a common and safe way to encode recursive object types. *)
23132354 method! object_type loc ot =
@@ -2334,6 +2375,20 @@ module Make (Context : C) (FlowAPIUtils : F with type cx = Context.t) :
23342375 add_output (Error_message. EReferenceInAnnotation (def_loc, name, loc))
23352376 )
23362377
2378+ method private error_on_reference_to_currently_declared_id_in_default id =
2379+ let (loc, { Ast.Identifier. name; _ }) = id in
2380+ let { val_ref = _; def_loc; _ } = this#env_read name in
2381+ Base.Option. iter def_loc ~f: (fun def_loc ->
2382+ match L.LMap. find_opt def_loc env_state.current_bindings with
2383+ | None -> ()
2384+ | Some binding_name ->
2385+ (* Only add the error - do NOT modify binding state (val_ref, write_entries).
2386+ For forward references (e.g., `b` in `{a = b, b}`), modifying the binding
2387+ state would corrupt it and cause spurious "name-already-bound" errors
2388+ when the actual binding is later processed. *)
2389+ add_output (Error_message. EReferenceInDefault (def_loc, binding_name, loc))
2390+ )
2391+
23372392 method! type_identifier_reference id =
23382393 this#error_on_reference_to_currently_declared_id id;
23392394 super#type_identifier_reference id
@@ -2352,6 +2407,8 @@ module Make (Context : C) (FlowAPIUtils : F with type cx = Context.t) :
23522407
23532408 method! identifier (ident : (ALoc.t, ALoc.t ) Ast.Identifier. t) =
23542409 let (loc, { Ast.Identifier. name = x; comments = _ }) = ident in
2410+ if env_state.in_param_default then
2411+ this#error_on_reference_to_currently_declared_id_in_default ident;
23552412 this#any_identifier loc x;
23562413 super#identifier ident
23572414
@@ -6513,17 +6570,27 @@ module Make (Context : C) (FlowAPIUtils : F with type cx = Context.t) :
65136570 | _ -> ()
65146571
65156572 method! component_param param =
6516- let (_, { Ast.Statement.ComponentDeclaration.Param. name; _ }) = param in
6573+ let (loc, { Ast.Statement.ComponentDeclaration.Param. name; local; default; shorthand }) =
6574+ param
6575+ in
65176576 begin
65186577 match name with
65196578 | Ast.Statement.ComponentDeclaration.Param. Identifier
6520- (loc , { Ast.Identifier. name = " ref" ; _ })
6579+ (ref_loc , { Ast.Identifier. name = " ref" ; _ })
65216580 | Ast.Statement.ComponentDeclaration.Param. StringLiteral
6522- (loc , { Ast.StringLiteral. value = " ref" ; _ }) ->
6523- this#any_identifier loc " React"
6581+ (ref_loc , { Ast.StringLiteral. value = " ref" ; _ }) ->
6582+ this#any_identifier ref_loc " React"
65246583 | _ -> ()
65256584 end ;
6526- super#component_param param
6585+ ignore @@ this#component_param_name name;
6586+ this#visit_function_or_component_param_pattern ~is_rest: false local;
6587+ this#with_current_pattern_bindings local ~f: (fun () ->
6588+ ignore @@ super#component_param_pattern local;
6589+ Base.Option. iter default ~f: (fun default_expr ->
6590+ this#with_in_param_default ~f: (fun () -> ignore @@ this#expression default_expr)
6591+ )
6592+ );
6593+ (loc, { Ast.Statement.ComponentDeclaration.Param. name; local; default; shorthand })
65276594
65286595 method! jsx_element loc expr =
65296596 let open Ast.JSX in
0 commit comments