File: VisualBasicParsingTests.cs
Web Access
Project: src\src\ExpressionEvaluator\Core\Test\FunctionResolver\Microsoft.CodeAnalysis.FunctionResolver.UnitTests.csproj (Microsoft.CodeAnalysis.ExpressionEvaluator.FunctionResolver.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.
 
#nullable disable
 
using Microsoft.CodeAnalysis.VisualBasic;
using Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator;
using Roslyn.Test.Utilities;
using System;
using System.Collections.Immutable;
using System.Linq;
using Xunit;
 
namespace Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests
{
    public class VisualBasicParsingTests : ParsingTestBase
    {
        // Verify the set of keywords in the parser matches the VB compiler.
        [Fact]
        public void Keywords()
        {
            var builder = ImmutableHashSet.CreateBuilder<SyntaxKind>();
            // SyntaxFacts.GetReservedKeywordKinds() contains ReferenceKeyword
            // although "Reference" should not be considered a keyword.
            // (see https://github.com/dotnet/roslyn/issues/15242).
            builder.Add(SyntaxKind.ReferenceKeyword);
            foreach (var text in MemberSignatureParser.Keywords)
            {
                var kind = SyntaxFacts.GetKeywordKind(text);
                Assert.NotEqual(SyntaxKind.None, kind);
                bool added = builder.Add(kind);
                Assert.True(added);
            }
 
            var actualKeywordKinds = builder.ToImmutable();
            var expectedKeywordKinds = ImmutableHashSet.CreateRange(SyntaxFacts.GetReservedKeywordKinds());
            AssertEx.SetEqual(actualKeywordKinds, expectedKeywordKinds);
        }
 
        // Verify the set of keywords used in the parser are valid.
        [Fact]
        public void KeywordKinds()
        {
            // Verify the labels are consistent with the VB compiler.
            foreach (var pair in MemberSignatureParser.KeywordKinds)
            {
                var expectedKind = SyntaxFacts.GetKeywordKind(pair.Key).ToString();
                var actualKind = pair.Value.ToString();
                Assert.Equal(expectedKind, actualKind);
            }
 
            // Verify all values are also in Keywords.
            foreach (var keyword in MemberSignatureParser.KeywordKinds.Keys)
            {
                Assert.True(MemberSignatureParser.Keywords.Contains(keyword));
            }
 
            // Verify all values of SyntaxKind are recognized.
            const string keywordSuffix = "Keyword";
            foreach (var value in typeof(MemberSignatureParser.SyntaxKind).GetEnumValues())
            {
                var kind = (MemberSignatureParser.SyntaxKind)value;
                if (kind == MemberSignatureParser.SyntaxKind.None)
                {
                    continue;
                }
                var pair = MemberSignatureParser.KeywordKinds.First(p => p.Value == kind);
                var kindText = kind.ToString();
                Assert.EndsWith(keywordSuffix, kindText);
                var expectedText = kindText.Substring(0, kindText.Length - keywordSuffix.Length);
                Assert.Equal(expectedText, pair.Key, StringComparer.OrdinalIgnoreCase);
            }
        }
 
        [Fact]
        public void Parsing()
        {
            // Method name only.
            VerifySignature("F",
                SignatureNameOnly(
                    Name("F")));
            // Method name and empty parameters.
            VerifySignature("F()",
                Signature(
                    Name("F")));
            // Method name and parameters.
            VerifySignature("F(A, B)",
                Signature(
                    Name("F"),
                    Identifier("A"),
                    Identifier("B")));
            // Type and method name.
            VerifySignature("C.F",
                SignatureNameOnly(
                    Qualified(Name("C"), "F")));
            // Qualified type and method name.
            VerifySignature("A.B.F",
                SignatureNameOnly(
                    Qualified(
                        Qualified(
                            Name("A"),
                            "B"),
                        "F")));
            // Generic types and method names.
            VerifySignature("A(Of T).B(Of U).F(Of V)",
                SignatureNameOnly(
                    Generic(
                        Qualified(
                            Generic(
                                Qualified(
                                    Generic(
                                        Name("A"),
                                        "T"),
                                    "B"),
                                "U"),
                            "F"),
                        "V")));
        }
 
        [Fact]
        public void Spaces()
        {
            VerifySignature(" \tC . F ( System.Object\t,object) ",
                Signature(
                    Qualified(Name("C"), "F"),
                    Qualified("System", "Object"),
                    Qualified("System", "Object")));
        }
 
        [Fact]
        public void Arrays()
        {
            VerifySignature("F(C(,,,))",
                Signature(
                    Name("F"),
                    Array(
                        Identifier("C"),
                        4)));
            VerifySignature("F(C(,)())",
                Signature(
                    Name("F"),
                    Array(
                        Array(
                            Identifier("C"),
                            2),
                        1)));
            VerifySignature("F(C(Of T(,)))",
                Signature(
                    Name("F"),
                    Generic(
                        Identifier("C"),
                        Array(
                            Identifier("T"),
                            2))));
        }
 
        [Fact]
        public void ParseErrors()
        {
            Assert.Null(MemberSignatureParser.Parse("A(Of)"));
            Assert.Null(MemberSignatureParser.Parse("A(Of Of)"));
            Assert.Null(MemberSignatureParser.Parse("A(Of T)B"));
            Assert.Null(MemberSignatureParser.Parse("A(Of (Of T))"));
            Assert.Null(MemberSignatureParser.Parse("A(Of T)(Of U)"));
            Assert.Null(MemberSignatureParser.Parse("A(Of T, Of U)"));
            Assert.Null(MemberSignatureParser.Parse("A.(Of T)"));
            Assert.Null(MemberSignatureParser.Parse("A(Of T).(Of U)"));
            Assert.Null(MemberSignatureParser.Parse("A+B"));
            Assert.Null(MemberSignatureParser.Parse("F("));
            Assert.Null(MemberSignatureParser.Parse("F())"));
            Assert.Null(MemberSignatureParser.Parse("F(]"));
            Assert.Null(MemberSignatureParser.Parse("F(,B)"));
            Assert.Null(MemberSignatureParser.Parse("F(A,)"));
            Assert.Null(MemberSignatureParser.Parse("F(Of "));
            Assert.Null(MemberSignatureParser.Parse("F(Of ()"));
            Assert.Null(MemberSignatureParser.Parse("F(Of T))"));
            Assert.Null(MemberSignatureParser.Parse("F(Of T()"));
            Assert.Null(MemberSignatureParser.Parse("F(Of T()"));
            Assert.Null(MemberSignatureParser.Parse("F?"));
            Assert.Null(MemberSignatureParser.Parse("F[]"));
            Assert.Null(MemberSignatureParser.Parse("F*"));
            Assert.Null(MemberSignatureParser.Parse(".F"));
            Assert.Null(MemberSignatureParser.Parse("()"));
            Assert.Null(MemberSignatureParser.Parse("(Of T)"));
            Assert.Null(MemberSignatureParser.Parse("1"));
            Assert.Null(MemberSignatureParser.Parse("F(C*)"));
            Assert.Null(MemberSignatureParser.Parse("F(C[])"));
            Assert.Null(MemberSignatureParser.Parse("global:C.F"));
        }
 
        [Fact]
        public void ByRef()
        {
            VerifySignature("F(ByVal A, ByRef B)",
                Signature(
                    Name("F"),
                    Identifier("A"),
                    Identifier("B")));
            Assert.Null(MemberSignatureParser.Parse("F(ByVal, B)"));
            Assert.Null(MemberSignatureParser.Parse("F(A, ByRef)"));
            Assert.Null(MemberSignatureParser.Parse("F(ByVal ByRef A, B)"));
            Assert.Null(MemberSignatureParser.Parse("F(A, ByRef ByVal B)"));
            Assert.Null(MemberSignatureParser.Parse("F(ByRef ByRef A)"));
            Assert.Null(MemberSignatureParser.Parse("F(A, ByVal ByVal B)"));
            Assert.Null(MemberSignatureParser.Parse("F(Of ByVal)"));
            Assert.Null(MemberSignatureParser.Parse("F(Of ByRef C)"));
            Assert.Null(MemberSignatureParser.Parse("F(C(Of ByRef))"));
            Assert.Null(MemberSignatureParser.Parse("F(C(Of ByRef C))"));
        }
 
        // Special types are treated as keywords in names,
        // but not recognized as special types.
        [Fact]
        public void SpecialTypes_Names()
        {
            // Method name only.
            Assert.Null(MemberSignatureParser.Parse("Integer"));
            Assert.Null(MemberSignatureParser.Parse("paramarray"));
            VerifySignature("[Integer]",
                SignatureNameOnly(
                    Name("Integer")));
            // Type and method name.
            VerifySignature("[Object].Integer",
                SignatureNameOnly(
                    Qualified(
                        Name("Object"),
                        "Integer")));
            // Type parameters.
            VerifySignature("F(Of Void)",
                SignatureNameOnly(
                    Generic(Name("F"),
                    "Void")));
            Assert.Null(MemberSignatureParser.Parse("F(Of boolean)"));
            Assert.Null(MemberSignatureParser.Parse("F(Of char)"));
            Assert.Null(MemberSignatureParser.Parse("F(Of SBYTE)"));
            Assert.Null(MemberSignatureParser.Parse("F(Of BYTE)"));
            Assert.Null(MemberSignatureParser.Parse("F(Of Short)"));
            Assert.Null(MemberSignatureParser.Parse("F(Of UShort)"));
            VerifySignature("F(Of [Boolean], [Char], [sbyte], [byte], [SHORT], [USHORT], [Integer], [UInteger], [Long], [ULong], [Single], [Double], [String], [Object], [Decimal], [Date])()",
                Signature(
                    Generic(Name("F"),
                    "Boolean",
                    "Char",
                    "sbyte",
                    "byte",
                    "SHORT",
                    "USHORT",
                    "Integer",
                    "UInteger",
                    "Long",
                    "ULong",
                    "Single",
                    "Double",
                    "String",
                    "Object",
                    "Decimal",
                    "Date")));
        }
 
        // Special types are recognized in type references.
        [Fact]
        public void SpecialTypes_TypeReferences()
        {
            // Parameters.
            VerifySignature("F(boolean, char, sbyte, byte, short, ushort, integer, uinteger, long, ulong, single, double, string, object, decimal, date)",
                Signature(
                    Name("F"),
                    Qualified("System", "Boolean"),
                    Qualified("System", "Char"),
                    Qualified("System", "SByte"),
                    Qualified("System", "Byte"),
                    Qualified("System", "Int16"),
                    Qualified("System", "UInt16"),
                    Qualified("System", "Int32"),
                    Qualified("System", "UInt32"),
                    Qualified("System", "Int64"),
                    Qualified("System", "UInt64"),
                    Qualified("System", "Single"),
                    Qualified("System", "Double"),
                    Qualified("System", "String"),
                    Qualified("System", "Object"),
                    Qualified("System", "Decimal"),
                    Qualified("System", "DateTime")));
            // Type arguments.
            VerifySignature("F(C(OF DECIMAL, INTEGER, STRING, OBJECT))",
                Signature(
                    Name("F"),
                    Generic(
                        Identifier("C"),
                        Qualified("System", "Decimal"),
                        Qualified("System", "Int32"),
                        Qualified("System", "String"),
                        Qualified("System", "Object"))));
            // Not special types.
            VerifySignature("F(Void, Int, [Object], A.[Integer], B.Single)",
                Signature(
                    Name("F"),
                    Identifier("Void"),
                    Identifier("Int"),
                    Identifier("Object"),
                    Qualified("A", "Integer"),
                    Qualified("B", "Single")));
        }
 
        [Fact]
        public void EscapedNames()
        {
            VerifySignature("[F3]",
                SignatureNameOnly(
                    Name("F3")));
            VerifySignature("[_]",
                SignatureNameOnly(
                    Name("_")));
            VerifySignature("[Integer]",
                SignatureNameOnly(
                    Name("Integer")));
            VerifySignature("A.B.[Integer]",
                SignatureNameOnly(
                    Qualified(
                        Qualified(
                            Name("A"),
                            "B"),
                        "Integer")));
            VerifySignature("F([Integer])",
                Signature(
                    Name("F"),
                    Identifier("Integer")));
            VerifySignature("F(System.[Integer])",
                Signature(
                    Name("F"),
                    Qualified(
                        Identifier("System"),
                        "Integer")));
            VerifySignature("A(Of [Object]).B(Of [Integer]).F(Of [Of])",
                SignatureNameOnly(
                    Generic(
                        Qualified(
                            Generic(
                                Qualified(
                                    Generic(
                                        Name("A"),
                                        "Object"),
                                    "B"),
                                "Integer"),
                            "F"),
                        "Of")));
            VerifySignature("F(C(Of Integer, [Date]))",
                Signature(
                    Name("F"),
                    Generic(
                        Identifier("C"),
                        Qualified("System", "Int32"),
                        Identifier("Date"))));
            Assert.Null(MemberSignatureParser.Parse("@"));
            Assert.Null(MemberSignatureParser.Parse("@Integer"));
            Assert.Null(MemberSignatureParser.Parse("["));
            Assert.Null(MemberSignatureParser.Parse("[]"));
            Assert.Null(MemberSignatureParser.Parse("[3"));
            Assert.Null(MemberSignatureParser.Parse("[3]"));
            Assert.Null(MemberSignatureParser.Parse("[[F"));
            Assert.Null(MemberSignatureParser.Parse("[F"));
            Assert.Null(MemberSignatureParser.Parse("[F["));
            Assert.Null(MemberSignatureParser.Parse("F]"));
            Assert.Null(MemberSignatureParser.Parse("[(T)"));
            Assert.Null(MemberSignatureParser.Parse("[Object]]"));
            Assert.Null(MemberSignatureParser.Parse("[Object+]"));
            Assert.Null(MemberSignatureParser.Parse("[Object ]"));
            Assert.Null(MemberSignatureParser.Parse("[.F"));
            Assert.Null(MemberSignatureParser.Parse("[()"));
            Assert.Null(MemberSignatureParser.Parse("F([)"));
            Assert.Null(MemberSignatureParser.Parse("F(A, [)"));
        }
 
        private static void VerifySignature(string str, RequestSignature expectedSignature)
        {
            var actualSignature = MemberSignatureParser.Parse(str);
            VerifySignature(actualSignature, expectedSignature);
        }
    }
}