From d255fb0b45325b9449a8185677fa5c6daf0802ba Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 17 Jun 2025 21:18:59 +0200 Subject: [PATCH 1/3] error message for when trying to await something that is not a promise --- compiler/ml/ast_await.ml | 19 +++++++++++++++++++ compiler/ml/error_message_utils.ml | 16 +++++++++++++++- .../awaiting_non_promise.res.expected | 17 +++++++++++++++++ .../fixtures/awaiting_non_promise.res | 3 +++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tests/build_tests/super_errors/expected/awaiting_non_promise.res.expected create mode 100644 tests/build_tests/super_errors/fixtures/awaiting_non_promise.res diff --git a/compiler/ml/ast_await.ml b/compiler/ml/ast_await.ml index 9fd1b9081b..588a0fa3a1 100644 --- a/compiler/ml/ast_await.ml +++ b/compiler/ml/ast_await.ml @@ -9,6 +9,25 @@ let create_await_expression (e : Parsetree.expression) = in Ast_helper.Exp.apply ~loc unsafe_await [(Nolabel, e)] +let is_await_expr (e : Parsetree.expression) = + match e with + | { + pexp_loc = {loc_ghost = true}; + pexp_desc = + Pexp_apply + { + funct = + { + pexp_loc = {loc_ghost = true}; + pexp_desc = Pexp_ident {txt = Ldot (Lident ident, "unsafe_await")}; + }; + args = [(Nolabel, _)]; + }; + } + when ident = Primitive_modules.promise -> + true + | _ -> false + (* Transform `@res.await M` to unpack(@res.await Js.import(module(M: __M0__))) *) let create_await_module_expression ~module_type_lid (e : Parsetree.module_expr) = diff --git a/compiler/ml/error_message_utils.ml b/compiler/ml/error_message_utils.ml index 27f30fe35d..478d73ac0b 100644 --- a/compiler/ml/error_message_utils.ml +++ b/compiler/ml/error_message_utils.ml @@ -102,6 +102,7 @@ type type_clash_context = | FunctionArgument of {optional: bool; name: string option} | Statement of type_clash_statement | ForLoopCondition + | Await let context_to_string = function | Some WhileCondition -> "WhileCondition" @@ -120,6 +121,7 @@ let context_to_string = function | Some (FunctionArgument _) -> "FunctionArgument" | Some ComparisonOperator -> "ComparisonOperator" | Some IfReturn -> "IfReturn" + | Some Await -> "Await" | None -> "None" let fprintf = Format.fprintf @@ -185,6 +187,10 @@ let error_expected_type_text ppf type_clash_context = "But it's being used with the @{%s@} operator, which works on:" operator | Some StringConcat -> fprintf ppf "But string concatenation is expecting:" + | Some Await -> + fprintf ppf + "But you're using @{await@} on this expression, so it is expected \ + to be of type:" | Some MaybeUnwrapOption | None -> fprintf ppf "But it's expected to have type:" @@ -282,6 +288,14 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env loc ppf "\n\n\ \ To fix this, change the highlighted code so it evaluates to a \ @{bool@}." + | Some Await, _ -> + fprintf ppf + "\n\n\ + \ You're trying to await something that is not a promise.\n\n\ + Possible solutions:\n\ + \ - Remove the @{await@} if this is not expected to be a promise\n\ + \ - Wrap the expression in @{Promise.resolve@} to convert the \ + expression to a promise" | Some IfReturn, _ -> fprintf ppf "\n\n\ @@ -533,7 +547,7 @@ let type_clash_context_from_function sexp sfunct = Some (MathOperator {for_float = true; operator; is_constant}) | Pexp_ident {txt = Lident (("/" | "*" | "+" | "-") as operator)} -> Some (MathOperator {for_float = false; operator; is_constant}) - | _ -> None + | _ -> if Ast_await.is_await_expr sexp then Some Await else None let type_clash_context_for_function_argument ~label type_clash_context sarg0 = match type_clash_context with diff --git a/tests/build_tests/super_errors/expected/awaiting_non_promise.res.expected b/tests/build_tests/super_errors/expected/awaiting_non_promise.res.expected new file mode 100644 index 0000000000..37021956d7 --- /dev/null +++ b/tests/build_tests/super_errors/expected/awaiting_non_promise.res.expected @@ -0,0 +1,17 @@ + + We've found a bug for you! + /.../fixtures/awaiting_non_promise.res:3:15 + + 1 │ let x = 1 + 2 │ + 3 │ let f = await x + + This has type: int + But you're using await on this expression, so it is expected to be of type: + promise<'a> + + You're trying to await something that is not a promise. + +Possible solutions: + - Remove the await if this is not expected to be a promise + - Wrap the expression in Promise.resolve to convert the expression to a promise \ No newline at end of file diff --git a/tests/build_tests/super_errors/fixtures/awaiting_non_promise.res b/tests/build_tests/super_errors/fixtures/awaiting_non_promise.res new file mode 100644 index 0000000000..678cbe67ad --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/awaiting_non_promise.res @@ -0,0 +1,3 @@ +let x = 1 + +let f = await x \ No newline at end of file From a0354b1e033a57315664a456e2b70b9e35ca139a Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 17 Jun 2025 21:21:32 +0200 Subject: [PATCH 2/3] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66cdf4e7d8..0f90555c57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ # 12.0.0-alpha.15 (Unreleased) +#### :nail_care: Polish + +- Better error message for when trying to await something that is not a promise. https://github.com/rescript-lang/rescript/pull/7561 + # 12.0.0-alpha.14 #### :boom: Breaking Change From bde0c4bdbf78cdbfef14cfacd4d5913ff4eab572 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 17 Jun 2025 21:38:39 +0200 Subject: [PATCH 3/3] format --- .../super_errors/expected/awaiting_non_promise.res.expected | 1 + .../build_tests/super_errors/fixtures/awaiting_non_promise.res | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/build_tests/super_errors/expected/awaiting_non_promise.res.expected b/tests/build_tests/super_errors/expected/awaiting_non_promise.res.expected index 37021956d7..fce390edff 100644 --- a/tests/build_tests/super_errors/expected/awaiting_non_promise.res.expected +++ b/tests/build_tests/super_errors/expected/awaiting_non_promise.res.expected @@ -5,6 +5,7 @@ 1 │ let x = 1 2 │ 3 │ let f = await x + 4 │ This has type: int But you're using await on this expression, so it is expected to be of type: diff --git a/tests/build_tests/super_errors/fixtures/awaiting_non_promise.res b/tests/build_tests/super_errors/fixtures/awaiting_non_promise.res index 678cbe67ad..2e3d940e23 100644 --- a/tests/build_tests/super_errors/fixtures/awaiting_non_promise.res +++ b/tests/build_tests/super_errors/fixtures/awaiting_non_promise.res @@ -1,3 +1,3 @@ let x = 1 -let f = await x \ No newline at end of file +let f = await x