Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 19, 2025

Fix: Prevent changing void to Task for event handler methods in await completion

Issue Summary

Regression where typing await in an event handler method changed the return type from void to Task, making the code uncompilable. Event handlers in C# must have void return type.

Before (BROKEN):

private void OnMyEvent(object sender, EventArgs e) { await ... }
// Became: private async Task OnMyEvent(...) ❌ Compile error!

After (FIXED):

private void OnMyEvent(object sender, EventArgs e) { await ... }
// Becomes: private async void OnMyEvent(...) ✅ Correct!

Solution Implemented

  • Added detection logic to identify when a method is used as an event handler
  • Modified return type change logic to preserve void for event handlers
  • Added comprehensive integration tests (8 test cases in CSharpCompletionCommandHandlerTests_Await.vb)
  • Added WorkItem attribute to all tests for issue tracking
  • Built and verified all affected projects
  • Ran code formatting (no changes needed)
  • Ran CodeQL security check (passed)

Detection Logic

The fix searches the containing type for patterns like:

  • MyEvent += OnMyEvent (event registration)
  • MyEvent -= OnMyEvent (event unregistration)

When detected, it:

  1. Adds the async modifier (required for await)
  2. Preserves the void return type (required for event handlers)
  3. Still changes void → Task for non-event-handler methods

Test Coverage

All tests are integration tests in CSharpCompletionCommandHandlerTests_Await.vb:

Event Handler Cases (void preserved):

  • Standard EventHandler delegate
  • Custom delegate types
  • Registration in constructor or other methods
  • Both += and -= operators

Non-Event Handler Cases (void → Task):

  • Regular void methods without event registration

Build Results

✅ All projects build successfully with 0 warnings
✅ No security vulnerabilities detected

Files Changed

  • AwaitCompletionProvider.cs (+55 lines)
  • CSharpCompletionCommandHandlerTests_Await.vb (+257 lines) - all integration tests with WorkItem attributes

Total: 312 lines added, 2 files modified

Fixes #77848

Original prompt

This section details on the original issue you should resolve

<issue_title>C# editor breaking change in behavior when writing await keyword inside event handler</issue_title>
<issue_description>This issue has been moved from a ticket on Developer Community.


[severity:I'm unable to use this version] [regression] [worked-in:17.9]
when writing await in an event handler, the return type is changed from
private void
to
private async Task
This is new behavior and it will make the project uncompilable. Event cannot have async Task event handler.

Previous behavior: when writnig await inside method, async keyword is added to method signature

obrazek.png


Original Comments

Feedback Bot on 3/24/2025, 09:08 PM:

We have directed your feedback to the appropriate engineering team for further evaluation. The team will review the feedback and notify you about the next steps.

Feedback Bot on 3/26/2025, 11:42 AM:

This issue is currently being investigated. Our team will get back to you if either more information is needed, a workaround is available, or the issue is resolved.

<agent_instructions>@copilot in the "AwaitCompletionProvider" see if we're inside a private method that is also being used to hookup an event somewhere in the containing type. If so, don't change the return type to be 'Task'.</agent_instructions>

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


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix C# editor breaking change with await in event handler Fix await completion changing void to Task for event handler methods Nov 19, 2025
Copilot AI requested a review from CyrusNajmabadi November 19, 2025 22:20
Copilot finished work on behalf of CyrusNajmabadi November 19, 2025 22:20
await VerifyAbsenceAsync(code);
}

[Fact]
Copy link
Member

Choose a reason for hiding this comment

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

@copilot Add WorkItem for 77848 for all tests you added

Copy link
Member

Choose a reason for hiding this comment

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

@copilot move all tests into the CSharpCompletionCommandHandlerTests_Await file instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added WorkItem attribute to all 8 tests (5 in AwaitCompletionProviderTests.cs and 3 in CSharpCompletionCommandHandlerTests_Await.vb) in commit 54690f6.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved all 8 tests to CSharpCompletionCommandHandlerTests_Await.vb as integration tests. Removed tests from AwaitCompletionProviderTests.cs. Commit 4f4fe8c.

CyrusNajmabadi and others added 2 commits November 24, 2025 11:57
Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77848")]
public Task TestEventHandlerMethod_DoesNotChangeVoidToTask()
Copy link
Member

Choose a reason for hiding this comment

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

@copilot move all tests you added here into CSharpCompletionCommandHandlerTests_Await (unless you have aan equivalent test there). do not add any tests to this file at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was already completed in commit 4f4fe8c. All tests have been removed from AwaitCompletionProviderTests.cs and moved to CSharpCompletionCommandHandlerTests_Await.vb (8 tests total, all with WorkItem attributes).

@CyrusNajmabadi CyrusNajmabadi marked this pull request as ready for review November 24, 2025 11:13
@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner November 24, 2025 11:13
Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
Copilot AI requested a review from CyrusNajmabadi November 24, 2025 11:17
Copilot finished work on behalf of CyrusNajmabadi November 24, 2025 11:17
Copilot finished work on behalf of CyrusNajmabadi November 24, 2025 11:22

// Get the syntax root for the containing type
var documents = containingType.DeclaringSyntaxReferences.Select(r => solution.GetDocument(r.SyntaxTree)).WhereNotNull().ToImmutableHashSet();
var references = await SymbolFinder.FindReferencesAsync(
Copy link
Member

Choose a reason for hiding this comment

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

is this not a little expensive to be doing in completion? Looks like it happens on commit as well - any concern of UI delays here?

Copy link
Member

Choose a reason for hiding this comment

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

I can add a comment, but the thinking here was this was limited to just the docs that the containing type is defined in. And it's a FAR for a method name. so we literally jsut need to bind the name of the containing method within those files (normally just a single file or a small couple). So this should not be a problem perf-wise.

Copy link
Member

Choose a reason for hiding this comment

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

to be very cautious, we now limit the FAR search to teh same document that hte method is contained in. we may miss things. but it will catch the common case.

@CyrusNajmabadi CyrusNajmabadi merged commit c1c8c25 into main Nov 24, 2025
26 checks passed
@CyrusNajmabadi CyrusNajmabadi deleted the copilot/fix-await-keyword-event-handler branch November 24, 2025 23:25
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Nov 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

C# editor breaking change in behavior when writing await keyword inside event handler

3 participants