File: EditAndContinue\Helpers\EditingTestBase.vb
Web Access
Project: src\src\Features\VisualBasicTest\Microsoft.CodeAnalysis.VisualBasic.Features.UnitTests.vbproj (Microsoft.CodeAnalysis.VisualBasic.Features.UnitTests)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports System.Collections.Immutable
Imports System.IO
Imports Microsoft.CodeAnalysis.Differencing
Imports Microsoft.CodeAnalysis.EditAndContinue
Imports Microsoft.CodeAnalysis.Contracts.EditAndContinue
Imports Microsoft.CodeAnalysis.EditAndContinue.UnitTests
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.EditAndContinue
Imports Microsoft.CodeAnalysis.Emit
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests
 
    Public MustInherit Class EditingTestBase
        Inherits BasicTestBase
 
        Public Shared ReadOnly ReloadableAttributeSrc As String = "
Imports System.Runtime.CompilerServices
Namespace System.Runtime.CompilerServices
    Class CreateNewOnMetadataUpdateAttribute
        Inherits Attribute
    End Class
End Namespace
"
 
        Friend Shared Function CreateAnalyzer() As VisualBasicEditAndContinueAnalyzer
            Return New VisualBasicEditAndContinueAnalyzer()
        End Function
 
        Public Enum MethodKind
            Regular
            Async
            Iterator
        End Enum
 
        Public Shared Function GetResource(keyword As String, symbolDisplayName As String) As String
            Dim resource = TryGetResource(keyword)
 
            If resource Is Nothing Then
                Throw ExceptionUtilities.UnexpectedValue(keyword)
            End If
 
            Return String.Format(FeaturesResources.member_kind_and_name, resource, symbolDisplayName)
        End Function
 
        Public Shared Function GetResource(keyword As String, symbolDisplayName As String, containerKeyword As String, containerDisplayName As String) As String
            Dim keywordResource = TryGetResource(keyword)
            If keywordResource Is Nothing Then
                Throw ExceptionUtilities.UnexpectedValue(keyword)
            End If
 
            Dim containerResource = TryGetResource(containerKeyword)
            If containerResource Is Nothing Then
                Throw ExceptionUtilities.UnexpectedValue(containerKeyword)
            End If
 
            Return String.Format(
                FeaturesResources.symbol_kind_and_name_of_member_kind_and_name,
                keywordResource,
                symbolDisplayName,
                containerResource,
                containerDisplayName)
        End Function
 
        Public Shared Function GetResource(keyword As String) As String
            Dim result = TryGetResource(keyword)
            If result Is Nothing Then
                Throw ExceptionUtilities.UnexpectedValue(keyword)
            End If
            Return result
        End Function
 
        Public Shared Function TryGetResource(keyword As String) As String
            Select Case keyword.ToLowerInvariant()
                Case "enum"
                    Return FeaturesResources.enum_
                Case "enum value"
                    Return FeaturesResources.enum_value
                Case "class"
                    Return FeaturesResources.class_
                Case "structure"
                    Return VBFeaturesResources.structure_
                Case "module"
                    Return VBFeaturesResources.module_
                Case "interface"
                    Return FeaturesResources.interface_
                Case "delegate"
                    Return FeaturesResources.delegate_
                Case "lambda"
                    Return VBFeaturesResources.Lambda
                Case "const field"
                    Return FeaturesResources.const_field
                Case "field"
                    Return FeaturesResources.field
                Case "auto-property"
                    Return FeaturesResources.auto_property
                Case "property"
                    Return FeaturesResources.property_
                Case "event"
                    Return FeaturesResources.event_
                Case "method"
                    Return FeaturesResources.method
                Case "constructor"
                    Return FeaturesResources.constructor
                Case "shared constructor"
                    Return VBFeaturesResources.Shared_constructor
                Case "parameter"
                    Return FeaturesResources.parameter
                Case "type parameter"
                    Return FeaturesResources.type_parameter
                Case "withevents field"
                    Return VBFeaturesResources.WithEvents_field
                Case "from clause"
                    Return VBFeaturesResources.From_clause
                Case "where clause"
                    Return VBFeaturesResources.Where_clause
                Case "join clause"
                    Return VBFeaturesResources.Join_clause
                Case "let clause"
                    Return VBFeaturesResources.Let_clause
                Case "orderby clause"
                    Return VBFeaturesResources.Ordering_clause
                Case "groupby clause"
                    Return VBFeaturesResources.Group_By_clause
                Case "select clause"
                    Return VBFeaturesResources.Select_clause
                Case Else
                    Return Nothing
            End Select
        End Function
 
        Friend Shared NoSemanticEdits As SemanticEditDescription() = Array.Empty(Of SemanticEditDescription)
 
        Friend Overloads Shared Function Diagnostic(rudeEditKind As RudeEditKind, squiggle As String, ParamArray arguments As String()) As RudeEditDiagnosticDescription
            Return New RudeEditDiagnosticDescription(rudeEditKind, squiggle, arguments, firstLine:=Nothing)
        End Function
 
        Friend Shared Function RuntimeRudeEdit(marker As Integer, rudeEditKind As RudeEditKind, position As LinePosition, ParamArray arguments As String()) As RuntimeRudeEditDescription
            Return New RuntimeRudeEditDescription(marker, rudeEditKind, position, arguments)
        End Function
 
        Friend Shared Function SemanticEdit(kind As SemanticEditKind,
                                            symbolProvider As Func(Of Compilation, ISymbol),
                                            syntaxMap As SyntaxMapDescription.Mapping?,
                                            Optional rudeEdits As IEnumerable(Of RuntimeRudeEditDescription) = Nothing,
                                            Optional partialType As String = Nothing,
                                            Optional deletedSymbolContainerProvider As Func(Of Compilation, ISymbol) = Nothing) As SemanticEditDescription
            Return SemanticEdit(kind, symbolProvider, syntaxMap?.Spans, rudeEdits, partialType, deletedSymbolContainerProvider)
        End Function
 
        Friend Shared Function SemanticEdit(kind As SemanticEditKind,
                                            symbolProvider As Func(Of Compilation, ISymbol),
                                            syntaxMap As IEnumerable(Of (TextSpan, TextSpan)),
                                            Optional rudeEdits As IEnumerable(Of RuntimeRudeEditDescription) = Nothing,
                                            Optional partialType As String = Nothing,
                                            Optional deletedSymbolContainerProvider As Func(Of Compilation, ISymbol) = Nothing) As SemanticEditDescription
            Return New SemanticEditDescription(
                kind,
                symbolProvider,
                If(partialType Is Nothing, Nothing, Function(c As Compilation) CType(c.GetMember(partialType), ITypeSymbol)),
                syntaxMap,
                rudeEdits,
                hasSyntaxMap:=syntaxMap IsNot Nothing,
                deletedSymbolContainerProvider)
        End Function
 
        Friend Shared Function SemanticEdit(kind As SemanticEditKind,
                                            symbolProvider As Func(Of Compilation, ISymbol),
                                            Optional partialType As String = Nothing,
                                            Optional preserveLocalVariables As Boolean = False,
                                            Optional deletedSymbolContainerProvider As Func(Of Compilation, ISymbol) = Nothing) As SemanticEditDescription
            Return New SemanticEditDescription(
                kind,
                symbolProvider,
                If(partialType Is Nothing, Nothing, Function(c As Compilation) CType(c.GetMember(partialType), ITypeSymbol)),
                syntaxMap:=Nothing,
                rudeEdits:=Nothing,
                hasSyntaxMap:=preserveLocalVariables,
                deletedSymbolContainerProvider)
        End Function
 
        Friend Shared Function DeletedSymbolDisplay(kind As String, displayName As String) As String
            Return String.Format(FeaturesResources.member_kind_and_name, kind, displayName)
        End Function
 
        Friend Shared Function DocumentResults(
            Optional activeStatements As ActiveStatementsDescription = Nothing,
            Optional semanticEdits As SemanticEditDescription() = Nothing,
            Optional diagnostics As RudeEditDiagnosticDescription() = Nothing) As DocumentAnalysisResultsDescription
            Return New DocumentAnalysisResultsDescription(activeStatements, semanticEdits, lineEdits:=Nothing, diagnostics)
        End Function
 
        Private Shared Function GetDocumentFilePath(documentIndex As Integer) As String
            Return Path.Combine(TempRoot.Root, documentIndex.ToString() & ".vb")
        End Function
 
        Private Shared Function ParseSource(markedSource As String, Optional documentIndex As Integer = 0) As SyntaxTree
            Return SyntaxFactory.ParseSyntaxTree(
                SourceMarkers.Clear(markedSource),
                VisualBasicParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest),
                path:=GetDocumentFilePath(documentIndex))
        End Function
 
        Friend Shared Function GetTopEdits(src1 As String, src2 As String, Optional documentIndex As Integer = 0) As EditScriptDescription
            Dim tree1 = ParseSource(src1, documentIndex)
            Dim tree2 = ParseSource(src2, documentIndex)
 
            tree1.GetDiagnostics().Verify()
            tree2.GetDiagnostics().Verify()
 
            Dim match = SyntaxComparer.TopLevel.ComputeMatch(tree1.GetRoot(), tree2.GetRoot())
            Return New EditScriptDescription(src1, src2, match.GetTreeEdits())
        End Function
 
        Friend Shared Function GetTopEdits(methodEdits As EditScriptDescription) As EditScriptDescription
            Dim oldMethodSource = methodEdits.Match.OldRoot.ToFullString()
            Dim newMethodSource = methodEdits.Match.NewRoot.ToFullString()
 
            Return GetTopEdits(WrapMethodBodyWithClass(oldMethodSource), WrapMethodBodyWithClass(newMethodSource))
        End Function
 
        Friend Shared Function GetMethodEdits(src1 As String, src2 As String, Optional methodKind As MethodKind = MethodKind.Regular) As EditScriptDescription
            Dim match = GetMethodMatch(src1, src2, methodKind)
            Return New EditScriptDescription(src1, src2, match.GetTreeEdits())
        End Function
 
        Friend Shared Function GetMethodMatch(src1 As String, src2 As String, Optional methodKind As MethodKind = MethodKind.Regular) As Match(Of SyntaxNode)
            Dim m1 = MakeMethodBody(src1, methodKind)
            Dim m2 = MakeMethodBody(src2, methodKind)
 
            Dim match = m1.ComputeSingleRootMatch(m2, knownMatches:=Nothing)
 
            Dim stateMachineInfo1 = m1.GetStateMachineInfo()
            Dim stateMachineInfo2 = m2.GetStateMachineInfo()
            Dim needsSyntaxMap = stateMachineInfo1.HasSuspensionPoints AndAlso stateMachineInfo2.HasSuspensionPoints
 
            Assert.Equal(methodKind <> MethodKind.Regular, needsSyntaxMap)
 
            Return match
        End Function
 
        Public Shared Function GetMethodMatches(src1 As String,
                                                src2 As String,
                                                Optional kind As MethodKind = MethodKind.Regular) As IEnumerable(Of KeyValuePair(Of SyntaxNode, SyntaxNode))
            Dim methodMatch = GetMethodMatch(src1, src2, kind)
            Return EditAndContinueTestVerifier.GetMethodMatches(CreateAnalyzer(), methodMatch)
        End Function
 
        Public Shared Function ToMatchingPairs(match As Match(Of SyntaxNode)) As MatchingPairs
            Return EditAndContinueTestVerifier.ToMatchingPairs(match)
        End Function
 
        Public Shared Function ToMatchingPairs(matches As IEnumerable(Of KeyValuePair(Of SyntaxNode, SyntaxNode))) As MatchingPairs
            Return EditAndContinueTestVerifier.ToMatchingPairs(matches)
        End Function
 
        Friend Shared Function MakeMethodBody(bodySource As String, Optional stateMachine As MethodKind = MethodKind.Regular) As MemberBody
            Dim source = WrapMethodBodyWithClass(bodySource, stateMachine)
 
            Dim tree = ParseSource(source)
            Dim root = tree.GetRoot()
            tree.GetDiagnostics().Verify()
 
            Dim declaration = DirectCast(DirectCast(root, CompilationUnitSyntax).Members(0), ClassBlockSyntax).Members(0)
            Return SyntaxUtilities.TryGetDeclarationBody(SyntaxFactory.SyntaxTree(declaration).GetRoot())
        End Function
 
        Private Shared Function WrapMethodBodyWithClass(bodySource As String, Optional kind As MethodKind = MethodKind.Regular) As String
            Select Case kind
                Case MethodKind.Iterator
                    Return "Class C" & vbLf & "Iterator Function F() As IEnumerable(Of Integer)" & vbLf & bodySource & " : End Function : End Class"
 
                Case MethodKind.Async
                    Return "Class C" & vbLf & "Async Function F() As Task(Of Integer)" & vbLf & bodySource & " : End Function : End Class"
 
                Case Else
                    Return "Class C" & vbLf & "Sub F()" & vbLf & bodySource & " : End Sub : End Class"
            End Select
        End Function
 
        Friend Shared Function GetActiveStatements(oldSource As String, newSource As String, Optional flags As ActiveStatementFlags() = Nothing, Optional documentIndex As Integer = 0) As ActiveStatementsDescription
            Return New ActiveStatementsDescription(oldSource, newSource, Function(source) SyntaxFactory.ParseSyntaxTree(source, path:=GetDocumentFilePath(documentIndex)), flags)
        End Function
 
        Friend Shared Function GetSyntaxMap(oldSource As String, newSource As String) As SyntaxMapDescription
            Return New SyntaxMapDescription(oldSource, newSource)
        End Function
    End Class
End Namespace