diff --git a/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs b/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs index 76dc07e72e453..9217c543dad2b 100644 --- a/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs +++ b/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs @@ -186,9 +186,10 @@ private static Document AddMethodNameAndAnnotationsToSolution( var root = document.GetSyntaxRootSynchronously(cancellationToken); var plusEqualsToken = root.FindTokenOnLeftOfPosition(position); var eventHookupExpression = plusEqualsToken.GetAncestor(); + var typeDecl = eventHookupExpression.GetAncestor(); var textToInsert = eventHandlerMethodName + ";"; - if (!eventHookupExpression.IsInStaticContext()) + if (!eventHookupExpression.IsInStaticContext() && typeDecl is not null) { // This will be simplified later if it's not needed. textToInsert = "this." + textToInsert; @@ -222,24 +223,30 @@ private static SyntaxNode AddGeneratedHandlerMethodToSolution( var root = document.Root; var eventHookupExpression = root.GetAnnotatedNodesAndTokens(plusEqualsTokenAnnotation).Single().AsToken().GetAncestor(); - var generatedMethodSymbol = GetMethodSymbol(document, eventHandlerMethodName, eventHookupExpression, cancellationToken); + var typeDecl = eventHookupExpression.GetAncestor(); + var methodKind = typeDecl is null + ? MethodKind.LocalFunction + : MethodKind.Ordinary; + + var generatedMethodSymbol = GetMethodSymbol(document, eventHandlerMethodName, eventHookupExpression, methodKind, cancellationToken); if (generatedMethodSymbol == null) { return null; } - var typeDecl = eventHookupExpression.GetAncestor(); + var container = (SyntaxNode)typeDecl ?? eventHookupExpression.GetAncestor(); - var typeDeclWithMethodAdded = CodeGenerator.AddMethodDeclaration(typeDecl, generatedMethodSymbol, document.Project.Solution.Workspace, new CodeGenerationOptions(afterThisLocation: eventHookupExpression.GetLocation())); + var newContainer = CodeGenerator.AddMethodDeclaration(container, generatedMethodSymbol, document.Project.Solution.Workspace, new CodeGenerationOptions(afterThisLocation: eventHookupExpression.GetLocation())); - return root.ReplaceNode(typeDecl, typeDeclWithMethodAdded); + return root.ReplaceNode(container, newContainer); } private static IMethodSymbol GetMethodSymbol( SemanticDocument semanticDocument, string eventHandlerMethodName, AssignmentExpressionSyntax eventHookupExpression, + MethodKind methodKind, CancellationToken cancellationToken) { var semanticModel = semanticDocument.SemanticModel; @@ -271,6 +278,7 @@ private static IMethodSymbol GetMethodSymbol( name: eventHandlerMethodName, typeParameters: default, parameters: delegateInvokeMethod.Parameters, + methodKind: methodKind, statements: ImmutableArray.Create( CodeGenerationHelpers.GenerateThrowStatement(syntaxFactory, semanticDocument, "System.NotImplementedException"))); } diff --git a/src/EditorFeatures/CSharpTest/EventHookup/EventHookupCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/EventHookup/EventHookupCommandHandlerTests.cs index 6fbb7e3cbe8fb..2cf1fe41ca81a 100644 --- a/src/EditorFeatures/CSharpTest/EventHookup/EventHookupCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/EventHookup/EventHookupCommandHandlerTests.cs @@ -1024,6 +1024,31 @@ private void C_MyEvent() testState.AssertCodeIs(expectedCode); } + [WpfFact, Trait(Traits.Feature, Traits.Features.EventHookup)] + [WorkItem(58474, "https://github.com/dotnet/roslyn/issues/58474")] + public async Task EventHookupInTopLevelCode() + { + var markup = @" + +System.AppDomain.CurrentDomain.UnhandledException +$$ + +"; + using var testState = EventHookupTestState.CreateTestState(markup); + testState.SendTypeChar('='); + testState.SendTab(); + await testState.WaitForAsynchronousOperationsAsync(); + + var expectedCode = @" + +System.AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + +void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e) +{ + throw new System.NotImplementedException(); +}"; + testState.AssertCodeIs(expectedCode); + } + private static OptionsCollection QualifyMethodAccessWithNotification(NotificationOption2 notification) => new OptionsCollection(LanguageNames.CSharp) { { CodeStyleOptions2.QualifyMethodAccess, true, notification } }; }