Skip to content

Derive the λ1012 code fix from ExtensionMemberCodeFix for a one-pass Fix-All - #1026

Merged
FreeApophis merged 1 commit into
fix-all-using-insertionfrom
prefer-or-none-fix-all
Oct 29, 2026
Merged

FreeApophis merged 1 commit into
fix-all-using-insertionfrom
prefer-or-none-fix-all

Conversation

@FreeApophis

Copy link
Copy Markdown
Member

Stacked on #1013 (fix-all-using-insertion). Retarget to main after #1013 merges; the diff then shows only this commit.

Closes #1020

What

The λ1012 code fix (prefer …OrNone() over …OrDefault() with a null check) 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.

PreferOrNoneCodeFix now derives from the ExtensionMemberCodeFix base introduced by #1013: document-based Fix-All provider, all flagged calls of a document rewritten at once, the directive added once. Titles and the single-fix behaviour are unchanged.

Notes for the review

  • The λ1012 rewrite replaces the whole xs.FirstOrDefault() ?? fallback coalesce with xs.FirstOrNone().GetOrElse(fallback). ExtensionMemberRewrite(Original, Replacement) covers that as-is (Original is the coalesce), no change to the base.
  • The only deviation is the binding probe: the base's default predicate IsDeclaredInFunckyExtensions checks the symbol of the whole replacement, which here is Option<T>.GetOrElse in Funcky.Monads, so it would always add the using. The fix overrides TryApplyAsync with symbol is IMethodSymbol: GetOrElse only binds when its …OrNone() receiver binds, which needs the directive. This corresponds to the old IsExtensionsNamespaceImported check; the old code also accepted a non-empty CandidateSymbols set, which the base's Func<ISymbol?, bool> cannot see. No existing test needed it.
  • Test first: ThreeFixesThatAllNeedTheUsingAreAppliedInOnePass (four fixes, file-level usings) and ThreeFixesInANamespaceThatAllNeedTheUsingAreAppliedInOnePass (three fixes, usings inside the namespace) both failed on the base branch with Expected '1' iterations but found '2' iterations., confirming the defect.
  • dotnet build Funcky.slnx -warnaserror clean, analyzer tests 248/248 green.
  • Changelog: the Fix-All needs a second pass when three or more extension-form fixes add the same using #1009 bullet under Analyzers (Funcky.Analyzers 2.1.0) now lists λ1012 and references λ1012 code fix: Fix-All needs a second pass when three or more fixes add the same using #1020.

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are reported for the λ1012 migration to the shared Fix-All infrastructure.

No open issues 🎉

What changed in this PR

Moves the λ1012 code fix onto the shared ExtensionMemberCodeFix infrastructure from #1013, addressing #1020 by avoiding conflicting using-directive insertions during Fix-All.

Changes:

  • Reuses document-based Fix-All to rewrite eligible calls together and add using Funcky.Extensions; once per using container.
  • Adds regression tests for multiple fixes with file-level and namespace-level usings.
  • Updates the changelog to include λ1012 in the Fix-All correction.
File Description
Funcky.Analyzers/​Funcky.Analyzers.Test/​OrNone/​PreferOrNoneTest.cs Adds one-pass Fix-All regression tests for both using placements.
Funcky.Analyzers/​Funcky.Analyzers.CodeFixes/​OrNone/​PreferOrNoneCodeFix.cs Adopts the shared base with a binding predicate tailored to GetOrElse.
changelog.md Adds λ1012 and #1020 to the shared Fix-All release note.

Review effort: Balanced

…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
FreeApophis force-pushed the prefer-or-none-fix-all branch from 69b64a2 to c6573a2 Compare October 29, 2026 12:45
@FreeApophis
FreeApophis merged commit 25b50c4 into main Oct 29, 2026
15 of 17 checks passed
@FreeApophis
FreeApophis deleted the prefer-or-none-fix-all branch October 29, 2026 13:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

λ1012 code fix: Fix-All needs a second pass when three or more fixes add the same using

3 participants