Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions compiler/ml/typetexp.ml
Original file line number Diff line number Diff line change
Expand Up @@ -565,8 +565,26 @@ and transl_type_aux env policy styp =
pack_txt = p;
})
ty
| Ptyp_extension ext ->
raise (Error_forward (Builtin_attributes.error_of_extension ext))
| Ptyp_extension ext -> (
match ext with
| {txt = "typeof"; loc = ext_loc}, payload -> (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this is surprisingly easy.
Might inspire me to take a stab at something like %nameof()

(* %typeof payload must be a single identifier *)
match Ast_payload.as_ident payload with
| Some ({txt = lid; loc = lid_loc} as _ident) ->
(* Lookup the value and embed a generic instance of its type.
Using a generic instance avoids capturing weak (non-generalized)
type variables from the value into a type position. *)
let _path, desc = find_value env lid_loc lid in
let ty = Ctype.generic_instance env desc.val_type in
(* Build a core_type node carrying the looked up type; we mark the
desc as any since downstream only consults ctyp_type for typing. *)
ctyp Ttyp_any ty
| None ->
let msg =
"%%typeof expects an identifier. Example: type t = %typeof(x)"
in
raise (Error_forward (Location.error ~loc:ext_loc msg)))
| _ -> raise (Error_forward (Builtin_attributes.error_of_extension ext)))

and transl_poly_type env policy t =
transl_type env policy (Ast_helper.Typ.force_poly t)
Expand Down
10 changes: 10 additions & 0 deletions tests/analysis_tests/tests/src/Hover.res
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,13 @@ module Arr = Belt.Array

type aliased = variant
// ^hov

let myFn = (a, b) => a ++ b->Int.toString

type fnType = %typeof(myFn)
// ^hov

let myFnPartial = myFn("hello", ...)

type fnTypePartial = %typeof(myFnPartial)
// ^hov
6 changes: 6 additions & 0 deletions tests/analysis_tests/tests/src/expected/Hover.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,9 @@ Hover src/Hover.res 278:8
Hover src/Hover.res 281:6
{"contents": {"kind": "markdown", "value": "```rescript\ntype aliased = variant\n```\n\n---\n\n```\n \n```\n```rescript\ntype variant = CoolVariant | OtherCoolVariant\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C251%2C0%5D)\n"}}

Hover src/Hover.res 286:6
{"contents": {"kind": "markdown", "value": "```rescript\ntype fnType = (string, int) => string\n```"}}

Hover src/Hover.res 291:6
{"contents": {"kind": "markdown", "value": "```rescript\ntype fnTypePartial = int => string\n```"}}

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

We've found a bug for you!
/.../fixtures/typeof_mismatch.res:6:28

4 │
5 │ let f: fnType = myFn
6 │ let ff: fnType = (a, b) => a->Int.toString + b
7 │

This has type: string
But this function argument is expecting: int

You can convert string to int with Int.fromString.
6 changes: 6 additions & 0 deletions tests/build_tests/super_errors/fixtures/typeof_mismatch.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
let myFn = (a, b) => a ++ b->Int.toString

type fnType = %typeof(myFn)

let f: fnType = myFn
let ff: fnType = (a, b) => a->Int.toString + b
14 changes: 14 additions & 0 deletions tests/tests/src/Typeof.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Generated by ReScript, PLEASE EDIT WITH CARE


function myFn(a, b) {
return a + b.toString();
}

let f = myFn;

export {
myFn,
f,
}
/* No side effect */
5 changes: 5 additions & 0 deletions tests/tests/src/Typeof.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
let myFn = (a, b) => a ++ b->Int.toString

type fnType = %typeof(myFn)

let f: fnType = myFn
Loading