File: Semantics\InteractiveUsingTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Semantic\Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Semantic.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 System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using Microsoft.CodeAnalysis.Test.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class InteractiveUsingTests : CSharpTestBase
    {
        [Fact]
        public void Using()
        {
            var sub = CreateSubmission("using System; typeof(String)");
            sub.VerifyDiagnostics();
 
            Assert.Equal(SpecialType.System_String, GetSpeculativeType(sub, "String").SpecialType);
        }
 
        [Fact]
        public void Alias()
        {
            var sub = CreateSubmission("using I = System.Int32; typeof(I)");
            sub.VerifyDiagnostics();
 
            Assert.Equal(SpecialType.System_Int32, GetSpeculativeType(sub, "I").SpecialType);
        }
 
        [Fact]
        public void UsingStatic()
        {
            var sub = CreateSubmission("using static System.Environment; NewLine");
            sub.VerifyDiagnostics();
 
            Assert.Equal(SymbolKind.Property, GetSpeculativeSymbol(sub, "NewLine").Kind);
        }
 
        [WorkItem(5450, "https://github.com/dotnet/roslyn/issues/5450")]
        [Fact]
        public void GlobalUsings()
        {
            var sub1 = CreateSubmission(
                "Combine(Environment.NewLine, Environment.NewLine)",
                options: TestOptions.DebugDll.WithUsings("System", "System.IO.Path"));
            sub1.VerifyDiagnostics();
 
            // No global usings specified - expect to reuse previous.
            var sub2 = CreateSubmission(
                "Combine(Environment.NewLine, Environment.NewLine)",
                previous: sub1);
            sub2.VerifyDiagnostics();
 
            // Global usings specified - expect to append to previous.
            var sub3 = CreateSubmission(
                "new StringBuilder().Append(Combine(Environment.NewLine, Environment.NewLine))",
                previous: sub2,
                options: TestOptions.DebugDll.WithUsings("System.Text"));
            sub3.VerifyDiagnostics();
        }
 
        [WorkItem(4811, "https://github.com/dotnet/roslyn/issues/4811")]
        [Fact]
        public void AliasCurrentSubmission()
        {
            const string source = @"
using T = Type;
 
class Type { }
";
 
            var sub = (Compilation)CreateSubmission(source);
            sub.VerifyDiagnostics();
 
            var typeSymbol = sub.ScriptClass.GetMember("Type");
 
            var tree = sub.SyntaxTrees.Single();
            var model = sub.GetSemanticModel(tree);
            var syntax = tree.GetRoot().DescendantNodes().OfType<UsingDirectiveSyntax>().Single();
 
            var aliasSymbol = model.GetDeclaredSymbol(syntax);
            Assert.Equal(SymbolKind.Alias, aliasSymbol.Kind);
            Assert.Equal(typeSymbol, ((IAliasSymbol)aliasSymbol).Target);
 
            Assert.Equal(typeSymbol, model.GetSymbolInfo(syntax.Name).Symbol);
 
            Assert.Equal(typeSymbol, GetSpeculativeType(sub, "Type"));
            Assert.Equal(typeSymbol, GetSpeculativeType(sub, "T"));
        }
 
        [WorkItem(4811, "https://github.com/dotnet/roslyn/issues/4811")]
        [Fact]
        public void AliasPreviousSubmission()
        {
            var sub1 = CreateSubmission("class A { }");
            var sub2 = CreateSubmission("class B : A { }", previous: sub1);
            var sub3 = CreateSubmission("class C : B { }", previous: sub2);
 
            CreateSubmission("using A1 = A;", previous: sub3).VerifyDiagnostics();
            CreateSubmission("using B1 = B;", previous: sub3).VerifyDiagnostics();
 
            var sub4 = CreateSubmission("using C1 = C; typeof(C1)", previous: sub3);
            sub4.VerifyDiagnostics();
 
            var typeSymbol = ((Compilation)sub3).ScriptClass.GetMember("C");
 
            var tree = sub4.SyntaxTrees.Single();
            var model = sub4.GetSemanticModel(tree);
            var syntax = tree.GetRoot().DescendantNodes().OfType<UsingDirectiveSyntax>().Single();
 
            var aliasSymbol = model.GetDeclaredSymbol(syntax);
            Assert.Equal(SymbolKind.Alias, aliasSymbol.Kind);
            Assert.Equal(typeSymbol, ((IAliasSymbol)aliasSymbol).Target);
 
            Assert.Equal(typeSymbol, model.GetSymbolInfo(syntax.Name).Symbol);
 
            Assert.Equal(typeSymbol, GetSpeculativeType(sub4, "C1"));
        }
 
        [Fact]
        public void AliasUnqualified()
        {
            const string source = @"
using I = Int32;
using System;
";
            var expectedDiagnostics = new[]
            {
                // (2,11): error CS0246: The type or namespace name 'Int32' could not be found (are you missing a using directive or an assembly reference?)
                // using I = Int32;
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Int32").WithArguments("Int32").WithLocation(2, 11)
            };
 
            CreateCompilation(source).GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Hidden).Verify(expectedDiagnostics);
            CreateSubmission(source).GetDiagnostics().Verify(expectedDiagnostics);
        }
 
        [Fact]
        public void AliasUnqualified_GlobalUsing()
        {
            const string source = @"
using I = Int32;
";
            var expectedDiagnostics = new[]
            {
                // (2,11): error CS0246: The type or namespace name 'Int32' could not be found (are you missing a using directive or an assembly reference?)
                // using I = Int32;
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Int32").WithArguments("Int32").WithLocation(2, 11)
            };
 
            var options = TestOptions.DebugDll.WithUsings("System");
 
            CreateCompilation(source, options: options).GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Hidden).Verify(expectedDiagnostics);
            CreateSubmission(source, options: options).GetDiagnostics().Verify(expectedDiagnostics);
        }
 
        [Fact]
        public void AliasOtherAlias()
        {
            const string source = @"
using I = System.Int32;
using J = I;
";
            var expectedDiagnostics = new[]
            {
                // (3,11): error CS0246: The type or namespace name 'I' could not be found (are you missing a using directive or an assembly reference?)
                // using J = I;
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "I").WithArguments("I").WithLocation(3, 11)
            };
 
            CreateCompilation(source).GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Hidden).Verify(expectedDiagnostics);
            CreateSubmission(source).GetDiagnostics().Verify(expectedDiagnostics);
        }
 
        [Fact]
        public void AliasHiding()
        {
            var sub1 = CreateSubmission("using A = System.Int32; typeof(A)");
            Assert.Equal(SpecialType.System_Int32, GetSpeculativeType(sub1, "A").SpecialType);
 
            var sub2 = CreateSubmission("using A = System.Int16; typeof(A)", previous: sub1);
            Assert.Equal(SpecialType.System_Int16, GetSpeculativeType(sub2, "A").SpecialType);
 
            var sub3 = CreateSubmission("class A { }", previous: sub2);
            Assert.Equal(((Compilation)sub3).ScriptClass, GetSpeculativeType(sub3, "A").ContainingType);
 
            var sub4 = CreateSubmission("using A = System.Int64; typeof(A)", previous: sub3);
            Assert.Equal(SpecialType.System_Int64, GetSpeculativeType(sub4, "A").SpecialType);
        }
 
        [WorkItem(4811, "https://github.com/dotnet/roslyn/issues/4811")]
        [Fact]
        public void UsingStaticCurrentSubmission()
        {
            const string source = @"
using static Type;
 
class Type
{
    public static readonly int Field = 1;
}
";
 
            var sub = (Compilation)CreateSubmission(source);
            sub.VerifyDiagnostics();
 
            Assert.Equal(sub.ScriptClass.GetMember("Type"), GetSpeculativeSymbol(sub, "Field").ContainingType);
        }
 
        [WorkItem(4811, "https://github.com/dotnet/roslyn/issues/4811")]
        [Fact]
        public void UsingStaticPreviousSubmission()
        {
            var sub1 = CreateSubmission("class A { public static int AA; }");
            var sub2 = CreateSubmission("class B { public static int BB; }", previous: sub1);
            var sub3 = CreateSubmission("class C { public static int CC; }", previous: sub2);
 
            CreateSubmission("using static A;", previous: sub3).VerifyDiagnostics();
            CreateSubmission("using static B;", previous: sub3).VerifyDiagnostics();
 
            var sub4 = CreateSubmission("using static C;", previous: sub3);
            sub4.VerifyDiagnostics();
 
            var typeSymbol = ((Compilation)sub3).ScriptClass.GetMember("C");
 
            Assert.Equal(typeSymbol, GetSpeculativeSymbol(sub4, "CC").ContainingType);
        }
 
        [Fact]
        public void UsingStaticUnqualified()
        {
            const string source = @"
using static Path;
using System.IO;
";
            var expectedDiagnostics = new[]
            {
                // (2,14): error CS0246: The type or namespace name 'Path' could not be found (are you missing a using directive or an assembly reference?)
                // using static Path;
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Path").WithArguments("Path").WithLocation(2, 14)
            };
 
            CreateCompilation(source).GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Hidden).Verify(expectedDiagnostics);
            CreateSubmission(source).GetDiagnostics().Verify(expectedDiagnostics);
        }
 
        [Fact]
        public void UsingStaticUnqualified_GlobalUsing()
        {
            const string source = @"
using static Path;
";
            var expectedDiagnostics = new[]
            {
                // (2,14): error CS0246: The type or namespace name 'Path' could not be found (are you missing a using directive or an assembly reference?)
                // using static Path;
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Path").WithArguments("Path").WithLocation(2, 14)
            };
 
            var options = TestOptions.DebugDll.WithUsings("System");
 
            CreateCompilation(source, options: options).GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Hidden).Verify(expectedDiagnostics);
            CreateSubmission(source, options: options).GetDiagnostics().Verify(expectedDiagnostics);
        }
 
        [Fact]
        public void DuplicateUsing_SameSubmission()
        {
            CreateSubmission("using System; using System;").VerifyDiagnostics(
                // (1,21): warning CS0105: The using directive for 'System' appeared previously in this namespace
                // using System; using System;
                Diagnostic(ErrorCode.WRN_DuplicateUsing, "System").WithArguments("System").WithLocation(1, 21));
        }
 
        [Fact]
        public void DuplicateUsing_DifferentSubmissions()
        {
            CreateSubmission("using System;", previous: CreateSubmission("using System;")).VerifyDiagnostics();
        }
 
        [Fact]
        public void DuplicateGlobalUsing_SameSubmission()
        {
            CreateSubmission("typeof(String)", options: TestOptions.DebugDll.WithUsings("System", "System")).VerifyDiagnostics();
        }
 
        [Fact]
        public void DuplicateGlobalUsing_PreviousSubmission()
        {
            var options = TestOptions.DebugDll.WithUsings("System");
 
            var sub1 = CreateSubmission("typeof(String)", options: options);
            sub1.VerifyDiagnostics();
 
            var sub2 = CreateSubmission("typeof(String)", options: options, previous: sub1);
            sub2.VerifyDiagnostics();
        }
 
        [Fact]
        public void UsingsRebound()
        {
            const string libSourceTemplate = @"
namespace A
{{
    public class A{0} {{ }}
}}
 
namespace B
{{
    public class B{0} {{ }}
}}
";
 
            var lib1 = CreateCompilation(string.Format(libSourceTemplate, 1), assemblyName: "Lib1").EmitToImageReference();
            var lib2 = CreateCompilation(string.Format(libSourceTemplate, 2), assemblyName: "Lib2").EmitToImageReference();
 
            var options = TestOptions.DebugDll.WithUsings("B");
 
            var sub1 = CreateSubmission("using A; typeof(A1) == typeof(B1)", new[] { lib1 }, options);
            sub1.VerifyDiagnostics();
 
            var sub2 = CreateSubmission("typeof(A1) == typeof(B1) && typeof(A2) == typeof(B2)", new[] { lib1, lib2 }, options: options, previous: sub1);
            sub2.VerifyDiagnostics();
        }
 
        [WorkItem(5423, "https://github.com/dotnet/roslyn/issues/5423")]
        [Fact]
        public void UsingsFromLoadedScript()
        {
            const string scriptSource = @"
using static System.IO.Path;
using System.IO;
using F = System.IO.File;
 
class C { }
";
 
            const string submissionSource = @"
#load ""a.csx""
 
System.Type t;
 
GetTempPath(); // using static not exposed
t = typeof(File); // using not exposed
t = typeof(F); // using alias not exposed
 
t = typeof(C); // declaration exposed
";
 
            var resolver = TestSourceReferenceResolver.Create(new Dictionary<string, string>
            {
                { "a.csx", scriptSource }
            });
 
            var compilation = CreateSubmission(
                submissionSource,
                options: TestOptions.DebugDll.WithSourceReferenceResolver(resolver));
 
            compilation.VerifyDiagnostics(
                // (6,1): error CS0103: The name 'GetTempPath' does not exist in the current context
                // GetTempPath(); // using static not exposed
                Diagnostic(ErrorCode.ERR_NameNotInContext, "GetTempPath").WithArguments("GetTempPath").WithLocation(6, 1),
                // (7,12): error CS0246: The type or namespace name 'File' could not be found (are you missing a using directive or an assembly reference?)
                // t = typeof(File); // using not exposed
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "File").WithArguments("File").WithLocation(7, 12),
                // (8,12): error CS0246: The type or namespace name 'F' could not be found (are you missing a using directive or an assembly reference?)
                // t = typeof(F); // using alias not exposed
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "F").WithArguments("F").WithLocation(8, 12));
        }
 
        [WorkItem(5423, "https://github.com/dotnet/roslyn/issues/5423")]
        [Fact]
        public void UsingsToLoadedScript()
        {
            const string scriptSource = @"
using System.Collections.Generic;
using AL = System.Collections.ArrayList;
using static System.Math;
 
class D { }
 
System.Type t;
 
// Previous submission
GetCommandLineArgs(); // using static not exposed
t = typeof(StringBuilder); // using not exposed
t = typeof(P); // using alias not exposed
t = typeof(B); // declaration exposed
 
// Current submission
GetTempPath(); // using static not exposed
t = typeof(File); // using not exposed
t = typeof(F); // using alias not exposed
t = typeof(C); // declaration exposed
 
// Current file - all available
Sin(1);
t = typeof(List<int>);
t = typeof(AL);
t = typeof(D);
";
 
            const string previousSubmissionSource = @"
using static System.Environment;
using System.Text;
using P = System.IO.Path;
 
class B { }
";
 
            const string submissionSource = @"
#load ""a.csx""
 
using static System.IO.Path;
using System.IO;
using F = System.IO.File;
 
class C { }
";
 
            var resolver = TestSourceReferenceResolver.Create(new Dictionary<string, string>
            {
                { "a.csx", scriptSource }
            });
 
            var compilation = CreateSubmission(
                submissionSource,
                options: TestOptions.DebugDll.WithSourceReferenceResolver(resolver),
                previous: CreateSubmission(previousSubmissionSource));
 
            compilation.VerifyDiagnostics(
                // Previous submission
 
                // a.csx(11,1): error CS0103: The name 'GetCommandLineArgs' does not exist in the current context
                // GetCommandLineArgs(); // using static not exposed
                Diagnostic(ErrorCode.ERR_NameNotInContext, "GetCommandLineArgs").WithArguments("GetCommandLineArgs").WithLocation(11, 1),
                // a.csx(12,12): error CS0246: The type or namespace name 'StringBuilder' could not be found (are you missing a using directive or an assembly reference?)
                // t = typeof(StringBuilder); // using not exposed
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "StringBuilder").WithArguments("StringBuilder").WithLocation(12, 12),
                // a.csx(13,12): error CS0246: The type or namespace name 'P' could not be found (are you missing a using directive or an assembly reference?)
                // t = typeof(P); // using alias not exposed
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "P").WithArguments("P").WithLocation(13, 12),
 
                // Current submission
 
                // a.csx(17,1): error CS0103: The name 'GetTempPath' does not exist in the current context
                // GetTempPath(); // using static not exposed
                Diagnostic(ErrorCode.ERR_NameNotInContext, "GetTempPath").WithArguments("GetTempPath").WithLocation(17, 1),
                // a.csx(18,12): error CS0246: The type or namespace name 'File' could not be found (are you missing a using directive or an assembly reference?)
                // t = typeof(File); // using not exposed
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "File").WithArguments("File").WithLocation(18, 12),
                // a.csx(19,12): error CS0246: The type or namespace name 'F' could not be found (are you missing a using directive or an assembly reference?)
                // t = typeof(F); // using alias not exposed
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "F").WithArguments("F").WithLocation(19, 12));
        }
 
        [Fact]
        public void GlobalUsingsToLoadedScript()
        {
            const string scriptSource = @"
System.Type t;
 
GetTempPath(); // global using static exposed
t = typeof(File); // global using exposed
";
 
            const string submissionSource = @"
#load ""a.csx""
";
 
            var resolver = TestSourceReferenceResolver.Create(new Dictionary<string, string>
            {
                { "a.csx", scriptSource }
            });
 
            var compilation = CreateSubmission(
                submissionSource,
                options: TestOptions.DebugDll.WithSourceReferenceResolver(resolver).WithUsings("System.IO", "System.IO.Path"));
 
            compilation.VerifyDiagnostics();
        }
 
        [WorkItem(4811, "https://github.com/dotnet/roslyn/issues/4811")]
        [ConditionalFact(typeof(NoUsedAssembliesValidation))]
        public void ConsumePreviousSubmissionUsings_Valid()
        {
            const string libSource = @"
namespace NOuter
{
    public class Test { }
 
    namespace NInner
    {
        public static class COuter
        {
            public static void M() { }
 
            public static class CInner
            {
                public static void N() { }
            }
        }
    }
}
";
 
            var lib = CreateCompilation(libSource).EmitToImageReference();
            var refs = new[] { lib };
 
            var submissions = new[]
            {
                "using NOuter;",
                "typeof(Test)",
                "using NI = NOuter.NInner;",
                "typeof(NI.COuter)",
                "using static NI.COuter;",
                "M()",
                "using static NI.COuter.CInner;",
                "N()",
            };
 
            CSharpCompilation prev = null;
            foreach (var submission in submissions)
            {
                var curr = CreateSubmission(submission, refs, previous: prev);
                curr.VerifyDiagnostics();
                prev = curr;
            }
        }
 
        [ConditionalFact(typeof(NoUsedAssembliesValidation))]
        public void ConsumePreviousSubmissionUsings_Invalid()
        {
            const string libSource = @"
namespace NOuter
{
    public class COuter { }
 
    namespace NInner
    {
        public static class CInner
        {
        }
    }
}
";
 
            var lib = CreateCompilation(libSource).EmitToImageReference();
            var refs = new[] { lib };
 
            CreateSubmission("using NInner;", refs, previous: CreateSubmission("using NOuter;", refs)).VerifyDiagnostics(
                // (1,7): error CS0246: The type or namespace name 'NInner' could not be found (are you missing a using directive or an assembly reference?)
                // using NInner;
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "NInner").WithArguments("NInner").WithLocation(1, 7));
 
            CreateSubmission("using NI = NInner;", refs, previous: CreateSubmission("using NOuter;", refs)).VerifyDiagnostics(
                // (1,12): error CS0246: The type or namespace name 'NInner' could not be found (are you missing a using directive or an assembly reference?)
                // using NI = NInner;
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "NInner").WithArguments("NInner").WithLocation(1, 12));
 
            CreateSubmission("using static COuter;", refs, previous: CreateSubmission("using NOuter;", refs)).VerifyDiagnostics(
                // (1,14): error CS0246: The type or namespace name 'COuter' could not be found (are you missing a using directive or an assembly reference?)
                // using static COuter;
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "COuter").WithArguments("COuter").WithLocation(1, 14));
 
            CreateSubmission("using static NInner.CInner;", refs, previous: CreateSubmission("using NOuter;", refs)).VerifyDiagnostics(
                // (1,14): error CS0246: The type or namespace name 'NInner' could not be found (are you missing a using directive or an assembly reference?)
                // using static NInner.CInner;
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "NInner").WithArguments("NInner").WithLocation(1, 14));
        }
 
        private static ISymbol GetSpeculativeSymbol(Compilation comp, string name)
        {
            var tree = comp.SyntaxTrees.Single();
            var model = comp.GetSemanticModel(tree);
            return model.GetSpeculativeSymbolInfo(
                tree.Length,
                SyntaxFactory.IdentifierName(name),
                SpeculativeBindingOption.BindAsExpression).Symbol;
        }
 
        private static ITypeSymbol GetSpeculativeType(Compilation comp, string name)
        {
            var tree = comp.SyntaxTrees.Single();
            var model = comp.GetSemanticModel(tree);
            return model.GetSpeculativeTypeInfo(
                tree.Length,
                SyntaxFactory.IdentifierName(name),
                SpeculativeBindingOption.BindAsTypeOrNamespace).Type;
        }
    }
}