File: Parser\ParseIteratorTests.vb
Web Access
Project: src\src\Compilers\VisualBasic\Test\Syntax\Microsoft.CodeAnalysis.VisualBasic.Syntax.UnitTests.vbproj (Microsoft.CodeAnalysis.VisualBasic.Syntax.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 Microsoft.CodeAnalysis.Test.Utilities
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Roslyn.Test.Utilities
 
Public Class ParseIteratorTests
    Inherits BasicTestBase
 
    <Fact>
    Public Sub ParseIteratorModifier()
        Dim tree = ParseAndVerify(<![CDATA[
Imports Iterator = System.Collections.Generic.IEnumerable(Of Integer)

Module Program
    Public Const Iterator As Integer = 0

    <Iterator(Iterator)>
    Iterator Function M() As Iterator

        Dim l = Function()
                    Dim iterator As Iterator = Nothing
                    Return iterator
                End Function

        Dim l2 = Iterator Function()
                     Dim iterator As Iterator = Nothing
                     Yield iterator
                 End Function
    End Function

    <Iterator(Iterator)>
    Iterator Property P1() As Iterator
        Get
            Dim l = Function()
                        Dim iterator As Iterator = Nothing
                        Return iterator
                    End Function

            Dim l2 = Iterator Function()
                         Dim iterator As Iterator = Nothing
                         Yield iterator
                     End Function
        End Get
        Set(value As Iterator)

        End Set
    End Property

    <Iterator(Iterator)>
    ReadOnly Iterator Property P2() As Iterator
        Get
            Dim l = Function()
                        Dim iterator As Iterator = Nothing
                        Return iterator
                    End Function

            Dim l2 = Iterator Function()
                         Dim iterator As Iterator = Nothing
                         Yield iterator
                     End Function
        End Get
    End Property

End Module

Class IteratorAttribute
    Inherits Attribute

    Sub New(p)
    End Sub
End Class]]>)
 
        Assert.Equal(6, Aggregate t In tree.GetRoot().DescendantTokens Where t.Kind = SyntaxKind.IteratorKeyword Into Count())
 
    End Sub
 
    <Fact>
    Public Sub ParseYieldStatements()
        Dim tree = VisualBasicSyntaxTree.ParseText(<![CDATA[
Module Program

    Iterator Function M()
        Yield (1)
    End Function

    Function M2()
        Yield 1
    End Function

    Iterator Property P1() As IEnumerable(Of Func(Of IEnumerable(Of Integer)))
        Get
            Yield Function()
                      Yield {0}
                  End Function

            Yield Iterator Function()
                      Yield 1
                  End Function
        End Get
        Set(value As IEnumerable(Of Func(Of IEnumerable(Of Integer))))
            ' This will be a semantic error.
            Yield -1
        End Set
    End Property

    Property P2 As IEnumerable(Of Func(Of IEnumerable(Of Integer)))
        Get
            Yield Function()
                      Yield {0}
                  End Function

            Yield Iterator Function()
                      Yield 1
                  End Function
        End Get
        Set(value As IEnumerable(Of Func(Of IEnumerable(Of Integer))))
            Yield -1
        End Set
    End Property

    ReadOnly Iterator Property P3 As IEnumerable(Of Func(Of IEnumerable(Of Integer)))
        Get
            Yield Function()
                      Return Yield({0})
                  End Function

            Yield Iterator Function()
                      Yield 1
                  End Function
        End Get
    End Property

    ReadOnly Property P4 As IEnumerable(Of Func(Of IEnumerable(Of Integer)))
        Get
            Yield Function()
                      Yield({0})
                  End Function

            Yield Iterator Function()
                      Yield 1
                  End Function
        End Get
    End Property

End Module]]>.Value)
 
        Dim yieldStatements = tree.GetRoot().DescendantNodes.OfType(Of YieldStatementSyntax)().ToArray()
 
        Assert.Equal(10, yieldStatements.Count)
 
        Dim methodSyntaxList = tree.GetRoot().DescendantNodes.OfType(Of MethodBlockBaseSyntax)().ToArray()
 
        ' Iterator Function M()
        Dim firstStatementOfM = methodSyntaxList(0).Statements.First
        Assert.Equal(SyntaxKind.YieldStatement, firstStatementOfM.Kind)     ' 1
 
        ' Function M2()
        yieldStatements = methodSyntaxList(1).DescendantNodes.OfType(Of YieldStatementSyntax)().ToArray()
        Assert.Equal(0, yieldStatements.Count)
 
        ' Iterator Property P1() As IEnumerable(Of Func(Of IEnumerable(Of Integer)))
        ' Getter
        Dim statements = methodSyntaxList(2).Statements
        Assert.Equal(SyntaxKind.YieldStatement, statements(0).Kind)     ' 2
        yieldStatements = statements(0).DescendantNodes.OfType(Of YieldStatementSyntax)().ToArray()
        Assert.Equal(0, yieldStatements.Count)
        Assert.Equal(SyntaxKind.YieldStatement, statements(1).Kind)     ' 3
        yieldStatements = statements(1).DescendantNodes.OfType(Of YieldStatementSyntax)().ToArray()
        Assert.Equal(1, yieldStatements.Count)      ' 4
        ' Setter
        statements = methodSyntaxList(3).Statements
        Assert.Equal(SyntaxKind.YieldStatement, statements(0).Kind)     ' 5
 
        ' Property P2 As IEnumerable(Of Func(Of IEnumerable(Of Integer)))
        ' Getter
        statements = methodSyntaxList(4).Statements
        Assert.NotEqual(SyntaxKind.YieldStatement, statements(0).Kind)
        yieldStatements = statements(0).DescendantNodes.OfType(Of YieldStatementSyntax)().ToArray()
        Assert.Equal(0, yieldStatements.Count)
        Assert.NotEqual(SyntaxKind.YieldStatement, statements(1).Kind)
        yieldStatements = statements(1).DescendantNodes.OfType(Of YieldStatementSyntax)().ToArray()
        Assert.Equal(1, yieldStatements.Count)      ' 6
        ' Setter
        statements = methodSyntaxList(5).Statements
        Assert.NotEqual(SyntaxKind.YieldStatement, statements(0).Kind)
 
        ' ReadOnly Iterator Property P3 As IEnumerable(Of Func(Of IEnumerable(Of Integer)))
        ' Getter
        statements = methodSyntaxList(6).Statements
        Assert.Equal(SyntaxKind.YieldStatement, statements(0).Kind)     ' 7
        yieldStatements = statements(0).DescendantNodes.OfType(Of YieldStatementSyntax)().ToArray()
        Assert.Equal(0, yieldStatements.Count)
        Assert.Equal(SyntaxKind.YieldStatement, statements(1).Kind)     ' 8
        yieldStatements = statements(1).DescendantNodes.OfType(Of YieldStatementSyntax)().ToArray()
        Assert.Equal(1, yieldStatements.Count)      ' 9
 
        ' ReadOnly Property P4 As IEnumerable(Of Func(Of IEnumerable(Of Integer)))
        ' Getter
        statements = methodSyntaxList(7).Statements
        Assert.NotEqual(SyntaxKind.YieldStatement, statements(0).Kind)
        yieldStatements = statements(0).DescendantNodes.OfType(Of YieldStatementSyntax)().ToArray()
        Assert.Equal(0, yieldStatements.Count)
        Assert.NotEqual(SyntaxKind.YieldStatement, statements(1).Kind)
        yieldStatements = statements(1).DescendantNodes.OfType(Of YieldStatementSyntax)().ToArray()
        Assert.Equal(1, yieldStatements.Count)  ' 10
    End Sub
 
    <Fact>
    Public Sub ParseYieldStatementsWithPrecedence()
        Dim tree = VisualBasicSyntaxTree.ParseText(<![CDATA[
Module Program
    Iterator Function M(a As Task(Of Integer), x As Task(Of Integer), y As Task(Of Integer), b As Task(Of Integer)) As Task(Of Integer)

        Yield a * Yield x ^ Yield y + Yield b
        
    End Function
End Module]]>.Value)
 
        Dim yieldStatements = tree.GetRoot().DescendantNodes.OfType(Of YieldStatementSyntax)().ToArray()
 
        Assert.Equal(1, yieldStatements.Count)
 
    End Sub
 
    <Fact>
    Public Sub ParseIteratorLambdas()
        Dim tree = ParseAndVerify(<![CDATA[
Module Program

    Private t1 As Task
    Private t2 As Task(Of Integer)
    Private f As Integer

    Sub Main()

        ' Iterator Sub lambdas make no sense and we'll need to catch this at binding time but
        ' both Iterator and Yield will parse correctly in this case.
        Dim slIteratorSub1 = Iterator Sub() Yield t1

        ' Again, the binder will need to report this as an error but Iterator and Yield will parse here.
        Dim mlIteratorSub = Iterator Sub()
                                Yield t1
                            End Sub

        Dim mlIteratorFunction = Iterator Function()
                                     Yield t1
                                 End Function

    End Sub
End Module]]>)
 
        Dim lambdas = tree.GetRoot().DescendantNodes.OfType(Of LambdaExpressionSyntax)().ToArray()
 
        Assert.Equal(3, lambdas.Count)
        Assert.Equal(1, lambdas.Count(Function(l) SyntaxFacts.IsSingleLineLambdaExpression(l.Kind)))
        Assert.Equal(2, lambdas.Count(Function(l) SyntaxFacts.IsMultiLineLambdaExpression(l.Kind)))
 
        ' Dim slIteratorSub1 = Iterator Sub() Yield t1
        Assert.Equal(SyntaxKind.YieldStatement, CType(lambdas(0), SingleLineLambdaExpressionSyntax).Body.Kind)
 
        Dim yieldStatements = lambdas(1).DescendantNodes.OfType(Of YieldStatementSyntax).ToArray()
        Assert.Equal(1, yieldStatements.Count)
 
        yieldStatements = lambdas(2).DescendantNodes.OfType(Of YieldStatementSyntax).ToArray()
        Assert.Equal(1, yieldStatements.Count)
    End Sub
 
    <Fact>
    Public Sub ParseIteratorWithNesting()
        Dim tree = VisualBasicSyntaxTree.ParseText(<![CDATA[
Imports Iterator = System.Threading.Tasks.Task

Class C
    Public Const Iterator As Integer = 0

    <Iterator(Iterator)>
    Iterator Function M() As Iterator

        Dim t As Task

        Yield (t) ' Yes

        Dim lambda1 = Function() Yield(t) ' No

        Dim lambda1a = Sub() Yield(t) ' No

        Dim lambda1b = Iterator Function() Yield t ' No

        Dim lambda1c = Iterator Function()
                           Yield (Sub()
                                      Yield(Function() Yield(Function() (Function() Iterator Sub() Yield t)())())
                                  End Sub) ' Yes, No, No, Yes

                            Return Yield t ' No
                       End Function

        [Yield] t ' No
    End Function

    Sub Yield(Optional p = Nothing)

        Dim t As Task

        Yield(t) ' No

        Dim lambda1 = Iterator Function() Yield (t) ' No

        Dim lambda1a = Iterator Sub() Yield (t) ' Yes

        Dim lambda1b = Function() Yield t ' No

        Dim lambda1c = Function()
                            Yield (Iterator Sub()
                                       Yield (Iterator Function() Yield (Iterator Function() (Function() Sub() Yield t)())())                        
                                   End Sub) ' No, Yes, No, No

                            Return Yield t ' No
                       End Function

        Yield t ' No
    End Sub

    Function Yield(t)
        Return Nothing
    End Function
End Class

Class IteratorAttribute
    Inherits Attribute

    Sub New(p)

    End Sub
End Class]]>.Value)
 
        Dim expected = {SyntaxKind.YieldStatement,
                        SyntaxKind.IdentifierName,
                        SyntaxKind.IdentifierName,
                        SyntaxKind.IdentifierName,
                        SyntaxKind.YieldStatement,
                        SyntaxKind.IdentifierName,
                        SyntaxKind.IdentifierName,
                        SyntaxKind.YieldStatement,
                        SyntaxKind.IdentifierName,
                        SyntaxKind.IdentifierName,
                        SyntaxKind.IdentifierName,
                        SyntaxKind.IdentifierName,
                        SyntaxKind.YieldStatement,
                        SyntaxKind.IdentifierName,
                        SyntaxKind.IdentifierName,
                        SyntaxKind.YieldStatement,
                        SyntaxKind.IdentifierName,
                        SyntaxKind.IdentifierName,
                        SyntaxKind.IdentifierName,
                        SyntaxKind.IdentifierName}
 
        Dim actual = From expression In tree.GetRoot().DescendantNodes()
                     Where expression.Kind = SyntaxKind.YieldStatement OrElse
                            (expression.Kind = SyntaxKind.IdentifierName AndAlso DirectCast(expression, IdentifierNameSyntax).Identifier.ValueText.Equals("Yield"))
                     Order By expression.FullSpan.Start
                     Select expression.Kind()
 
        Assert.Equal(expected, actual)
    End Sub
 
    <Fact>
    Public Sub ParseYieldInScriptingAndInteractive()
 
        Dim source = "
Yield T                                     ' No
Yield (T)                                   ' No
Yield T + Yield (T)                         ' No, No
Dim i = Yield T + Yield (T)                 ' No, No
 
Dim l = Sub()
            Yield T                         ' No
            Yield (T)                       ' No
        End Sub
 
Function M()
    Return Yield T                          ' No
    Yield T                                 ' No
    Yield (T)                               ' No
End Function
 
Iterator Sub N()
    Yield T                                 ' Yes
    Yield (T)                               ' Yes
    Yield T + Yield (T)                     ' Yes, No
    Dim i = Yield T + Yield (T)             ' No, No
    Return Yield T                          ' No
End Sub
 
Iterator Function F()
    Yield T                                 ' Yes
    Yield (T)                               ' Yes
    Yield T + Yield (T)                     ' Yes, No
    Dim i = Yield T + Yield (T)             ' No, No
    Return Yield T                          ' No
End Function"
 
        Dim tree = VisualBasicSyntaxTree.ParseText(source, options:=TestOptions.Script)
 
        Dim yieldStatements = tree.GetRoot().DescendantNodes.OfType(Of YieldStatementSyntax).ToArray()
 
        Assert.Equal(6, yieldStatements.Count)
 
        For Each yieldStatement In yieldStatements
            Assert.True(IsInIteratorMethod(yieldStatement))
        Next
    End Sub
 
    Private Shared Function IsIteratorMethod(methodSyntax As MethodBlockBaseSyntax) As Boolean
        Return methodSyntax.BlockStatement.Modifiers.Contains(Function(t As SyntaxToken) t.Kind = SyntaxKind.IteratorKeyword)
    End Function
 
    Private Shared Function IsInIteratorMethod(yieldStatement As YieldStatementSyntax) As Boolean
        Return IsIteratorMethod(DirectCast(yieldStatement.Parent, MethodBlockBaseSyntax))
    End Function
End Class