diff --git a/cli/reactjs_jsx_ppx.ml b/cli/reactjs_jsx_ppx.ml index bc8ea02c..7b3a0096 100644 --- a/cli/reactjs_jsx_ppx.ml +++ b/cli/reactjs_jsx_ppx.ml @@ -78,6 +78,13 @@ let hasAttr (loc, _) = loc.txt = "react.component" let hasAttrOnBinding {pvb_attributes} = List.find_opt hasAttr pvb_attributes <> None +let raiseError ~loc msg = Location.raise_errorf ~loc msg + +let raiseErrorMultipleReactComponent ~loc = + raiseError ~loc + "Only one component definition is allowed for each module. Move to a \ + submodule or other file if necessary." + module V3 = struct let nolabel = Nolabel @@ -163,10 +170,9 @@ module V3 = struct | [] -> [] | [(Nolabel, {pexp_desc = Pexp_construct ({txt = Lident "()"}, None)})] -> acc - | (Nolabel, _) :: _rest -> - raise - (Invalid_argument - "JSX: found non-labelled argument before the last position") + | (Nolabel, {pexp_loc}) :: _rest -> + raiseError ~loc:pexp_loc + "JSX: found non-labelled argument before the last position" | arg :: rest -> allButLast_ rest (arg :: acc) [@@raises Invalid_argument] in @@ -185,9 +191,7 @@ module V3 = struct if removeLastPositionUnit then allButLast props else props ) | [(_, childrenExpr)], props -> (childrenExpr, if removeLastPositionUnit then allButLast props else props) - | _ -> - raise - (Invalid_argument "JSX: somehow there's more than one `children` label") + | _ -> raiseError ~loc "JSX: somehow there's more than one `children` label" [@@raises Invalid_argument] let unerasableIgnore loc = @@ -204,8 +208,8 @@ module V3 = struct match binding with | {ppat_desc = Ppat_var {txt}} -> txt | {ppat_desc = Ppat_constraint (pat, _)} -> getFnName pat - | _ -> - raise (Invalid_argument "react.component calls cannot be destructured.") + | {ppat_loc} -> + raiseError ~loc:ppat_loc "react.component calls cannot be destructured." [@@raises Invalid_argument] let makeNewBinding binding expression newName = @@ -218,8 +222,8 @@ module V3 = struct pvb_expr = expression; pvb_attributes = [merlinFocus]; } - | _ -> - raise (Invalid_argument "react.component calls cannot be destructured.") + | {pvb_loc} -> + raiseError ~loc:pvb_loc "react.component calls cannot be destructured." [@@raises Invalid_argument] (* Lookup the value of `props` otherwise raise Invalid_argument error *) @@ -227,11 +231,10 @@ module V3 = struct match (loc, exp) with | {txt = Lident "props"}, {pexp_desc = Pexp_ident {txt = Lident str}} -> {propsName = str} - | {txt}, _ -> - raise - (Invalid_argument - ("react.component only accepts props as an option, given: " - ^ Longident.last txt)) + | {txt; loc}, _ -> + raiseError ~loc + "react.component only accepts props as an option, given: { %s }" + (Longident.last txt) [@@raises Invalid_argument] (* Lookup the `props` record or string as part of [@react.component] and store the name for use when rewriting *) @@ -254,10 +257,9 @@ module V3 = struct } :: _rest)) -> {propsName = "props"} - | Some (PStr ({pstr_desc = Pstr_eval (_, _)} :: _rest)) -> - raise - (Invalid_argument - "react.component accepts a record config with props as an options.") + | Some (PStr ({pstr_desc = Pstr_eval (_, _); pstr_loc} :: _rest)) -> + raiseError ~loc:pstr_loc + "react.component accepts a record config with props as an options." | _ -> defaultProps [@@raises Invalid_argument] @@ -470,9 +472,8 @@ module V3 = struct | Lident path -> Lident (path ^ "Props") | Ldot (ident, path) -> Ldot (ident, path ^ "Props") | _ -> - raise - (Invalid_argument - "JSX name can't be the result of function applications") + raiseError ~loc + "JSX name can't be the result of function applications" in let props = Exp.apply ~attrs ~loc (Exp.ident ~loc {loc; txt = propsIdent}) args @@ -511,11 +512,10 @@ module V3 = struct } -> "createDOMElementVariadic" (* [@JSX] div(~children= value), coming from
...(value)
*) - | _ -> - raise - (Invalid_argument - "A spread as a DOM element's children don't make sense written \ - together. You can simply remove the spread.") + | {pexp_loc} -> + raiseError ~loc:pexp_loc + "A spread as a DOM element's children don't make sense written \ + together. You can simply remove the spread." in let args = match nonChildrenProps with @@ -560,16 +560,14 @@ module V3 = struct (* TODO: make this show up with a loc. *) | Pexp_fun (Labelled "key", _, _, _) | Pexp_fun (Optional "key", _, _, _) -> - raise - (Invalid_argument - "Key cannot be accessed inside of a component. Don't worry - you \ - can always key a component from its parent!") + raiseError ~loc:expr.pexp_loc + "Key cannot be accessed inside of a component. Don't worry - you can \ + always key a component from its parent!" | Pexp_fun (Labelled "ref", _, _, _) | Pexp_fun (Optional "ref", _, _, _) -> - raise - (Invalid_argument - "Ref cannot be passed as a normal prop. Please use `forwardRef` \ - API instead.") + raiseError ~loc:expr.pexp_loc + "Ref cannot be passed as a normal prop. Please use `forwardRef` API \ + instead." | Pexp_fun (arg, default, pattern, expression) when isOptional arg || isLabelled arg -> let () = @@ -759,10 +757,9 @@ module V3 = struct in [externalPropsDecl; newStructure] | _ -> - raise - (Invalid_argument - "Only one react.component call can exist on a component at one \ - time")) + raiseError ~loc:pstr_loc + "Only one react.component call can exist on a component at one time" + ) (* let component = ... *) | {pstr_loc; pstr_desc = Pstr_value (recFlag, valueBindings)} -> ( let fileName = filenameFromLoc pstr_loc in @@ -810,11 +807,10 @@ module V3 = struct | {pexp_desc = Pexp_constraint (innerFunctionExpression, _typ)} -> spelunkForFunExpression innerFunctionExpression - | _ -> - raise - (Invalid_argument - "react.component calls can only be on function \ - definitions or component wrappers (forwardRef, memo).") + | {pexp_loc} -> + raiseError ~loc:pexp_loc + "react.component calls can only be on function definitions \ + or component wrappers (forwardRef, memo)." [@@raises Invalid_argument] in spelunkForFunExpression expression @@ -1207,10 +1203,9 @@ module V3 = struct in [externalPropsDecl; newStructure] | _ -> - raise - (Invalid_argument - "Only one react.component call can exist on a component at one \ - time")) + raiseError ~loc:psig_loc + "Only one react.component call can exist on a component at one time" + ) | _ -> [item] [@@raises Invalid_argument] in @@ -1219,41 +1214,38 @@ module V3 = struct match callExpression.pexp_desc with | Pexp_ident caller -> ( match caller with - | {txt = Lident "createElement"} -> - raise - (Invalid_argument - "JSX: `createElement` should be preceeded by a module name.") + | {txt = Lident "createElement"; loc} -> + raiseError ~loc + "JSX: `createElement` should be preceeded by a module name." (* Foo.createElement(~prop1=foo, ~prop2=bar, ~children=[], ()) *) | {loc; txt = Ldot (modulePath, ("createElement" | "make"))} -> ( match config.version with | 3 -> transformUppercaseCall3 modulePath mapper loc attrs callExpression callArguments - | _ -> raise (Invalid_argument "JSX: the JSX version must be 3")) + | _ -> raiseError ~loc "JSX: the JSX version must be 3") (* div(~prop1=foo, ~prop2=bar, ~children=[bla], ()) *) (* turn that into ReactDOMRe.createElement(~props=ReactDOMRe.props(~props1=foo, ~props2=bar, ()), [|bla|]) *) | {loc; txt = Lident id} -> ( match config.version with | 3 -> transformLowercaseCall3 mapper loc attrs callArguments id - | _ -> raise (Invalid_argument "JSX: the JSX version must be 3")) - | {txt = Ldot (_, anythingNotCreateElementOrMake)} -> - raise - (Invalid_argument - ("JSX: the JSX attribute should be attached to a \ - `YourModuleName.createElement` or `YourModuleName.make` call. \ - We saw `" ^ anythingNotCreateElementOrMake ^ "` instead")) - | {txt = Lapply _} -> + | _ -> raiseError ~loc "JSX: the JSX version must be 3") + | {txt = Ldot (_, anythingNotCreateElementOrMake); loc} -> + raiseError ~loc + "JSX: the JSX attribute should be attached to a \ + `YourModuleName.createElement` or `YourModuleName.make` call. We \ + saw `%s` instead" + anythingNotCreateElementOrMake + | {txt = Lapply _; loc} -> (* don't think there's ever a case where this is reached *) - raise - (Invalid_argument - "JSX: encountered a weird case while processing the code. \ - Please report this!")) + raiseError ~loc + "JSX: encountered a weird case while processing the code. Please \ + report this!") | _ -> - raise - (Invalid_argument - "JSX: `createElement` should be preceeded by a simple, direct \ - module name.") + raiseError ~loc:callExpression.pexp_loc + "JSX: `createElement` should be preceeded by a simple, direct module \ + name." [@@raises Invalid_argument] in @@ -1385,7 +1377,7 @@ module V4 = struct type 'a children = ListLiteral of 'a | Exact of 'a (* if children is a list, convert it to an array while mapping each element. If not, just map over it, as usual *) - let transformChildrenIfListUpper ~loc ~mapper theList = + let transformChildrenIfListUpper ~mapper theList = let rec transformChildren_ theList accum = (* not in the sense of converting a list to an array; convert the AST reprensentation of a list to the AST reprensentation of an array *) @@ -1393,7 +1385,7 @@ module V4 = struct | {pexp_desc = Pexp_construct ({txt = Lident "[]"}, None)} -> ( match accum with | [singleElement] -> Exact singleElement - | accum -> ListLiteral (Exp.array ~loc (List.rev accum))) + | accum -> ListLiteral (Exp.array (List.rev accum))) | { pexp_desc = Pexp_construct @@ -1404,13 +1396,13 @@ module V4 = struct in transformChildren_ theList [] - let transformChildrenIfList ~loc ~mapper theList = + let transformChildrenIfList ~mapper theList = let rec transformChildren_ theList accum = (* not in the sense of converting a list to an array; convert the AST reprensentation of a list to the AST reprensentation of an array *) match theList with | {pexp_desc = Pexp_construct ({txt = Lident "[]"}, None)} -> - Exp.array ~loc (List.rev accum) + Exp.array (List.rev accum) | { pexp_desc = Pexp_construct @@ -1427,10 +1419,9 @@ module V4 = struct | [] -> [] | [(Nolabel, {pexp_desc = Pexp_construct ({txt = Lident "()"}, None)})] -> acc - | (Nolabel, _) :: _rest -> - raise - (Invalid_argument - "JSX: found non-labelled argument before the last position") + | (Nolabel, {pexp_loc}) :: _rest -> + raiseError ~loc:pexp_loc + "JSX: found non-labelled argument before the last position" | arg :: rest -> allButLast_ rest (arg :: acc) [@@raises Invalid_argument] in @@ -1445,13 +1436,11 @@ module V4 = struct with | [], props -> (* no children provided? Place a placeholder list *) - ( Exp.construct ~loc {loc; txt = Lident "[]"} None, + ( Exp.construct {loc = Location.none; txt = Lident "[]"} None, if removeLastPositionUnit then allButLast props else props ) | [(_, childrenExpr)], props -> (childrenExpr, if removeLastPositionUnit then allButLast props else props) - | _ -> - raise - (Invalid_argument "JSX: somehow there's more than one `children` label") + | _ -> raiseError ~loc "JSX: somehow there's more than one `children` label" [@@raises Invalid_argument] let merlinFocus = ({loc = Location.none; txt = "merlin.focus"}, PStr []) @@ -1464,8 +1453,8 @@ module V4 = struct match binding with | {ppat_desc = Ppat_var {txt}} -> txt | {ppat_desc = Ppat_constraint (pat, _)} -> getFnName pat - | _ -> - raise (Invalid_argument "react.component calls cannot be destructured.") + | {ppat_loc} -> + raiseError ~loc:ppat_loc "react.component calls cannot be destructured." [@@raises Invalid_argument] let makeNewBinding binding expression newName = @@ -1478,8 +1467,8 @@ module V4 = struct pvb_expr = expression; pvb_attributes = [merlinFocus]; } - | _ -> - raise (Invalid_argument "react.component calls cannot be destructured.") + | {pvb_loc} -> + raiseError ~loc:pvb_loc "react.component calls cannot be destructured." [@@raises Invalid_argument] (* Lookup the filename from the location information on the AST node and turn it into a valid module identifier *) @@ -1510,12 +1499,6 @@ module V4 = struct let fullModuleName = String.concat "$" fullModuleName in fullModuleName - let raiseError ~loc msg = Location.raise_errorf ~loc msg - - let raiseErrorMultipleReactComponent ~loc = - raiseError ~loc - "Only one component definition is allowed for each module. Move to a \ - submodule or other file if necessary." (* AST node builders These functions help us build AST nodes that are needed when transforming a [@react.component] into a @@ -1523,16 +1506,15 @@ module V4 = struct *) (* make record from props and spread props if exists *) - let recordFromProps ?(removeKey = false) callArguments = + let recordFromProps ~loc ?(removeKey = false) callArguments = let rec removeLastPositionUnitAux props acc = match props with | [] -> acc | [(Nolabel, {pexp_desc = Pexp_construct ({txt = Lident "()"}, None)})] -> acc - | (Nolabel, _) :: _rest -> - raise - (Invalid_argument - "JSX: found non-labelled argument before the last position") + | (Nolabel, {pexp_loc}) :: _rest -> + raiseError ~loc:pexp_loc + "JSX: found non-labelled argument before the last position" | prop :: rest -> removeLastPositionUnitAux rest (prop :: acc) in let props, propsToSpread = @@ -1561,19 +1543,19 @@ module V4 = struct | [] -> { pexp_desc = Pexp_record (fields, None); - pexp_loc = Location.none; + pexp_loc = loc; pexp_attributes = []; } | [spreadProps] -> { pexp_desc = Pexp_record (fields, Some spreadProps); - pexp_loc = Location.none; + pexp_loc = loc; pexp_attributes = []; } | spreadProps :: _ -> { pexp_desc = Pexp_record (fields, Some spreadProps); - pexp_loc = Location.none; + pexp_loc = loc; pexp_attributes = []; } @@ -1640,13 +1622,13 @@ module V4 = struct let makePropsRecordTypeSig propsName loc namedTypeList = Sig.type_ Nonrecursive (makeTypeDecls propsName loc namedTypeList) - let transformUppercaseCall3 ~config modulePath mapper loc attrs callArguments - = + let transformUppercaseCall3 ~config modulePath mapper jsxExprLoc callExprLoc + attrs callArguments = let children, argsWithLabels = - extractChildren ~loc ~removeLastPositionUnit:true callArguments + extractChildren ~removeLastPositionUnit:true ~loc:jsxExprLoc callArguments in let argsForMake = argsWithLabels in - let childrenExpr = transformChildrenIfListUpper ~loc ~mapper children in + let childrenExpr = transformChildrenIfListUpper ~mapper children in let recursivelyTransformedArgsForMake = argsForMake |> List.map (fun (label, expression) -> @@ -1674,7 +1656,8 @@ module V4 = struct | _ -> [ ( labelled "children", - Exp.ident ~loc {loc; txt = Ldot (Lident "React", "null")} ); + Exp.ident + {loc = Location.none; txt = Ldot (Lident "React", "null")} ); ]) in @@ -1702,9 +1685,10 @@ module V4 = struct match config.mode with (* The new jsx transform *) | "automatic" -> - let record = recordFromProps ~removeKey:true args in + let record = recordFromProps ~loc:jsxExprLoc ~removeKey:true args in let props = - if isEmptyRecord record then recordWithOnlyKey ~loc else record + if isEmptyRecord record then recordWithOnlyKey ~loc:jsxExprLoc + else record in let keyProp = args |> List.filter (fun (arg_label, _) -> "key" = getLabel arg_label) @@ -1727,22 +1711,30 @@ module V4 = struct [] ) in Exp.apply ~attrs jsxExpr - ([(nolabel, Exp.ident {txt = ident; loc}); (nolabel, props)] @ key) + ([ + (nolabel, Exp.ident {txt = ident; loc = callExprLoc}); + (nolabel, props); + ] + @ key) | _ -> ( - let record = recordFromProps args in + let record = recordFromProps ~loc:jsxExprLoc args in (* check if record which goes to Foo.make({ ... } as record) empty or not if empty then change it to {key: @optional None} only for upper case jsx This would be redundant regarding PR progress https://github.com/rescript-lang/syntax/pull/299 *) let props = - if isEmptyRecord record then recordWithOnlyKey ~loc else record + if isEmptyRecord record then recordWithOnlyKey ~loc:jsxExprLoc + else record in match !childrenArg with | None -> Exp.apply ~attrs (Exp.ident {loc = Location.none; txt = Ldot (Lident "React", "createElement")}) - [(nolabel, Exp.ident {txt = ident; loc}); (nolabel, props)] + [ + (nolabel, Exp.ident {txt = ident; loc = callExprLoc}); + (nolabel, props); + ] | Some children -> Exp.apply ~attrs (Exp.ident @@ -1751,22 +1743,24 @@ module V4 = struct txt = Ldot (Lident "React", "createElementVariadic"); }) [ - (nolabel, Exp.ident {txt = ident; loc}); + (nolabel, Exp.ident {txt = ident; loc = callExprLoc}); (nolabel, props); (nolabel, children); ]) [@@raises Invalid_argument] - let transformLowercaseCall3 ~config mapper loc attrs callArguments id = - let componentNameExpr = constantString ~loc id in + let transformLowercaseCall3 ~config mapper jsxExprLoc callExprLoc attrs + callArguments id = + let componentNameExpr = constantString ~loc:callExprLoc id in match config.mode with (* the new jsx transform *) | "automatic" -> let children, nonChildrenProps = - extractChildren ~removeLastPositionUnit:true ~loc callArguments + extractChildren ~removeLastPositionUnit:true ~loc:jsxExprLoc + callArguments in let argsForMake = nonChildrenProps in - let childrenExpr = transformChildrenIfListUpper ~loc ~mapper children in + let childrenExpr = transformChildrenIfListUpper ~mapper children in let recursivelyTransformedArgsForMake = argsForMake |> List.map (fun (label, expression) -> @@ -1795,9 +1789,10 @@ module V4 = struct | Pexp_record (labelDecls, _) when List.length labelDecls = 0 -> true | _ -> false in - let record = recordFromProps ~removeKey:true args in + let record = recordFromProps ~loc:jsxExprLoc ~removeKey:true args in let props = - if isEmptyRecord record then recordWithOnlyKey ~loc else record + if isEmptyRecord record then recordWithOnlyKey ~loc:jsxExprLoc + else record in let keyProp = args |> List.filter (fun (arg_label, _) -> "key" = getLabel arg_label) @@ -1805,21 +1800,29 @@ module V4 = struct let jsxExpr, key = match (!childrenArg, keyProp) with | None, (_, keyExpr) :: _ -> - ( Exp.ident ~loc {loc; txt = Ldot (Lident "ReactDOM", "jsxKeyed")}, + ( Exp.ident + {loc = Location.none; txt = Ldot (Lident "ReactDOM", "jsxKeyed")}, [(nolabel, keyExpr)] ) | None, [] -> - (Exp.ident ~loc {loc; txt = Ldot (Lident "ReactDOM", "jsx")}, []) + ( Exp.ident + {loc = Location.none; txt = Ldot (Lident "ReactDOM", "jsx")}, + [] ) | Some _, (_, keyExpr) :: _ -> - ( Exp.ident ~loc {loc; txt = Ldot (Lident "ReactDOM", "jsxsKeyed")}, + ( Exp.ident + {loc = Location.none; txt = Ldot (Lident "ReactDOM", "jsxsKeyed")}, [(nolabel, keyExpr)] ) | Some _, [] -> - (Exp.ident ~loc {loc; txt = Ldot (Lident "ReactDOM", "jsxs")}, []) + ( Exp.ident + {loc = Location.none; txt = Ldot (Lident "ReactDOM", "jsxs")}, + [] ) in - Exp.apply ~loc ~attrs jsxExpr + Exp.apply ~attrs jsxExpr ([(nolabel, componentNameExpr); (nolabel, props)] @ key) | _ -> - let children, nonChildrenProps = extractChildren ~loc callArguments in - let childrenExpr = transformChildrenIfList ~loc ~mapper children in + let children, nonChildrenProps = + extractChildren ~loc:jsxExprLoc callArguments + in + let childrenExpr = transformChildrenIfList ~mapper children in let createElementCall = match children with (* [@JSX] div(~children=[a]), coming from
a
*) @@ -1831,11 +1834,10 @@ module V4 = struct } -> "createDOMElementVariadic" (* [@JSX] div(~children= value), coming from
...(value)
*) - | _ -> - raise - (Invalid_argument - "A spread as a DOM element's children don't make sense written \ - together. You can simply remove the spread.") + | {pexp_loc} -> + raiseError ~loc:pexp_loc + "A spread as a DOM element's children don't make sense written \ + together. You can simply remove the spread." in let args = match nonChildrenProps with @@ -1848,9 +1850,12 @@ module V4 = struct ] | nonEmptyProps -> let propsCall = - Exp.apply ~loc - (Exp.ident ~loc - {loc; txt = Ldot (Lident "ReactDOMRe", "domProps")}) + Exp.apply + (Exp.ident + { + loc = Location.none; + txt = Ldot (Lident "ReactDOMRe", "domProps"); + }) (nonEmptyProps |> List.map (fun (label, expression) -> (label, mapper.expr mapper expression))) @@ -1864,12 +1869,13 @@ module V4 = struct (nolabel, childrenExpr); ] in - Exp.apply - ~loc (* throw away the [@JSX] attribute and keep the others, if any *) - ~attrs + Exp.apply ~loc:jsxExprLoc ~attrs (* ReactDOMRe.createElement *) - (Exp.ident ~loc - {loc; txt = Ldot (Lident "ReactDOMRe", createElementCall)}) + (Exp.ident + { + loc = Location.none; + txt = Ldot (Lident "ReactDOMRe", createElementCall); + }) args [@@raises Invalid_argument] @@ -1879,15 +1885,13 @@ module V4 = struct match expr.pexp_desc with (* TODO: make this show up with a loc. *) | Pexp_fun (Labelled "key", _, _, _) | Pexp_fun (Optional "key", _, _, _) -> - raise - (Invalid_argument - "Key cannot be accessed inside of a component. Don't worry - you \ - can always key a component from its parent!") + raiseError ~loc:expr.pexp_loc + "Key cannot be accessed inside of a component. Don't worry - you can \ + always key a component from its parent!" | Pexp_fun (Labelled "ref", _, _, _) | Pexp_fun (Optional "ref", _, _, _) -> - raise - (Invalid_argument - "Ref cannot be passed as a normal prop. Please use `forwardRef` API \ - instead.") + raiseError ~loc:expr.pexp_loc + "Ref cannot be passed as a normal prop. Please use `forwardRef` API \ + instead." | Pexp_fun (arg, default, pattern, expression) when isOptional arg || isLabelled arg -> let () = @@ -2070,10 +2074,8 @@ module V4 = struct in [propsRecordType; newStructure]) | _ -> - raise - (Invalid_argument - "Only one react.component call can exist on a component at one \ - time")) + raiseError ~loc:pstr_loc + "Only one react.component call can exist on a component at one time") (* let component = ... *) | {pstr_loc; pstr_desc = Pstr_value (recFlag, valueBindings)} -> ( let fileName = filenameFromLoc pstr_loc in @@ -2125,11 +2127,10 @@ module V4 = struct | {pexp_desc = Pexp_constraint (innerFunctionExpression, _typ)} -> spelunkForFunExpression innerFunctionExpression - | _ -> - raise - (Invalid_argument - "react.component calls can only be on function \ - definitions or component wrappers (forwardRef, memo).") + | {pexp_loc} -> + raiseError ~loc:pexp_loc + "react.component calls can only be on function definitions \ + or component wrappers (forwardRef, memo)." [@@raises Invalid_argument] in spelunkForFunExpression expression @@ -2540,54 +2541,54 @@ module V4 = struct in [propsRecordType; newStructure] | _ -> - raise - (Invalid_argument - "Only one react.component call can exist on a component at one \ - time")) + raiseError ~loc:psig_loc + "Only one react.component call can exist on a component at one time") | _ -> [item] [@@raises Invalid_argument] - let transformJsxCall ~config mapper callExpression callArguments attrs = + let transformJsxCall ~config mapper callExpression callArguments jsxExprLoc + attrs = match callExpression.pexp_desc with | Pexp_ident caller -> ( match caller with - | {txt = Lident "createElement"} -> - raise - (Invalid_argument - "JSX: `createElement` should be preceeded by a module name.") + | {txt = Lident "createElement"; loc} -> + raiseError ~loc + "JSX: `createElement` should be preceeded by a module name." (* Foo.createElement(~prop1=foo, ~prop2=bar, ~children=[], ()) *) | {loc; txt = Ldot (modulePath, ("createElement" | "make"))} -> - transformUppercaseCall3 ~config modulePath mapper loc attrs + transformUppercaseCall3 ~config modulePath mapper jsxExprLoc loc attrs callArguments (* div(~prop1=foo, ~prop2=bar, ~children=[bla], ()) *) (* turn that into ReactDOMRe.createElement(~props=ReactDOMRe.props(~props1=foo, ~props2=bar, ()), [|bla|]) *) | {loc; txt = Lident id} -> - transformLowercaseCall3 ~config mapper loc attrs callArguments id - | {txt = Ldot (_, anythingNotCreateElementOrMake)} -> - raise - (Invalid_argument - ("JSX: the JSX attribute should be attached to a \ - `YourModuleName.createElement` or `YourModuleName.make` call. \ - We saw `" ^ anythingNotCreateElementOrMake ^ "` instead")) - | {txt = Lapply _} -> + transformLowercaseCall3 ~config mapper jsxExprLoc loc attrs + callArguments id + | {txt = Ldot (_, anythingNotCreateElementOrMake); loc} -> + raiseError ~loc + "JSX: the JSX attribute should be attached to a \ + `YourModuleName.createElement` or `YourModuleName.make` call. We \ + saw `%s` instead" + anythingNotCreateElementOrMake + | {txt = Lapply _; loc} -> (* don't think there's ever a case where this is reached *) - raise - (Invalid_argument - "JSX: encountered a weird case while processing the code. Please \ - report this!")) + raiseError ~loc + "JSX: encountered a weird case while processing the code. Please \ + report this!") | _ -> - raise - (Invalid_argument - "JSX: `createElement` should be preceeded by a simple, direct \ - module name.") + raiseError ~loc:callExpression.pexp_loc + "JSX: `createElement` should be preceeded by a simple, direct module \ + name." [@@raises Invalid_argument] let expr ~config mapper expression = match expression with (* Does the function application have the @JSX attribute? *) - | {pexp_desc = Pexp_apply (callExpression, callArguments); pexp_attributes} - -> ( + | { + pexp_desc = Pexp_apply (callExpression, callArguments); + pexp_attributes; + pexp_loc; + } -> ( let jsxAttribute, nonJSXAttributes = List.partition (fun (attribute, _) -> attribute.txt = "JSX") @@ -2597,7 +2598,7 @@ module V4 = struct (* no JSX attribute *) | [], _ -> default_mapper.expr mapper expression | _, nonJSXAttributes -> - transformJsxCall ~config mapper callExpression callArguments + transformJsxCall ~config mapper callExpression callArguments pexp_loc nonJSXAttributes) (* is it a list with jsx attribute? Reason <>foo desugars to [@JSX][foo]*) | { @@ -2624,7 +2625,7 @@ module V4 = struct | "classic" | _ -> Exp.ident ~loc {loc; txt = Ldot (Lident "ReasonReact", "fragment")} in - let childrenExpr = transformChildrenIfList ~loc ~mapper listItems in + let childrenExpr = transformChildrenIfList ~mapper listItems in let args = [ (nolabel, fragment); diff --git a/tests/ppx/react/expected/forwardRef.res.txt b/tests/ppx/react/expected/forwardRef.res.txt index 3bd5e497..b0804f13 100644 --- a/tests/ppx/react/expected/forwardRef.res.txt +++ b/tests/ppx/react/expected/forwardRef.res.txt @@ -157,7 +157,6 @@ module V4A = { @react.component let make = (_: props) => { let input = React.useRef(Js.Nullable.null) - ReactDOM.jsx( "div", {