Skip to content

Conversation

nojaf
Copy link
Member

@nojaf nojaf commented Sep 19, 2025

image

Copy link

pkg-pr-new bot commented Sep 19, 2025

Open in StackBlitz

rescript

npm i https://pkg.pr.new/rescript-lang/rescript@7905

@rescript/darwin-arm64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/darwin-arm64@7905

@rescript/darwin-x64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/darwin-x64@7905

@rescript/linux-arm64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/linux-arm64@7905

@rescript/linux-x64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/linux-x64@7905

@rescript/runtime

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/runtime@7905

@rescript/win32-x64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/win32-x64@7905

commit: 7dc6ff1

| None -> []))

(* Collect exception constructor names from cmt infos. *)
let exceptions_from_cmt_infos (infos : Cmt_format.cmt_infos) :
Copy link
Member Author

Choose a reason for hiding this comment

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

This part is vibe coded I'm afraid.
Please review it and let me know if there is a better way.

Copy link
Member

Choose a reason for hiding this comment

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

Can a typed tree AST iterator be used here instead of the hand rolled traversal functions below? Would feel more idiomatic, and make the code clearer as you could just add a visitor for the relevant extension nodes.

(* Predefined Stdlib/Pervasives exceptions. *)
let predefined_exceptions : (string * bool) list =
[
("Not_found", true);
Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

I can't help but wonder whether we should be encouraging using these builtins. They're really just heritage from the OCaml era. So I think a case could be made to not include them at all.

@nojaf nojaf marked this pull request as ready for review September 19, 2025 09:04
@nojaf nojaf requested review from zth, cristianoc and Copilot September 19, 2025 09:05
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds completion support for the throw keyword in ReScript, providing suggestions for both built-in and user-defined exceptions when typing "throw".

  • Implements completion functionality that suggests exception constructors when typing "throw"
  • Adds logic to extract exception information from compiled module files (CMT)
  • Includes predefined standard library exceptions with appropriate argument placeholders

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
analysis/src/CompletionBackEnd.ml Core implementation of throw completion logic including exception extraction and completion generation
tests/analysis_tests/tests/src/Throw.res Test file with custom exceptions for validation
tests/analysis_tests/tests/src/expected/Throw.res.txt Expected completion output for throw keyword test
tests/analysis_tests/tests/src/expected/CompletionJsxProps.res.txt Updated test expectations including new Throw module
tests/analysis_tests/tests/src/expected/Completion.res.txt Updated test expectations including new Throw module
CHANGELOG.md Documentation of the new feature

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Member

@zth zth left a comment

Choose a reason for hiding this comment

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

Nice! Some initial questions.

(* Predefined Stdlib/Pervasives exceptions. *)
let predefined_exceptions : (string * bool) list =
[
("Not_found", true);
Copy link
Member

Choose a reason for hiding this comment

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

I can't help but wonder whether we should be encouraging using these builtins. They're really just heritage from the OCaml era. So I think a case could be made to not include them at all.

Comment on lines +1146 to +1149
match (result, path) with
| [], [prefix] when Utils.startsWith "throw" prefix ->
completionsForThrow ~env ~full
| _ -> result)
Copy link
Member

Choose a reason for hiding this comment

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

This should probably be done in a "safer" way that matches by type and not by name. We'd need to check if this thing actually resolves to Pervasives.throw.

Also, why Utils.startsWith? Is it not supposed to be an exact match?

Copy link
Member

Choose a reason for hiding this comment

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

@nojaf this has not been answered I believe.

Copy link
Member Author

Choose a reason for hiding this comment

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

We'd need to check if this thing actually resolves to Pervasives.throw.

If you only have thr you can't do that.

Also, why Utils.startsWith? Is it not supposed to be an exact match?

As mentioned, I want the completions to show up while I'm typing towards the word throw. Only having it once the full word is typed I find unintuitive.

| None -> []))

(* Collect exception constructor names from cmt infos. *)
let exceptions_from_cmt_infos (infos : Cmt_format.cmt_infos) :
Copy link
Member

Choose a reason for hiding this comment

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

Can a typed tree AST iterator be used here instead of the hand rolled traversal functions below? Would feel more idiomatic, and make the code clearer as you could just add a visitor for the relevant extension nodes.

@nojaf
Copy link
Member Author

nojaf commented Sep 24, 2025

Maybe this should more autocomplete JsError.throwWithMessage("Hello!") and JsExn.throw("some non-error value!") instead. (docs)

I cannot ever remember those things and I basically want to have something show up when I type the throw keyword. (Or I'm typing toward that keyword, should trigger on thr as well).

I'm okay with leaving the camels behind.

@nojaf nojaf added the editor Everything concerning the analysis binary label Sep 26, 2025
Copy link
Member

@mediremi mediremi left a comment

Choose a reason for hiding this comment

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

I agree with showing completions for JsError.throwWithMessage and JsExn.throw in addition to throw 👍

@nojaf nojaf requested a review from zth September 26, 2025 09:57
Copy link
Member

@zth zth left a comment

Choose a reason for hiding this comment

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

Still some outstanding questions and comments from me. Also, is this just for the throw case, without parenthesis and args? A natural extension of this would be to also complete inside of throw(<com>). Unless that already works ofc.

Comment on lines +1146 to +1149
match (result, path) with
| [], [prefix] when Utils.startsWith "throw" prefix ->
completionsForThrow ~env ~full
| _ -> result)
Copy link
Member

Choose a reason for hiding this comment

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

@nojaf this has not been answered I believe.

(match item.str_desc with
| Tstr_exception _ -> in_toplevel_exception := true
| _ -> ());
()
Copy link
Member

Choose a reason for hiding this comment

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

Why is there an extra unit here...?

(match item.sig_desc with
| Tsig_exception _ -> in_toplevel_exception := true
| _ -> ());
()
Copy link
Member

Choose a reason for hiding this comment

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

Same, extra unit.

Comment on lines +821 to +822
let module Iter = TypedtreeIter.MakeIterator (struct
include TypedtreeIter.DefaultIteratorArgument
Copy link
Member

Choose a reason for hiding this comment

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

Do we use the functor based approach elsewhere in the code base? Or is there no way for the typed tree to just define a mapper like we do for the AST mapper? I don't remember how this works exactly.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, this was weird to me as well, it is a departure from what we have on the untyped side, but this seems to be the mapper we have?

Comment on lines +818 to +819
(* Only collect top-level exception declarations (Tstr_exception/Tsig_exception).
Avoid picking up exceptions from Texp_letexception by tracking context. *)
Copy link
Member

Choose a reason for hiding this comment

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

In theory I guess the most robust approach would be to collect and use whatever defined exceptions are in scope at the current position. Collecting at the top level is good and simple, but you could also define them in sub modules, and so on.

It might be worth exploring making exceptions part of the tracked scope instead, and track them just like we track types and values today when doing completion. Then we could just extend the current mechanism a bit, and hopefully get the correct scope tracking for free.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
editor Everything concerning the analysis binary
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants