Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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