diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs index 69bbfe7b1342c3a592b50f5e2f76b2318f4227ba..2d7f36f29bd99a25ec1ca4aef32ceb7c1f37367b 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs @@ -550,6 +550,21 @@ internal static ImmutableArray GetMissingAssemblyIdentitiesHel } } break; + case ErrorCode.ERR_DottedTypeNameNotFoundInNS: + if (arguments.Count == 2) + { + var namespaceName = arguments[0] as string; + var containingNamespace = arguments[1] as NamespaceSymbol; + if (namespaceName != null && (object)containingNamespace != null && + containingNamespace.ConstituentNamespaces.Any(n => n.ContainingAssembly.Identity.IsWindowsAssemblyIdentity())) + { + // This is just a heuristic, but it has the advantage of being portable, particularly + // across different versions of (desktop) windows. + var identity = new AssemblyIdentity($"{containingNamespace.ToDisplayString()}.{namespaceName}", contentType: System.Reflection.AssemblyContentType.WindowsRuntime); + return ImmutableArray.Create(identity); + } + } + break; case ErrorCode.ERR_DynamicAttributeMissing: case ErrorCode.ERR_DynamicRequiredTypesMissing: // MSDN says these might come from System.Dynamic.Runtime diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MissingAssemblyTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MissingAssemblyTests.cs index 8910302bc4daad8a4a7aca719ada09508dd5d030..5a9b4bb665ef61000742c155b6068ce1b449aefa 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MissingAssemblyTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MissingAssemblyTests.cs @@ -380,7 +380,87 @@ static void M() Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single()); } + [WorkItem(1114866)] + [ConditionalFact(typeof(OSVersionWin8))] + public void NotYetLoadedWinMds() + { + var source = +@"class C +{ + static void M(Windows.Storage.StorageFolder f) + { + } +}"; + var comp = CreateCompilationWithMscorlib(source, WinRtRefs, TestOptions.DebugDll); + var runtimeAssemblies = ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.Storage"); + Assert.True(runtimeAssemblies.Any()); + var context = CreateMethodContextWithReferences(comp, "C.M", ImmutableArray.Create(MscorlibRef).Concat(runtimeAssemblies)); + + const string expectedError = "error CS0234: The type or namespace name 'UI' does not exist in the namespace 'Windows' (are you missing an assembly reference?)"; + var expectedMissingAssemblyIdentity = new AssemblyIdentity("Windows.UI", contentType: System.Reflection.AssemblyContentType.WindowsRuntime); + + ResultProperties resultProperties; + string actualError; + ImmutableArray actualMissingAssemblyIdentities; + context.CompileExpression( + InspectionContextFactory.Empty, + "typeof(@Windows.UI.Colors)", + DkmEvaluationFlags.None, + DiagnosticFormatter.Instance, + out resultProperties, + out actualError, + out actualMissingAssemblyIdentities, + EnsureEnglishUICulture.PreferredOrNull, + testData: null); + Assert.Equal(expectedError, actualError); + Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single()); + } + + /// + /// Windows.UI.Xaml is the only (win8) winmd with more than two parts. + /// + [WorkItem(1114866)] + [ConditionalFact(typeof(OSVersionWin8))] + public void NotYetLoadedWinMds_MultipleParts() + { + var source = +@"class C +{ + static void M(Windows.UI.Colors c) + { + } +}"; + var comp = CreateCompilationWithMscorlib(source, WinRtRefs, TestOptions.DebugDll); + var runtimeAssemblies = ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.UI"); + Assert.True(runtimeAssemblies.Any()); + var context = CreateMethodContextWithReferences(comp, "C.M", ImmutableArray.Create(MscorlibRef).Concat(runtimeAssemblies)); + + const string expectedError = "error CS0234: The type or namespace name 'Xaml' does not exist in the namespace 'Windows.UI' (are you missing an assembly reference?)"; + var expectedMissingAssemblyIdentity = new AssemblyIdentity("Windows.UI.Xaml", contentType: System.Reflection.AssemblyContentType.WindowsRuntime); + + ResultProperties resultProperties; + string actualError; + ImmutableArray actualMissingAssemblyIdentities; + context.CompileExpression( + InspectionContextFactory.Empty, + "typeof(Windows.@UI.Xaml.Application)", + DkmEvaluationFlags.None, + DiagnosticFormatter.Instance, + out resultProperties, + out actualError, + out actualMissingAssemblyIdentities, + EnsureEnglishUICulture.PreferredOrNull, + testData: null); + Assert.Equal(expectedError, actualError); + Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single()); + } + private EvaluationContext CreateMethodContextWithReferences(Compilation comp, string methodName, params MetadataReference[] references) + { + return CreateMethodContextWithReferences(comp, methodName, ImmutableArray.CreateRange(references)); + } + + private EvaluationContext CreateMethodContextWithReferences(Compilation comp, string methodName, ImmutableArray references) { byte[] exeBytes; byte[] pdbBytes; @@ -388,7 +468,7 @@ private EvaluationContext CreateMethodContextWithReferences(Compilation comp, st var result = comp.EmitAndGetReferences(out exeBytes, out pdbBytes, out unusedReferences); Assert.True(result); - var runtime = CreateRuntimeInstance(GetUniqueName(), ImmutableArray.CreateRange(references), exeBytes, new SymReader(pdbBytes)); + var runtime = CreateRuntimeInstance(GetUniqueName(), references, exeBytes, new SymReader(pdbBytes)); return CreateMethodContext(runtime, methodName); } diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/MetadataUtilities.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/MetadataUtilities.cs index 00410d62ab2481aa7514417300d01eda19988712..5e29b8b0007df842e72b60997ce6cc113a3299be 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/MetadataUtilities.cs +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/MetadataUtilities.cs @@ -194,6 +194,12 @@ internal static bool IsWindowsAssemblyName(string assemblyName) return assemblyName.Equals("windows", StringComparison.OrdinalIgnoreCase); } + internal static bool IsWindowsAssemblyIdentity(this AssemblyIdentity assemblyIdentity) + { + return IsWindowsAssemblyName(assemblyIdentity.Name) && + assemblyIdentity.ContentType == System.Reflection.AssemblyContentType.WindowsRuntime; + } + internal static LocalInfo GetLocalInfo( this MetadataDecoder metadataDecoder, byte[] signature) diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb index f23844408add08e67591fe73778489845442f00e..cdec4a09b61e3580b9b4fd137446fa72bb09969d 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb @@ -562,17 +562,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator End Sub Friend Overrides Function GetMissingAssemblyIdentities(diagnostic As Diagnostic) As ImmutableArray(Of AssemblyIdentity) - Return GetMissingAssemblyIdentitiesHelper(CType(diagnostic.Code, ERRID), diagnostic.Arguments) + Return GetMissingAssemblyIdentitiesHelper(CType(diagnostic.Code, ERRID), diagnostic.Arguments, Me.Compilation.GlobalNamespace) End Function ''' ''' Friend for testing. ''' - Friend Shared Function GetMissingAssemblyIdentitiesHelper(code As ERRID, arguments As IReadOnlyList(Of Object)) As ImmutableArray(Of AssemblyIdentity) - Select Case code + Friend Shared Function GetMissingAssemblyIdentitiesHelper(code As ERRID, arguments As IReadOnlyList(Of Object), globalNamespace As NamespaceSymbol) As ImmutableArray(Of AssemblyIdentity) + Select Case code Case ERRID.ERR_UnreferencedAssemblyEvent3, ERRID.ERR_UnreferencedAssembly3 For Each argument As Object In arguments - Dim identity = If(TryCast(argument, AssemblyIdentity), TryCast(argument, AssemblySymbol)?.Identity) + Dim identity = If(TryCast(argument, AssemblyIdentity), TryCast(argument, AssemblySymbol)?.Identity) If IsValidMissingAssemblyIdentity(identity) Then Return ImmutableArray.Create(identity) End If @@ -584,6 +584,49 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator Return ImmutableArray.Create(identity) End If End If + Case ERRID.ERR_NameNotMember2 + If arguments.Count = 2 Then + Dim namespaceName = TryCast(arguments(0), String) + Dim containingNamespace = TryCast(arguments(1), NamespaceSymbol) + If namespaceName IsNot Nothing AndAlso containingNamespace IsNot Nothing AndAlso HasConstituentFromWindowsAssembly(containingNamespace) Then + ' This is just a heuristic, but it has the advantage of being portable, particularly + ' across different versions of (desktop) windows. + Dim identity = New AssemblyIdentity($"{containingNamespace.ToDisplayString}.{namespaceName}", contentType:=AssemblyContentType.WindowsRuntime) + Return ImmutableArray.Create(identity) + End If + End If + Case ERRID.ERR_UndefinedType1 + If arguments.Count = 1 Then + Dim qualifiedName = TryCast(arguments(0), String) + If Not String.IsNullOrEmpty(qualifiedName) Then + Dim nameParts = qualifiedName.Split("."c) + Dim numParts = nameParts.Length + Dim pos = 0 + If CaseInsensitiveComparison.Comparer.Equals(nameParts(0), "global") Then + pos = 1 + Debug.Assert(pos < numParts) + End If + Dim currNamespace = globalNamespace + While pos < numParts + Dim nextNamespace = currNamespace.GetMembers(nameParts(pos)).OfType(Of NamespaceSymbol).SingleOrDefault() + If nextNamespace Is Nothing Then + Exit While + End If + pos += 1 + currNamespace = nextNamespace + End While + + If currNamespace IsNot globalNamespace AndAlso HasConstituentFromWindowsAssembly(currNamespace) AndAlso pos < numParts Then + Dim nextNamePart = nameParts(pos) + If nextNamePart.All(AddressOf SyntaxFacts.IsIdentifierPartCharacter) Then + ' This is just a heuristic, but it has the advantage of being portable, particularly + ' across different versions of (desktop) windows. + Dim identity = New AssemblyIdentity($"{currNamespace.ToDisplayString}.{nameParts(pos)}", contentType:=AssemblyContentType.WindowsRuntime) + Return ImmutableArray.Create(identity) + End If + End If + End If + End If Case ERRID.ERR_XmlFeaturesNotAvailable Return ImmutableArray.Create(SystemIdentity, SystemCoreIdentity, SystemXmlIdentity, SystemXmlLinqIdentity) Case ERRID.ERR_MissingRuntimeHelper @@ -593,6 +636,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator Return Nothing End Function + Private Shared Function HasConstituentFromWindowsAssembly(namespaceSymbol As NamespaceSymbol) As Boolean + Return namespaceSymbol.ConstituentNamespaces.Any(Function(n) n.ContainingAssembly.Identity.IsWindowsAssemblyIdentity) + End Function + Private Shared Function IsValidMissingAssemblyIdentity(identity As AssemblyIdentity) As Boolean Return identity IsNot Nothing AndAlso Not identity.Equals(MissingCorLibrarySymbol.Instance.Identity) End Function diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/MissingAssemblyTests.vb b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/MissingAssemblyTests.vb index 42174f806eafbad0b7a45995c3cb0081123efa13..845e557dc3058129c030e6a1aca58b837460add5 100644 --- a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/MissingAssemblyTests.vb +++ b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/MissingAssemblyTests.vb @@ -229,6 +229,43 @@ End Class Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single()) End Sub + + + Public Sub ERR_UndefinedType1() + Dim source = " +Class C + Sub M() + End Sub +End Class +" + + Dim comp = CreateCompilationWithReferences({VisualBasicSyntaxTree.ParseText(source)}, {MscorlibRef}.Concat(WinRtRefs), TestOptions.DebugDll) + Dim runtimeAssemblies = ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.UI") + Assert.True(runtimeAssemblies.Any()) + Dim context = CreateMethodContextWithReferences(comp, "C.M", ImmutableArray.Create(MscorlibRef).Concat(runtimeAssemblies)) + Dim globalNamespace = context.Compilation.GlobalNamespace + + Dim expectedIdentity = New AssemblyIdentity("Windows.Storage", contentType:=Reflection.AssemblyContentType.WindowsRuntime) + + Dim actualIdentity = EvaluationContext.GetMissingAssemblyIdentitiesHelper(ERRID.ERR_UndefinedType1, {"Windows.Storage"}, globalNamespace).Single() + Assert.Equal(expectedIdentity, actualIdentity) + + actualIdentity = EvaluationContext.GetMissingAssemblyIdentitiesHelper(ERRID.ERR_UndefinedType1, {"Global.Windows.Storage"}, globalNamespace).Single() + Assert.Equal(expectedIdentity, actualIdentity) + + actualIdentity = EvaluationContext.GetMissingAssemblyIdentitiesHelper(ERRID.ERR_UndefinedType1, {"Global.Windows.Storage.Additional"}, globalNamespace).Single() + Assert.Equal(expectedIdentity, actualIdentity) + + + expectedIdentity = New AssemblyIdentity("Windows.UI.Xaml", contentType:=Reflection.AssemblyContentType.WindowsRuntime) + + actualIdentity = EvaluationContext.GetMissingAssemblyIdentitiesHelper(ERRID.ERR_UndefinedType1, {"Windows.UI.Xaml"}, globalNamespace).Single() + Assert.Equal(expectedIdentity, actualIdentity) + + + Assert.True(EvaluationContext.GetMissingAssemblyIdentitiesHelper(ERRID.ERR_UndefinedType1, {"Windows.UI.Xaml(Of T)"}, globalNamespace).IsDefault) + End Sub + @@ -264,19 +301,131 @@ End Class Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single()) End Sub + + + Public Sub NotYetLoadedWinMds() + Dim source = " +Class C + Shared Sub M(f As Windows.Storage.StorageFolder) + End Sub +End Class +" + + Dim comp = CreateCompilationWithReferences({VisualBasicSyntaxTree.ParseText(source)}, {MscorlibRef}.Concat(WinRtRefs), TestOptions.DebugDll) + Dim runtimeAssemblies = ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.Storage") + Assert.True(runtimeAssemblies.Any()) + Dim context = CreateMethodContextWithReferences(comp, "C.M", ImmutableArray.Create(MscorlibRef).Concat(runtimeAssemblies)) + + Const expectedError = "(1,1): error BC30456: 'UI' is not a member of 'Windows'." + Dim expectedMissingAssemblyIdentity = New AssemblyIdentity("Windows.UI", contentType:=System.Reflection.AssemblyContentType.WindowsRuntime) + + Dim resultProperties As ResultProperties = Nothing + Dim actualError As String = Nothing + Dim actualMissingAssemblyIdentities As ImmutableArray(Of AssemblyIdentity) = Nothing + context.CompileExpression( + InspectionContextFactory.Empty, + "Windows.UI.Colors", + DkmEvaluationFlags.None, + DiagnosticFormatter.Instance, + resultProperties, + actualError, + actualMissingAssemblyIdentities, + EnsureEnglishUICulture.PreferredOrNull, + testData:=Nothing) + Assert.Equal(expectedError, actualError) + Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single()) + End Sub + + ''' + ''' Windows.UI.Xaml is the only (win8) winmd with more than two parts. + ''' + + + Public Sub NotYetLoadedWinMds_MultipleParts() + Dim source = " +Class C + Shared Sub M(c As Windows.UI.Colors) + End Sub +End Class +" + + Dim comp = CreateCompilationWithReferences({VisualBasicSyntaxTree.ParseText(source)}, {MscorlibRef}.Concat(WinRtRefs), TestOptions.DebugDll) + Dim runtimeAssemblies = ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.UI") + Assert.True(runtimeAssemblies.Any()) + Dim context = CreateMethodContextWithReferences(comp, "C.M", ImmutableArray.Create(MscorlibRef).Concat(runtimeAssemblies)) + + Const expectedError = "(1,1): error BC30456: 'Xaml' is not a member of 'Windows.UI'." + Dim expectedMissingAssemblyIdentity = New AssemblyIdentity("Windows.UI.Xaml", contentType:=System.Reflection.AssemblyContentType.WindowsRuntime) + + Dim resultProperties As ResultProperties = Nothing + Dim actualError As String = Nothing + Dim actualMissingAssemblyIdentities As ImmutableArray(Of AssemblyIdentity) = Nothing + context.CompileExpression( + InspectionContextFactory.Empty, + "Windows.[UI].Xaml.Application", + DkmEvaluationFlags.None, + DiagnosticFormatter.Instance, + resultProperties, + actualError, + actualMissingAssemblyIdentities, + EnsureEnglishUICulture.PreferredOrNull, + testData:=Nothing) + Assert.Equal(expectedError, actualError) + Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single()) + End Sub + + + + Public Sub NotYetLoadedWinMds_GetType() + Dim source = " +Class C + Shared Sub M(f As Windows.Storage.StorageFolder) + End Sub +End Class +" + + Dim comp = CreateCompilationWithReferences({VisualBasicSyntaxTree.ParseText(source)}, {MscorlibRef}.Concat(WinRtRefs), TestOptions.DebugDll) + Dim runtimeAssemblies = ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.Storage") + Assert.True(runtimeAssemblies.Any()) + Dim context = CreateMethodContextWithReferences(comp, "C.M", ImmutableArray.Create(MscorlibRef).Concat(runtimeAssemblies)) + + Const expectedError = "(1,9): error BC30002: Type 'Windows.UI.Colors' is not defined." + Dim expectedMissingAssemblyIdentity = New AssemblyIdentity("Windows.UI", contentType:=System.Reflection.AssemblyContentType.WindowsRuntime) + + Dim resultProperties As ResultProperties = Nothing + Dim actualError As String = Nothing + Dim actualMissingAssemblyIdentities As ImmutableArray(Of AssemblyIdentity) = Nothing + context.CompileExpression( + InspectionContextFactory.Empty, + "GetType([Windows].UI.Colors)", + DkmEvaluationFlags.None, + DiagnosticFormatter.Instance, + resultProperties, + actualError, + actualMissingAssemblyIdentities, + EnsureEnglishUICulture.PreferredOrNull, + testData:=Nothing) + Assert.Equal(expectedError, actualError) + Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single()) + End Sub + Private Function CreateMethodContextWithReferences(comp As Compilation, methodName As String, ParamArray references As MetadataReference()) As EvaluationContext + Return CreateMethodContextWithReferences(comp, methodName, ImmutableArray.CreateRange(references)) + End Function + + Private Function CreateMethodContextWithReferences(comp As Compilation, methodName As String, references As ImmutableArray(Of MetadataReference)) As EvaluationContext Dim exeBytes As Byte() = Nothing Dim pdbBytes As Byte() = Nothing Dim unusedReferences As ImmutableArray(Of MetadataReference) = Nothing Dim result = comp.EmitAndGetReferences(exeBytes, pdbBytes, unusedReferences) Assert.True(result) - Dim runtime = CreateRuntimeInstance(GetUniqueName(), ImmutableArray.CreateRange(references), exeBytes, New SymReader(pdbBytes)) + Dim runtime = CreateRuntimeInstance(GetUniqueName(), references, exeBytes, New SymReader(pdbBytes)) Return CreateMethodContext(runtime, methodName) End Function Private Shared Function GetMissingAssemblyIdentities(code As ERRID, ParamArray arguments As Object()) As ImmutableArray(Of AssemblyIdentity) - Return EvaluationContext.GetMissingAssemblyIdentitiesHelper(code, arguments) + Return EvaluationContext.GetMissingAssemblyIdentitiesHelper(code, arguments, globalNamespace:=Nothing) End Function End Class End Namespace \ No newline at end of file