|
| 1 | +# Migration Framework – Current Capabilities |
| 2 | + |
| 3 | +This document captures what the migration framework currently supports, based on `tools/src/migrate.ml` (and helpers in `tools/src/transforms.ml`, `compiler/ml/builtin_attributes.ml`, and `analysis/src/Cmt.ml`). |
| 4 | + |
| 5 | +## Inputs & Preconditions |
| 6 | + |
| 7 | +- Targets: `*.res` and `*.resi` files. |
| 8 | +- Requires build artifacts for the file’s module (CMT/CMTI). If not found, migration aborts with an error message asking to build the project. |
| 9 | +- Output modes: write back to file or print to stdout. |
| 10 | + |
| 11 | +## Deprecation Sources |
| 12 | + |
| 13 | +- Migration data is harvested from `@deprecated({ reason, migrate, migrateInPipeChain })` attributes recorded in CMT extras. |
| 14 | +- Captured context where deprecation was used: |
| 15 | + - `FunctionCall` – a function call site |
| 16 | + - `Reference` – a value/identifier reference (non‑call) |
| 17 | + |
| 18 | +## Supported Rewrites |
| 19 | + |
| 20 | +- Direct function calls (non‑pipe): |
| 21 | + - If `migrate` is an application expression, the call is rewritten according to the template (see Templates & Placeholders). |
| 22 | +- Pipe calls using `->`: |
| 23 | + - If `migrateInPipeChain` is provided, it is used for piped form. Special handling for single‑step chains (see Pipe Semantics). |
| 24 | +- Identifier references (non‑call): |
| 25 | + - If `migrate` is provided, the reference is replaced by that expression. |
| 26 | + - Special case: if the template is `f()` (unit call), treat it as `f` to avoid adding a spurious unit application. |
| 27 | +- Type constructor references: |
| 28 | + - If `migrate` is `%replace.type(: <core_type>)`, type constructor occurrences within the deprecation’s location range are replaced by `<core_type>`. |
| 29 | + - If the template is a constructor with its own type arguments, original type arguments are appended; otherwise original arguments are dropped. |
| 30 | +- Extension rename: |
| 31 | + - Extension `todo_` is remapped to `todo`. |
| 32 | + |
| 33 | +## Templates & Placeholders |
| 34 | + |
| 35 | +- Template form: application expressions only for call‑site rewrites; arbitrary expressions for reference rewrites. |
| 36 | +- Placeholders inside template expressions: |
| 37 | + - `%insert.unlabelledArgument(<int>)` |
| 38 | + - 0‑based index into the source call’s unlabelled arguments. |
| 39 | + - `%insert.labelledArgument("<name>")` |
| 40 | + - Refers to a labelled or optional source argument by name. |
| 41 | +- Placeholder replacement occurs anywhere inside the template expression, not only as direct arguments. |
| 42 | +- Consumed arguments are dropped from the original call; remaining arguments keep order. |
| 43 | +- Label renaming: |
| 44 | + - If a template argument has a label and its expression is a labelled placeholder, the corresponding source argument is emitted under the template’s label (rename). |
| 45 | + |
| 46 | +## Pipe Semantics |
| 47 | + |
| 48 | +- For piped calls, the pipe LHS is considered unlabelled argument index 0 for placeholder resolution. |
| 49 | +- When constructing the inner call, unlabelled drop positions are adjusted to account for the fact that the LHS does not appear as an inner argument. |
| 50 | +- Single‑step collapse: |
| 51 | + - If the LHS is not itself a pipe and `migrateInPipeChain` exists: |
| 52 | + - If `migrate` exists, prefer collapsing the step by applying `migrate` with the LHS inserted as unlabelled argument index 0. |
| 53 | + - Otherwise use `migrateInPipeChain` in piped form. |
| 54 | +- Empty‑args piped form: |
| 55 | + - If piping into a bare identifier and the chosen piped template inserts no arguments, the result remains `lhs -> newFn` (no extra parentheses). |
| 56 | + |
| 57 | +## Transforms (`@apply.transforms`) |
| 58 | + |
| 59 | +- Attribute name: `@apply.transforms(["<id>", ...])` on expressions. |
| 60 | +- Resolution: IDs map to functions in `tools/src/transforms.ml`. Unknown IDs are ignored. |
| 61 | +- Application: |
| 62 | + - Attributes attached to template or placeholder expressions are preserved on replacements and applied in a second pass over `.res` implementations. |
| 63 | + - Currently implemented transform: `dropUnitArgumentsInApply` (drops only unlabelled unit arguments from application nodes). |
| 64 | + - Other registry entries are stubs and currently no‑ops. |
| 65 | +- Note: The second pass runs for `.res` (implementations). Interfaces (`.resi`) do not contain expression bodies; transform pass is not applied there. |
| 66 | + |
| 67 | +## Limitations / Not Supported (Today) |
| 68 | + |
| 69 | +- Call‑site rewrite requires the template to be an application expression; non‑application templates for calls are ignored. |
| 70 | +- Placeholders with negative indices are ignored (no replacement, no drop). |
| 71 | +- No special handling for method sends beyond normal call/pipe matching. |
| 72 | +- Transforms run only in `.res`. Attachments in `.resi` will be carried in attrs but not executed. |
| 73 | +- Only type constructor occurrences are replaced via `%replace.type`; no pattern matching over other type forms. |
| 74 | +- Transform registry is minimal; most listed transforms are placeholders. |
| 75 | + |
| 76 | +## Summary of Behavior |
| 77 | + |
| 78 | +1. Collect deprecated uses from CMT (with optional templates and contexts). |
| 79 | +2. Rewrite references/calls/pipes according to provided templates and placeholder rules. |
| 80 | +3. Adjust labels, drop consumed args, and append inserted template args. |
| 81 | +4. For `.res`, run a second pass applying any `@apply.transforms` attributes that were attached to expressions during rewriting. |
| 82 | +5. Print updated AST; write file or stdout. |
0 commit comments