diff --git a/src/Compilers/CSharp/Portable/Binder/InteractiveUsingsBinder.cs b/src/Compilers/CSharp/Portable/Binder/InteractiveUsingsBinder.cs index 568f037116556e40a17ada9ba3195848dbe2ecab..341db17ea0e626eab4d053dad8041b9a3672b867 100644 --- a/src/Compilers/CSharp/Portable/Binder/InteractiveUsingsBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/InteractiveUsingsBinder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; diff --git a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj index 1c6080228a0a80a42b070d032550012fccf1c21c..739f9df02e6ad5afb668b47061e95a6f84c9d66b 100644 --- a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj +++ b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj @@ -112,6 +112,7 @@ + @@ -143,4 +144,4 @@ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InteractiveUsingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InteractiveUsingTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..1bc00b93d145cd796ae5423f418794b57e7aaa8f --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InteractiveUsingTests.cs @@ -0,0 +1,447 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +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;"); + sub.VerifyDiagnostics(); + + Assert.Equal(SpecialType.System_Int32, GetSpeculativeType(sub, "I").SpecialType); + } + + [Fact] + public void UsingStatic() + { + var sub = CreateSubmission("using static System.Environment;"); + sub.VerifyDiagnostics(); + + Assert.Equal(SymbolKind.Property, GetSpeculativeSymbol(sub, "NewLine").Kind); + } + + [WorkItem(5450, "https://github.com/dotnet/roslyn/issues/5450")] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/5450")] + 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(Skip = "https://github.com/dotnet/roslyn/issues/4811")] + public void AliasCurrentSubmission() + { + const string source = @" +using T = Type; + +class Type { } +"; + + var sub = 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().Single(); + + var aliasSymbol = model.GetDeclaredSymbol(syntax); + Assert.Equal(SymbolKind.Alias, aliasSymbol.Kind); + Assert.Equal(typeSymbol, ((AliasSymbol)aliasSymbol).Target); + + Assert.Equal(typeSymbol, model.GetSymbolInfo(syntax.Name).Symbol); + + Assert.Equal(typeSymbol, GetSpeculativeType(sub, "Type")); + } + + [WorkItem(4811, "https://github.com/dotnet/roslyn/issues/4811")] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/4811")] + 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;", previous: sub3); + sub4.VerifyDiagnostics(); + + var typeSymbol = sub3.ScriptClass.GetMember("C"); + + var tree = sub4.SyntaxTrees.Single(); + var model = sub4.GetSemanticModel(tree); + var syntax = tree.GetRoot().DescendantNodes().OfType().Single(); + + var aliasSymbol = model.GetDeclaredSymbol(syntax); + Assert.Equal(SymbolKind.Alias, aliasSymbol.Kind); + Assert.Equal(typeSymbol, ((AliasSymbol)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) + }; + + CreateCompilationWithMscorlib(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"); + + CreateCompilationWithMscorlib(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) + }; + + CreateCompilationWithMscorlib(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;"); + Assert.Equal(SpecialType.System_Int32, GetSpeculativeType(sub1, "A").SpecialType); + + var sub2 = CreateSubmission("using A = System.Int16;", previous: sub1); + Assert.Equal(SpecialType.System_Int16, GetSpeculativeType(sub2, "A").SpecialType); + + var sub3 = CreateSubmission("class A { }", previous: sub2); + Assert.Equal(sub3.ScriptClass, GetSpeculativeType(sub3, "A").ContainingType); + + var sub4 = CreateSubmission("using A = System.Int64;", previous: sub3); + Assert.Equal(SpecialType.System_Int64, GetSpeculativeType(sub4, "A").SpecialType); + } + + [WorkItem(4811, "https://github.com/dotnet/roslyn/issues/4811")] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/4811")] + public void UsingStaticCurrentSubmission() + { + const string source = @" +using static Type; + +class Type +{ + public static readonly int Field = 1; +} +"; + + var sub = CreateSubmission(source); + sub.VerifyDiagnostics(); + + Assert.Equal(sub.ScriptClass.GetMember("Type"), GetSpeculativeSymbol(sub, "Field").ContainingType); + } + + [WorkItem(4811, "https://github.com/dotnet/roslyn/issues/4811")] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/4811")] + 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 = 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) + }; + + CreateCompilationWithMscorlib(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"); + + CreateCompilationWithMscorlib(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 UsingsRebound() + { + const string libSourceTemplate = @" +namespace A +{{ + public class A{0} {{ }} +}} + +namespace B +{{ + public class B{0} {{ }} +}} +"; + + var lib1 = CreateCompilationWithMscorlib(string.Format(libSourceTemplate, 1), assemblyName: "Lib1").EmitToImageReference(); + var lib2 = CreateCompilationWithMscorlib(string.Format(libSourceTemplate, 2), assemblyName: "Lib2").EmitToImageReference(); + + var options = TestOptions.DebugDll.WithUsings("B"); + + var sub1 = CreateSubmission("using A;", new[] { lib1 }, options); + sub1.VerifyDiagnostics(); + + var sub2 = CreateSubmission("typeof(A1) == typeof(A2) && typeof(B1) == typeof(B2)", new[] { lib1, lib2 }, options: options, previous: sub1); + sub2.VerifyDiagnostics(); + } + + [WorkItem(5423, "https://github.com/dotnet/roslyn/issues/5423")] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/5423")] + 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 + { + { "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(Skip = "https://github.com/dotnet/roslyn/issues/5423")] + void UsingsToLoadedScript() + { + const string scriptSource = @" +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 +"; + + 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 + { + { "a.csx", scriptSource } + }); + + var compilation = CreateSubmission( + submissionSource, + options: TestOptions.DebugDll.WithSourceReferenceResolver(resolver)); + + compilation.VerifyDiagnostics( + // a.csx(4,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(4, 1), + // a.csx(5,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(5, 12), + // a.csx(6,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(6, 12)); + } + + [Fact] + 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 + { + { "a.csx", scriptSource } + }); + + var compilation = CreateSubmission( + submissionSource, + options: TestOptions.DebugDll.WithSourceReferenceResolver(resolver).WithUsings("System.IO", "System.IO.Path")); + + compilation.VerifyDiagnostics(); + } + + private static Symbol GetSpeculativeSymbol(CSharpCompilation comp, string name) + { + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + return (Symbol)model.GetSpeculativeSymbolInfo( + tree.Length, + SyntaxFactory.IdentifierName(name), + SpeculativeBindingOption.BindAsExpression).Symbol; + } + + private static TypeSymbol GetSpeculativeType(CSharpCompilation comp, string name) + { + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + return (TypeSymbol)model.GetSpeculativeTypeInfo( + tree.Length, + SyntaxFactory.IdentifierName(name), + SpeculativeBindingOption.BindAsTypeOrNamespace).Type; + } + } +} \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/LoadDirectiveTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/LoadDirectiveTests.cs index 91b2432ba5766404e61bdf970fea9102e8a1a602..fd1601ae64048d577003ec5e3aa2801025d39e1c 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/LoadDirectiveTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/LoadDirectiveTests.cs @@ -1,14 +1,9 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Xunit; +using Roslyn.Test.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { @@ -91,56 +86,12 @@ private static SourceReferenceResolver CreateResolver(params KeyValuePair Script(string path, string source) { return new KeyValuePair(path, source); } - - private class TestSourceReferenceResolver : SourceReferenceResolver - { - private readonly IDictionary _sources; - - public static TestSourceReferenceResolver Default { get; } = new TestSourceReferenceResolver(); - - public TestSourceReferenceResolver(IDictionary sources = null) - { - _sources = sources; - } - - public override string NormalizePath(string path, string baseFilePath) - { - return path; - } - - public override string ResolveReference(string path, string baseFilePath) - { - return ((_sources != null) && _sources.ContainsKey(path)) ? path : null; - } - - public override Stream OpenRead(string resolvedPath) - { - if (_sources != null) - { - return new MemoryStream(Encoding.UTF8.GetBytes(_sources[resolvedPath])); - } - else - { - throw new IOException(); - } - } - - public override bool Equals(object other) - { - return this.Equals(other); - } - - public override int GetHashCode() - { - return this.GetHashCode(); - } - } } } diff --git a/src/Test/Utilities/Desktop/TestSourceReferenceResolver.cs b/src/Test/Utilities/Desktop/TestSourceReferenceResolver.cs new file mode 100644 index 0000000000000000000000000000000000000000..2df85330249dd3b3d2ae89e153c0d13ed5aa5ca7 --- /dev/null +++ b/src/Test/Utilities/Desktop/TestSourceReferenceResolver.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using Microsoft.CodeAnalysis; + +namespace Roslyn.Test.Utilities +{ + public sealed class TestSourceReferenceResolver : SourceReferenceResolver + { + public static readonly SourceReferenceResolver Default = new TestSourceReferenceResolver(sources: null); + + public static SourceReferenceResolver Create(Dictionary sources = null) + { + return (sources == null || sources.Count == 0) ? Default : new TestSourceReferenceResolver(sources); + } + + private readonly Dictionary _sources; + + private TestSourceReferenceResolver(Dictionary sources) + { + _sources = sources; + } + + public override string NormalizePath(string path, string baseFilePath) => path; + + public override string ResolveReference(string path, string baseFilePath) => + _sources?.ContainsKey(path) == true ? path : null; + + public override Stream OpenRead(string resolvedPath) + { + if (_sources != null && resolvedPath != null) + { + return new MemoryStream(Encoding.UTF8.GetBytes(_sources[resolvedPath])); + } + else + { + throw new IOException(); + } + } + + public override bool Equals(object other) => ReferenceEquals(this, other); + + public override int GetHashCode() => RuntimeHelpers.GetHashCode(this); + } +} diff --git a/src/Test/Utilities/Desktop/TestUtilities.Desktop.csproj b/src/Test/Utilities/Desktop/TestUtilities.Desktop.csproj index 75073b8ecd1073fa63c3abbfb1863b0e177ad534..00be27a0b46171b82b4b4ad7f31ae94cbd23845e 100644 --- a/src/Test/Utilities/Desktop/TestUtilities.Desktop.csproj +++ b/src/Test/Utilities/Desktop/TestUtilities.Desktop.csproj @@ -117,6 +117,7 @@ True TestResource.resx +