Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#### :nail_care: Polish

- Rewatch cli: do not show build command options in the root help. https://github.com/rescript-lang/rescript/pull/7715
- Deprecate reanalyze `@raises` in favor of `@throws`. https://github.com/rescript-lang/rescript/pull/7932

#### :house: Internal

Expand Down
26 changes: 6 additions & 20 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,30 +334,15 @@ Note that there's currently still a manual step involved on [rescript-lang.org](

## Contribute to the API Reference

The API reference is generated from doc comments in the source code. [Here](https://github.com/rescript-lang/rescript-compiler/blob/99650/jscomp/others/js_re.mli#L146-L161)'s a good example.
The API reference is generated from doc comments in the source code. [Here](https://github.com/rescript-lang/rescript/blob/57c696b1a38f53badaddcc082ed29188d80df70d/packages/%40rescript/runtime/Stdlib_String.resi#L441-L458)'s a good example.

Some tips:

- The first sentence or line should be a very short summary. This is used in indexes and by tools like merlin.
- Ideally, every function should have **at least one** `@example`.
- Cross-reference another definition with `{! identifier}`. But use them sparingly, they’re a bit verbose (currently, at least).
- Wrap non-cross-referenced identifiers and other code in `[ ... ]`.
- Escape `{`, `}`, `[`, `]` and `@` using `\`.
- It’s possible to use `{%html ...}` to generate custom html, but use this very, very sparingly.
- A number of "documentation tags" are provided that would be nice to use, but unfortunately they’re often not supported for \`external\`s. Which is of course most of the API.
- `@param` usually doesn’t work. Use `{b <param>} ...` instead
- `@returns` usually doesn’t work. Use `{b returns} ...` instead.
- The first sentence or line should show the function call with a very short summary.
- Ideally, every function should have an `## Examples` section with **at least one** example. The examples are compiled to check that they are correct. Use `==` to generate tests from the examples.
- Always use `@deprecated` when applicable.
- Always use `@raise` when applicable.
- Always provide a `@see` tag pointing to MDN for more information when available.

See [Ocamldoc documentation](http://caml.inria.fr/pub/docs/manual-ocaml/ocamldoc.html#sec333) for more details.

To generate the html:

```sh
../scripts/ninja docs
```
- Always use `@throw` when applicable.
- Always provide a `See` section pointing to MDN for more information when available.

## Contribute to JSX `domProps`

Expand Down Expand Up @@ -389,6 +374,7 @@ Adding a new entry there requires re-running the analysis tests. Follow these st
(If a `make` command fails, consider using the [DevContainer](#b-devcontainer).)

Finally, add a line to [CHANGELOG.md](CHANGELOG.md), using the `#### :nail_care: Polish` section.

## Code structure

The highlevel architecture is illustrated as below:
Expand Down
6 changes: 3 additions & 3 deletions analysis/reanalyze/src/Common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,12 @@ type line = {mutable declarations: decl list; original: string}

module ExnSet = Set.Make (Exn)

type missingRaiseInfo = {
type missingThrowInfo = {
exnName: string;
exnTable: (Exn.t, LocSet.t) Hashtbl.t;
locFull: Location.t;
missingAnnotations: ExnSet.t;
raiseSet: ExnSet.t;
throwSet: ExnSet.t;
}

type severity = Warning | Error
Expand All @@ -234,7 +234,7 @@ type lineAnnotation = (decl * line) option
type description =
| Circular of {message: string}
| ExceptionAnalysis of {message: string}
| ExceptionAnalysisMissing of missingRaiseInfo
| ExceptionAnalysisMissing of missingThrowInfo
| DeadModule of {message: string}
| DeadOptional of {deadOptional: deadOptional; message: string}
| DeadWarning of {
Expand Down
96 changes: 50 additions & 46 deletions analysis/reanalyze/src/Exception.ml
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ module Event = struct
type kind =
| Catches of t list (* with | E => ... *)
| Call of {callee: Common.Path.t; modulePath: Common.Path.t} (* foo() *)
| DoesNotRaise of
t list (* DoesNotRaise(events) where events come from an expression *)
| Raises (** raise E *)
| DoesNotThrow of
t list (* DoesNotThrow(events) where events come from an expression *)
| Throws (** throw E *)

and t = {exceptions: Exceptions.t; kind: kind; loc: Location.t}

Expand All @@ -81,14 +81,14 @@ module Event = struct
(modulePath |> Common.Path.toString)
(Exceptions.pp ~exnTable:None)
exceptions
| {kind = DoesNotRaise nestedEvents; loc} ->
Format.fprintf ppf "%s DoesNotRaise(%a)@."
| {kind = DoesNotThrow nestedEvents; loc} ->
Format.fprintf ppf "%s DoesNotThrow(%a)@."
(loc.loc_start |> posToString)
(fun ppf () ->
nestedEvents |> List.iter (fun e -> Format.fprintf ppf "%a " print e))
()
| {kind = Raises; exceptions; loc} ->
Format.fprintf ppf "%s raises %a@."
| {kind = Throws; exceptions; loc} ->
Format.fprintf ppf "%s throws %a@."
(loc.loc_start |> posToString)
(Exceptions.pp ~exnTable:None)
exceptions
Expand Down Expand Up @@ -118,7 +118,7 @@ module Event = struct
in
let rec loop exnSet events =
match events with
| ({kind = Raises; exceptions; loc} as ev) :: rest ->
| ({kind = Throws; exceptions; loc} as ev) :: rest ->
if !Common.Cli.debug then Log_.item "%a@." print ev;
exceptions |> Exceptions.iter (fun exn -> extendExnTable exn loc);
loop (Exceptions.union exnSet exceptions) rest
Expand All @@ -134,7 +134,7 @@ module Event = struct
in
exceptions |> Exceptions.iter (fun exn -> extendExnTable exn loc);
loop (Exceptions.union exnSet exceptions) rest
| ({kind = DoesNotRaise nestedEvents; loc} as ev) :: rest ->
| ({kind = DoesNotThrow nestedEvents; loc} as ev) :: rest ->
if !Common.Cli.debug then Log_.item "%a@." print ev;
let nestedExceptions = loop Exceptions.empty nestedEvents in
(if Exceptions.isEmpty nestedExceptions (* catch-all *) then
Expand All @@ -148,8 +148,8 @@ module Event = struct
{
message =
Format.asprintf
"@{<info>%s@} does not raise and is annotated with \
redundant @doesNotRaise"
"@{<info>%s@} does not throw and is annotated with \
redundant @doesNotThrow"
(name |> Name.toString);
}));
loop exnSet rest
Expand All @@ -158,12 +158,12 @@ module Event = struct
if Exceptions.isEmpty exceptions then loop exnSet rest
else
let nestedExceptions = loop Exceptions.empty nestedEvents in
let newRaises = Exceptions.diff nestedExceptions exceptions in
let newThrows = Exceptions.diff nestedExceptions exceptions in
exceptions
|> Exceptions.iter (fun exn ->
nestedEvents
|> List.iter (fun event -> shrinkExnTable exn event.loc));
loop (Exceptions.union exnSet newRaises) rest
loop (Exceptions.union exnSet newThrows) rest
| [] -> exnSet
in
let exnSet = loop Exceptions.empty events in
Expand All @@ -188,31 +188,31 @@ module Checks = struct
checks := {events; exceptions; loc; locFull; moduleName; exnName} :: !checks

let doCheck {events; exceptions; loc; locFull; moduleName; exnName} =
let raiseSet, exnTable = events |> Event.combine ~moduleName in
let missingAnnotations = Exceptions.diff raiseSet exceptions in
let redundantAnnotations = Exceptions.diff exceptions raiseSet in
let throwSet, exnTable = events |> Event.combine ~moduleName in
let missingAnnotations = Exceptions.diff throwSet exceptions in
let redundantAnnotations = Exceptions.diff exceptions throwSet in
(if not (Exceptions.isEmpty missingAnnotations) then
let description =
Common.ExceptionAnalysisMissing
{exnName; exnTable; raiseSet; missingAnnotations; locFull}
{exnName; exnTable; throwSet; missingAnnotations; locFull}
in
Log_.warning ~loc description);
if not (Exceptions.isEmpty redundantAnnotations) then
Log_.warning ~loc
(Common.ExceptionAnalysis
{
message =
(let raisesDescription ppf () =
if raiseSet |> Exceptions.isEmpty then
Format.fprintf ppf "raises nothing"
(let throwsDescription ppf () =
if throwSet |> Exceptions.isEmpty then
Format.fprintf ppf "throws nothing"
else
Format.fprintf ppf "might raise %a"
Format.fprintf ppf "might throw %a"
(Exceptions.pp ~exnTable:(Some exnTable))
raiseSet
throwSet
in
Format.asprintf
"@{<info>%s@} %a and is annotated with redundant @raises(%a)"
exnName raisesDescription ()
"@{<info>%s@} %a and is annotated with redundant @throws(%a)"
exnName throwsDescription ()
(Exceptions.pp ~exnTable:None)
redundantAnnotations);
})
Expand Down Expand Up @@ -249,35 +249,38 @@ let traverseAst () =
case.c_guard |> iterExprOpt self;
case.c_rhs |> iterExpr self)
in
let isRaise s = s = "Pervasives.raise" || s = "Pervasives.throw" in
let raiseArgs args =
let isThrow s = s = "Pervasives.raise" || s = "Pervasives.throw" in
let throwArgs args =
match args with
| [(_, Some {Typedtree.exp_desc = Texp_construct ({txt}, _, _)})] ->
[Exn.fromLid txt] |> Exceptions.fromList
| [(_, Some {Typedtree.exp_desc = Texp_ident _})] ->
[Exn.fromString "genericException"] |> Exceptions.fromList
| _ -> [Exn.fromString "TODO_from_raise1"] |> Exceptions.fromList
in
let doesNotRaise attributes =
let doesNotThrow attributes =
attributes
|> Annotation.getAttributePayload (fun s ->
s = "doesNotRaise" || s = "doesnotraise" || s = "DoesNoRaise"
|| s = "doesNotraise" || s = "doNotRaise" || s = "donotraise"
|| s = "DoNoRaise" || s = "doNotraise")
|> Annotation.getAttributePayload (function
| "doesNotRaise" | "doesnotraise" | "DoesNoRaise" | "doesNotraise"
| "doNotRaise" | "donotraise" | "DoNoRaise" | "doNotraise"
| "doesNotThrow" | "doesnotthrow" | "DoesNoThrow" | "doesNotthrow"
| "doNotThrow" | "donotthrow" | "DoNoThrow" | "doNotthrow" ->
true
| _ -> false)
<> None
in
let expr (self : Tast_mapper.mapper) (expr : Typedtree.expression) =
let loc = expr.exp_loc in
let isDoesNoRaise = expr.exp_attributes |> doesNotRaise in
let isDoesNoThrow = expr.exp_attributes |> doesNotThrow in
let oldEvents = !currentEvents in
if isDoesNoRaise then currentEvents := [];
if isDoesNoThrow then currentEvents := [];
(match expr.exp_desc with
| Texp_ident (callee_, _, _) ->
let callee =
callee_ |> Common.Path.fromPathT |> ModulePath.resolveAlias
in
let calleeName = callee |> Common.Path.toName in
if calleeName |> Name.toString |> isRaise then
if calleeName |> Name.toString |> isThrow then
Log_.warning ~loc
(Common.ExceptionAnalysis
{
Expand All @@ -299,17 +302,17 @@ let traverseAst () =
args = [(_lbl1, Some {exp_desc = Texp_ident (callee, _, _)}); arg];
}
when (* raise @@ Exn(...) *)
atat |> Path.name = "Pervasives.@@" && callee |> Path.name |> isRaise
atat |> Path.name = "Pervasives.@@" && callee |> Path.name |> isThrow
->
let exceptions = [arg] |> raiseArgs in
currentEvents := {Event.exceptions; loc; kind = Raises} :: !currentEvents;
let exceptions = [arg] |> throwArgs in
currentEvents := {Event.exceptions; loc; kind = Throws} :: !currentEvents;
arg |> snd |> iterExprOpt self
| Texp_apply {funct = {exp_desc = Texp_ident (callee, _, _)} as e; args} ->
let calleeName = Path.name callee in
if calleeName |> isRaise then
let exceptions = args |> raiseArgs in
if calleeName |> isThrow then
let exceptions = args |> throwArgs in
currentEvents :=
{Event.exceptions; loc; kind = Raises} :: !currentEvents
{Event.exceptions; loc; kind = Throws} :: !currentEvents
else e |> iterExpr self;
args |> List.iter (fun (_, eOpt) -> eOpt |> iterExprOpt self)
| Texp_match (e, casesOk, casesExn, partial) ->
Expand All @@ -332,7 +335,7 @@ let traverseAst () =
{
Event.exceptions = [Exn.matchFailure] |> Exceptions.fromList;
loc;
kind = Raises;
kind = Throws;
}
:: !currentEvents
| Texp_try (e, cases) ->
Expand All @@ -348,21 +351,22 @@ let traverseAst () =
{Event.exceptions; loc; kind = Catches !currentEvents} :: oldEvents;
cases |> iterCases self
| _ -> super.expr self expr |> ignore);
(if isDoesNoRaise then
(if isDoesNoThrow then
let nestedEvents = !currentEvents in
currentEvents :=
{
Event.exceptions = Exceptions.empty;
loc;
kind = DoesNotRaise nestedEvents;
kind = DoesNotThrow nestedEvents;
}
:: oldEvents);
expr
in
let getExceptionsFromAnnotations attributes =
let raisesAnnotationPayload =
let throwsAnnotationPayload =
attributes
|> Annotation.getAttributePayload (fun s -> s = "raises" || s = "raise")
|> Annotation.getAttributePayload (fun s ->
s = "throws" || s = "throw" || s = "raises" || s = "raise")
in
let rec getExceptions payload =
match payload with
Expand All @@ -379,7 +383,7 @@ let traverseAst () =
|> List.concat |> Exceptions.fromList
| _ -> Exceptions.empty
in
match raisesAnnotationPayload with
match throwsAnnotationPayload with
| None -> Exceptions.empty
| Some payload -> payload |> getExceptions
in
Expand Down
16 changes: 8 additions & 8 deletions analysis/reanalyze/src/Log_.ml
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ let missingRaiseInfoToText {missingAnnotations; locFull} =
Format.asprintf "%a" (Exceptions.pp ~exnTable:None) missingAnnotations
in
if !Cli.json then
EmitJson.emitAnnotate ~action:"Add @raises annotation"
EmitJson.emitAnnotate ~action:"Add @throws annotation"
~pos:(EmitJson.locToPos locFull)
~text:(Format.asprintf "@raises(%s)\\n" missingTxt)
~text:(Format.asprintf "@throws(%s)\\n" missingTxt)
else ""

let logAdditionalInfo ~(description : description) =
Expand All @@ -117,17 +117,17 @@ let logAdditionalInfo ~(description : description) =
missingRaiseInfoToText missingRaiseInfo
| _ -> ""

let missingRaiseInfoToMessage {exnTable; exnName; missingAnnotations; raiseSet}
let missingThrowInfoToMessage {exnTable; exnName; missingAnnotations; throwSet}
=
let raisesTxt =
Format.asprintf "%a" (Exceptions.pp ~exnTable:(Some exnTable)) raiseSet
let throwsTxt =
Format.asprintf "%a" (Exceptions.pp ~exnTable:(Some exnTable)) throwSet
in
let missingTxt =
Format.asprintf "%a" (Exceptions.pp ~exnTable:None) missingAnnotations
in
Format.asprintf
"@{<info>%s@} might raise %s and is not annotated with @raises(%s)" exnName
raisesTxt missingTxt
"@{<info>%s@} might throw %s and is not annotated with @throws(%s)" exnName
throwsTxt missingTxt

let descriptionToMessage (description : description) =
match description with
Expand All @@ -138,7 +138,7 @@ let descriptionToMessage (description : description) =
Format.asprintf "@{<info>%s@} %s" path message
| ExceptionAnalysis {message} -> message
| ExceptionAnalysisMissing missingRaiseInfo ->
missingRaiseInfoToMessage missingRaiseInfo
missingThrowInfoToMessage missingRaiseInfo
| Termination {message} -> message

let descriptionToName (description : description) =
Expand Down
Loading
Loading