From 03f4130762dae22d7973268a3ca9855a1268e7d0 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 24 Sep 2024 12:18:13 -0700 Subject: [PATCH] Fix null dereference in GetAssemblyReferenceAlias (#75094) Fixes #74872. --- .../CSharp/Test/Emit2/PDB/PDBUsingTests.cs | 542 ++++++++++++++++++ .../Portable/NativePdbWriter/PdbWriter.cs | 50 +- 2 files changed, 574 insertions(+), 18 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/PDB/PDBUsingTests.cs b/src/Compilers/CSharp/Test/Emit2/PDB/PDBUsingTests.cs index 5b1dc439dace6..620709f79c097 100644 --- a/src/Compilers/CSharp/Test/Emit2/PDB/PDBUsingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/PDB/PDBUsingTests.cs @@ -843,6 +843,548 @@ static void F(A2::A? a) { } "); } + [WorkItem("https://github.com/dotnet/roslyn/issues/74872")] + [Fact] + public void ExternAliases6() + { + string sourceA1 = +@" +namespace N +{ + public class A { } +} +"; + var comp = CreateCompilation(sourceA1, assemblyName: "A1"); + var refA1 = comp.EmitToImageReference(aliases: ImmutableArray.Create("A2")); + + string sourceA4 = +@" +namespace N +{ + public class A4 { } +} +"; + comp = CreateCompilation(sourceA4, assemblyName: "A4"); + var refA4 = comp.EmitToImageReference(aliases: ImmutableArray.Create("A5")); + + string sourceB = +@" +class B +{ + static void F(A x) { } +} +"; + string sourceC = +@" +extern alias A2; +global using A2::N; +"; + comp = CreateCompilation([sourceB, sourceC], references: new[] { refA1, refA4 }, options: TestOptions.DebugDll); + comp.VerifyDiagnostics(); + + comp.VerifyPdb( +@" + + + + + + + + + + + + + + + + + + + + + + + +", + format: DebugInformationFormat.Pdb); + + comp.VerifyPdb( +@" + + + + + + + + + + + + + +", + format: DebugInformationFormat.PortablePdb); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/74872")] + [Fact] + public void ExternAliases7() + { + string sourceA1 = +@" +namespace N +{ + public class A { } +} +"; + var comp = CreateCompilation(sourceA1, assemblyName: "A1"); + var refA1 = comp.EmitToImageReference(aliases: ImmutableArray.Create("A2")); + + string sourceA4 = +@" +namespace N +{ + public class A4 { } +} +"; + comp = CreateCompilation(sourceA4, assemblyName: "A4"); + var refA4 = comp.EmitToImageReference(aliases: ImmutableArray.Create("A5")); + + string sourceB = +@" +class B +{ + static void F(N2.A x) { } +} +"; + string sourceC = +@" +extern alias A2; +global using N2 = A2::N; +"; + comp = CreateCompilation([sourceB, sourceC], references: new[] { refA1, refA4 }, options: TestOptions.DebugDll); + comp.VerifyDiagnostics(); + + comp.VerifyPdb( +@" + + + + + + + + + + + + + + + + + + + + + + + +", + format: DebugInformationFormat.Pdb); + + comp.VerifyPdb( +@" + + + + + + + + + + + + + +", + format: DebugInformationFormat.PortablePdb); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/74872")] + [Fact] + public void ExternAliases8() + { + string sourceA1 = +@" +namespace N +{ + public class A { } +} +"; + var comp = CreateCompilation(sourceA1, assemblyName: "A1"); + var refA1 = comp.EmitToImageReference(aliases: ImmutableArray.Create("A2", "A3")); + + string sourceA4 = +@" +namespace N +{ + public class A4 { } +} +"; + comp = CreateCompilation(sourceA4, assemblyName: "A4"); + var refA4 = comp.EmitToImageReference(aliases: ImmutableArray.Create("A5")); + + string sourceB = +@" +extern alias A3; +class B +{ + static void F(A x) { } +} +"; + string sourceC = +@" +extern alias A2; +global using A2::N; +"; + comp = CreateCompilation([sourceB, sourceC], references: new[] { refA1, refA4 }, options: TestOptions.DebugDll); + comp.VerifyDiagnostics( + // (2,1): hidden CS8020: Unused extern alias. + // extern alias A3; + Diagnostic(ErrorCode.HDN_UnusedExternAlias, "extern alias A3;").WithLocation(2, 1) + ); + + comp.VerifyPdb( +@" + + + + + + + + + + + + + + + + + + + + + + + + + +", + format: DebugInformationFormat.Pdb); + + comp.VerifyPdb( +@" + + + + + + + + + + + + + +", + format: DebugInformationFormat.PortablePdb); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/74872")] + [Fact] + public void ExternAliases9() + { + string sourceA1 = +@" +namespace N +{ + public class A { } +} +"; + var comp = CreateCompilation(sourceA1, assemblyName: "A1"); + var refA1 = comp.EmitToImageReference(aliases: ImmutableArray.Create("A2", "A3")); + + string sourceA4 = +@" +namespace N +{ + public class A4 { } +} +"; + comp = CreateCompilation(sourceA4, assemblyName: "A4"); + var refA4 = comp.EmitToImageReference(aliases: ImmutableArray.Create("A5")); + + string sourceB = +@" +extern alias A3; +class B +{ + static void F(N2.A x) { } +} +"; + string sourceC = +@" +extern alias A2; +global using N2 = A2::N; +"; + comp = CreateCompilation([sourceB, sourceC], references: new[] { refA1, refA4 }, options: TestOptions.DebugDll); + comp.VerifyDiagnostics( + // (2,1): hidden CS8020: Unused extern alias. + // extern alias A3; + Diagnostic(ErrorCode.HDN_UnusedExternAlias, "extern alias A3;").WithLocation(2, 1) + ); + + comp.VerifyPdb( +@" + + + + + + + + + + + + + + + + + + + + + + + + + +", + format: DebugInformationFormat.Pdb); + + comp.VerifyPdb( +@" + + + + + + + + + + + + + +", + format: DebugInformationFormat.PortablePdb); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/74872")] + [Fact] + public void ExternAliases10() + { + string sourceA1 = +@" +namespace N +{ + public class A { } +} +"; + var comp = CreateCompilation(sourceA1, assemblyName: "A1"); + var refA1 = comp.EmitToImageReference(aliases: ImmutableArray.Create("A2")); + + string sourceA4 = +@" +namespace N +{ + public class A4 { } +} +"; + comp = CreateCompilation(sourceA4, assemblyName: "A4"); + var refA4 = comp.EmitToImageReference(aliases: ImmutableArray.Create("A5")); + + string sourceB = +@" +extern alias A5; +class B +{ + static void F(A x) { } +} +"; + string sourceC = +@" +extern alias A2; +global using A2::N; +"; + comp = CreateCompilation([sourceB, sourceC], references: new[] { refA1, refA4 }, options: TestOptions.DebugDll); + comp.VerifyDiagnostics( + // (2,1): hidden CS8020: Unused extern alias. + // extern alias A5; + Diagnostic(ErrorCode.HDN_UnusedExternAlias, "extern alias A5;").WithLocation(2, 1) + ); + + comp.VerifyPdb( +@" + + + + + + + + + + + + + + + + + + + + + + + + +", + format: DebugInformationFormat.Pdb); + + comp.VerifyPdb( +@" + + + + + + + + + + + + + +", + format: DebugInformationFormat.PortablePdb); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/74872")] + [Fact] + public void ExternAliases11() + { + string sourceA1 = +@" +namespace N +{ + public class A { } +} +"; + var comp = CreateCompilation(sourceA1, assemblyName: "A1"); + var refA1 = comp.EmitToImageReference(aliases: ImmutableArray.Create("A2")); + + string sourceA4 = +@" +namespace N +{ + public class A4 { } +} +"; + comp = CreateCompilation(sourceA4, assemblyName: "A4"); + var refA4 = comp.EmitToImageReference(aliases: ImmutableArray.Create("A5")); + + string sourceB = +@" +extern alias A5; +class B +{ + static void F(N2.A x) { } +} +"; + string sourceC = +@" +extern alias A2; +global using N2 = A2::N; +"; + comp = CreateCompilation([sourceB, sourceC], references: new[] { refA1, refA4 }, options: TestOptions.DebugDll); + comp.VerifyDiagnostics( + // (2,1): hidden CS8020: Unused extern alias. + // extern alias A5; + Diagnostic(ErrorCode.HDN_UnusedExternAlias, "extern alias A5;").WithLocation(2, 1) + ); + + comp.VerifyPdb( +@" + + + + + + + + + + + + + + + + + + + + + + + + +", + format: DebugInformationFormat.Pdb); + + comp.VerifyPdb( +@" + + + + + + + + + + + + + +", + format: DebugInformationFormat.PortablePdb); + } + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/25737")] public void TestExternAliases_ExplicitAndGlobal() { diff --git a/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs b/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs index e2146c1a9d70d..5b7acff12829c 100644 --- a/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs +++ b/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs @@ -414,36 +414,50 @@ private string SerializeVisualBasicImportTypeReference(ITypeReference typeRefere return result.ToStringAndFree(); } - private string GetAssemblyReferenceAlias(IAssemblyReference assembly, HashSet declaredExternAliases) - { - // no extern alias defined in scope at all -> error in compiler - Debug.Assert(declaredExternAliases != null); +#nullable enable + private string GetAssemblyReferenceAlias(IAssemblyReference assembly, HashSet? declaredExternAliases) + { var allAliases = _metadataWriter.Context.Module.GetAssemblyReferenceAliases(_metadataWriter.Context); - foreach (AssemblyReferenceAlias alias in allAliases) - { - // Multiple aliases may be given to an assembly reference. - // We find one that is in scope (was imported via extern alias directive). - // If multiple are in scope then use the first one. - // NOTE: Dev12 uses the one that appeared in source, whereas we use - // the first one that COULD have appeared in source. (DevDiv #913022) - // The reason we're not just using the alias from the syntax is that - // it is non-trivial to locate. In particular, since "." may be used in - // place of "::", determining whether the first identifier in the name is - // the alias requires binding. For example, "using A.B;" could refer to - // either "A::B" or "global::A.B". + if (declaredExternAliases is not null) + { + foreach (AssemblyReferenceAlias alias in allAliases) + { + // Multiple aliases may be given to an assembly reference. + // We find one that is in scope (was imported via extern alias directive). + // If multiple are in scope then use the first one. + + // NOTE: Dev12 uses the one that appeared in source, whereas we use + // the first one that COULD have appeared in source. (DevDiv #913022) + // The reason we're not just using the alias from the syntax is that + // it is non-trivial to locate. In particular, since "." may be used in + // place of "::", determining whether the first identifier in the name is + // the alias requires binding. For example, "using A.B;" could refer to + // either "A::B" or "global::A.B". + + if (assembly == alias.Assembly && declaredExternAliases.Contains(alias.Name)) + { + return alias.Name; + } + } + } - if (assembly == alias.Assembly && declaredExternAliases.Contains(alias.Name)) + // no alias defined in scope for given assembly -> must be a 'global' using, use the first defined alias + foreach (AssemblyReferenceAlias alias in allAliases) + { + if (assembly == alias.Assembly) { return alias.Name; } } - // no alias defined in scope for given assembly -> error in compiler + // no alias defined for given assembly -> error in compiler throw ExceptionUtilities.Unreachable(); } +#nullable disable + private void DefineLocalScopes(ImmutableArray scopes, StandaloneSignatureHandle localSignatureHandleOpt) { // VB scope ranges are end-inclusive