Skip to content

Commit fe25788

Browse files
authored
Merge pull request #4119 from rickyvetter/ppx-loc
Change ReactJS PPX to avoid modifying locations of existing code
2 parents 50d60fe + f8dc8d5 commit fe25788

File tree

6 files changed

+1283
-865
lines changed

6 files changed

+1283
-865
lines changed

jscomp/syntax/reactjs_jsx_ppx.cppo.ml

Lines changed: 113 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,8 @@ let makeExternalDecl fnName loc namedArgListWithKeyAndRef namedTypeList =
320320
(List.map pluckLabelDefaultLocType namedArgListWithKeyAndRef)
321321
(makePropsType ~loc namedTypeList)
322322

323+
let unerasableIgnore loc = ({loc; txt = "warning"}, (PStr [Str.eval (Exp.constant (Pconst_string ("-16", None)))]))
324+
323325
(* TODO: some line number might still be wrong *)
324326
let jsxMapper () =
325327

@@ -516,13 +518,12 @@ let jsxMapper () =
516518
| _ -> None) in
517519

518520
recursivelyTransformNamedArgsForMake mapper expression ((arg, default, pattern, alias, pattern.ppat_loc, type_) :: list)
519-
520521
| Pexp_fun (Nolabel, _, { ppat_desc = (Ppat_construct ({txt = Lident "()"}, _) | Ppat_any)}, expression) ->
521-
(expression.pexp_desc, list, None)
522+
(list, None)
522523
| Pexp_fun (Nolabel, _, { ppat_desc = Ppat_var ({txt})}, expression) ->
523-
(expression.pexp_desc, list, Some txt)
524+
(list, Some txt)
524525

525-
| innerExpression -> (innerExpression, list, None)
526+
| _ -> (list, None)
526527
in
527528

528529

@@ -624,53 +625,80 @@ let jsxMapper () =
624625
valueBindings
625626
)
626627
} ->
628+
let fileName = filenameFromLoc pstr_loc in
629+
let emptyLoc = Location.in_file fileName in
627630
let mapBinding binding = if (hasAttrOnBinding binding) then
628631
let fnName = getFnName binding in
629-
let fileName = filenameFromLoc pstr_loc in
630632
let fullModuleName = makeModuleName fileName !nestedModules fnName in
631-
let emptyLoc = Location.in_file fileName in
632-
let modifiedBinding binding =
633+
let modifiedBindingOld binding =
633634
let expression = binding.pvb_expr in
634-
let wrapExpressionWithBinding expressionFn expression = {(filterAttrOnBinding binding) with pvb_expr = expressionFn expression} in
635635
(* TODO: there is a long-tail of unsupported features inside of blocks - Pexp_letmodule , Pexp_letexception , Pexp_ifthenelse *)
636636
let rec spelunkForFunExpression expression = (match expression with
637637
(* let make = (~prop) => ... *)
638638
| {
639639
pexp_desc = Pexp_fun _
640-
} -> ((fun expressionDesc -> {expression with pexp_desc = expressionDesc}), expression)
640+
} -> expression
641641
(* let make = {let foo = bar in (~prop) => ...} *)
642642
| {
643643
pexp_desc = Pexp_let (recursive, vbs, returnExpression)
644644
} ->
645645
(* here's where we spelunk! *)
646-
let (wrapExpression, realReturnExpression) = spelunkForFunExpression returnExpression in
647-
((fun expressionDesc -> {expression with pexp_desc = Pexp_let (recursive, vbs, wrapExpression expressionDesc)}), realReturnExpression)
646+
spelunkForFunExpression returnExpression
648647
(* let make = React.forwardRef((~prop) => ...) *)
649648

650649
| { pexp_desc = Pexp_apply (wrapperExpression, [(Nolabel, innerFunctionExpression)]) } ->
651-
let (wrapExpression, realReturnExpression) = spelunkForFunExpression innerFunctionExpression in
652-
((fun expressionDesc -> {
653-
expression with pexp_desc =
654-
Pexp_apply (wrapperExpression, [(nolabel, wrapExpression expressionDesc)])
655-
}),
656-
realReturnExpression
657-
)
650+
spelunkForFunExpression innerFunctionExpression
658651
| {
659652
pexp_desc = Pexp_sequence (wrapperExpression, innerFunctionExpression)
660653
} ->
661-
let (wrapExpression, realReturnExpression) = spelunkForFunExpression innerFunctionExpression in
662-
((fun expressionDesc -> {
663-
expression with pexp_desc =
664-
Pexp_sequence (wrapperExpression, wrapExpression expressionDesc)
665-
}),
666-
realReturnExpression
667-
)
654+
spelunkForFunExpression innerFunctionExpression
668655
| _ -> raise (Invalid_argument "react.component calls can only be on function definitions or component wrappers (forwardRef, memo).")
669656
) in
670-
let (wrapExpression, expression) = spelunkForFunExpression expression in
671-
(wrapExpressionWithBinding wrapExpression, expression)
657+
spelunkForFunExpression expression
672658
in
673-
let (bindingWrapper, expression) = modifiedBinding binding in
659+
let modifiedBinding binding =
660+
let wrapExpressionWithBinding expressionFn expression = Vb.mk ~attrs:(List.filter otherAttrsPure binding.pvb_attributes) (Pat.var {loc = emptyLoc; txt = fnName}) (expressionFn expression) in
661+
let expression = binding.pvb_expr in
662+
let unerasableIgnoreExp exp = { exp with pexp_attributes = (unerasableIgnore emptyLoc) :: exp.pexp_attributes } in
663+
(* TODO: there is a long-tail of unsupported features inside of blocks - Pexp_letmodule , Pexp_letexception , Pexp_ifthenelse *)
664+
let rec spelunkForFunExpression expression = (match expression with
665+
(* let make = (~prop) => ... with no final unit *)
666+
| {
667+
pexp_desc = Pexp_fun ((Labelled(_) | Optional(_) as label), default, pattern, ({pexp_desc = Pexp_fun _} as internalExpression))
668+
} ->
669+
let (wrap, hasUnit, exp) = spelunkForFunExpression internalExpression in
670+
(wrap, hasUnit, unerasableIgnoreExp {expression with pexp_desc = Pexp_fun (label, default, pattern, exp)})
671+
(* let make = (()) => ... *)
672+
(* let make = (_) => ... *)
673+
| {
674+
pexp_desc = Pexp_fun (Nolabel, default, { ppat_desc = Ppat_construct ({txt = Lident "()"}, _) | Ppat_any}, internalExpression)
675+
} -> ((fun a -> a), true, expression)
676+
(* let make = (~prop) => ... *)
677+
| {
678+
pexp_desc = Pexp_fun (label, default, pattern, internalExpression)
679+
} -> ((fun a -> a), false, unerasableIgnoreExp expression)
680+
(* let make = {let foo = bar in (~prop) => ...} *)
681+
| {
682+
pexp_desc = Pexp_let (recursive, vbs, internalExpression)
683+
} ->
684+
(* here's where we spelunk! *)
685+
let (wrap, hasUnit, exp) = spelunkForFunExpression internalExpression in
686+
(wrap, hasUnit, {expression with pexp_desc = Pexp_let (recursive, vbs, exp)})
687+
(* let make = React.forwardRef((~prop) => ...) *)
688+
| { pexp_desc = Pexp_apply (wrapperExpression, [(Nolabel, internalExpression)]) } ->
689+
let (_, hasUnit, exp) = spelunkForFunExpression internalExpression in
690+
((fun exp -> Exp.apply wrapperExpression [(nolabel, exp)]), hasUnit, exp)
691+
| {
692+
pexp_desc = Pexp_sequence (wrapperExpression, internalExpression)
693+
} ->
694+
let (wrap, hasUnit, exp) = spelunkForFunExpression internalExpression in
695+
(wrap, hasUnit, {expression with pexp_desc = Pexp_sequence (wrapperExpression, exp)})
696+
| e -> ((fun a -> a), false, e)
697+
) in
698+
let (wrapExpression, hasUnit, expression) = spelunkForFunExpression expression in
699+
(wrapExpressionWithBinding wrapExpression, hasUnit, expression)
700+
in
701+
let (bindingWrapper, hasUnit, expression) = modifiedBinding binding in
674702
let reactComponentAttribute = try
675703
Some(List.find hasAttr binding.pvb_attributes)
676704
with | Not_found -> None in
@@ -679,41 +707,43 @@ let jsxMapper () =
679707
| None -> (emptyLoc, None) in
680708
let props = getPropsAttr payload in
681709
(* do stuff here! *)
682-
let (innerFunctionExpression, namedArgList, forwardRef) = recursivelyTransformNamedArgsForMake mapper expression [] in
710+
let (namedArgList, forwardRef) = recursivelyTransformNamedArgsForMake mapper (modifiedBindingOld binding) [] in
711+
let binding = { binding with pvb_expr = expression; pvb_attributes = [] } in
683712
let namedArgListWithKeyAndRef = (optional("key"), None, Pat.var {txt = "key"; loc = emptyLoc}, "key", emptyLoc, Some(keyType emptyLoc)) :: namedArgList in
684713
let namedArgListWithKeyAndRef = match forwardRef with
685714
| Some(_) -> (optional("ref"), None, Pat.var {txt = "key"; loc = emptyLoc}, "ref", emptyLoc, None) :: namedArgListWithKeyAndRef
686715
| None -> namedArgListWithKeyAndRef
687716
in
688-
let namedTypeList = List.fold_left argToType [] namedArgList in
689-
let externalDecl = makeExternalDecl fnName attr_loc namedArgListWithKeyAndRef namedTypeList in
690-
let makeLet innerExpression (label, default, pattern, _alias, loc, _type) =
691-
let labelString = (match label with | label when isOptional label || isLabelled label -> getLabel label | _ -> raise (Invalid_argument "This should never happen")) in
692-
let expression = (Exp.apply ~loc
693-
(Exp.ident ~loc {txt = (Lident "##"); loc })
694-
[
695-
(nolabel, Exp.ident ~loc {txt = (Lident props.propsName); loc });
696-
(nolabel, Exp.ident ~loc {
697-
txt = (Lident labelString);
717+
let namedArgListWithKeyAndRefForNew = match forwardRef with
718+
| Some(_) -> namedArgList @ [(nolabel, None, Pat.var {txt = "ref"; loc = emptyLoc}, "ref", emptyLoc, None)]
719+
| None -> namedArgList
720+
in
721+
let pluckArg (label, _, _, alias, loc, _) =
722+
let labelString = (match label with | label when isOptional label || isLabelled label -> getLabel label | _ -> "") in
723+
(label,
724+
(match labelString with
725+
| "" -> (Exp.ident ~loc {
726+
txt = (Lident alias);
698727
loc
699728
})
700-
]
729+
| labelString -> (Exp.apply ~loc
730+
(Exp.ident ~loc {txt = (Lident "##"); loc })
731+
[
732+
(nolabel, Exp.ident ~loc {txt = (Lident props.propsName); loc });
733+
(nolabel, Exp.ident ~loc {
734+
txt = (Lident labelString);
735+
loc
736+
})
737+
]
738+
)
739+
)
701740
) in
702-
let expression = match (default) with
703-
| (Some default) -> Exp.match_ expression [
704-
Exp.case
705-
(Pat.construct {loc; txt=Lident "Some"} (Some (Pat.var ~loc {txt = labelString; loc})))
706-
(Exp.ident ~loc {txt = (Lident labelString); loc = { loc with Location.loc_ghost = true }});
707-
Exp.case
708-
(Pat.construct {loc; txt=Lident "None"} None)
709-
default
710-
]
711-
| None -> expression in
712-
let letExpression = Vb.mk
713-
pattern
714-
expression in
715-
Exp.let_ ~loc Nonrecursive [letExpression] innerExpression in
716-
let innerExpression = List.fold_left makeLet (Exp.mk innerFunctionExpression) namedArgList in
741+
let namedTypeList = List.fold_left argToType [] namedArgList in
742+
let loc = emptyLoc in
743+
let externalDecl = makeExternalDecl fnName loc namedArgListWithKeyAndRef namedTypeList in
744+
let innerExpressionArgs = (List.map pluckArg namedArgListWithKeyAndRefForNew) @
745+
if hasUnit then [(Nolabel, Exp.construct {loc; txt = Lident "()"} None)] else [] in
746+
let innerExpression = Exp.apply (Exp.ident {loc; txt = Lident(fnName)}) innerExpressionArgs in
717747
let innerExpressionWithRef = match (forwardRef) with
718748
| Some txt ->
719749
{innerExpression with pexp_desc = Pexp_fun (nolabel, None, {
@@ -723,51 +753,59 @@ let jsxMapper () =
723753
}, innerExpression)}
724754
| None -> innerExpression
725755
in
726-
let fullExpression = (Pexp_fun (
727-
nolabel,
728-
None,
756+
let fullExpression = Exp.fun_
757+
nolabel
758+
None
729759
{
730760
ppat_desc = Ppat_constraint (
731761
makePropsName ~loc:emptyLoc props.propsName,
732762
makePropsType ~loc:emptyLoc namedTypeList
733763
);
734764
ppat_loc = emptyLoc;
735765
ppat_attributes = [];
736-
},
737-
innerExpressionWithRef
738-
)) in
766+
}
767+
innerExpressionWithRef in
739768
let fullExpression = match (fullModuleName) with
740769
| ("") -> fullExpression
741-
| (txt) -> Pexp_let (
742-
Nonrecursive,
770+
| (txt) -> Exp.let_
771+
Nonrecursive
743772
[Vb.mk
744773
~loc:emptyLoc
745774
(Pat.var ~loc:emptyLoc {loc = emptyLoc; txt})
746-
(Exp.mk ~loc:emptyLoc fullExpression)
747-
],
748-
(Exp.ident ~loc:emptyLoc {loc = emptyLoc; txt = Lident txt})
749-
)
750-
in
775+
fullExpression
776+
]
777+
(Exp.ident ~loc:emptyLoc {loc = emptyLoc; txt = Lident txt}) in
751778
let newBinding = bindingWrapper fullExpression in
752-
(Some externalDecl, newBinding)
779+
(Some externalDecl, binding, Some newBinding)
753780
else
754-
(None, binding)
781+
(None, binding, None)
755782
in
756783
let structuresAndBinding = List.map mapBinding valueBindings in
757-
let otherStructures (extern, binding) (externs, bindings) =
784+
let otherStructures (extern, binding, newBinding) (externs, bindings, newBindings) =
758785
let externs = match extern with
759786
| Some extern -> extern :: externs
760787
| None -> externs in
761-
(externs, binding :: bindings)
788+
let newBindings = match newBinding with
789+
| Some newBinding -> newBinding :: newBindings
790+
| None -> newBindings in
791+
(externs, binding :: bindings, newBindings)
762792
in
763-
let (externs, bindings) = List.fold_right otherStructures structuresAndBinding ([], []) in
764-
externs @ {
793+
let (externs, bindings, newBindings) = List.fold_right otherStructures structuresAndBinding ([], [], []) in
794+
externs @ [{
765795
pstr_loc;
766796
pstr_desc = Pstr_value (
767797
recFlag,
768798
bindings
769799
)
770-
} :: returnStructures
800+
}] @ (match newBindings with
801+
| [] -> []
802+
| newBindings -> [{
803+
pstr_loc = emptyLoc;
804+
pstr_desc = Pstr_value (
805+
recFlag,
806+
newBindings
807+
)
808+
}]) @ returnStructures
771809
| structure -> structure :: returnStructures in
772810

773811
let reactComponentTransform mapper structures =

0 commit comments

Comments
 (0)