diff --git a/test/Compiler.Tests/Emit/ImportedExtensionDeclaringAssemblyInvariantEmitTests.cs b/test/Compiler.Tests/Emit/ImportedExtensionDeclaringAssemblyInvariantEmitTests.cs
new file mode 100644
index 00000000..b8c0307a
--- /dev/null
+++ b/test/Compiler.Tests/Emit/ImportedExtensionDeclaringAssemblyInvariantEmitTests.cs
@@ -0,0 +1,209 @@
+//
+// Copyright (C) GSharp Authors. All rights reserved.
+//
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Xunit;
+
+namespace GSharp.Compiler.Tests.Emit;
+
+///
+/// Verifies that an imported extension method's MemberRef retains its
+/// assembly identity
+/// when another reference defines the same fully-qualified host type.
+///
+public sealed class ImportedExtensionDeclaringAssemblyInvariantEmitTests
+{
+ private const string PrimarySource = """
+ namespace ClassLibrary1;
+
+ public sealed class A { }
+
+ public static class B
+ {
+ public static string Call(this A value) => "correct";
+ }
+ """;
+
+ private const string ShadowSource = """
+ namespace ClassLibrary1;
+
+ public static class B
+ {
+ public static string Shadow(this A value) => "shadow";
+ }
+ """;
+
+ private const string GSharpSource = """
+ package MyApp
+
+ import System
+ import ClassLibrary1
+
+ var a = A()
+ Console.WriteLine(a.Call())
+ """;
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void MemberRef_PreservesDeclaringAssembly_RegardlessOfReferenceOrder(bool shadowReferenceFirst)
+ {
+ using var artifacts = Compile(shadowReferenceFirst);
+
+ IlVerifier.Verify(
+ artifacts.OutputPath,
+ additionalReferences: new[] { artifacts.PrimaryPath, artifacts.ShadowPath });
+
+ using var stream = File.OpenRead(artifacts.OutputPath);
+ using var peReader = new PEReader(stream);
+ var reader = peReader.GetMetadataReader();
+ var callHandle = Assert.Single(
+ reader.MemberReferences,
+ handle => reader.GetString(reader.GetMemberReference(handle).Name) == "Call");
+ var call = reader.GetMemberReference(callHandle);
+ Assert.Equal(HandleKind.TypeReference, call.Parent.Kind);
+ var parent = reader.GetTypeReference((TypeReferenceHandle)call.Parent);
+ Assert.Equal(HandleKind.AssemblyReference, parent.ResolutionScope.Kind);
+ var assembly = reader.GetAssemblyReference((AssemblyReferenceHandle)parent.ResolutionScope);
+
+ Assert.Equal("ClassLibrary1", reader.GetString(assembly.Name));
+ Assert.Equal("correct\n", Run(artifacts.OutputPath));
+ }
+
+ private static CompilationArtifacts Compile(bool shadowReferenceFirst)
+ {
+ var directory = Path.Combine(
+ AppContext.BaseDirectory,
+ "imported-extension-assembly-identity-artifacts",
+ Guid.NewGuid().ToString("N"));
+ Directory.CreateDirectory(directory);
+
+ var primaryPath = Path.Combine(directory, "ClassLibrary1.dll");
+ EmitFixture("ClassLibrary1", PrimarySource, primaryPath);
+ var shadowPath = Path.Combine(directory, "AClassLibrary3.dll");
+ EmitFixture(
+ "AClassLibrary3",
+ ShadowSource,
+ shadowPath,
+ new[] { MetadataReference.CreateFromFile(primaryPath) });
+
+ var sourcePath = Path.Combine(directory, "Program.gs");
+ var outputPath = Path.Combine(directory, "MyApp.dll");
+ File.WriteAllText(sourcePath, GSharpSource);
+
+ using var stdout = new StringWriter();
+ using var stderr = new StringWriter();
+ var previousOut = Console.Out;
+ var previousError = Console.Error;
+ Console.SetOut(stdout);
+ Console.SetError(stderr);
+ int exitCode;
+ try
+ {
+ var firstReference = shadowReferenceFirst ? shadowPath : primaryPath;
+ var secondReference = shadowReferenceFirst ? primaryPath : shadowPath;
+ exitCode = Program.Main(
+ new[]
+ {
+ "/out:" + outputPath,
+ "/target:exe",
+ "/targetframework:net10.0",
+ "/reference:" + firstReference,
+ "/reference:" + secondReference,
+ sourcePath,
+ });
+ }
+ finally
+ {
+ Console.SetOut(previousOut);
+ Console.SetError(previousError);
+ }
+
+ var diagnostics = stdout.ToString() + stderr.ToString();
+ Assert.True(exitCode == 0, $"gsc failed:\n{diagnostics}");
+ return new CompilationArtifacts(directory, primaryPath, shadowPath, outputPath);
+ }
+
+ private static void EmitFixture(
+ string assemblyName,
+ string source,
+ string path,
+ MetadataReference[] additionalReferences = null)
+ {
+ var references = ((string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")!)
+ .Split(Path.PathSeparator)
+ .Select(reference => MetadataReference.CreateFromFile(reference))
+ .Concat(additionalReferences ?? Array.Empty());
+ var compilation = CSharpCompilation.Create(
+ assemblyName,
+ new[] { CSharpSyntaxTree.ParseText(source) },
+ references,
+ new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
+ var result = compilation.Emit(path);
+ Assert.True(result.Success, string.Join(Environment.NewLine, result.Diagnostics));
+ }
+
+ private static string Run(string assemblyPath)
+ {
+ var startInfo = new ProcessStartInfo("dotnet")
+ {
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ WorkingDirectory = Path.GetDirectoryName(assemblyPath)!,
+ };
+ startInfo.ArgumentList.Add("exec");
+ startInfo.ArgumentList.Add("--runtimeconfig");
+ startInfo.ArgumentList.Add(Path.ChangeExtension(assemblyPath, ".runtimeconfig.json"));
+ startInfo.ArgumentList.Add(assemblyPath);
+
+ using var process = Process.Start(startInfo)!;
+ var stdout = process.StandardOutput.ReadToEnd();
+ var stderr = process.StandardError.ReadToEnd();
+ Assert.True(process.WaitForExit(30_000), "dotnet exec timed out.");
+ Assert.True(process.ExitCode == 0, $"exited {process.ExitCode}\n{stderr}");
+ return stdout.Replace("\r\n", "\n", StringComparison.Ordinal);
+ }
+
+ private sealed class CompilationArtifacts : IDisposable
+ {
+ public CompilationArtifacts(
+ string directory,
+ string primaryPath,
+ string shadowPath,
+ string outputPath)
+ {
+ Directory = directory;
+ PrimaryPath = primaryPath;
+ ShadowPath = shadowPath;
+ OutputPath = outputPath;
+ }
+
+ public string Directory { get; }
+
+ public string PrimaryPath { get; }
+
+ public string ShadowPath { get; }
+
+ public string OutputPath { get; }
+
+ public void Dispose()
+ {
+ try
+ {
+ System.IO.Directory.Delete(Directory, recursive: true);
+ }
+ catch
+ {
+ }
+ }
+ }
+}