From 86bd9734fcaf278f153a554a78e98eac8f87fca4 Mon Sep 17 00:00:00 2001 From: Wonseok Chae Date: Thu, 12 Mar 2015 14:32:42 -0700 Subject: [PATCH] Enable turning a method into an async method or iterator in VB Enables an edit that updates a regular method to an async method or an iterator in VB. As in C#, there is the restriction in the presence of an active statement in the method body since the debugger doesn't support remapping active statements to a different method (from the original method to the new MoveNext added due to the change to state machine). --- .../EditAndContinue/ActiveStatementTests.vb | 159 +++++++++++++++++- .../EditAndContinue/RudeEditTopLevelTests.vb | 3 +- .../VisualBasicEditAndContinueAnalyzer.vb | 28 ++- 3 files changed, 181 insertions(+), 9 deletions(-) diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb index fb0ea9d93f5..17b10300152 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb @@ -4386,32 +4386,179 @@ End Class #Region "State Machines" Public Sub MethodToIteratorMethod_WithActiveStatement() - ' TODO + Dim src1 = " +Imports System +Imports System.Collections.Generic +Class C + Function F() As IEnumerable(Of Integer) + Console.WriteLine(1) + Return {1, 1} + End Function +End Class +" + Dim src2 = " +Imports System +Imports System.Collections.Generic +Class C + Iterator Function F() As IEnumerable(Of Integer) + Console.WriteLine(1) + Yield 1 + End Function +End Class +" + Dim edits = GetTopEdits(src1, src2) + Dim active = GetActiveStatements(src1, src2) + + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.InsertAroundActiveStatement, "Yield 1", "Yield statement")) End Sub Public Sub MethodToIteratorMethod_WithActiveStatementInLambda() - ' TODO + Dim src1 = " +Imports System +Imports System.Collections.Generic +Class C + Function F() As IEnumerable(Of Integer) + Dim a = Sub() Console.WriteLine(1) + a() + Return {1, 1} + End Function +End Class +" + Dim src2 = " +Imports System +Imports System.Collections.Generic +Class C + Iterator Function F() As IEnumerable(Of Integer) + Dim a = Sub() Console.WriteLine(1) + a() + Yield 1 + End Function +End Class +" + Dim edits = GetTopEdits(src1, src2) + Dim active = GetActiveStatements(src1, src2) + + ' should not contain RUDE_EDIT_INSERT_AROUND + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.RUDE_EDIT_LAMBDA_EXPRESSION, "Sub()", "method")) End Sub Public Sub MethodToIteratorMethod_WithoutActiveStatement() - ' TODO + Dim src1 = " +Imports System +Imports System.Collections.Generic +Class C + Function F() As IEnumerable(Of Integer) + Console.WriteLine(1) + Return {1, 1} + End Function +End Class +" + Dim src2 = " +Imports System +Imports System.Collections.Generic +Class C + Iterator Function F() As IEnumerable(Of Integer) + Console.WriteLine(1) + Yield 1 + End Function +End Class +" + Dim edits = GetTopEdits(src1, src2) + Dim active = GetActiveStatements(src1, src2) + + edits.VerifyRudeDiagnostics(active) End Sub Public Sub MethodToAsyncMethod_WithActiveStatement() - ' TODO + Dim src1 = " +Imports System +Imports System.Threading.Tasks +Class C + Function F() As Task(Of Integer) + Console.WriteLine(1) + Return Task.FromResult(1) + End Function +End Class +" + Dim src2 = " +Imports System +Imports System.Threading.Tasks +Class C + Async Function F() As Task(Of Integer) + Console.WriteLine(1) + Return Await Task.FromResult(1) + End Function +End Class +" + Dim edits = GetTopEdits(src1, src2) + Dim active = GetActiveStatements(src1, src2) + + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.InsertAroundActiveStatement, "Await", "Await expression")) End Sub Public Sub MethodToAsyncMethod_WithActiveStatementInLambda() - ' TODO + Dim src1 = " +Imports System +Imports System.Threading.Tasks +Class C + Function F() As Task(Of Integer) + Dim a = Sub() Console.WriteLine(1) + a() + Return Task.FromResult(1) + End Function +End Class +" + Dim src2 = " +Imports System +Imports System.Threading.Tasks +Class C + Async Function F() As Task(Of Integer) + Dim a = Sub() Console.WriteLine(1) + a() + Return Await Task.FromResult(1) + End Function +End Class +" + Dim edits = GetTopEdits(src1, src2) + Dim active = GetActiveStatements(src1, src2) + + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.RUDE_EDIT_LAMBDA_EXPRESSION, "Sub()", "method")) End Sub Public Sub MethodToAsyncMethod_WithoutActiveStatement() - ' TODO + Dim src1 = " +Imports System +Imports System.Threading.Tasks +Class C + Function F() As Task(Of Integer) + Console.WriteLine(1) + Return Task.FromResult(1) + End Function +End Class +" + Dim src2 = " +Imports System +Imports System.Threading.Tasks +Class C + Async Function F() As Task(Of Integer) + Console.WriteLine(1) + Return Await Task.FromResult(1) + End Function +End Class +" + Dim edits = GetTopEdits(src1, src2) + Dim active = GetActiveStatements(src1, src2) + + edits.VerifyRudeDiagnostics(active) End Sub #End Region diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditTopLevelTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditTopLevelTests.vb index 6df51672cd1..031fb815b94 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditTopLevelTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditTopLevelTests.vb @@ -1905,8 +1905,7 @@ End Class edits.VerifyEdits( "Update [Async Function F() As Task(Of String)]@11 -> [Function F() As Task(Of String)]@11") - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "Function F()", "method")) + edits.VerifyRudeDiagnostics() End Sub diff --git a/src/Features/VisualBasic/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb b/src/Features/VisualBasic/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb index 3aa81179c80..2ac51e03df8 100644 --- a/src/Features/VisualBasic/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb +++ b/src/Features/VisualBasic/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb @@ -2378,7 +2378,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return End If - If Not SyntaxFactory.AreEquivalent(oldNode.Modifiers, newNode.Modifiers) Then + If Not ClassifyMethodModifierUpdate(oldNode.Modifiers, newNode.Modifiers) Then ReportError(RudeEditKind.ModifiersUpdate) Return End If @@ -2395,6 +2395,32 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End If End Sub + Private Function ClassifyMethodModifierUpdate(oldModifiers As SyntaxTokenList, newModifiers As SyntaxTokenList) As Boolean + Dim oldAsyncIndex = oldModifiers.IndexOf(SyntaxKind.AsyncKeyword) + Dim newAsyncIndex = newModifiers.IndexOf(SyntaxKind.AsyncKeyword) + + If oldAsyncIndex >= 0 Then + oldModifiers = oldModifiers.RemoveAt(oldAsyncIndex) + End If + + If newAsyncIndex >= 0 Then + newModifiers = newModifiers.RemoveAt(newAsyncIndex) + End If + + Dim oldIteratorIndex = oldModifiers.IndexOf(SyntaxKind.IteratorKeyword) + Dim newIteratorIndex = newModifiers.IndexOf(SyntaxKind.IteratorKeyword) + + If oldIteratorIndex >= 0 Then + oldModifiers = oldModifiers.RemoveAt(oldIteratorIndex) + End If + + If newIteratorIndex >= 0 Then + newModifiers = newModifiers.RemoveAt(newIteratorIndex) + End If + + Return SyntaxFactory.AreEquivalent(oldModifiers, newModifiers) + End Function + Private Sub ClassifyUpdate(oldNode As DeclareStatementSyntax, newNode As DeclareStatementSyntax) If Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier) Then ReportError(RudeEditKind.Renamed) -- GitLab