|
// 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.Collections.Immutable;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.CSharp.UnitTests;
using Microsoft.CodeAnalysis.ExpressionEvaluator;
using Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.DiaSymReader;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.UnitTests
{
using System;
using Debugging;
using static MethodDebugInfoValidation;
public class UsingDebugInfoTests : ExpressionCompilerTestBase
{
#region Grouped import strings
[Fact]
public void SimplestCase()
{
var source = @"
using System;
class C
{
void M()
{
}
}
";
var comp = CreateCompilation(source);
WithRuntimeInstance(comp, runtime =>
{
GetMethodDebugInfo(runtime, "C.M").ImportRecordGroups.Verify(@"
{
Namespace: string='System'
}");
});
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/21386")]
public void Gaps()
{
var source = @"
using System;
namespace N1
{
namespace N2
{
using System.Collections;
namespace N3
{
class C { void M() { } }
}
}
}
";
var comp = CreateCompilation(source);
WithRuntimeInstance(comp, runtime =>
{
GetMethodDebugInfo(runtime, "N1.N2.N3.C.M").ImportRecordGroups.Verify(@"
{
}
{
Namespace: string='System.Collections'
}
{
}
{
Namespace: string='System'
}");
});
}
[Fact]
public void NestedScopes()
{
var source = @"
using System;
class C
{
void M()
{
int i = 1;
{
int j = 2;
}
}
}
";
var comp = CreateCompilation(source, options: TestOptions.DebugDll);
CompileAndVerify(comp).VerifyIL("C.M", @"
{
// Code size 8 (0x8)
.maxstack 1
.locals init (int V_0, //i
int V_1) //j
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: stloc.0
IL_0003: nop
IL_0004: ldc.i4.2
IL_0005: stloc.1
IL_0006: nop
IL_0007: ret
}
");
WithRuntimeInstance(comp, runtime =>
{
GetMethodDebugInfo(runtime, "C.M", ilOffset: 0x0004).ImportRecordGroups.Verify(@"
{
Namespace: string='System'
}");
});
}
[Fact]
public void NestedNamespaces()
{
var source = @"
using System;
namespace A
{
using System.IO;
using System.Text;
class C
{
void M()
{
}
}
}
";
var comp = CreateCompilation(source);
WithRuntimeInstance(comp, runtime =>
{
GetMethodDebugInfo(runtime, "A.C.M").ImportRecordGroups.Verify(@"
{
Namespace: string='System.IO'
Namespace: string='System.Text'
}
{
Namespace: string='System'
}");
});
}
[Fact]
public void Forward()
{
var source = @"
using System;
namespace A
{
using System.IO;
using System.Text;
class C
{
// One of these methods will forward to the other since they're adjacent.
void M1() { }
void M2() { }
}
}
";
var comp = CreateCompilation(source);
WithRuntimeInstance(comp, runtime =>
{
GetMethodDebugInfo(runtime, "A.C.M1").ImportRecordGroups.Verify(@"
{
Namespace: string='System.IO'
Namespace: string='System.Text'
}
{
Namespace: string='System'
}");
GetMethodDebugInfo(runtime, "A.C.M2").ImportRecordGroups.Verify(@"
{
Namespace: string='System.IO'
Namespace: string='System.Text'
}
{
Namespace: string='System'
}");
});
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/30030")]
public void ImportKinds()
{
var source = @"
extern alias A;
using S = System;
namespace B
{
using F = S.IO.File;
using System.Text;
class C
{
void M()
{
}
}
}
";
var parseOptions = TestOptions.Regular.WithNoRefSafetyRulesAttribute();
var aliasedRef = CreateEmptyCompilation("", assemblyName: "Lib", parseOptions: parseOptions).EmitToImageReference(aliases: ImmutableArray.Create("A"));
var comp = CreateCompilation(source, new[] { aliasedRef }, parseOptions: parseOptions);
WithRuntimeInstance(comp, runtime =>
{
var info = GetMethodDebugInfo(runtime, "B.C.M");
info.ImportRecordGroups.Verify(@"
{
Namespace: string='System.Text'
Type: alias='F' type='System.IO.File'
}
{
Assembly: alias='A'
Namespace: alias='S' string='System'
}");
info.ExternAliasRecords.Verify(
"A = 'Lib, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'");
});
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1084059")]
public void ImportKinds_StaticType()
{
var libSource = @"
namespace N
{
public static class Static
{
}
}
";
var source = @"
extern alias A;
using static System.Math;
namespace B
{
using static A::N.Static;
class C
{
void M()
{
}
}
}
";
var parseOptions = TestOptions.Regular.WithNoRefSafetyRulesAttribute();
var aliasedRef = CreateCompilation(libSource, assemblyName: "Lib", parseOptions: parseOptions).EmitToImageReference(aliases: ImmutableArray.Create("A"));
var comp = CreateCompilation(source, new[] { aliasedRef }, parseOptions: parseOptions);
WithRuntimeInstance(comp, runtime =>
{
var info = GetMethodDebugInfo(runtime, "B.C.M");
info.ImportRecordGroups.Verify(@"
{
Type: type='N.Static'
}
{
Assembly: alias='A'
Type: type='System.Math'
}");
info.ExternAliasRecords.Verify(
"A = 'Lib, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'");
});
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/30030")]
public void ForwardToModule()
{
var source = @"
extern alias A;
namespace B
{
using System;
class C
{
void M1()
{
}
}
}
namespace D
{
using System.Text; // Different using to prevent normal forwarding.
class E
{
void M2()
{
}
}
}
";
var parseOptions = TestOptions.Regular.WithNoRefSafetyRulesAttribute();
var aliasedRef = CreateEmptyCompilation("", assemblyName: "Lib", parseOptions: parseOptions).EmitToImageReference(aliases: ImmutableArray.Create("A"));
var comp = CreateCompilation(source, new[] { aliasedRef }, parseOptions: parseOptions);
WithRuntimeInstance(comp, runtime =>
{
var debugInfo1 = GetMethodDebugInfo(runtime, "B.C.M1");
debugInfo1.ImportRecordGroups.Verify(@"
{
Namespace: string='System'
}
{
Assembly: alias='A'
}");
debugInfo1.ExternAliasRecords.Verify(
"A = 'Lib, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'");
var debugInfo2 = GetMethodDebugInfo(runtime, "D.E.M2");
debugInfo2.ImportRecordGroups.Verify(@"
{
Namespace: string='System.Text'
}
{
Assembly: alias='A'
}");
debugInfo2.ExternAliasRecords.Verify(
"A = 'Lib, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'");
});
}
#endregion
#region Invalid PDBs
[Fact]
public void BadPdb_ForwardChain()
{
const int methodToken1 = 0x600057a; // Forwards to 2
const int methodToken2 = 0x600055d; // Forwards to 3
const int methodToken3 = 0x6000540; // Has a using
const string importString = "USystem";
var getMethodCustomDebugInfo = new Func<int, int, byte[]>((token, _) =>
{
switch (token)
{
case methodToken1: return new MethodDebugInfoBytes.Builder().AddForward(methodToken2).Build().Bytes.ToArray();
case methodToken2: return new MethodDebugInfoBytes.Builder().AddForward(methodToken3).Build().Bytes.ToArray();
case methodToken3: return new MethodDebugInfoBytes.Builder(new[] { new[] { importString } }).Build().Bytes.ToArray();
default: throw null;
}
});
var getMethodImportStrings = new Func<int, int, ImmutableArray<string>>((token, _) =>
{
switch (token)
{
case methodToken3: return ImmutableArray.Create(importString);
default: throw null;
}
});
ImmutableArray<string> externAliasStrings;
var importStrings = CustomDebugInfoReader.GetCSharpGroupedImportStrings(methodToken1, 0, getMethodCustomDebugInfo, getMethodImportStrings, out externAliasStrings);
Assert.True(importStrings.IsDefault);
Assert.True(externAliasStrings.IsDefault);
importStrings = CustomDebugInfoReader.GetCSharpGroupedImportStrings(methodToken2, 0, getMethodCustomDebugInfo, getMethodImportStrings, out externAliasStrings);
Assert.Equal(importString, importStrings.Single().Single());
Assert.Equal(0, externAliasStrings.Length);
importStrings = CustomDebugInfoReader.GetCSharpGroupedImportStrings(methodToken2, 0, getMethodCustomDebugInfo, getMethodImportStrings, out externAliasStrings);
Assert.Equal(importString, importStrings.Single().Single());
Assert.Equal(0, externAliasStrings.Length);
}
[Fact]
public void BadPdb_Cycle()
{
const int methodToken1 = 0x600057a; // Forwards to itself
var getMethodCustomDebugInfo = new Func<int, int, byte[]>((token, _) =>
{
switch (token)
{
case methodToken1: return new MethodDebugInfoBytes.Builder().AddForward(methodToken1).Build().Bytes.ToArray();
default: throw null;
}
});
var getMethodImportStrings = new Func<int, int, ImmutableArray<string>>((token, _) =>
{
return ImmutableArray<string>.Empty;
});
ImmutableArray<string> externAliasStrings;
var importStrings = CustomDebugInfoReader.GetCSharpGroupedImportStrings(methodToken1, 0, getMethodCustomDebugInfo, getMethodImportStrings, out externAliasStrings);
Assert.True(importStrings.IsDefault);
Assert.True(externAliasStrings.IsDefault);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/999086")]
public void BadPdb_InvalidAliasSyntax()
{
var source = @"
public class C
{
public static void Main()
{
}
}
";
var comp = CreateCompilation(source);
var peImage = comp.EmitToArray();
var symReader = ExpressionCompilerTestHelpers.ConstructSymReaderWithImports(
peImage,
"Main",
"USystem", // Valid.
"UACultureInfo TSystem.Globalization.CultureInfo, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", // Invalid - skipped.
"ASI USystem.IO"); // Valid.
var module = ModuleInstance.Create(peImage, symReader);
var runtime = CreateRuntimeInstance(module, new[] { MscorlibRef });
var evalContext = CreateMethodContext(runtime, "C.Main");
var compContext = evalContext.CreateCompilationContext(); // Used to throw.
var imports = compContext.NamespaceBinder.ImportChain.Single();
Assert.Equal("System", imports.Usings.Single().NamespaceOrType.ToTestDisplayString());
Assert.Equal("SI", imports.UsingAliases.Keys.Single());
Assert.Equal(0, imports.ExternAliases.Length);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/999086")]
public void BadPdb_DotInAlias()
{
var source = @"
public class C
{
public static void Main()
{
}
}
";
var comp = CreateCompilation(source);
var peImage = comp.EmitToArray();
var symReader = ExpressionCompilerTestHelpers.ConstructSymReaderWithImports(
peImage,
"Main",
"USystem", // Valid.
"AMy.Alias TSystem.Globalization.CultureInfo, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", // Invalid - skipped.
"ASI USystem.IO"); // Valid.
var module = ModuleInstance.Create(peImage, symReader);
var runtime = CreateRuntimeInstance(module, new[] { MscorlibRef });
var evalContext = CreateMethodContext(runtime, "C.Main");
var compContext = evalContext.CreateCompilationContext(); // Used to throw.
var imports = compContext.NamespaceBinder.ImportChain.Single();
Assert.Equal("System", imports.Usings.Single().NamespaceOrType.ToTestDisplayString());
Assert.Equal("SI", imports.UsingAliases.Keys.Single());
Assert.Equal(0, imports.ExternAliases.Length);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1007917")]
public void BadPdb_NestingLevel_TooMany()
{
var source = @"
public class C
{
public static void Main()
{
}
}
";
var comp = CreateCompilation(source);
var peImage = comp.EmitToArray();
ISymUnmanagedReader symReader;
using (var peReader = new PEReader(peImage))
{
var metadataReader = peReader.GetMetadataReader();
var methodHandle = metadataReader.MethodDefinitions.Single(h => metadataReader.StringComparer.Equals(metadataReader.GetMethodDefinition(h).Name, "Main"));
var methodToken = metadataReader.GetToken(methodHandle);
symReader = new MockSymUnmanagedReader(new Dictionary<int, MethodDebugInfoBytes>
{
{ methodToken, new MethodDebugInfoBytes.Builder(new [] { new[] { "USystem", "USystem.IO" } }, suppressUsingInfo: true).AddUsingInfo(1, 1).Build() },
}.ToImmutableDictionary());
}
var module = ModuleInstance.Create(peImage, symReader);
var runtime = CreateRuntimeInstance(module, new[] { MscorlibRef });
var evalContext = CreateMethodContext(runtime, "C.Main");
var compContext = evalContext.CreateCompilationContext();
var imports = compContext.NamespaceBinder.ImportChain.Single();
Assert.Equal("System.IO", imports.Usings.Single().NamespaceOrType.ToTestDisplayString()); // Note: some information is preserved.
Assert.Equal(0, imports.UsingAliases.Count);
Assert.Equal(0, imports.ExternAliases.Length);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1007917")]
public void BadPdb_NestingLevel_TooFew()
{
var source = @"
namespace N
{
public class C
{
public static void Main()
{
}
}
}
";
var comp = CreateCompilation(source);
var peImage = comp.EmitToArray();
ISymUnmanagedReader symReader;
using (var peReader = new PEReader(peImage))
{
var metadataReader = peReader.GetMetadataReader();
var methodHandle = metadataReader.MethodDefinitions.Single(h => metadataReader.StringComparer.Equals(metadataReader.GetMethodDefinition(h).Name, "Main"));
var methodToken = metadataReader.GetToken(methodHandle);
symReader = new MockSymUnmanagedReader(new Dictionary<int, MethodDebugInfoBytes>
{
{ methodToken, new MethodDebugInfoBytes.Builder(new [] { new[] { "USystem" } }, suppressUsingInfo: true).AddUsingInfo(1).Build() },
}.ToImmutableDictionary());
}
var module = ModuleInstance.Create(peImage, symReader);
var runtime = CreateRuntimeInstance(module, new[] { MscorlibRef });
var evalContext = CreateMethodContext(runtime, "N.C.Main");
var compContext = evalContext.CreateCompilationContext();
var imports = compContext.NamespaceBinder.ImportChain.Single();
Assert.Equal("System", imports.Usings.Single().NamespaceOrType.ToTestDisplayString()); // Note: some information is preserved.
Assert.Equal(0, imports.UsingAliases.Count);
Assert.Equal(0, imports.ExternAliases.Length);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1084059")]
public void BadPdb_NonStaticTypeImport()
{
var source = @"
namespace N
{
public class C
{
public static void Main()
{
}
}
}
";
var comp = CreateCompilation(source);
var peImage = comp.EmitToArray();
ISymUnmanagedReader symReader;
using (var peReader = new PEReader(peImage))
{
var metadataReader = peReader.GetMetadataReader();
var methodHandle = metadataReader.MethodDefinitions.Single(h => metadataReader.StringComparer.Equals(metadataReader.GetMethodDefinition(h).Name, "Main"));
var methodToken = metadataReader.GetToken(methodHandle);
symReader = new MockSymUnmanagedReader(new Dictionary<int, MethodDebugInfoBytes>
{
{ methodToken, new MethodDebugInfoBytes.Builder(new [] { new[] { "TSystem.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" } }, suppressUsingInfo: true).AddUsingInfo(1).Build() },
}.ToImmutableDictionary());
}
var module = ModuleInstance.Create(peImage, symReader);
var runtime = CreateRuntimeInstance(module, new[] { MscorlibRef });
var evalContext = CreateMethodContext(runtime, "N.C.Main");
var compContext = evalContext.CreateCompilationContext();
var imports = compContext.NamespaceBinder.ImportChain.Single();
Assert.Equal(0, imports.Usings.Length); // Note: the import is dropped
Assert.Equal(0, imports.UsingAliases.Count);
Assert.Equal(0, imports.ExternAliases.Length);
}
#endregion Invalid PDBs
#region Binder chain
[Fact]
public void ImportsForSimpleUsing()
{
var source = @"
using System;
class C
{
int M()
{
return 1;
}
}
";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular7);
comp.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Info).Verify();
WithRuntimeInstance(comp, runtime =>
{
var importsList = GetImports(runtime, "C.M");
var imports = importsList.Single();
Assert.Equal(0, imports.UsingAliases.Count);
Assert.Equal(0, imports.ExternAliases.Length);
var actualNamespace = imports.Usings.Single().NamespaceOrType;
Assert.Equal(SymbolKind.Namespace, actualNamespace.Kind);
Assert.Equal(NamespaceKind.Module, ((NamespaceSymbol)actualNamespace).Extent.Kind);
Assert.Equal("System", actualNamespace.ToTestDisplayString());
});
}
[Fact]
public void ImportsForMultipleUsings()
{
var source = @"
using System;
using System.IO;
using System.Text;
class C
{
int M()
{
return 1;
}
}
";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular7);
comp.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Info).Verify();
WithRuntimeInstance(comp, runtime =>
{
var importsList = GetImports(runtime, "C.M");
var imports = importsList.Single();
Assert.Equal(0, imports.UsingAliases.Count);
Assert.Equal(0, imports.ExternAliases.Length);
var usings = imports.Usings.Select(u => u.NamespaceOrType).ToArray();
Assert.Equal(3, usings.Length);
var expectedNames = new[] { "System", "System.IO", "System.Text" };
for (int i = 0; i < usings.Length; i++)
{
var actualNamespace = usings[i];
Assert.Equal(SymbolKind.Namespace, actualNamespace.Kind);
Assert.Equal(NamespaceKind.Module, ((NamespaceSymbol)actualNamespace).Extent.Kind);
Assert.Equal(expectedNames[i], actualNamespace.ToTestDisplayString());
}
});
}
[Fact]
public void ImportsForNestedNamespaces()
{
var source = @"
using System;
namespace A
{
using System.IO;
class C
{
int M()
{
return 1;
}
}
}
";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular7);
comp.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Info).Verify();
WithRuntimeInstance(comp, runtime =>
{
var importsList = GetImports(runtime, "A.C.M").AsEnumerable().ToArray();
Assert.Equal(2, importsList.Length);
var expectedNames = new[] { "System.IO", "System" }; // Innermost-to-outermost
for (int i = 0; i < importsList.Length; i++)
{
var imports = importsList[i];
Assert.Equal(0, imports.UsingAliases.Count);
Assert.Equal(0, imports.ExternAliases.Length);
var actualNamespace = imports.Usings.Single().NamespaceOrType;
Assert.Equal(SymbolKind.Namespace, actualNamespace.Kind);
Assert.Equal(NamespaceKind.Module, ((NamespaceSymbol)actualNamespace).Extent.Kind);
Assert.Equal(expectedNames[i], actualNamespace.ToTestDisplayString());
}
});
}
[Fact]
public void ImportsForNamespaceAlias()
{
var source = @"
using S = System;
class C
{
int M()
{
return 1;
}
}
";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular7);
comp.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Info).Verify();
WithRuntimeInstance(comp, runtime =>
{
var importsList = GetImports(runtime, "C.M");
var imports = importsList.Single();
Assert.Equal(0, imports.Usings.Length);
Assert.Equal(0, imports.ExternAliases.Length);
var usingAliases = imports.UsingAliases;
Assert.Equal(1, usingAliases.Count);
Assert.Equal("S", usingAliases.Keys.Single());
var aliasSymbol = usingAliases.Values.Single().Alias;
Assert.Equal("S", aliasSymbol.Name);
var namespaceSymbol = aliasSymbol.Target;
Assert.Equal(SymbolKind.Namespace, namespaceSymbol.Kind);
Assert.Equal(NamespaceKind.Module, ((NamespaceSymbol)namespaceSymbol).Extent.Kind);
Assert.Equal("System", namespaceSymbol.ToTestDisplayString());
});
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1084059")]
public void ImportsForStaticType()
{
var source = @"
using static System.Math;
class C
{
int M()
{
return 1;
}
}
";
var comp = CreateCompilation(source);
comp.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Info).Verify();
WithRuntimeInstance(comp, runtime =>
{
var importsList = GetImports(runtime, "C.M");
var imports = importsList.Single();
Assert.Equal(0, imports.UsingAliases.Count);
Assert.Equal(0, imports.ExternAliases.Length);
var actualType = imports.Usings.Single().NamespaceOrType;
Assert.Equal(SymbolKind.NamedType, actualType.Kind);
Assert.Equal("System.Math", actualType.ToTestDisplayString());
});
}
[Fact]
public void ImportsForTypeAlias()
{
var source = @"
using I = System.Int32;
class C
{
int M()
{
return 1;
}
}
";
var comp = CreateCompilation(source);
comp.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Info).Verify();
WithRuntimeInstance(comp, runtime =>
{
var importsList = GetImports(runtime, "C.M");
var imports = importsList.Single();
Assert.Equal(0, imports.Usings.Length);
Assert.Equal(0, imports.ExternAliases.Length);
var usingAliases = imports.UsingAliases;
Assert.Equal(1, usingAliases.Count);
Assert.Equal("I", usingAliases.Keys.Single());
var aliasSymbol = usingAliases.Values.Single().Alias;
Assert.Equal("I", aliasSymbol.Name);
var typeSymbol = aliasSymbol.Target;
Assert.Equal(SymbolKind.NamedType, typeSymbol.Kind);
Assert.Equal(SpecialType.System_Int32, ((NamedTypeSymbol)typeSymbol).SpecialType);
});
}
[Fact]
public void ImportsForVerbatimIdentifiers()
{
var source = @"
using @namespace;
using @object = @namespace;
using @string = @namespace.@class<@namespace.@interface>.@struct;
namespace @namespace
{
public class @class<T>
{
public struct @struct
{
}
}
public interface @interface
{
}
}
class C
{
int M()
{
return 1;
}
}
";
var comp = CreateCompilation(source);
comp.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Info).Verify();
WithRuntimeInstance(comp, runtime =>
{
var importsList = GetImports(runtime, "C.M");
var imports = importsList.Single();
Assert.Equal(0, imports.ExternAliases.Length);
var @using = imports.Usings.Single();
var importedNamespace = @using.NamespaceOrType;
Assert.Equal(SymbolKind.Namespace, importedNamespace.Kind);
Assert.Equal("namespace", importedNamespace.Name);
var usingAliases = imports.UsingAliases;
const string keyword1 = "object";
const string keyword2 = "string";
AssertEx.SetEqual(usingAliases.Keys, keyword1, keyword2);
var namespaceAlias = usingAliases[keyword1];
var typeAlias = usingAliases[keyword2];
Assert.Equal(keyword1, namespaceAlias.Alias.Name);
var aliasedNamespace = namespaceAlias.Alias.Target;
Assert.Equal(SymbolKind.Namespace, aliasedNamespace.Kind);
Assert.Equal("@namespace", aliasedNamespace.ToTestDisplayString());
Assert.Equal(keyword2, typeAlias.Alias.Name);
var aliasedType = typeAlias.Alias.Target;
Assert.Equal(SymbolKind.NamedType, aliasedType.Kind);
Assert.Equal("@namespace.@class<@namespace.@interface>.@struct", aliasedType.ToTestDisplayString());
});
}
[Fact]
public void ImportsForGenericTypeAlias()
{
var source = @"
using I = System.Collections.Generic.IEnumerable<string>;
class C
{
int M()
{
return 1;
}
}
";
var comp = CreateCompilation(source);
comp.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Info).Verify();
WithRuntimeInstance(comp, runtime =>
{
var importsList = GetImports(runtime, "C.M");
var imports = importsList.Single();
Assert.Equal(0, imports.Usings.Length);
Assert.Equal(0, imports.ExternAliases.Length);
var usingAliases = imports.UsingAliases;
Assert.Equal(1, usingAliases.Count);
Assert.Equal("I", usingAliases.Keys.Single());
var aliasSymbol = usingAliases.Values.Single().Alias;
Assert.Equal("I", aliasSymbol.Name);
var typeSymbol = aliasSymbol.Target;
Assert.Equal(SymbolKind.NamedType, typeSymbol.Kind);
Assert.Equal("System.Collections.Generic.IEnumerable<System.String>", typeSymbol.ToTestDisplayString());
});
}
[Fact]
public void ImportsForExternAlias()
{
var source = @"
extern alias X;
class C
{
int M()
{
X::System.Xml.Linq.LoadOptions.None.ToString();
return 1;
}
}
";
var comp = CreateCompilation(source, new[] { SystemXmlLinqRef.WithAliases(ImmutableArray.Create("X")) });
comp.VerifyDiagnostics();
WithRuntimeInstance(comp, runtime =>
{
var importsList = GetImports(runtime, "C.M");
var imports = importsList.Single();
Assert.Equal(0, imports.Usings.Length);
Assert.Equal(0, imports.UsingAliases.Count);
var externAliases = imports.ExternAliases;
Assert.Equal(1, externAliases.Length);
var aliasSymbol = externAliases.Single().Alias;
Assert.Equal("X", aliasSymbol.Name);
var targetSymbol = aliasSymbol.Target;
Assert.Equal(SymbolKind.Namespace, targetSymbol.Kind);
Assert.True(((NamespaceSymbol)targetSymbol).IsGlobalNamespace);
Assert.Equal("System.Xml.Linq", targetSymbol.ContainingAssembly.Name);
});
}
[Fact]
public void ImportsForUsingsConsumingExternAlias()
{
var source = @"
extern alias X;
using SXL = X::System.Xml.Linq;
using LO = X::System.Xml.Linq.LoadOptions;
using X::System.Xml;
class C
{
int M()
{
X::System.Xml.Linq.LoadOptions.None.ToString();
return 1;
}
}
";
var comp = CreateCompilation(source, new[] { SystemXmlLinqRef.WithAliases(ImmutableArray.Create("X")) });
comp.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Info).Verify();
WithRuntimeInstance(comp, runtime =>
{
var importsList = GetImports(runtime, "C.M");
var imports = importsList.Single();
Assert.Equal(1, imports.ExternAliases.Length);
var @using = imports.Usings.Single();
var importedNamespace = @using.NamespaceOrType;
Assert.Equal(SymbolKind.Namespace, importedNamespace.Kind);
Assert.Equal("System.Xml", importedNamespace.ToTestDisplayString());
var usingAliases = imports.UsingAliases;
Assert.Equal(2, usingAliases.Count);
AssertEx.SetEqual(usingAliases.Keys, "SXL", "LO");
var typeAlias = usingAliases["SXL"].Alias;
Assert.Equal("SXL", typeAlias.Name);
Assert.Equal("System.Xml.Linq", typeAlias.Target.ToTestDisplayString());
var namespaceAlias = usingAliases["LO"].Alias;
Assert.Equal("LO", namespaceAlias.Name);
Assert.Equal("System.Xml.Linq.LoadOptions", namespaceAlias.Target.ToTestDisplayString());
});
}
[Fact]
public void ImportsForUsingsConsumingExternAliasAndGlobal()
{
var source = @"
extern alias X;
using A = X::System.Xml.Linq;
using B = global::System.Xml.Linq;
class C
{
int M()
{
A.LoadOptions.None.ToString();
B.LoadOptions.None.ToString();
return 1;
}
}
";
var comp = CreateCompilation(source, new[] { SystemXmlLinqRef.WithAliases(ImmutableArray.Create("global", "X")) });
comp.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Info).Verify();
WithRuntimeInstance(comp, runtime =>
{
var importsList = GetImports(runtime, "C.M");
var imports = importsList.Single();
Assert.Equal(0, imports.Usings.Length);
Assert.Equal(1, imports.ExternAliases.Length);
var usingAliases = imports.UsingAliases;
Assert.Equal(2, usingAliases.Count);
AssertEx.SetEqual(usingAliases.Keys, "A", "B");
var aliasA = usingAliases["A"].Alias;
Assert.Equal("A", aliasA.Name);
Assert.Equal("System.Xml.Linq", aliasA.Target.ToTestDisplayString());
var aliasB = usingAliases["B"].Alias;
Assert.Equal("B", aliasB.Name);
Assert.Equal(aliasA.Target, aliasB.Target);
});
}
[Fact]
public void ImportsForUsingsToTypes()
{
var source = @"
using A = int;
using B = (int x, int y);
class C
{
int M()
{
A.Parse(""0"");
return 1;
}
}
";
var comp = CreateCompilation(source);
comp.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Info).Verify();
WithRuntimeInstance(comp, runtime =>
{
var importsList = GetImports(runtime, "C.M");
var imports = importsList.Single();
Assert.Equal(0, imports.Usings.Length);
var usingAliases = imports.UsingAliases;
Assert.Equal(2, usingAliases.Count);
AssertEx.SetEqual(usingAliases.Keys, "A", "B");
var aliasA = usingAliases["A"].Alias;
Assert.Equal("A", aliasA.Name);
Assert.Equal("System.Int32", aliasA.Target.ToTestDisplayString());
var aliasB = usingAliases["B"].Alias;
Assert.Equal("B", aliasB.Name);
Assert.NotEqual(aliasA.Target, aliasB.Target);
});
}
private static ImportChain GetImports(RuntimeInstance runtime, string methodName)
{
var evalContext = CreateMethodContext(runtime, methodName);
var compContext = evalContext.CreateCompilationContext();
return compContext.NamespaceBinder.ImportChain;
}
#endregion Binder chain
[Fact]
public void NoSymbols()
{
var source =
@"using N;
class A
{
static void M() { }
}
namespace N
{
class B
{
static void M() { }
}
}";
ResultProperties resultProperties;
string error;
// With symbols, type reference without namespace qualifier.
var testData = Evaluate(
source,
OutputKind.DynamicallyLinkedLibrary,
methodName: "A.M",
expr: "typeof(B)",
resultProperties: out resultProperties,
error: out error,
includeSymbols: true);
Assert.Null(error);
// Without symbols, type reference without namespace qualifier.
testData = Evaluate(
source,
OutputKind.DynamicallyLinkedLibrary,
methodName: "A.M",
expr: "typeof(B)",
resultProperties: out resultProperties,
error: out error,
includeSymbols: false);
Assert.Equal("error CS0246: The type or namespace name 'B' could not be found (are you missing a using directive or an assembly reference?)", error);
// With symbols, type reference inside namespace.
testData = Evaluate(
source,
OutputKind.DynamicallyLinkedLibrary,
methodName: "N.B.M",
expr: "typeof(B)",
resultProperties: out resultProperties,
error: out error,
includeSymbols: true);
Assert.Null(error);
// Without symbols, type reference inside namespace.
testData = Evaluate(
source,
OutputKind.DynamicallyLinkedLibrary,
methodName: "N.B.M",
expr: "typeof(B)",
resultProperties: out resultProperties,
error: out error,
includeSymbols: false);
Assert.Null(error);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/2441")]
public void AssemblyQualifiedNameResolutionWithUnification()
{
var source1 = @"
using SI = System.Int32;
public class C1
{
void M()
{
}
}
";
var source2 = @"
public class C2 : C1
{
}
";
var comp1 = CreateEmptyCompilation(source1, new[] { MscorlibRef_v20 }, TestOptions.DebugDll);
var module1 = comp1.ToModuleInstance();
var comp2 = CreateEmptyCompilation(source2, new[] { MscorlibRef_v4_0_30316_17626, module1.GetReference() }, TestOptions.DebugDll);
var module2 = comp2.ToModuleInstance();
var runtime = CreateRuntimeInstance(new[]
{
module1,
module2,
MscorlibRef_v4_0_30316_17626.ToModuleInstance(),
ExpressionCompilerTestHelpers.IntrinsicAssemblyReference.ToModuleInstance()
});
var context = CreateMethodContext(runtime, "C1.M");
string error;
var testData = new CompilationTestData();
context.CompileExpression("typeof(SI)", out error, testData);
Assert.Null(error);
testData.GetMethodData("<>x.<>m0").VerifyIL(@"
{
// Code size 11 (0xb)
.maxstack 1
IL_0000: ldtoken ""int""
IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_000a: ret
}
");
}
}
internal static class ImportChainExtensions
{
internal static Imports Single(this ImportChain importChain)
{
return importChain.AsEnumerable().Single();
}
internal static IEnumerable<Imports> AsEnumerable(this ImportChain importChain)
{
for (var chain = importChain; chain != null; chain = chain.ParentOpt)
{
yield return chain.Imports;
}
}
}
}
|