Skip to content

Fix SYSLIB1045 quick fix for C# 14 extension members#124311

Merged
stephentoub merged 3 commits intomainfrom
copilot/fix-syslib1045-issue
Feb 12, 2026
Merged

Fix SYSLIB1045 quick fix for C# 14 extension members#124311
stephentoub merged 3 commits intomainfrom
copilot/fix-syslib1045-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 12, 2026

Description

The SYSLIB1045 code fixer generates invalid partial extension(...) syntax when applied inside C# 14 extension blocks. ExtensionBlockDeclarationSyntax inherits from TypeDeclarationSyntax, so the fixer was treating extension blocks like classes when adding partial modifiers and placing generated members.

Before fix:

static partial class Foo
{
    partial extension(string value)  // ❌ invalid syntax
    {
        public bool Test() => MyRegex.IsMatch(value);
        [GeneratedRegex(@"\d+")]
        private static partial Regex MyRegex { get; }
    }
}

After fix:

static partial class Foo
{
    extension(string value)
    {
        public bool Test() => MyRegex.IsMatch(value);
    }

    [GeneratedRegex(@"\d+")]
    private static partial Regex MyRegex { get; }
}

Changes

  • TryPartialize: Filter out ExtensionBlockDeclarationSyntax when adding partial to ancestor types
  • CreateGeneratedRegexProperty: Skip extension blocks when resolving the parent type for member placement
  • ConvertFieldToGeneratedRegexProperty / ConvertPropertyToGeneratedRegexProperty: Defensive skip of extension blocks when resolving parent type (fields and properties are not valid inside extension blocks per CS9282, so these paths are unreachable in practice)
  • Added CodeFixSupportsExtensionMembers_StaticInvocation test covering the static method invocation path (Regex.IsMatch) inside an extension block
  • Added CodeFixSupportsExtensionMembers_Constructor test covering the constructor path (new Regex) inside an extension block
Original prompt

This section details on the original issue you should resolve

<issue_title>SYSLIB1045 quick fix generates incorrect code for extension members</issue_title>
<issue_description>### Description

The quick fix for SYSLIB1045 Use 'GeneratedRegexAttribute' to generate the regular expression implementation at compile-time. is not (yet) aware of C# 14 extension members.

Reproduction Steps

static class Foo
{
    extension(string value)
    {
        public bool Test() => Regex.IsMatch(value, @"\d+");
    }
}

Expected behavior

The quick fix is either not suggested, or it performs a fix that is compatible with extension members.

Actual behavior

static partial class Foo
{
    partial extension(string value)
    {
        public bool Test() => MyRegex().IsMatch(value);
        [GeneratedRegex(@"\d+")]
        private static partial Regex MyRegex();
    }
}

partial extension is invalid syntax.

The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type.

Regression?

No response

Known Workarounds

No response

Configuration

No response

Other information

No response</issue_description>

<agent_instructions>The fixer for generated regexes should place the static partial in the parent class of the extension block, as extension blocks can't themselves be partial. </agent_instructions>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

…l modifier

The fixer now correctly skips ExtensionBlockDeclarationSyntax nodes when:
- Adding the partial modifier to ancestor types (extension blocks can't be partial)
- Finding the parent type to place the generated regex property in

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix incorrect code generation for SYSLIB1045 quick fix Fix SYSLIB1045 quick fix for C# 14 extension members Feb 12, 2026
Copilot AI requested a review from stephentoub February 12, 2026 03:08
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-text-regularexpressions
See info in area-owners.md if you want to be subscribed.

@stephentoub stephentoub marked this pull request as ready for review February 12, 2026 03:41
Copilot AI review requested due to automatic review settings February 12, 2026 03:41
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a bug in the SYSLIB1045 code fixer where it generated invalid partial extension(...) syntax when applied to Regex calls inside C# 14 extension blocks. Extension blocks inherit from TypeDeclarationSyntax but cannot be marked partial, causing the fixer to incorrectly treat them like regular type declarations.

Changes:

  • Added filtering to skip ExtensionBlockDeclarationSyntax when traversing type ancestors to add partial modifiers or place generated regex properties
  • Ensures generated regex members are placed in the parent class rather than inside the extension block
  • Added test coverage for extension member scenarios

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/libraries/System.Text.RegularExpressions/gen/UpgradeToGeneratedRegexCodeFixer.cs Added ExtensionBlockDeclarationSyntax filtering in 4 locations: CreateGeneratedRegexProperty, ConvertFieldToGeneratedRegexProperty, ConvertPropertyToGeneratedRegexProperty, and TryPartialize methods
src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/UpgradeToGeneratedRegexAnalyzerTests.cs Added CodeFixSupportsExtensionMembers test to verify the fix correctly handles Regex.IsMatch calls inside extension blocks

Split the extension member test into two: one for static invocation
(Regex.IsMatch) and one for constructor (new Regex), covering both
code paths through CreateGeneratedRegexProperty.

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
@stephentoub
Copy link
Copy Markdown
Member

@copilot please invoke the code-review skill and post the analysis/results as a comment on the PR

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 12, 2026

@copilot please invoke the code-review skill and post the analysis/results as a comment on the PR

🤖 Copilot Code Review — PR #124388

Holistic Assessment

Motivation: The problem is real and well-defined. ExtensionBlockDeclarationSyntax inherits from TypeDeclarationSyntax in Roslyn 5.x, so the fixer was incorrectly treating extension blocks as type declarations — adding partial to them and placing generated members inside them, producing invalid syntax.

Approach: The approach is correct — filtering out ExtensionBlockDeclarationSyntax at each point where TypeDeclarationSyntax ancestors are queried. This is the minimal, targeted fix.

Summary: ✅ LGTM. The fix is straightforward, correct, and well-tested. All four changed locations are necessary and consistent. The code changes are minimal, focused, and match the existing patterns in the file.


Detailed Findings

✅ Correctness — All four filter locations are necessary and correct

The four changes form two logical groups:

  1. TryPartialize (line 346): Prevents adding partial modifier to extension blocks. This is the root cause fix — without it, the fixer generates partial extension(...) which is invalid syntax.

  2. Parent type resolution (lines 109, 214, 259): Ensures the generated regex member is placed in the parent class, not inside the extension block. Line 109 (CreateGeneratedRegexProperty) is the primary path exercised by the tests. Lines 214 and 259 (ConvertFieldToGeneratedRegexProperty / ConvertPropertyToGeneratedRegexProperty) are defensive — static fields and properties can't appear inside extension blocks (CS9282), so these paths can't be triggered from within extension blocks in practice. The defensive changes are still worthwhile for robustness.

✅ Test coverage — Adequate for the reachable code paths

Two tests cover both reachable CreateGeneratedRegexProperty paths: static method invocation (Regex.IsMatch) and constructor (new Regex). Both verify that:

  • The parent class gets partial (not the extension block)
  • The generated member is placed in the parent class (not inside the extension block)
  • The call site replacement is correct

The field/property conversion paths (lines 214, 259) are unreachable from extension blocks due to CS9282, so they don't need dedicated test coverage — the defensive filtering there is a sensible safeguard.

✅ Style — Consistent with existing code patterns

The filter pattern t => t is not ExtensionBlockDeclarationSyntax is clean and idiomatic C#. The added comment on line 107 explains the "why" concisely.

💡 Minor observation — Duplicate filter pattern

The expression .FirstOrDefault(t => t is not ExtensionBlockDeclarationSyntax) appears 3 times (lines 109, 214, 259) and the .Where(...) variant once (line 346). This is fine for a 4-occurrence pattern in a single file, but if extension block handling needs to grow more complex in the future, extracting a helper might be warranted. Not a blocker — just a note for future maintainers.

@stephentoub
Copy link
Copy Markdown
Member

/ba-g timeout

@stephentoub stephentoub enabled auto-merge (squash) February 12, 2026 15:03
@stephentoub stephentoub merged commit 7b14251 into main Feb 12, 2026
91 of 93 checks passed
@MihaZupan MihaZupan deleted the copilot/fix-syslib1045-issue branch February 12, 2026 15:21
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
@github-actions github-actions Bot locked and limited conversation to collaborators Mar 15, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SYSLIB1045 quick fix generates incorrect code for extension members

4 participants