File: Symbols\Source\ExternAliasTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Symbol\Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Symbol.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.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class ExternAliasTests : CSharpTestBase
    {
        private static MetadataReference s_goo1;
        public static MetadataReference Goo1
        {
            get
            {
                if (s_goo1 == null)
                {
                    var src =
        @"
namespace NS
{
    public class Goo
    {
      public int M() { return 1; }
    }
}
";
                    var comp = CreateCompilation(src, assemblyName: "Goo1", options: TestOptions.ReleaseDll);
                    s_goo1 = comp.EmitToImageReference(aliases: ImmutableArray.Create("Bar"));
                }
 
                return s_goo1;
            }
        }
 
        private static MetadataReference s_goo2;
        public static MetadataReference Goo2
        {
            get
            {
                if (s_goo2 == null)
                {
                    var src =
        @"
namespace NS
{
    public class Goo2
    {
      public int M() { return 2; }
    }
}
";
                    var comp = CreateCompilation(src, assemblyName: "Goo2", options: TestOptions.ReleaseDll);
                    s_goo2 = comp.EmitToImageReference(aliases: ImmutableArray.Create("Bar"));
                }
 
                return s_goo2;
            }
        }
 
        [Fact]
        public void ExternCanBeUsedByUsingAlias()
        {
            var src =
@"
extern alias Bar;
using MClass=Bar::NS.Goo2;
 
class Maine
{
    public static void Main()
    {
        MClass c = new MClass();
        Bar::NS.Goo d = new Bar::NS.Goo();
    }
}
";
            var comp = CreateCompilation(src);
            comp = comp.AddReferences(Goo1, Goo2);
            comp.GetDiagnostics().Verify();
        }
 
        [Fact]
        public void ExternAliasInScript()
        {
            var src =
@"
extern alias Bar;
Bar::NS.Goo d = new Bar::NS.Goo();
";
            var comp = CreateCompilationWithMscorlib461(src, options: TestOptions.DebugExe, parseOptions: TestOptions.Script);
            comp = comp.AddReferences(Goo1, Goo2);
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void ExternAliasInInteractive_Error()
        {
            var src = "extern alias Bar;";
 
            var comp = CSharpCompilation.CreateScriptCompilation(
                GetUniqueName(),
                syntaxTree: SyntaxFactory.ParseSyntaxTree(src, options: TestOptions.Script),
                references: new MetadataReference[] { MscorlibRef, ExternAliasTests.Goo1, ExternAliasTests.Goo2 });
 
            comp.VerifyDiagnostics(
                // (1,1): error CS7015: 'extern alias' is not valid in this context
                // extern alias Bar;
                Diagnostic(ErrorCode.ERR_ExternAliasNotAllowed, "extern alias Bar;"));
        }
 
        [Fact]
        public void ExternInNamespace()
        {
            var src =
            @"
namespace NS
{
    extern alias Bar;
 
    class C
    {
        public static void Main()
        {
            Bar::NS.Goo d = new Bar::NS.Goo();
        }
    }
}
";
            var comp = CreateCompilation(src);
            comp = comp.AddReferences(Goo1, Goo2);
            comp.GetDiagnostics().Verify();
        }
 
        [Fact]
        public void Error_DuplicateAliases()
        {
            var src =
            @"
extern alias Bar;
using Bar = System.Console;
 
class Maine
{
    public static void Main()
    {
    }
}
";
            var comp = CreateCompilation(src);
            comp = comp.AddReferences(Goo1, Goo2);
            comp.VerifyDiagnostics(
                // (3,1): error CS1537: The using alias 'Bar' appeared previously in this namespace
                // using Bar = System.Console;
                Diagnostic(ErrorCode.ERR_DuplicateAlias, "using Bar = System.Console;").WithArguments("Bar"),
                // (3,1): info CS8019: Unnecessary using directive.
                // using Bar = System.Console;
                Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using Bar = System.Console;"),
                // (2,1): info CS8020: Unused extern alias.
                // extern alias Bar;
                Diagnostic(ErrorCode.HDN_UnusedExternAlias, "extern alias Bar;"));
        }
 
        [Fact]
        public void Error_BadExternAlias()
        {
            var src =
            @"
extern alias bar;
 
class Maine
{
    public static void Main()
    {
    }
}
";
            var comp = CreateCompilation(src);
            comp = comp.AddReferences(Goo1);
            comp.VerifyDiagnostics(
                // (2,14): error CS0430: The extern alias 'bar' was not specified in a /reference option
                // extern alias bar;
                Diagnostic(ErrorCode.ERR_BadExternAlias, "bar").WithArguments("bar"),
                // (2,1): info CS8020: Unused extern alias.
                // extern alias bar;
                Diagnostic(ErrorCode.HDN_UnusedExternAlias, "extern alias bar;"));
        }
 
        [Fact]
        public void ExternAliasDoesntFailNonSourceBinds()
        {
            // Ensure that adding an alias doesn't interfere with resolution among metadata references. The alias only affects source usage
            // of the types defined in the aliased assembly.
 
            var src =
        @"
namespace NS
{
    public class Baz
    {
      public int M() { return 1; }
    }
}
";
            var comp = CreateCompilation(src, assemblyName: "Baz.dll", options: TestOptions.ReleaseDll);
            var outputMetadata = AssemblyMetadata.CreateFromImage(comp.EmitToArray());
            var goo1 = outputMetadata.GetReference();
            var goo1Alias = outputMetadata.GetReference(aliases: ImmutableArray.Create("Baz"));
 
            src =
        @"
namespace NS
{
    public class Bar : Baz
    {
      public int M2() { return 2; }
    }
}
";
            comp = CreateCompilation(src, assemblyName: "Bar.dll", options: TestOptions.ReleaseDll);
            comp = comp.AddReferences(goo1);
            var goo2 = MetadataReference.CreateFromImage(comp.EmitToArray());
 
            src =
            @"
class Maine
{
    public static void Main()
    {
            NS.Bar d = null;
    }
}
";
            comp = CreateCompilation(src);
            comp = comp.AddReferences(goo2, goo1Alias);
            comp.VerifyDiagnostics(
                // (6,20): warning CS0219: The variable 'd' is assigned but its value is never used
                //             NS.Bar d = null;
                Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "d").WithArguments("d")
            );
        }
 
        [Fact]
        public void Error_DontLookInExternAliasWithoutQualifier()
        {
            var src =
            @"
class Maine
{
    public static void Main()
    {
            NS.Goo d = null;    //shouldn't be able to see this type w/o qualification. it is in an extern alias.
    }
}
";
            var comp = CreateCompilation(src);
            comp = comp.AddReferences(Goo1);
            comp.VerifyDiagnostics(
                // (6,13): error CS0246: The type or namespace name 'NS' could not be found (are you missing a using directive or an assembly reference?)
                //             NS.Goo d = null;    //shouldn't be able to see this type w/o qualification. it is in an extern alias.
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "NS").WithArguments("NS")
                );
        }
 
        [Fact]
        public void Error_SameExternTwice()
        {
            var src =
            @"
extern alias Bar;
extern alias Bar;
 
class Maine
{
    public static void Main()
    {
    }
}
";
            var comp = CreateCompilation(src);
            comp = comp.AddReferences(Goo1);
            comp.VerifyDiagnostics(
                // (2,14): error CS1537: The using alias 'Bar' appeared previously in this namespace
                // extern alias Bar;
                Diagnostic(ErrorCode.ERR_DuplicateAlias, "Bar").WithArguments("Bar"),
                // (2,1): info CS8020: Unused extern alias.
                // extern alias Bar;
                Diagnostic(ErrorCode.HDN_UnusedExternAlias, "extern alias Bar;"),
                // (3,1): info CS8020: Unused extern alias.
                // extern alias Bar;
                Diagnostic(ErrorCode.HDN_UnusedExternAlias, "extern alias Bar;"));
        }
 
        [Fact]
        public void Error_ExternAliasIdentifierIsGlobalKeyword()
        {
            var src =
        @"
namespace NS
{
    public class Baz
    {
      public int M() { return 1; }
    }
}
";
            var comp = CreateCompilation(src, options: TestOptions.ReleaseDll);
            var goo1Alias = comp.EmitToImageReference(aliases: ImmutableArray.Create("global"));
 
            src =
            @"
extern alias global;
 
class Maine
{
    public static void Main()
    {
    }
}
";
            comp = CreateCompilation(src);
            comp = comp.AddReferences(goo1Alias);
            comp.VerifyDiagnostics(
                // (2,14): error CS1681: You cannot redefine the global extern alias
                // extern alias global;
                Diagnostic(ErrorCode.ERR_GlobalExternAlias, "global"),
                // (2,1): info CS8020: Unused extern alias.
                // extern alias global;
                Diagnostic(ErrorCode.HDN_UnusedExternAlias, "extern alias global;"));
        }
 
        [Fact]
        public void GetAliasInfo()
        {
            var text =
@"extern alias Bar;
 
class A : Bar::NS.Goo {}
";
            var tree = Parse(text);
            var root = tree.GetCompilationUnitRoot() as CompilationUnitSyntax;
            var comp = CreateCompilation(tree);
            comp = comp.AddReferences(Goo1);
 
            var model = comp.GetSemanticModel(tree);
 
            //find the alias qualifier on the base type and get its semantic info.
            var a1 = root.Members[0] as TypeDeclarationSyntax;
            var base1 = a1.BaseList.Types[0].Type as QualifiedNameSyntax;
            var left = base1.Left as AliasQualifiedNameSyntax;
            var qualifier = left.Alias;
 
            var alias1 = model.GetAliasInfo(qualifier);
 
            Assert.NotNull(alias1);
            Assert.Equal(SymbolKind.Alias, alias1.Kind);
        }
 
        [WorkItem(546729, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546729")]
        [Fact]
        public void Crash16681()
        {
            var text =
@"namespace X.Y
{
    extern alias Bar;
    class Program
    {
        public static void Main(string[] args)
        {
            System.Console.WriteLine(12);
        }
    }
}";
            var comp = CreateCompilation(text).AddReferences(Goo1);
            comp.VerifyDiagnostics(
                // (3,5): info CS8020: Unused extern alias.
                //     extern alias Bar;
                Diagnostic(ErrorCode.HDN_UnusedExternAlias, "extern alias Bar;"));
        }
 
        [WorkItem(529751, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529751")]
        [Fact]
        public void SameExternAliasInMultipleTreesValid()
        {
            var comp1 = CreateCompilation("public class C { }", assemblyName: "A1");
            var ref1 = comp1.EmitToImageReference(aliases: ImmutableArray.Create("X"));
 
            var comp2 = CreateCompilation("public class D { }", assemblyName: "A2");
            var ref2 = comp2.EmitToImageReference(aliases: ImmutableArray.Create("X"));
 
            const int numFiles = 20;
            var comp3 = CreateCompilation(Enumerable.Range(1, numFiles).Select(x => "extern alias X;").ToArray(), new[] { ref1, ref2 }, assemblyName: "A3.dll");
 
            var targets = comp3.SyntaxTrees.AsParallel().Select(tree =>
            {
                var model = comp3.GetSemanticModel(tree);
 
                var aliasSyntax = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ExternAliasDirectiveSyntax>().Single();
 
                var aliasSymbol = model.GetDeclaredSymbol(aliasSyntax);
                return (INamespaceSymbol)aliasSymbol.Target;
            }).ToArray(); //force evaluation
 
            var firstTarget = targets.First();
            Assert.NotNull(firstTarget);
            Assert.IsType<MergedNamespaceSymbol>(firstTarget.GetSymbol());
            firstTarget.GetMember<INamedTypeSymbol>("C");
            firstTarget.GetMember<INamedTypeSymbol>("D");
 
            Assert.True(targets.All(target => ReferenceEquals(firstTarget, target)));
        }
 
        [WorkItem(529751, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529751")]
        [Fact]
        public void SameExternAliasInMultipleTreesInvalid()
        {
            const int numFiles = 20;
            var comp3 = CreateCompilation(Enumerable.Range(1, numFiles).Select(x => "extern alias X;").ToArray(), assemblyName: "A3.dll");
 
            var targets = comp3.SyntaxTrees.AsParallel().Select(tree =>
            {
                var model = comp3.GetSemanticModel(tree);
 
                var aliasSyntax = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ExternAliasDirectiveSyntax>().Single();
 
                var aliasSymbol = model.GetDeclaredSymbol(aliasSyntax);
                return (INamespaceSymbol)aliasSymbol.Target;
            }).ToArray(); //force evaluation
 
            var firstTarget = targets.First();
            Assert.NotNull(firstTarget);
            Assert.IsType<MissingNamespaceSymbol>(firstTarget.GetSymbol());
            Assert.Empty(firstTarget.GetMembers());
 
            Assert.True(targets.All(target => ReferenceEquals(firstTarget, target)));
        }
 
        [WorkItem(875899, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/875899")]
        [Fact]
        public void SymbolInfoForExternAliasInAliasTarget()
        {
            var libSource = @"
namespace N
{
    public class C { }
}
";
 
            var source = @"
extern alias A;
 
using C = A::N.C;
 
class Test
{
    static void Main()
    {
        C c = new C();
    }
}";
            var libRef = new CSharpCompilationReference(CreateCompilation(libSource, assemblyName: "lib"), aliases: ImmutableArray.Create("A"));
            var comp = (Compilation)CreateCompilation(source, new[] { libRef });
            comp.VerifyDiagnostics();
 
            var tree = comp.SyntaxTrees.Single();
            var model = comp.GetSemanticModel(tree);
 
            var root = tree.GetRoot();
            var externAliasSyntax = root.DescendantNodes().OfType<ExternAliasDirectiveSyntax>().Single();
            var usingSyntax = root.DescendantNodes().OfType<UsingDirectiveSyntax>().Single();
            var usingTargetSyntax = (QualifiedNameSyntax)usingSyntax.Name;
            var aliasQualifiedNameSyntax = (AliasQualifiedNameSyntax)usingTargetSyntax.Left;
 
            var aliasedGlobalNamespace = ((IAssemblySymbol)comp.GetAssemblyOrModuleSymbol(libRef)).GlobalNamespace;
            var namespaceN = aliasedGlobalNamespace.GetMember<INamespaceSymbol>("N");
            var typeC = namespaceN.GetMember<INamedTypeSymbol>("C");
 
            var externAliasSymbol = model.GetDeclaredSymbol(externAliasSyntax);
            Assert.Equal("A", externAliasSymbol.Name);
            Assert.Equal(aliasedGlobalNamespace, externAliasSymbol.Target);
            Assert.Equal(1, externAliasSymbol.DeclaringSyntaxReferences.Length);
            Assert.Same(externAliasSyntax, externAliasSymbol.DeclaringSyntaxReferences.Single().GetSyntax());
 
            var usingAliasSymbol = model.GetDeclaredSymbol(usingSyntax);
            Assert.Equal("C", usingAliasSymbol.Name);
            Assert.Equal(typeC, usingAliasSymbol.Target);
 
            var qualifiedNameInfo = model.GetSymbolInfo(usingTargetSyntax);
            Assert.Equal(typeC, qualifiedNameInfo.Symbol);
 
            var aliasQualifiedNameInfo = model.GetSymbolInfo(aliasQualifiedNameSyntax);
            Assert.Equal(typeC.ContainingNamespace, aliasQualifiedNameInfo.Symbol);
 
            var aliasNameInfo = model.GetSymbolInfo(aliasQualifiedNameSyntax.Alias);
            Assert.Equal(aliasedGlobalNamespace, aliasNameInfo.Symbol);
        }
    }
}