Repository navigation
Rewrite all extension-form calls of a document in one Fix-All pass - #1013
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Replacement annotations persist across batches and can cause subsequent fixes to discard valid rewrites or throw during import insertion.
1 open issue
What changed in this PR
This PR addresses #1009 by replacing batch fixing with document-level rewriting for four extension-form code fixes, avoiding collisions when multiple fixes add using Funcky.Extensions;.
Changes:
- Introduces a shared Fix-All provider and coordinated rewrite/import handling.
- Preserves binding validation for
Then, filtering out unsuccessful rewrites. - Expands regression tests to three or more diagnostics and documents the fix.
| File | Description |
|---|---|
Funcky.Analyzers/Funcky.Analyzers.Test/PreferXunitAssertTest.cs |
Tests three assertion rewrites requiring an import. |
Funcky.Analyzers/Funcky.Analyzers.Test/PreferStaticParseOrNoneTest.cs |
Expands multi-call parsing regression cases. |
Funcky.Analyzers/Funcky.Analyzers.Test/PreferEnumerableSequenceConstructorsTest.cs |
Tests three sequence-constructor rewrites. |
Funcky.Analyzers/Funcky.Analyzers.Test/OptionFromBooleanCodeFixTest.cs |
Tests three FromBoolean overloads together. |
Funcky.Analyzers/Funcky.Analyzers.CodeFixes/OptionFromBooleanCodeFix.cs |
Adopts shared batching with binding validation. |
Funcky.Analyzers/Funcky.Analyzers.CodeFixes/ExtensionMemberCodeFix.cs |
Adds the document-based Fix-All base class. |
Funcky.Analyzers/Funcky.Analyzers.CodeFixes/ExtensionForms/PreferXunitAssertCodeFix.cs |
Migrates assertion fixes to the shared provider. |
Funcky.Analyzers/Funcky.Analyzers.CodeFixes/ExtensionForms/PreferStaticParseOrNoneCodeFix.cs |
Migrates parsing fixes to the shared provider. |
Funcky.Analyzers/Funcky.Analyzers.CodeFixes/ExtensionForms/PreferEnumerableSequenceConstructorsCodeFix.cs |
Migrates sequence fixes to the shared provider. |
Funcky.Analyzers/Funcky.Analyzers.CodeFixes/CodeAnalysisExtensions/FunckyExtensionsImport.cs |
Batches replacements and groups import insertion. |
Funcky.Analyzers/Funcky.Analyzers.CodeFixes/CodeAnalysisExtensions/ExtensionMemberRewrite.cs |
Adds a shared rewrite representation. |
Funcky.Analyzers/Funcky.Analyzers.CodeFixes/CodeAnalysisExtensions/BooleanThen.cs |
Validates batched rewrites and retries valid subsets. |
changelog.md |
Documents the Fix-All improvement. |
Review effort: Balanced
This was referenced Oct 27, 2026
Closed
The code fixes for λ1201, λ1202, λ1203 and the Option.FromBoolean obsoletion all add `using Funcky.Extensions;` when needed. The batch fixer treats three or more identical directive insertions in a document as colliding edits and drops those actions, call rewrite included, so a second Fix-All pass was needed. The new ExtensionMemberCodeFix base owns a DocumentBasedFixAllProvider that applies every rewrite of a document at once through the plural FunckyExtensionsImport.ReplaceWithExtensionMemberCalls and adds the using once per using container. BooleanThen keeps its post-check that every Then call binds to Funcky's extension and leaves out the calls that do not. The single-diagnostic actions keep their behaviour. The "adds the using" tests of all four fixes now have three or more diagnostics per document. Closes #1009 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The per-rewrite annotations that FunckyExtensionsImport uses to find the replacements stayed in the returned document. A host that keeps the syntax trees between two fixes (the test harness re-parses them) then handed them to the next fix on the same document: BooleanThen took a leftover parse replacement for one of its own Then calls, saw that it does not bind to Then and withheld the valid fix; a later fix needing the import could hit duplicate indices in AddUsings. ReplaceWithExtensionMemberCalls now strips the annotations before the document is returned; BooleanThen binds the annotated root and strips them afterwards. ExtensionMemberCodeFixTest applies a λ1201 fix and then the Option.FromBoolean fix to the changed solution directly, and PreferStaticParseOrNoneTest covers two namespace declarations that each get their own using. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
FreeApophis
added a commit
that referenced
this pull request
Oct 29, 2026
…Fix-All The fix inserted `using Funcky.Extensions;` through its own path with the default BatchFixer, so three or more diagnostics in one document that all needed the directive collided and some were only rewritten by a second Fix-All pass, the same defect as #1009. It now derives from the base of #1013, which rewrites all flagged calls of a document at once and adds the directive once. The rewrite replaces the whole `?? fallback` coalesce, which the base covers unchanged; only the binding probe differs, since the replacement binds to `Option.GetOrElse` in `Funcky.Monads` rather than to a member of `Funcky.Extensions`, so the fix overrides `TryApplyAsync` with a predicate that takes any bound method as proof the directive is in place. Two tests with three and four fixes per document, with the usings at file level and inside the namespace, failed before with "Expected '1' iterations but found '2' iterations". Closes #1020 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
FreeApophis
force-pushed
the
fix-all-using-insertion
branch
from
October 29, 2026 12:45
6c3f2b3 to
567a03d
Compare
JudoJosef
approved these changes
Oct 29, 2026
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.

Closes #1009
Problem
The code fixes for λ1201, λ1202, λ1203 and the CS0618 fix on
Option.FromBooleanall addusing Funcky.Extensions;when the namespace is not imported. With the defaultBatchFixer, three or more such fixes in one document insert the identical directive, which the batch fixer treats as colliding edits: it drops the colliding actions together with their call rewrite, so "Fix all in document" needed a second pass. Two diagnostics worked, which is why the existing tests stopped there.Fix
ExtensionMemberCodeFixfor the four providers. It owns aDocumentBasedFixAllProviderthat collects the rewrites of all flagged calls in a document (TryCreateRewriteper diagnostic), applies them in oneReplaceNodesand adds theusingonce per using container (file or namespace declaration with usings). Project and solution scope work document by document through the base class. The single-diagnostic actions keep their titles, equivalence keys and lazy/eager behaviour.FunckyExtensionsImport.ReplaceWithExtensionMemberCallsis the shared plural helper (indexed annotations, binding predicate), the single-call overload delegates to it. A rewrite nested in another flagged call is skipped and left for the next pass, as before.BooleanThen.TryReplaceAllAsynckeeps the post-check that everyThencall binds to Funcky's extension after the using was added; calls that do not bind (anotherThenin scope) are left out instead of withholding the whole document.OptionSomeWhereToThenRefactoringkeeps using the single-call wrapper.Expected '1' iterations but found '2'before the change).Not touched:
PreferOrNoneCodeFix(λ1012, shipped in 2.0.0) adds the same using through its own path and most likely has the same limitation; it could adopt the base class in a follow-up.🤖 Generated with Claude Code