Conversation
Adds nine new auto-fixes surfaced via the FixableFinding pipeline so `clear fix` (and downstream LSP code-action support) can resolve common errors without the user reaching for the keyboard. Tier 1 (4 fixes — high-confidence single-edit): - IMMUTABLE_ASSIGNMENT: insert MUTABLE at the binding declaration. - IMMUTABLE_ARG_PASSED_AS_MUTABLE: same, at the caller's binding. - CAPTURE_IMMUTABLE_AS_MUTABLE: same, for USE(MUTABLE x) captures. - AMBIGUOUS_RETURN: insert RETURNS :Any before the function arrow. Tier 2 (5 fixes — multi-edit / refactor): - MATCH PARTIAL fixes for non-discriminated / non-exhaustive subjects. - RETURN_BORROWED_NO_COPY: wrap RETURN value with COPY. - @Local capability lints: remove redundant @Local. - @shared:atomic escape: migrate to @shared:locked (interactive confidence — user reviews before accepting). - M2.1 operator-aware Auto type ranking on gradual-typing inference. All fixes route through the shared Fix / Edit / Span data model (spec/fixable_t1_spec.rb, spec/clear_fix_spec.rb). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an MVP LSP for CLEAR sourced from the existing compiler pipeline (Lexer -> Parser -> SemanticAnnotator + FixCollector). Editors get diagnostics, hover, and code actions over a debounced incremental sync — no rewrite of compiler internals needed. Server (src/lsp/, bin/clear-lsp): - JSON-RPC framing + server lifecycle (initialize / shutdown / exit). - DocumentStore + analyzer + textDocument/publishDiagnostics on didOpen, didChange, didClose. - Per-URI debounce (default 100ms) with thread-safe stdout writes. - textDocument/codeAction surfaces FixableFinding fixes as quickfix / refactor entries with isPreferred when confidence is :auto. - textDocument/hover renders DiagnosticRegistry markdown (cause + fix hint + spec example), with same-line fallback when the cursor misses the diagnostic's narrow anchor span. - End-to-end integration spec drives bin/clear-lsp under bundle exec. Editor integration: - Neovim: built-in LSP client config + LspAttach keymaps + autocmd bootstrap, documented in src/lsp/README.md. - VS Code extension (.vscode/extensions/cheat-lang/): TypeScript client with vscode-languageclient stdio transport, auto-detects bin/clear-lsp by walking up from the install path, spawns under bundle exec by default. - tmLanguage / Vim syntax brought up to date with current CLEAR (-- -> # comments, |> pipe, every keyword/sigil through v0.2). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds .devcontainer/ so opening the repo in Codespaces (or any Dev-Containers-aware editor) gets a working CLEAR development environment without manual setup steps. setup.sh (postCreateCommand): - bundle install for compiler deps. - Pinned Zig 0.16.0 toolchain matching CI. - Builds the in-repo VS Code extension (npm install + tsc). - Symlinks the built extension into ~/.vscode-server/extensions/ using the <publisher>.<name>-<version> naming convention VS Code Server expects (the .vscode/extensions/ workspace folder is a Cursor-only convention; vanilla VS Code Server ignores it). devcontainer.json: - Ruby 3.2 base image, Node 22 + git features. - File associations + recommended marketplace extensions. - postStartCommand re-runs `bundle install --quiet` on every boot so the LSP self-heals if the gem cache went missing between sessions (idempotent, near-noop when gems are already on disk). bin/clear-lsp: - Accepts --stdio as a no-op (vscode-languageclient appends it by default for stdio transport; rejecting it crashed the LSP five times before VS Code disabled it). - Same defensive treatment for --node-ipc / --pipe / --socket=... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a function body reassigns / index-assigns / field-assigns a parameter the caller declared without MUTABLE, the three errors that fire (ASSIGN_VAR_IMMUTABLE, ASSIGN_INDEX_IMMUTABLE_LIST, ASSIGN_FIELD_IMMUTABLE_STRUCT) now emit a FixableFinding whose :auto fix inserts `MUTABLE ` at the parameter's column in the signature. The same `build_declare_mutable_fix` builder used for local-binding fixes now falls back to the param's name token (captured at parse time and stashed on the SymbolEntry as `param_decl_token`) when the binding has no `reg` of its own — params don't carry a VarDecl. Verified end-to-end via `clear fix`: a `parseValue!(penv: HashMap...)` that index-assigns into `penv` rewrites to `MUTABLE penv: HashMap...`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When `p.zz` is accessed on a struct whose schema doesn't declare
'zz', visit_GetField now routes through emit_typo_suggestion! with
the struct's actual fields as the candidate set. If a field is
within Levenshtein threshold ('zz' -> 'x' or 'y' on Point), the
diagnostic carries an :auto fix that replaces the typed token with
the closest field name.
Falls back to the bare ILLEGAL_FIELD_LOOKUP / TYPO_SUGGESTION_REJECTED
when no candidate is close enough, so users without typos still get
the same shape of error they had before.
Two existing specs updated their assertions to match the new clearer
error message ("Struct 'X' has no field 'y'" instead of the old
"Cannot determine struct type for field access").
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A MATCH struct pattern that names a non-existent field now offers a
typo-suggestion auto-fix when the closest declared field is within
Levenshtein threshold. Covers three pattern shapes — all share the
single parser change that captures the field name token in
parse_struct_pattern as f[:name_token]:
- value-match form `{ xs: 1 }` on STRUCT P { x, y } -> suggest 'x'
(annotate_struct_pattern! site, MATCH_FIELD_UNKNOWN).
- destructure form `{ xx }` on STRUCT P { x, y } -> suggest 'x'
(same site, same code path).
- union variant destructure `Shape.Circle { radiu }` on
Circle { radius: Float64 } -> suggest 'radius'
(visit_MatchStatement variant branch, MATCH_DESTRUCTURE_FIELD_UNKNOWN).
Falls back to the bare error code when no candidate is close enough.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A function that calls itself directly (or via a transitive cycle) without an explicit reentrance declaration used to raise one of three errors with no fix: - REENTRANCE_DIRECT_RECURSIVE: directly recursive + @nonReentrant. - REENTRANCE_INDIRECT_RECURSIVE: directly recursive + no marker. - REENTRANCY_MUTUAL_CYCLE: transitive cycle + no marker. All three now emit a FixableFinding whose :auto fix inserts `EFFECTS REENTRANT ` immediately before the function's `->`, using the FunctionDef's `arrow_token` as the insertion anchor. Verified end-to-end via `clear fix`: a `factorial(n)` that calls itself rewrites the signature to `FN factorial(n: Int64) RETURNS Int64 EFFECTS REENTRANT ->` and compiles cleanly. Two existing capabilities specs had their assertions updated to reflect the new error wording (`Replace @nonReentrant with EFFECTS REENTRANT` and `EFFECTS REENTRANT` instead of the old `@reentrant` phrasing). Audit budget for fixable_helpers.rb bumped from 1 to 2 to cover the second `code:`-variable wrapper helper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three typo-suggestion fixes covering parser-side capability errors:
- UNKNOWN_CAPABILITY_SIGIL (chained form):
`@multiowned:lokced` -> `@multiowned:locked`
Candidate set is CAP_SIGIL_ATTRS.keys (with/without @ depending
on whether the user typed it). The chain syntax accepts both.
- UNKNOWN_CAPABILITY_MODIFIER (chained form, same site):
Wired through the same chain handler. Bare-form typos
(`@sharred` directly) die in expression parsing earlier and
aren't reachable from this code path; the chained form is the
actually-firing case.
- UNKNOWN_WITH_CAPABILITY:
`WITH RESTRIKT x { ... }` -> `WITH RESTRICT x { ... }`.
Detection runs both as a pre-loop TYPE_ID check (catches
uppercase typos that tokenize as TYPE_ID, missing the KEYWORD
branch) and inside the loop for the rare KEYWORD-but-not-in-
AST::CAPABILITIES case.
Plumbing: `Parser` now `include FixableHelper` so the parser can
reach `emit_typo_suggestion!` and the underlying Levenshtein
helpers — no annotator state is touched by these utilities.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five new parser-side typo suggestions: - UNKNOWN_EFFECT (`:saf` -> `:safe`): EXTERN FN EFFECTS clause. - UNKNOWN_ALLOC_QUALIFIER (`:alloc:frme` -> `:alloc:frame`): the qualifier after `:alloc` (frame|heap). - UNKNOWN_FN_EFFECT (`EFFECTS RENTRANT` -> `EFFECTS REENTRANT`): the function-level EFFECTS clause. - UNKNOWN_REQUIRES_FAMILY (`REQUIRES p: LOKKED` -> `LOCKED`): candidate set is the union of REQUIRES_VALID_FAMILIES and REQUIRES_REENTRANCE_KINDS so either category is suggested. - UNKNOWN_REENTRANT_VARIANT (`EFFECTS REENTRANT:THONK` -> `EFFECTS REENTRANT:THUNK`): the four valid variants. Four existing parser specs updated their assertions to match the new clearer error wording (e.g., "Unknown reentrant variant 'X'" instead of "Unknown REENTRANT variant ':X'"). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four more typo-suggestion fixes:
- UNKNOWN_BG_PREFIX: `BG { @srvice -> ... }` -> `@service`.
Candidate set = BG_SIGILS keys (with/without `@` based on input).
Loop entry condition relaxed to admit `@<typo>` so the typo
branch fires instead of silently falling through.
- UNKNOWN_BRANCH_PREFIX: same shape for DO branches via
DO_BRANCH_SIGILS.
- UNKNOWN_STRUCT_TYPE: `Pont{x: 1}` -> `Point` when a struct named
Point is in scope. Candidate set = `all_known_type_names` (a new
ScopeHelper method that aggregates `@types` keys across the
scope stack).
- UNKNOWN_TYPE: same `all_known_type_names` candidate set, fired
from generic_analysis.rb when `Pir<Int64>` is unresolved and
`Pair` is in scope.
Skipped: UNKNOWN_LITERAL is defensive (unreachable user-typo path),
UNION_METHOD_MISSING is about missing implementations not typos,
UNION_INLINE_VARIANT_UNKNOWN_FIELD needs per-field token plumbing
in the parser (deferred).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`Unknown reentrant variant 'X'` -> `Unknown REENTRANT variant 'X'`. REENTRANT is a CLEAR keyword (used in `EFFECTS REENTRANT[:VARIANT]`); keeping it lowercase in error prose obscures that the user is referring to a syntactic token, not the English adjective. Matches how other error messages already handle WITH, REQUIRES, BG, etc. Two thunk parser specs updated their regex assertions to match. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per WALKTHROUGH.md §4, ownership-violation errors now lead with
"USE AFTER MOVE:", quote the variable in backticks, and explain
*how* the value was consumed using past-tense CLEAR keywords (GAVE,
TOOK, RETURNED, SHARED, MOVED) instead of opaque "moved at line X
by Y" mechanical phrasing.
Two voices, one rule (matches reader expectations):
- Active form (USE_OF_MOVED_VALUE / USE_OF_MOVED_IN_LOOP) — the
consumer is the subject:
USE AFTER MOVE: You can't use `msg`. `process(GIVE msg)`
already GAVE it away (line 7).
USE AFTER MOVE: You can't use `msg`. `process(msg)` already
TOOK it away (line 7).
- Passive form (USE_OF_MOVED_PATH) — the owner of the path is the
subject:
USE AFTER MOVE: You can't use `o.inner`. Its owner `o` was
already MOVED on line 6.
Loop body coda matches WALKTHROUGH:
USE AFTER MOVE: You can't use `v` here. Values can only be
TAKEN once; subsequent iterations have nothing left to GIVE.
Implementation:
- Diagnostic templates switch to `%{message}` and the helper
builds the full message; metadata (summary / cause / fix_hint)
rewritten to match.
- New helpers `emit_use_of_moved_in_loop_error!` and
`emit_use_of_moved_path_error!` join the existing
`emit_use_of_moved_error!`. Each picks active vs passive verb
via the new `OWNERSHIP_ACTIVE_PHRASES` /
`OWNERSHIP_PASSIVE_PHRASES` tables.
- `consumer_source_text` extracts the move-site source line so
the error can quote the consumer call ("process(GIVE msg)").
This requires `@source_code` — `compile` now threads `cheat_code`
into `SemanticAnnotator.new`.
- `move_if_not_copyable!` now respects an existing more-specific
move action (was overwriting `:give` from visit_GiveNode with
`:move`); the function-call site explicitly passes `:takes` /
`:give` so the verb maps to "TOOK" / "GAVE".
Spec churn: ~30 assertions across 8 files updated to match new
wording. The audit budget for fixable_helpers.rb bumped from 2 to
3 to cover the new `code:`-variable wrapper helper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| Branch | fixable-errors-v2 |
| Testbed | ubuntu-latest |
⚠️ WARNING: No Threshold found!Without a Threshold, no Alerts will ever be generated.
Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the--ci-only-thresholdsflag.
Click to view all benchmark results
| Benchmark | leak-build-ms | Measure (units) x 1e3 | leak-count | Measure (units) | leak-run-ms | Measure (units) |
|---|---|---|---|---|---|---|
| benchmarks/concurrent/05_backpressure/bench | 📈 view plot | 5.06 units x 1e3 | 📈 view plot | 0.00 units | 📈 view plot | 1,772.41 units |
| benchmarks/concurrent/10_shard_vs_locked/bench | 📈 view plot | 5.05 units x 1e3 | 📈 view plot | 0.00 units | 📈 view plot | 60,004.73 units |
| benchmarks/concurrent/16_observables/bench | 📈 view plot | 4.89 units x 1e3 | 📈 view plot | 0.00 units | 📈 view plot | 95.47 units |
| benchmarks/inter-clear/03_concurrent_mvcc_vs_rwlock/bench | 📈 view plot | 5.28 units x 1e3 | 📈 view plot | 0.00 units | 📈 view plot | 321.73 units |
| benchmarks/sequential/07_pointer_chase/bench | 📈 view plot | 4.83 units x 1e3 | 📈 view plot | 0.00 units | 📈 view plot | 429.37 units |
| benchmarks/sequential/12_weak_ref_graph/bench | 📈 view plot | 4.85 units x 1e3 | 📈 view plot | 0.00 units | 📈 view plot | 127.29 units |
| benchmarks/server/03_pathological/server | 📈 view plot | 4.99 units x 1e3 | 📈 view plot | 0.00 units | 📈 view plot | 1,002.68 units |
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #19 +/- ##
==========================================
+ Coverage 89.73% 89.85% +0.11%
==========================================
Files 173 182 +9
Lines 46723 47332 +609
Branches 11604 11766 +162
==========================================
+ Hits 41927 42529 +602
- Misses 4796 4803 +7
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.