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
12 changes: 12 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,18 @@ tests/

### Development Workflow

Read the area guide before changing a compiler subsystem:

- [`compiler/syntax/README.md`](compiler/syntax/README.md) for parsing,
printing, and JSX transformation
- [`compiler/ml/README.md`](compiler/ml/README.md) for the type checker and
typed tree
- [`compiler/core/README.md`](compiler/core/README.md) for Lambda, Lam, and
JavaScript generation
- [`analysis/README.md`](analysis/README.md) for editor analysis
- [`rewatch/README.md`](rewatch/README.md) for the build system
- [`tools/README.md`](tools/README.md) for `rescript-tools`

1. **Understand which layer you're working on:**
- **Syntax layer** (`compiler/syntax/`): Parsing and surface syntax
- **ML layer** (`compiler/ml/`): Type checking and AST transformations
Expand Down
18 changes: 18 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,24 @@ We tried to keep the installation process as simple as possible. In case you are

Happy hacking!

## Repository guides

This document covers repository-wide setup and workflow. Start with the guide
for the compiler area you are changing:

- [parser, printer, and JSX transformation](compiler/syntax/README.md)
- [type checker and typed tree](compiler/ml/README.md)
- [Lambda, Lam, and JavaScript generation](compiler/core/README.md)
- [editor analysis](analysis/README.md)
- [build system](rewatch/README.md)
- [`rescript-tools`](tools/README.md)

Public language and library documentation belongs on the
[ReScript website](https://github.com/rescript-association/rescript-lang.org).
The guides in this repository document its current implementation and
contributor workflows. Detailed caller contracts belong in module interfaces;
algorithm and representation invariants belong beside their implementation.

## Setup

> Most of our contributors are working on Apple machines, so all our instructions are currently macOS / Linux centric. Contributions for Windows development welcome!
Expand Down
152 changes: 64 additions & 88 deletions analysis/README.md
Original file line number Diff line number Diff line change
@@ -1,106 +1,82 @@
# Analysis Library and Binary

This subfolder builds a private command line binary used by the plugin to power a few functionalities such as jump to definition, hover and autocomplete.

The binary reads the `.cmt` and `.cmti` files and analyses them.

For installation & build instructions, see the main CONTRIBUTING.md.

## Overview

See main CONTRIBUTING.md's repo structure. Check out `test.sh` (invoked through `make test`) to see the snapshots testing workflow stored in `tests/`.

## Usage

```shell
# Editor analysis

The analysis executable powers editor features such as completion, hover,
references, semantic tokens, and code actions. It reads compiler-produced
`.cmt` and `.cmti` files, so an analysis binary and the project artifacts it
inspects must be built with compatible compiler representations.

## Code map

- [`bin/main.ml`](bin/main.ml) starts the `rescript-editor-analysis` command.
- [`src/commands.ml`](src/commands.ml) dispatches commands, including the
source-annotated test command.
- `src/completion_*.ml` implements the completion frontend, context-specific
completion logic, and result conversion.
- [`src/hover.ml`](src/hover.ml), [`src/references.ml`](src/references.ml),
[`src/semantic_tokens.ml`](src/semantic_tokens.ml), and
[`src/code_actions.ml`](src/code_actions.ml) own the corresponding features.
- [`src/cmt.ml`](src/cmt.ml), [`src/process_cmt.ml`](src/process_cmt.ml), and
[`src/process_extra.ml`](src/process_extra.ml) are the main typed-artifact
boundary. Shared typed-tree definitions and traversal utilities live under
`compiler/ml`.
- [`reactive/README.md`](reactive/README.md) documents the reactive analysis
library. [`reanalyze/README.md`](reanalyze/README.md) covers Reanalyze, which
is a separate analysis pipeline in this directory.

Run the binary from the repository root:

```sh
dune exec -- rescript-editor-analysis --help
```

Add verbose logging via:

```shell
dune exec -- rescript-editor-analysis debug-dump verbose test
```

## History

This project is based on a fork of [Reason Language Server](https://github.com/jaredly/reason-language-server).

## Tests

### Prerequisites
Build the compiler and runtime, then run the repository target:

- Ensure the compiler is built (`make build` in the repository root).
- Ensure the library is built (`make lib` in the repository root).
```sh
make lib
make test-analysis
```

### Running the Tests
The target runs the suites under `tests/analysis_tests/`, including the main
snapshot suite and focused projects for generic JSX, incremental type checking,
namespaced references, and source-directory dependencies.

Run `make test` in `tests/analysis_tests/tests`.
The main suite uses directives embedded in ReScript comments. For example:

### Key Concept
```rescript
let value = 5
// value.
// ^com
```

The tests in the `tests/analysis_tests/tests` folder are based on the `dune exec -- rescript-editor-analysis test` command. This special subcommand processes a file and executes specific editor analysis functionality based on special syntax found in code comments.
`^com` asks the test command to compute completion at that position. See the
directive match in `analysis/src/commands.ml` for the current set. Tests compile
a temporary source file and compare command output with checked-in snapshots.
After an intentional change, inspect every updated snapshot rather than
accepting the directory wholesale.

Consider the following code:
To inspect one test while developing:

```res
let a = 5
// a.
// ^com
```sh
dune exec -- rescript-editor-analysis test tests/analysis_tests/tests/src/CompletePrioritize1.res
```

After building the ReScript project (**⚠️ this is a requirement**), you can execute `dune exec -- rescript-editor-analysis test Sample.res`, and completion will be performed for the cursor position indicated by `^`. The `com` directive requests completion. To see other commands, check out the pattern match in the `test` function in [Commands.ml](./src/Commands.ml).

> [!WARNING]
> Ensure there are no spaces in the code comments, as the commands are captured by a regular expression that expects spaces and not tabs!
Use paths from the repository root, and ensure the test project has first been
built with the local compiler.

Here’s how it works: once a command is found in a comment, a copy of the source file is created inside a temporary directory, where the line above `^com` is uncommented. The corresponding analysis functionality is then processed, typically with `~debug:true`. With debug enabled, code paths like
## Changing typed compiler representations

```ml
if Debug.verbose () then
print_endline "[complete_typed_value]--> Tfunction #other";
```
Editor analysis consumes typedtree nodes and compiler type representations
directly. When adding or changing a parsetree or typedtree node:

will print to stdout. This is helpful for observing what happens during the analysis.

When you run `make test` (from the `tests/analysis_tests` folder), `dune exec -- rescript-editor-analysis test <file>` will be executed for each `*.res` file in `analysis/tests/src`. The stdout will be compared to the corresponding `analysis/tests/src/expected` file. If `git diff` indicates changes, `make test` will fail, as these differences might be unintentional.

## Testing on Your Own Projects

To use a local version of `rescript-editor-analysis`, the targeted project needs to be compiled with the local compiler.

Install your local ReScript with `npm i /path/to/your-local-rescript-repo`.
Reinstall the dependencies and run `npx rescript` in your project. This ensures the project is compiled with the same compiler version that the `rescript-editor-analysis` will process.

## Debugging

It is possible to debug `analysis` via [ocamlearlybird](https://github.com/hackwaly/ocamlearlybird).

1. Install `opam install earlybird`.
2. Install the [earlybird extension](https://marketplace.visualstudio.com/items?itemName=hackwaly.ocamlearlybird).
3. Create a launch configuration (`.vscode/launch.json`):

```json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug analysis",
"type": "ocaml.earlybird",
"request": "launch",
"program": "${workspaceFolder}/_build/default/analysis/bin/main.bc",
"stopOnEntry": true,
"cwd": "/projects/your-project",
"env": {
"CAML_LD_LIBRARY_PATH": "${workspaceFolder}/_build/default/compiler/ext"
},
"arguments": [
"test",
"src/Main.res"
]
}
]
}
```
1. search this directory for matches on the surrounding constructors, rather
than relying only on exhaustiveness warnings;
2. check completion, hover/type printing, references, document symbols,
semantic tokens, code actions, and interface generation as applicable;
3. update both positive results and recovery behavior for incomplete source;
4. run `make test-analysis` in addition to compiler tests.

The `CAML_LD_LIBRARY_PATH` environment variable is required to tell OCaml where `dllext_stubs.so` can be loaded from.
Keep compiler representation contracts in the owning compiler `.mli` files.
Put analysis-specific assumptions beside the analysis code that relies on them,
and use this guide only for navigation and cross-cutting workflow.
6 changes: 5 additions & 1 deletion analysis/reactive/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ This prevents issues like:
- Anti-joins seeing partial data (e.g., refs without matching decls)
- Multi-level unions causing spurious additions/removals

The `fixpoint` implementation maintains exact reachability when roots and
edges are added or removed. See
[IncrementalFixpointReport.md](IncrementalFixpointReport.md) for the algorithm,
its invariants, and the limits of its evaluation data.

## Usage in Reanalyze

This library powers the reactive dead code analysis in reanalyze:
Expand All @@ -106,4 +111,3 @@ This library powers the reactive dead code analysis in reanalyze:
- `ReactiveMerge`: Merges per-file data into global collections
- `ReactiveLiveness`: Computes live declarations via fixpoint
- `ReactiveSolver`: Generates dead code issues reactively

11 changes: 7 additions & 4 deletions analysis/reanalyze/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,20 @@ AnalysisResult.get_issues analysis_result

---

## Incremental Updates (Future)
## Incremental updates in the non-reactive pipeline

The architecture enables incremental updates when a file changes:
The phase boundaries permit the non-reactive pipeline to update one file's
input without retaining mutable per-file analysis state:

1. Re-run Phase 1 for changed file only → new `file_data`
2. Replace in `file_data` map (keyed by filename)
3. Re-run Phase 2 (merge) - fast, pure function
4. Re-run Phase 3 (solve) - fast, pure function

The key insight: **immutable data structures enable safe incremental updates** - you can swap one file's data without affecting others.
Immutable phase outputs allow one file's data to be replaced without mutating
the retained outputs for other files. The current reactive pipeline below goes
further by propagating deltas through derived collections rather than rerunning
the complete merge and solve phases.

---

Expand Down Expand Up @@ -334,4 +338,3 @@ Use `-timing` flag to see per-node statistics:
| `Issue` | Issue type definitions |
| `Log_` | Phase 4: Logging output |
| `ReactiveSolver` | Reactive dead_decls → issues computation |

Loading
Loading