Repository navigation
Conversation
Changelog🐛 Bug fix
Semantic Diff10 commits · Full page Complete the constructors of extensible variantse751edb · cknitt + Claude · Oct 27, 2026, 13:06 UTC
Diagnostic modified (completion of an extensible variant) type attr = ..
type attr += Str(string) | Num(int)
// let a: attr = Nu
-Nullable, Null
+Num(_), Nullable, NullDiagnostic modified (exhaustive switch snippet) let value: attr = Str("s")
// switch valu
switch value {
| Str(_) => %todo
| Num(_) => %todo
| Flag => %todo
| _ => %todo
}Review
Match extension constructors by type and name them by module path5639dff · cknitt + Claude · Oct 27, 2026, 17:50 UTC Codex review on #9085: extensions were matched to an open type by its last name only, so Name extension constructors relative to where completion runsf2fd425 · cknitt + Claude · Oct 27, 2026, 20:29 UTC Codex review on #9085: extension constructors were named relative to the root of their file and to the file of the completed value. Inside the declaring module that inserted Qualify extension constructors of namespaced modules by source patha62bfe5 · cknitt + Claude · Oct 27, 2026, 21:45 UTC Codex review on #9085: the module name of another file comes from its compiled name, which in a namespaced project is Offer extensions in scope at the cursor and from interface modulese6a28af · cknitt + Claude · Oct 28, 2026, 07:47 UTC Codex review on #9085: an extension of the edited file declared after the cursor was offered although it isn't in scope there, and an extension inside a module of an interface ( Qualify extensions of nested interface modules and complete their payloads52ee62d · cknitt + Claude · Oct 29, 2026, 06:34 UTC Codex review on #9085: an extension two modules deep in an interface got only the outer module in its path, because the signature walk didn't extend the module path when entering a module, and completion inside the payload of a qualified constructor such as Type extension payloads in their own file and match qualified constructorsb03db7b · cknitt + Claude · Oct 29, 2026, 10:10 UTC Codex review on #9085: a payload type declared next to the extension, as in Look up unqualified extension payloads in the scope at the cursord56bea1 · cknitt + Claude · Oct 29, 2026, 10:33 UTC Codex review on #9085: ranking same-named extension constructors let an open outrank a local extension declared after it, so after Keep a catch-all when expanding or switching on an open typefb97e50 · cknitt + Claude · Oct 29, 2026, 18:02 UTC
Codeaction modified (Expand catch-all) type attr = ..
type attr += Str(string) | Num(int)
let f = (value: attr) =>
switch value {
| Str(_) => "str"
| _ => "other"
}
-Num(_)
+Num(_) | _Codeaction modified (Exhaustive switch) type attr = ..
type attr += Str(string) | Num(int)
let value: attr = Str("s")
value
switch value {
| Str(_) => %todo
| Num(_) => %todo
+| _ => %todo
}Review
Resolve an open's extension constructor only through its module347aa66 · cknitt + Claude · Oct 29, 2026, 18:03 UTC
Completion modified (extension payload after an open) type attr = ..
module A = {
type attr += Value(bool)
}
type attr += Value(option<int>)
open A
let x: attr = Value()
-option<int>, the payload of the local Value
+bool, the payload of A.Value: true, false |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #9085 +/- ##
==========================================
+ Coverage 81.72% 81.76% +0.03%
==========================================
Files 460 460
Lines 64817 64973 +156
==========================================
+ Hits 52969 53122 +153
- Misses 11848 11851 +3
🚀 New features to boost your workflow:
|
rescript
@rescript/belt
@rescript/darwin-arm64
@rescript/linux-arm64
@rescript/linux-x64
@rescript/runtime
@rescript/win32-x64
commit: |
|
Developer playground preview: https://rescript-lang.github.io/rescript/dev-playground/?version=pr-9085 |
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 98ecc79d93
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex review on #9085: extensions were matched to an open type by its last name only, so `A.t` got the constructors of `B.t`, and a constructor declared in a nested module was inserted without its module, which doesn't compile because an extension constructor can't be found from the expected type. An extension now records the type it extends and is matched by that type's file and stamp, and it is named by its module path, for example `Sub.Flag`; a prefix may match the last part. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Signed-off-by: Christoph Knittel <ck@cca.io>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f678bb32e5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex review on #9085: extension constructors were named relative to the root of their file and to the file of the completed value. Inside the declaring module that inserted `Sub.Flag`, which is not in scope there, and for a value imported from another file the constructors were not qualified with its module, while extensions in the edited file were missed. A completion request now records the edited file and the module path at the cursor; constructors of that file drop the module path shared with the cursor, constructors of other files get their module's name, and extensions are collected from the edited file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Signed-off-by: Christoph Knittel <ck@cca.io>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 56cff12ba2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex review on #9085: the module name of another file comes from its compiled name, which in a namespaced project is `Base-MyNamespace`, so completion inserted `Base-MyNamespace.Flag`. The name is turned into its source path `MyNamespace.Base` first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Signed-off-by: Christoph Knittel <ck@cca.io>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3408b27a6e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex review on #9085: extensions were matched to an open type by its last name only, so `A.t` got the constructors of `B.t`, and a constructor declared in a nested module was inserted without its module, which doesn't compile because an extension constructor can't be found from the expected type. An extension now records the type it extends and is matched by that type's file and stamp, and it is named by its module path, for example `Sub.Flag`; a prefix may match the last part. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Signed-off-by: Christoph Knittel <ck@cca.io>
3408b27 to
84e1386
Compare
Codex review on #9085: extension constructors were named relative to the root of their file and to the file of the completed value. Inside the declaring module that inserted `Sub.Flag`, which is not in scope there, and for a value imported from another file the constructors were not qualified with its module, while extensions in the edited file were missed. A completion request now records the edited file and the module path at the cursor; constructors of that file drop the module path shared with the cursor, constructors of other files get their module's name, and extensions are collected from the edited file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Signed-off-by: Christoph Knittel <ck@cca.io>
Codex review on #9085: the module name of another file comes from its compiled name, which in a namespaced project is `Base-MyNamespace`, so completion inserted `Base-MyNamespace.Flag`. The name is turned into its source path `MyNamespace.Base` first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Signed-off-by: Christoph Knittel <ck@cca.io>
Codex review on #9085: an extension of the edited file declared after the cursor was offered although it isn't in scope there, and an extension inside a module of an interface (`module Sub: { type attr += Flag }`) was never registered, because only typed-tree extensions were. The completion scope now records the cursor position and skips later extensions of the edited file, and extensions in signatures are registered too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Signed-off-by: Christoph Knittel <ck@cca.io>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 84e1386175
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…loads Codex review on #9085: an extension two modules deep in an interface got only the outer module in its path, because the signature walk didn't extend the module path when entering a module, and completion inside the payload of a qualified constructor such as `Sub.Toggle(...)` found no constructor, because the payload position keeps only `Toggle`. Signature modules now extend the path, and payload lookup also matches the last part of a qualified constructor name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Signed-off-by: Christoph Knittel <ck@cca.io>
|
@codex review |
1 similar comment
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3cb8834566
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ctors Codex review on #9085: a payload type declared next to the extension, as in `type Base.attr += Color(color)` with a local `color`, was resolved in the open type's file, so payload completion lost it; and of two same-named extensions, `B.Value(...)` completed the payload of the first one, `A.Value`, because the payload position kept only `Value`. Each extension constructor now carries the environment of its file, and the payload position keeps the written path: the constructor sharing the longest suffix with it is meant, ties going to the most recent open, then to the last declared one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gGkPma9LAZimuQjnrP Signed-off-by: Christoph Knittel <ck@cca.io>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 462f92c4a4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex review on #9085: ranking same-named extension constructors let an open outrank a local extension declared after it, so after `open A` and a later local `Value(...)`, completion used `A.Value`'s payload; and an extension inside a module type, `module type S = { type attr += Phantom }`, was offered as `S.Phantom` and added to exhaustive switches. A qualified constructor is now the one its written path names, and an unqualified one is looked up in the completion scope as the compiler does, which now records extension constructors, the most recent declaration or open winning. Extensions in module types are skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gGkPma9LAZimuQjnrP Signed-off-by: Christoph Knittel <ck@cca.io>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b1191669a3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex review on #9085: extensions were matched to an open type by its last name only, so `A.t` got the constructors of `B.t`, and a constructor declared in a nested module was inserted without its module, which doesn't compile because an extension constructor can't be found from the expected type. An extension now records the type it extends and is matched by that type's file and stamp, and it is named by its module path, for example `Sub.Flag`; a prefix may match the last part. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel <ck@cca.io>
b119166 to
3f10535
Compare
Codex review on #9085: extension constructors were named relative to the root of their file and to the file of the completed value. Inside the declaring module that inserted `Sub.Flag`, which is not in scope there, and for a value imported from another file the constructors were not qualified with its module, while extensions in the edited file were missed. A completion request now records the edited file and the module path at the cursor; constructors of that file drop the module path shared with the cursor, constructors of other files get their module's name, and extensions are collected from the edited file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel <ck@cca.io>
Codex review on #9085: the module name of another file comes from its compiled name, which in a namespaced project is `Base-MyNamespace`, so completion inserted `Base-MyNamespace.Flag`. The name is turned into its source path `MyNamespace.Base` first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel <ck@cca.io>
Codex review on #9085: an extension of the edited file declared after the cursor was offered although it isn't in scope there, and an extension inside a module of an interface (`module Sub: { type attr += Flag }`) was never registered, because only typed-tree extensions were. The completion scope now records the cursor position and skips later extensions of the edited file, and extensions in signatures are registered too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel <ck@cca.io>
…loads Codex review on #9085: an extension two modules deep in an interface got only the outer module in its path, because the signature walk didn't extend the module path when entering a module, and completion inside the payload of a qualified constructor such as `Sub.Toggle(...)` found no constructor, because the payload position keeps only `Toggle`. Signature modules now extend the path, and payload lookup also matches the last part of a qualified constructor name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel <ck@cca.io>
…ctors Codex review on #9085: a payload type declared next to the extension, as in `type Base.attr += Color(color)` with a local `color`, was resolved in the open type's file, so payload completion lost it; and of two same-named extensions, `B.Value(...)` completed the payload of the first one, `A.Value`, because the payload position kept only `Value`. Each extension constructor now carries the environment of its file, and the payload position keeps the written path: the constructor sharing the longest suffix with it is meant, ties going to the most recent open, then to the last declared one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gGkPma9LAZimuQjnrP Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel <ck@cca.io>
Codex review on #9085: ranking same-named extension constructors let an open outrank a local extension declared after it, so after `open A` and a later local `Value(...)`, completion used `A.Value`'s payload; and an extension inside a module type, `module type S = { type attr += Phantom }`, was offered as `S.Phantom` and added to exhaustive switches. A qualified constructor is now the one its written path names, and an unqualified one is looked up in the completion scope as the compiler does, which now records extension constructors, the most recent declaration or open winning. Extensions in module types are skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gGkPma9LAZimuQjnrP Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel <ck@cca.io>
Why:
Codex review on #9085: now that an open type extracts as a variant,
the Expand catch-all and Exhaustive switch code actions replaced or
built the switch from the constructors known here, dropping the `_`
that matches constructors added in other files.
Codeaction modified (Expand catch-all):
Example:
type attr = ..
type attr += Str(string) | Num(int)
let f = (value: attr) =>
switch value {
| Str(_) => "str"
| _ => "other"
}
Before (text): the `_` is replaced by
Num(_)
After (text): the `_` is replaced by
Num(_) | _
Codeaction modified (Exhaustive switch):
Example:
type attr = ..
type attr += Str(string) | Num(int)
let value: attr = Str("s")
value
Before (rescript):
switch value {
| Str(_) => %todo
| Num(_) => %todo
}
After (rescript):
switch value {
| Str(_) => %todo
| Num(_) => %todo
| _ => %todo
}
Review: Why keep the `_` as an alternative instead of a separate case?
Replacing only the pattern needs no copy of the case body, and the
expanded or-pattern lists the known constructors for splitting by hand.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo
Signed-off-by: Christoph Knittel <ck@cca.io>
Why:
Codex review on #9085: an `open A` after a local extension `Value`
resolved the payload of an unqualified `Value(...)` to the local
constructor. Matching `A.Value` against the constructors also accepted
the bare local `Value` as a suffix, and the later declaration won. A
constructor named without a module is declared around the cursor, so
an open no longer brings it into scope.
Completion modified (extension payload after an open):
Example:
type attr = ..
module A = {
type attr += Value(bool)
}
type attr += Value(option<int>)
open A
let x: attr = Value()
Before (text): payload completion inside `Value()` completes
option<int>, the payload of the local Value
After (text): payload completion inside `Value()` completes
bool, the payload of A.Value: true, false
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo
Signed-off-by: Christoph Knittel <ck@cca.io>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 417ad95d74
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let named path = | ||
| constructors | ||
| |> List.filter (fun c -> | ||
| ends_with (parts c) path || ends_with path (parts c)) |
There was a problem hiding this comment.
Resolve module aliases when matching payload constructors
When an extension constructor is accessed through a module alias, textual path matching cannot find its payload. For example, with module B = { type attr += Value(bool) } and module Alias = B, completing inside Alias.Value(...) compares ["Alias"; "Value"] against the recorded ["B"; "Value"]; neither comparison succeeds, so boolean payload suggestions disappear in both expressions and patterns. Opening Alias has the same problem. Resolve the written module path before selecting the constructor. The fresh counterexample uses a module alias, which the existing direct-qualified-path tests do not cover.
Useful? React with 👍 / 👎.
| Type_utils.with_completion_scope ~file:full.file | ||
| ~module_path:(module_path_at ~full ~pos) ~pos ~scope (fun () -> | ||
| process_completable_in_scope ~state ~debug ~full ~scope ~env ~pos | ||
| ~for_hover completable) |
There was a problem hiding this comment.
Keep the completion scope active during code-action extraction
For code actions inside a nested module, this wrapper restores completion_scope before extension constructors are collected: Xform.extract_type_from_expr calls process_completable at lines 14–17, but calls Type_utils.extract_type afterward at lines 31–36. Consequently, an “Exhaustive switch” or “Expand catch-all” action inside module Sub uses an empty scope path and inserts Sub.Flag for an extension declared in Sub, where that qualification is not accessible. Constructors declared after the action position are also no longer filtered. Extend the scope lifetime through the code action's type extraction, or pass the context explicitly. This is fresh evidence from the code-action consumer, rather than the completion paths covered by the earlier fixes.
Useful? React with 👍 / 👎.
Why:
Editor analysis didn't record the constructors added by `type t += ...`,
and an open type has none in its declaration, so completion offered no
constructor where a value of the type was expected. They are now
registered as constructors of an open type, and typed completion of an
open type offers those declared in the type's file and in the current
file. Since other files can extend the type, the exhaustive switch
snippet for one adds a `_` case.
Diagnostic modified (completion of an extensible variant):
Example:
type attr = ..
type attr += Str(string) | Num(int)
// let a: attr = Nu
Before (text): tests/analysis_tests/tests/src/CompletionExtensibleVariant.res
Nullable, Null
After (text): tests/analysis_tests/tests/src/CompletionExtensibleVariant.res
Num(_), Nullable, Null
Diagnostic modified (exhaustive switch snippet):
Example:
let value: attr = Str("s")
// switch valu
After (rescript):
switch value {
| Str(_) => %todo
| Num(_) => %todo
| Flag => %todo
| _ => %todo
}
Review: Which extensions are offered?
Those in the file that declares the type and in the current file;
an extension in a third file is not known to the current file's
analysis.
Fixes: #9073
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3
Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo
Signed-off-by: Christoph Knittel <ck@cca.io>
Codex review on #9085: extensions were matched to an open type by its last name only, so `A.t` got the constructors of `B.t`, and a constructor declared in a nested module was inserted without its module, which doesn't compile because an extension constructor can't be found from the expected type. An extension now records the type it extends and is matched by that type's file and stamp, and it is named by its module path, for example `Sub.Flag`; a prefix may match the last part. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel <ck@cca.io>
Codex review on #9085: extension constructors were named relative to the root of their file and to the file of the completed value. Inside the declaring module that inserted `Sub.Flag`, which is not in scope there, and for a value imported from another file the constructors were not qualified with its module, while extensions in the edited file were missed. A completion request now records the edited file and the module path at the cursor; constructors of that file drop the module path shared with the cursor, constructors of other files get their module's name, and extensions are collected from the edited file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel <ck@cca.io>
Codex review on #9085: the module name of another file comes from its compiled name, which in a namespaced project is `Base-MyNamespace`, so completion inserted `Base-MyNamespace.Flag`. The name is turned into its source path `MyNamespace.Base` first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel <ck@cca.io>
Codex review on #9085: an extension of the edited file declared after the cursor was offered although it isn't in scope there, and an extension inside a module of an interface (`module Sub: { type attr += Flag }`) was never registered, because only typed-tree extensions were. The completion scope now records the cursor position and skips later extensions of the edited file, and extensions in signatures are registered too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel <ck@cca.io>
…loads Codex review on #9085: an extension two modules deep in an interface got only the outer module in its path, because the signature walk didn't extend the module path when entering a module, and completion inside the payload of a qualified constructor such as `Sub.Toggle(...)` found no constructor, because the payload position keeps only `Toggle`. Signature modules now extend the path, and payload lookup also matches the last part of a qualified constructor name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel <ck@cca.io>
…ctors Codex review on #9085: a payload type declared next to the extension, as in `type Base.attr += Color(color)` with a local `color`, was resolved in the open type's file, so payload completion lost it; and of two same-named extensions, `B.Value(...)` completed the payload of the first one, `A.Value`, because the payload position kept only `Value`. Each extension constructor now carries the environment of its file, and the payload position keeps the written path: the constructor sharing the longest suffix with it is meant, ties going to the most recent open, then to the last declared one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gGkPma9LAZimuQjnrP Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel <ck@cca.io>
Codex review on #9085: ranking same-named extension constructors let an open outrank a local extension declared after it, so after `open A` and a later local `Value(...)`, completion used `A.Value`'s payload; and an extension inside a module type, `module type S = { type attr += Phantom }`, was offered as `S.Phantom` and added to exhaustive switches. A qualified constructor is now the one its written path names, and an unqualified one is looked up in the completion scope as the compiler does, which now records extension constructors, the most recent declaration or open winning. Extensions in module types are skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gGkPma9LAZimuQjnrP Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel <ck@cca.io>
Why:
Codex review on #9085: now that an open type extracts as a variant,
the Expand catch-all and Exhaustive switch code actions replaced or
built the switch from the constructors known here, dropping the `_`
that matches constructors added in other files.
Codeaction modified (Expand catch-all):
Example:
type attr = ..
type attr += Str(string) | Num(int)
let f = (value: attr) =>
switch value {
| Str(_) => "str"
| _ => "other"
}
Before (text): the `_` is replaced by
Num(_)
After (text): the `_` is replaced by
Num(_) | _
Codeaction modified (Exhaustive switch):
Example:
type attr = ..
type attr += Str(string) | Num(int)
let value: attr = Str("s")
value
Before (rescript):
switch value {
| Str(_) => %todo
| Num(_) => %todo
}
After (rescript):
switch value {
| Str(_) => %todo
| Num(_) => %todo
| _ => %todo
}
Review: Why keep the `_` as an alternative instead of a separate case?
Replacing only the pattern needs no copy of the case body, and the
expanded or-pattern lists the known constructors for splitting by hand.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo
Signed-off-by: Christoph Knittel <ck@cca.io>
Why:
Codex review on #9085: an `open A` after a local extension `Value`
resolved the payload of an unqualified `Value(...)` to the local
constructor. Matching `A.Value` against the constructors also accepted
the bare local `Value` as a suffix, and the later declaration won. A
constructor named without a module is declared around the cursor, so
an open no longer brings it into scope.
Completion modified (extension payload after an open):
Example:
type attr = ..
module A = {
type attr += Value(bool)
}
type attr += Value(option<int>)
open A
let x: attr = Value()
Before (text): payload completion inside `Value()` completes
option<int>, the payload of the local Value
After (text): payload completion inside `Value()` completes
bool, the payload of A.Value: true, false
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo
Signed-off-by: Christoph Knittel <ck@cca.io>
417ad95 to
347aa66
Compare
Fixes #9073.
Analysis didn't record constructors added by
type t += ..., so completion offered none for an extensible variant. They are now registered as constructors of an open type. Typed completion of an open type, in both expressions and patterns, offers the ones declared in the type's file and in the current file. The exhaustive-switch snippet for an open type ends with a_case, since other files can extend it.New analysis test:
CompletionExtensibleVariant.Constructors are named so that they are in scope where completion runs: unqualified inside their declaring module,
Sub.Flagelsewhere in the file, andModule.Flagfrom another file, using source paths in namespaced projects.Known limitation: an extension declared inside a module whose signature hides it (
module Hidden: {} = { type attr += Flag }) is still offered. Filtering through the visible signature is left for a follow-up.🤖 Generated with Claude Code
https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3