Skip to content

Commit 2fb86e5

Browse files
authored
feat(compiler): Add warning for calls to IntXX.fromNumber and FloatXX.fromNumber with literal integers/floats (#1218)
1 parent 37b63c4 commit 2fb86e5

File tree

3 files changed

+91
-7
lines changed

3 files changed

+91
-7
lines changed

compiler/src/typed/typed_well_formedness.re

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ module WellFormednessArg: TypedtreeIter.IteratorArgument = {
8383

8484
let enter_expression: expression => unit =
8585
({exp_desc, exp_loc, exp_attributes} as exp) => {
86-
// Check #1: Avoid using Pervasives equality ops with WasmXX types
86+
// Check: Avoid using Pervasives equality ops with WasmXX types
8787
switch (exp_desc) {
8888
| TExpLet(_) when is_marked_unsafe(exp_attributes) => push_unsafe(true)
8989
| TExpApp(
@@ -107,9 +107,57 @@ module WellFormednessArg: TypedtreeIter.IteratorArgument = {
107107
Grain_parsing.Location.prerr_warning(exp_loc, warning);
108108
};
109109
}
110+
// Check: Warn if using Int32.fromNumber(<literal>)
111+
| TExpApp(
112+
{
113+
exp_desc:
114+
TExpIdent(
115+
Path.PExternal(
116+
Path.PIdent({name: modname}),
117+
"fromNumber",
118+
_,
119+
),
120+
_,
121+
_,
122+
),
123+
},
124+
[
125+
{
126+
exp_desc:
127+
TExpConstant(
128+
Const_number(
129+
(Const_number_int(_) | Const_number_float(_)) as n,
130+
),
131+
),
132+
},
133+
],
134+
)
135+
when
136+
modname == "Int32"
137+
|| modname == "Int64"
138+
|| modname == "Float32"
139+
|| modname == "Float64" =>
140+
// NOTE: Due to type-checking, we shouldn't need to worry about ending up with a FloatXX value and a Const_number_float
141+
let n_str =
142+
switch (n) {
143+
| Const_number_int(nint) => Int64.to_string(nint)
144+
| Const_number_float(nfloat) => Float.to_string(nfloat)
145+
| _ => failwith("Impossible")
146+
};
147+
let warning =
148+
switch (modname) {
149+
| "Int32" => Grain_utils.Warnings.FromNumberLiteralI32(n_str)
150+
| "Int64" => Grain_utils.Warnings.FromNumberLiteralI64(n_str)
151+
| "Float32" => Grain_utils.Warnings.FromNumberLiteralF32(n_str)
152+
| "Float64" => Grain_utils.Warnings.FromNumberLiteralF64(n_str)
153+
| _ => failwith("Impossible")
154+
};
155+
if (Grain_utils.Warnings.is_active(warning)) {
156+
Grain_parsing.Location.prerr_warning(exp_loc, warning);
157+
};
110158
| _ => ()
111159
};
112-
// Check #2: Forbid usage of WasmXX types outside of disableGC context
160+
// Check: Forbid usage of WasmXX types outside of disableGC context
113161
switch (exp_desc) {
114162
| TExpLambda(_) => push_in_lambda(true)
115163
| _ => ()

compiler/src/utils/warnings.re

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ type t =
2323
| UnreachableCase
2424
| ShadowConstructor(string)
2525
| NoCmiFile(string, option(string))
26-
| FuncWasmUnsafe(string);
26+
| FuncWasmUnsafe(string)
27+
| FromNumberLiteralI32(string)
28+
| FromNumberLiteralI64(string)
29+
| FromNumberLiteralF32(string)
30+
| FromNumberLiteralF64(string);
2731

2832
let number =
2933
fun
@@ -43,9 +47,13 @@ let number =
4347
| NoCmiFile(_) => 14
4448
| NonClosedRecordPattern(_) => 15
4549
| UnusedExtension => 16
46-
| FuncWasmUnsafe(_) => 17;
50+
| FuncWasmUnsafe(_) => 17
51+
| FromNumberLiteralI32(_) => 18
52+
| FromNumberLiteralI64(_) => 19
53+
| FromNumberLiteralF32(_) => 20
54+
| FromNumberLiteralF64(_) => 21;
4755

48-
let last_warning_number = 17;
56+
let last_warning_number = 21;
4957

5058
let message =
5159
fun
@@ -108,7 +116,27 @@ let message =
108116
| FuncWasmUnsafe(func) =>
109117
"it looks like you are using "
110118
++ func
111-
++ " on two unsafe Wasm values here.\nThis is generally unsafe and will cause errors. Use one of the equivalent functions in `WasmI32`, `WasmI64`, `WasmF32`, or `WasmF64` instead.";
119+
++ " on two unsafe Wasm values here.\nThis is generally unsafe and will cause errors. Use one of the equivalent functions in `WasmI32`, `WasmI64`, `WasmF32`, or `WasmF64` instead."
120+
| FromNumberLiteralI32(n) =>
121+
Printf.sprintf(
122+
"it looks like you are calling Int32.fromNumber() with a constant number. Try using the literal syntax (e.g. `%sl`) instead.",
123+
n,
124+
)
125+
| FromNumberLiteralI64(n) =>
126+
Printf.sprintf(
127+
"it looks like you are calling Int64.fromNumber() with a constant number. Try using the literal syntax (e.g. `%sL`) instead.",
128+
n,
129+
)
130+
| FromNumberLiteralF32(n) =>
131+
Printf.sprintf(
132+
"it looks like you are calling Float32.fromNumber() with a constant number. Try using the literal syntax (e.g. `%sf`) instead.",
133+
n,
134+
)
135+
| FromNumberLiteralF64(n) =>
136+
Printf.sprintf(
137+
"it looks like you are calling Float64.fromNumber() with a constant number. Try using the literal syntax (e.g. `%sd`) instead.",
138+
n,
139+
);
112140

113141
let sub_locs =
114142
fun
@@ -151,6 +179,10 @@ let defaults = [
151179
ShadowConstructor(""),
152180
NoCmiFile("", None),
153181
FuncWasmUnsafe(""),
182+
FromNumberLiteralI32(""),
183+
FromNumberLiteralI64(""),
184+
FromNumberLiteralF32(""),
185+
FromNumberLiteralF64(""),
154186
];
155187

156188
let _ = List.iter(x => current^.active[number(x)] = true, defaults);

compiler/src/utils/warnings.rei

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ type t =
3737
| UnreachableCase
3838
| ShadowConstructor(string)
3939
| NoCmiFile(string, option(string))
40-
| FuncWasmUnsafe(string);
40+
| FuncWasmUnsafe(string)
41+
| FromNumberLiteralI32(string)
42+
| FromNumberLiteralI64(string)
43+
| FromNumberLiteralF32(string)
44+
| FromNumberLiteralF64(string);
4145

4246
let is_active: t => bool;
4347
let is_error: t => bool;

0 commit comments

Comments
 (0)