Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
# Claude code
.claude/

*.dll
14 changes: 13 additions & 1 deletion src/Refasmer/Importer/ImportLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,21 @@ private void ImportTypeDefinitionAccessories( TypeDefinitionHandle srcHandle, Ty
srcImpl =>
{
var body = Import(srcImpl.MethodBody);

// If the method body wasn't imported (e.g., private explicit interface implementation),
// skip importing the declaration. For generic interfaces, the declaration is a
// MemberReference whose import triggers signature processing. If the signature contains
// internal types that weren't preserved, this would throw UnknownTypeInSignature.
// See: https://github.com/JetBrains/Refasmer/issues/54
//
// Note: a non-generic interface method implementation is not a MemberReference, but a
// MethodDefinition which doesn't trigger signature processing (should be already in the method
// definition cache).
if (body.IsNil)
return default;
var decl = Import(srcImpl.MethodDeclaration);

return body.IsNil || decl.IsNil
return decl.IsNil
? default
: _builder.AddMethodImplementation(dstHandle, body, decl);
},
Expand Down
7 changes: 5 additions & 2 deletions tests/Refasmer.Tests/ExitCodeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ private async Task DoTest(int expectedCode, params string[] additionalArgs)
var outputPath = Path.GetTempFileName();
using var collector = CollectConsoleOutput();
var exitCode = ExecuteRefasmAndGetExitCode(assemblyPath, outputPath, additionalArgs);
if (exitCode != expectedCode)
{
await TestContext.Out.WriteLineAsync($"StdOut:\n{collector.StdOut}\nStdErr: {collector.StdErr}");
}
Assert.That(
exitCode,
Is.EqualTo(expectedCode),
$"Refasmer returned exit code {exitCode}, while {expectedCode} was expected." +
$" StdOut:\n{collector.StdOut}\nStdErr: {collector.StdErr}");
$"Refasmer returned exit code {exitCode}, while {expectedCode} was expected.");
}
}
16 changes: 11 additions & 5 deletions tests/Refasmer.Tests/IntegrationTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@ protected static async Task<string> BuildTestAssembly()
Console.WriteLine($"Building project {testProject}…");
var result = await Command.Run("dotnet", "build", testProject, "--configuration", "Release").Task;

if (result.ExitCode != 0)
{
await TestContext.Out.WriteLineAsync($"StdOut:\n{result.StandardOutput}\nStdErr: {result.StandardError}");
}

Assert.That(
result.ExitCode,
Is.EqualTo(0),
$"Failed to build test assembly, exit code {result.ExitCode}. StdOut:\n{result.StandardOutput}\nStdErr: {result.StandardError}");
$"Failed to build test assembly, exit code {result.ExitCode}.");

return Path.Combine(
root,
Expand Down Expand Up @@ -67,10 +72,11 @@ protected static string RefasmTestAssembly(string assemblyPath, bool omitNonApiM
var options = new[]{ "--omit-non-api-members", omitNonApiMembers.ToString(CultureInfo.InvariantCulture) };
using var collector = CollectConsoleOutput();
var exitCode = ExecuteRefasmAndGetExitCode(assemblyPath, outputPath, options);
Assert.That(
exitCode,
Is.EqualTo(0),
$"Refasmer returned exit code {exitCode}. StdOut:\n{collector.StdOut}\nStdErr: {collector.StdErr}");
if (exitCode != 0)
{
TestContext.Out.WriteLine($"Refasmer returned exit code {exitCode}. StdOut:\n{collector.StdOut}\nStdErr: {collector.StdErr}");
}
Assert.That(exitCode, Is.EqualTo(0));

return outputPath;
}
Expand Down
1 change: 1 addition & 0 deletions tests/Refasmer.Tests/IntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ await VerifyTypeContents(
[TestCase("PublicClassImplementingInternal", "IInterface1ToBeMarkedInternal")]
[TestCase("PublicClassWithInternalInterfaceImpl", "Class3ToBeMarkedInternal,IInterface2ToBeMarkedInternal`1")]
[TestCase("PublicClassWithInternalTypeInExplicitImpl", "IInterface3")]
[TestCase("ExplicitImplOfInternalInterface", "IInternalInterface`1")]
public async Task InternalTypeInPublicApi(string mainClassName, string auxiliaryClassNames)
{
var assemblyPath = await BuildTestAssemblyWithInternalTypeInPublicApi();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public class: RefasmerTestAssembly.ExplicitImplOfInternalInterface
- interface impl: RefasmerTestAssembly.IInternalInterface`1<System.String>
methods:
- .ctor(): System.Void:
internal interface: RefasmerTestAssembly.IInternalInterface`1
16 changes: 16 additions & 0 deletions tests/RefasmerTestAssembly/InternalInteraceImplBug.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace RefasmerTestAssembly;

// See #53 and #54.
public class ExplicitImplOfInternalInterface : IInternalInterface<string>
{
void IInternalInterface<string>.Method(string x, Internal2 y)
{
}
}

internal class Internal2{}

internal interface IInternalInterface<T>
{
void Method(T x, Internal2 y);
}