From 27e2a40353419c714950ef50b5d1f2611a94b7ce Mon Sep 17 00:00:00 2001 From: tsnobip Date: Fri, 3 Oct 2025 10:51:48 +0200 Subject: [PATCH 1/7] Document the deprecation of @raises over @throws Following the changes introduced by rescript-lang/rescript#7932 --- misc_docs/syntax/decorator_does_not_raise.mdx | 5 ++++- misc_docs/syntax/decorator_does_not_throw.mdx | 15 +++++++++++++++ misc_docs/syntax/decorator_raises.mdx | 5 ++++- misc_docs/syntax/decorator_throws.mdx | 15 +++++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 misc_docs/syntax/decorator_does_not_throw.mdx create mode 100644 misc_docs/syntax/decorator_throws.mdx diff --git a/misc_docs/syntax/decorator_does_not_raise.mdx b/misc_docs/syntax/decorator_does_not_raise.mdx index 639c51fdf..db85b3777 100644 --- a/misc_docs/syntax/decorator_does_not_raise.mdx +++ b/misc_docs/syntax/decorator_does_not_raise.mdx @@ -4,11 +4,14 @@ keywords: ["doesNotRaise", "decorator"] name: "@doesNotRaise" summary: "This is the `@doesNotRaise` decorator." category: "decorators" +status: "deprecated" --- +> Deprecated since v12.0.0. Use the [@doesNotThrow](https://rescript-lang.org/syntax-lookup#does-not-throw-decorator) decorator instead. + > This decorator requires [`reanalyze`](https://github.com/rescript-lang/reanalyze), a code analysis tool for ReScript, to be installed. [Click here to read about how you get started with reanalyze.](https://github.com/rescript-lang/reanalyze). -`@doesNotRaise` is used to override the reanalyze's exception analysis and state that an expression does not raise any exceptions, even though the analysis reports otherwise. This can happen for example in the case of array access where the analysis does not perform range checks but takes a conservative stance that any access could potentially raise. +`@doesNotRaise` is used to override the reanalyze's exception analysis and state that an expression does not throw any exceptions, even though the analysis reports otherwise. This can happen for example in the case of array access where the analysis does not perform range checks but takes a conservative stance that any access could potentially throw. ### References diff --git a/misc_docs/syntax/decorator_does_not_throw.mdx b/misc_docs/syntax/decorator_does_not_throw.mdx new file mode 100644 index 000000000..057e7f726 --- /dev/null +++ b/misc_docs/syntax/decorator_does_not_throw.mdx @@ -0,0 +1,15 @@ +--- +id: "does-not-throw-decorator" +keywords: ["doesNotThrow", "decorator"] +name: "@doesNotThrow" +summary: "This is the `@doesNotThrow` decorator." +category: "decorators" +--- + +> This decorator requires [`reanalyze`](https://github.com/rescript-lang/reanalyze), a code analysis tool for ReScript, to be installed. [Click here to read about how you get started with reanalyze.](https://github.com/rescript-lang/reanalyze). + +`@doesNotThrow` is used to override the reanalyze's exception analysis and state that an expression does not throw any exceptions, even though the analysis reports otherwise. This can happen for example in the case of array access where the analysis does not perform range checks but takes a conservative stance that any access could potentially throw. + +### References + +- [Reanalyze: Exception Analysis](https://github.com/rescript-lang/reanalyze/blob/master/EXCEPTION.md) diff --git a/misc_docs/syntax/decorator_raises.mdx b/misc_docs/syntax/decorator_raises.mdx index 264aadd07..551b49a23 100644 --- a/misc_docs/syntax/decorator_raises.mdx +++ b/misc_docs/syntax/decorator_raises.mdx @@ -4,11 +4,14 @@ keywords: ["raises", "decorator"] name: "@raises" summary: "This is the `@raises` decorator." category: "decorators" +status: "deprecated" --- +> Deprecated since v12.0.0. Use the [@throws](https://rescript-lang.org/syntax-lookup#throws-decorator) decorator instead. + > This decorator requires [`reanalyze`](https://github.com/rescript-lang/reanalyze), a code analysis tool for ReScript, to be installed. [Click here to read about how you get started with reanalyze.](https://github.com/rescript-lang/reanalyze). -`@raises` is picked up by reanalyze's exception analysis, and acknowledges that a function can raise exceptions that are not caught, and suppresses a warning in that case. Callers of the functions are then subjected to the same rule. Example `@raises(Exn)` or `@raises([E1, E2, E3])` for multiple exceptions. +`@raises` is picked up by reanalyze's exception analysis, and acknowledges that a function can throw exceptions that are not caught, and suppresses a warning in that case. Callers of the functions are then subjected to the same rule. Example `@raises(Exn)` or `@raises([E1, E2, E3])` for multiple exceptions. ### References diff --git a/misc_docs/syntax/decorator_throws.mdx b/misc_docs/syntax/decorator_throws.mdx new file mode 100644 index 000000000..c2e30176d --- /dev/null +++ b/misc_docs/syntax/decorator_throws.mdx @@ -0,0 +1,15 @@ +--- +id: "throws-decorator" +keywords: ["throws", "decorator"] +name: "@throws" +summary: "This is the `@throws` decorator." +category: "decorators" +--- + +> This decorator requires [`reanalyze`](https://github.com/rescript-lang/reanalyze), a code analysis tool for ReScript, to be installed. [Click here to read about how you get started with reanalyze.](https://github.com/rescript-lang/reanalyze). + +`@throws` is picked up by reanalyze's exception analysis, and acknowledges that a function can throw exceptions that are not caught, and suppresses a warning in that case. Callers of the functions are then subjected to the same rule. Example `@throws(Exn)` or `@throws([E1, E2, E3])` for multiple exceptions. + +### References + +- [Reanalyze: Exception Analysis](https://github.com/rescript-lang/reanalyze/blob/master/EXCEPTION.md) From 566e4d66240f19a02a1cf723e0dd02598047e8b6 Mon Sep 17 00:00:00 2001 From: tsnobip Date: Fri, 3 Oct 2025 15:14:20 +0200 Subject: [PATCH 2/7] document exception analysis in editor-plugins --- misc_docs/syntax/decorator_does_not_raise.mdx | 6 +- misc_docs/syntax/decorator_does_not_throw.mdx | 4 +- misc_docs/syntax/decorator_raises.mdx | 6 +- misc_docs/syntax/decorator_throws.mdx | 4 +- pages/docs/manual/v12.0.0/editor-plugins.mdx | 84 ++++++++++++++++++- 5 files changed, 93 insertions(+), 11 deletions(-) diff --git a/misc_docs/syntax/decorator_does_not_raise.mdx b/misc_docs/syntax/decorator_does_not_raise.mdx index db85b3777..1568fb2b6 100644 --- a/misc_docs/syntax/decorator_does_not_raise.mdx +++ b/misc_docs/syntax/decorator_does_not_raise.mdx @@ -7,12 +7,12 @@ category: "decorators" status: "deprecated" --- -> Deprecated since v12.0.0. Use the [@doesNotThrow](https://rescript-lang.org/syntax-lookup#does-not-throw-decorator) decorator instead. +> Deprecated since v12.0.0. Use the [@doesNotThrow](/syntax-lookup#does-not-throw-decorator) decorator instead. -> This decorator requires [`reanalyze`](https://github.com/rescript-lang/reanalyze), a code analysis tool for ReScript, to be installed. [Click here to read about how you get started with reanalyze.](https://github.com/rescript-lang/reanalyze). +> This decorator requires [`reanalyze`](https://github.com/rescript-lang/reanalyze), a code analysis tool for ReScript, to be installed. [Click here to read about how you get started with reanalyze.](/docs/manual/v12.0.0/editor-plugins#code-analysis). `@doesNotRaise` is used to override the reanalyze's exception analysis and state that an expression does not throw any exceptions, even though the analysis reports otherwise. This can happen for example in the case of array access where the analysis does not perform range checks but takes a conservative stance that any access could potentially throw. ### References -- [Reanalyze: Exception Analysis](https://github.com/rescript-lang/reanalyze/blob/master/EXCEPTION.md) +- [Reanalyze: Exception Analysis](/docs/manual/v12.0.0/editor-plugins#exception-analysis) diff --git a/misc_docs/syntax/decorator_does_not_throw.mdx b/misc_docs/syntax/decorator_does_not_throw.mdx index 057e7f726..f52b946fa 100644 --- a/misc_docs/syntax/decorator_does_not_throw.mdx +++ b/misc_docs/syntax/decorator_does_not_throw.mdx @@ -6,10 +6,10 @@ summary: "This is the `@doesNotThrow` decorator." category: "decorators" --- -> This decorator requires [`reanalyze`](https://github.com/rescript-lang/reanalyze), a code analysis tool for ReScript, to be installed. [Click here to read about how you get started with reanalyze.](https://github.com/rescript-lang/reanalyze). +> This decorator requires [`reanalyze`](https://github.com/rescript-lang/reanalyze), a code analysis tool for ReScript, to be installed. [Click here to read about how you get started with reanalyze.](/docs/manual/v12.0.0/editor-plugins#code-analysis). `@doesNotThrow` is used to override the reanalyze's exception analysis and state that an expression does not throw any exceptions, even though the analysis reports otherwise. This can happen for example in the case of array access where the analysis does not perform range checks but takes a conservative stance that any access could potentially throw. ### References -- [Reanalyze: Exception Analysis](https://github.com/rescript-lang/reanalyze/blob/master/EXCEPTION.md) +- [Reanalyze: Exception Analysis](/docs/manual/v12.0.0/editor-plugins#exception-analysis) diff --git a/misc_docs/syntax/decorator_raises.mdx b/misc_docs/syntax/decorator_raises.mdx index 551b49a23..30ce210cb 100644 --- a/misc_docs/syntax/decorator_raises.mdx +++ b/misc_docs/syntax/decorator_raises.mdx @@ -7,12 +7,12 @@ category: "decorators" status: "deprecated" --- -> Deprecated since v12.0.0. Use the [@throws](https://rescript-lang.org/syntax-lookup#throws-decorator) decorator instead. +> Deprecated since v12.0.0. Use the [@throws](/syntax-lookup#throws-decorator) decorator instead. -> This decorator requires [`reanalyze`](https://github.com/rescript-lang/reanalyze), a code analysis tool for ReScript, to be installed. [Click here to read about how you get started with reanalyze.](https://github.com/rescript-lang/reanalyze). +> This decorator requires [`reanalyze`](https://github.com/rescript-lang/reanalyze), a code analysis tool for ReScript, to be installed. [Click here to read about how you get started with reanalyze.](/docs/manual/v12.0.0/editor-plugins#code-analysis). `@raises` is picked up by reanalyze's exception analysis, and acknowledges that a function can throw exceptions that are not caught, and suppresses a warning in that case. Callers of the functions are then subjected to the same rule. Example `@raises(Exn)` or `@raises([E1, E2, E3])` for multiple exceptions. ### References -- [Reanalyze: Exception Analysis](https://github.com/rescript-lang/reanalyze/blob/master/EXCEPTION.md) +- [Reanalyze: Exception Analysis](/docs/manual/v12.0.0/editor-plugins#exception-analysis) diff --git a/misc_docs/syntax/decorator_throws.mdx b/misc_docs/syntax/decorator_throws.mdx index c2e30176d..384b12e63 100644 --- a/misc_docs/syntax/decorator_throws.mdx +++ b/misc_docs/syntax/decorator_throws.mdx @@ -6,10 +6,10 @@ summary: "This is the `@throws` decorator." category: "decorators" --- -> This decorator requires [`reanalyze`](https://github.com/rescript-lang/reanalyze), a code analysis tool for ReScript, to be installed. [Click here to read about how you get started with reanalyze.](https://github.com/rescript-lang/reanalyze). +> This decorator requires [`reanalyze`](https://github.com/rescript-lang/reanalyze), a code analysis tool for ReScript, to be installed. [Click here to read about how you get started with reanalyze.](/docs/manual/v12.0.0/editor-plugins#code-analysis). `@throws` is picked up by reanalyze's exception analysis, and acknowledges that a function can throw exceptions that are not caught, and suppresses a warning in that case. Callers of the functions are then subjected to the same rule. Example `@throws(Exn)` or `@throws([E1, E2, E3])` for multiple exceptions. ### References -- [Reanalyze: Exception Analysis](https://github.com/rescript-lang/reanalyze/blob/master/EXCEPTION.md) +- [Reanalyze: Exception Analysis](/docs/manual/v12.0.0/editor-plugins#exception-analysis) diff --git a/pages/docs/manual/v12.0.0/editor-plugins.mdx b/pages/docs/manual/v12.0.0/editor-plugins.mdx index f475259bb..637fb6e34 100644 --- a/pages/docs/manual/v12.0.0/editor-plugins.mdx +++ b/pages/docs/manual/v12.0.0/editor-plugins.mdx @@ -40,7 +40,89 @@ The code analysis provides extra checks for your ReScript project, such as detec ### Configuration Add a `reanalyze` section to your `rescript.json` to control what the analyzer checks or ignores. You’ll get autocomplete for config options in the editor. -More details: [reanalyze config docs](https://github.com/rescript-association/reanalyze#configuration-via-bsconfigjson) +More details: [reanalyze config docs](https://github.com/rescript-associlangation/reanalyze#configuration-via-bsconfigjson) + +### Exception analysis + +The exception analysis is designed to keep track statically of the exceptions that might be thrown at runtime. It works by issuing warnings and recognizing annotations. Warnings are issued whenever an exception is thrown and not immediately caught. Annotations are used to push warnings from he local point where the exception is thrown, to the outside context: callers of the current function. +Nested functions need to be annotated separately. + +Instructions on how to run the exception analysis using the `-exception` and `-exception-cmt` command-line arguments, or how to add `"analysis": ["exception"]` in `rescript.json` are contained in the [reanalyze config docs](https://github.com/rescript-associlangation/reanalyze#configuration-via-bsconfigjson). + +Here's an example, where the analysis reports a warning any time an exception is thrown, and not caught: + +```rescript +let throws = () => throw(Not_found) +``` + +reports: + +```sh + + Exception Analysis + File "A.res", line 1, characters 4-10 + throws might throw Not_found (A.res:1:19) and is not annotated with @throws(Not_found) +``` + +No warning is reported when a `@throws` annotation is added: + +```rescript +@throws(Not_found) +let throws = () => throw(Not_found) +``` + +When a function throws multiple exceptions, a tuple annotation is used: + + +```rescript +exception A +exception B + +@throws([A, B]) +let twoExceptions = (x, y) => { + if (x) { + throw(A) + } + if (y) { + throw(B) + } +} +``` + +It is possible to silence the analysis by adding a `@doesNotThrow` annotation: + +```rescript +@throws(Invalid_argument) +let stringMake1 = String.make(12, ' ') + +// Silence only the make function +let stringMake2 = (@doesNotThrow String.make)(12, ' ') + +// Silence the entire call (including arguments to make) +let stringMake3 = @doesNotThrow String.make(12, ' ') + +``` + +#### Limitations + +- The libraries currently modeled are limited to `Array`, `Buffer`, `Bytes`, `Char`, `Filename`, `Hashtbl`, `List`, `Pervasives`, `Str`, `String` from the standard library, and `bs-json`, and `Json` from `Js`. Models are currently vendored in the analysis, and are easy to add (see [`src/ExnLib.ml`](src/ExnLib.ml)) +- Generic exceptions are not understood by the analysis. For example `exn` is not recognized below (only concrete exceptions are): + +```rescript +try (foo()) { | exn => throw(exn) } +``` + +- Uses of e.g. `List.hd` are interpreted as belonging to the standard library. If you re-define `List` in the local scope, the analysis it will think it's dealing with `List` from the standard library. +- There is no special support for functors. So with `Hashtbl.Make(...)` the builtin model will not apply. So the analysis won't report that the following can throw `Not_found`: + +```rescript +module StringHash = + Hashtbl.Make({ + include String + let hash = Hashtbl.hash + }) +let specializedHash = tbl => StringHash.find(tbl, "abc") +``` ### Guide From 958d3c7ccbec977d0e9e5e742f5b46882813cbc3 Mon Sep 17 00:00:00 2001 From: tsnobip Date: Fri, 3 Oct 2025 15:26:45 +0200 Subject: [PATCH 3/7] format --- pages/docs/manual/v12.0.0/editor-plugins.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/pages/docs/manual/v12.0.0/editor-plugins.mdx b/pages/docs/manual/v12.0.0/editor-plugins.mdx index 637fb6e34..3e384a87b 100644 --- a/pages/docs/manual/v12.0.0/editor-plugins.mdx +++ b/pages/docs/manual/v12.0.0/editor-plugins.mdx @@ -73,7 +73,6 @@ let throws = () => throw(Not_found) When a function throws multiple exceptions, a tuple annotation is used: - ```rescript exception A exception B From 07c5d8539adf4725a3b5c4889059bcc8cf7bcd10 Mon Sep 17 00:00:00 2001 From: tsnobip Date: Fri, 3 Oct 2025 15:43:59 +0200 Subject: [PATCH 4/7] update libraries set up to use exception analysis --- pages/docs/manual/v12.0.0/editor-plugins.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/docs/manual/v12.0.0/editor-plugins.mdx b/pages/docs/manual/v12.0.0/editor-plugins.mdx index 3e384a87b..cb6339513 100644 --- a/pages/docs/manual/v12.0.0/editor-plugins.mdx +++ b/pages/docs/manual/v12.0.0/editor-plugins.mdx @@ -104,7 +104,7 @@ let stringMake3 = @doesNotThrow String.make(12, ' ') #### Limitations -- The libraries currently modeled are limited to `Array`, `Buffer`, `Bytes`, `Char`, `Filename`, `Hashtbl`, `List`, `Pervasives`, `Str`, `String` from the standard library, and `bs-json`, and `Json` from `Js`. Models are currently vendored in the analysis, and are easy to add (see [`src/ExnLib.ml`](src/ExnLib.ml)) +- The libraries currently modeled are limited to the standard library, Belt and Js modules. Models are currently vendored in the analysis, and are easy to add (see [`analysis/reanalyze/src/ExnLib.ml`](https://github.com/rescript-lang/rescript/blob/master/analysis/reanalyze/src/ExnLib.ml)) - Generic exceptions are not understood by the analysis. For example `exn` is not recognized below (only concrete exceptions are): ```rescript From 30d95fd49c7e5ea55164adcffe7443d90c7a1618 Mon Sep 17 00:00:00 2001 From: tsnobip Date: Fri, 3 Oct 2025 15:44:26 +0200 Subject: [PATCH 5/7] remove the outdated functor example for exception analysis --- pages/docs/manual/v12.0.0/editor-plugins.mdx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/pages/docs/manual/v12.0.0/editor-plugins.mdx b/pages/docs/manual/v12.0.0/editor-plugins.mdx index cb6339513..d3104bbb3 100644 --- a/pages/docs/manual/v12.0.0/editor-plugins.mdx +++ b/pages/docs/manual/v12.0.0/editor-plugins.mdx @@ -112,16 +112,7 @@ try (foo()) { | exn => throw(exn) } ``` - Uses of e.g. `List.hd` are interpreted as belonging to the standard library. If you re-define `List` in the local scope, the analysis it will think it's dealing with `List` from the standard library. -- There is no special support for functors. So with `Hashtbl.Make(...)` the builtin model will not apply. So the analysis won't report that the following can throw `Not_found`: - -```rescript -module StringHash = - Hashtbl.Make({ - include String - let hash = Hashtbl.hash - }) -let specializedHash = tbl => StringHash.find(tbl, "abc") -``` +- There is no special support for functors. ### Guide From 259c57018292118efe8dc7e17b7fb9cfe03e80aa Mon Sep 17 00:00:00 2001 From: tsnobip Date: Fri, 3 Oct 2025 16:07:50 +0200 Subject: [PATCH 6/7] rename List.hd to List.head (its name in Stdlib) --- pages/docs/manual/v12.0.0/editor-plugins.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/docs/manual/v12.0.0/editor-plugins.mdx b/pages/docs/manual/v12.0.0/editor-plugins.mdx index d3104bbb3..4e39f8b55 100644 --- a/pages/docs/manual/v12.0.0/editor-plugins.mdx +++ b/pages/docs/manual/v12.0.0/editor-plugins.mdx @@ -111,7 +111,7 @@ let stringMake3 = @doesNotThrow String.make(12, ' ') try (foo()) { | exn => throw(exn) } ``` -- Uses of e.g. `List.hd` are interpreted as belonging to the standard library. If you re-define `List` in the local scope, the analysis it will think it's dealing with `List` from the standard library. +- Uses of e.g. `List.head` are interpreted as belonging to the standard library. If you re-define `List` in the local scope, the analysis it will think it's dealing with `List` from the standard library. - There is no special support for functors. ### Guide From 0826b9f398794c9f517a18c2cfb5297ec2fa00df Mon Sep 17 00:00:00 2001 From: tsnobip Date: Fri, 3 Oct 2025 17:04:37 +0200 Subject: [PATCH 7/7] rename functors to module functions --- pages/docs/manual/v12.0.0/editor-plugins.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/docs/manual/v12.0.0/editor-plugins.mdx b/pages/docs/manual/v12.0.0/editor-plugins.mdx index 4e39f8b55..4c6d7cd81 100644 --- a/pages/docs/manual/v12.0.0/editor-plugins.mdx +++ b/pages/docs/manual/v12.0.0/editor-plugins.mdx @@ -112,7 +112,7 @@ try (foo()) { | exn => throw(exn) } ``` - Uses of e.g. `List.head` are interpreted as belonging to the standard library. If you re-define `List` in the local scope, the analysis it will think it's dealing with `List` from the standard library. -- There is no special support for functors. +- There is no special support for module functions. ### Guide