dotnet / dotnet/sdk

API Compat reports CP0008 false positive for internal interfaces from friend assemblies

Open
#54,451 1 comment 0 reactions 0 assignees View on GitHub
Area-ApiCompat untriaged
Dominant language
C#
Stars
3.2k
Forks
1.3k
PR merge metrics
PR metrics pending

Description

# API Compat reports CP0008 false positive for internal interfaces from friend assemblies

## Description

API Compat incorrectly reports CP0008 errors when comparing assemblies that implement internal interfaces from friend assemblies (via `InternalsVisibleTo`), even when the interface implementations haven't changed. Since internal interfaces are not part of the public API contract, these should not be reported as breaking changes.

## Steps to Reproduce

### 1. Create AssemblyA with internal interface

**AssemblyA.csproj:**
```xml


net11.0



```

**IInternal.cs:**
```csharp
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("AssemblyB")]

namespace TestNamespace
{
internal interface IInternalInterface
{
string GetValue();
}

public interface IPublicInterface
{
void DoWork();
}
}
```

Build: `dotnet build AssemblyA.csproj -o shared`

### 2. Create AssemblyB v1.0.0

**AssemblyB.csproj:**
```xml


net11.0
1.0.0



```

**MyClass.cs:**
```csharp
using TestNamespace;

namespace AssemblyB
{
public class MyClass : IPublicInterface, IInternalInterface
{
public void DoWork() { }
public string GetValue() => "v1.0.0";
}
}
```

Pack: `dotnet pack AssemblyB.csproj -o local-nuget`

### 3. Update to v1.1.0 with package validation

Update **AssemblyB.csproj:**
```xml


net11.0
1.1.0


true
1.0.0



```

Update **MyClass.cs** (bug fix only - no API change):
```csharp
using TestNamespace;

namespace AssemblyB
{
public class MyClass : IPublicInterface, IInternalInterface
{
public void DoWork() { }
public string GetValue() => "v1.1.0 - bug fix"; // <-- Only change
}
}
```

Create **nuget.config:**
```xml




```

Pack: `dotnet pack`

## Expected Behavior

No API compatibility errors should be reported because:
1. Both versions implement the same interfaces (IPublicInterface + IInternalInterface)
2. Only internal implementation changed (return value)
3. IInternalInterface is internal and not part of the public API surface
4. External consumers cannot see or depend on IInternalInterface

## Actual Behavior

```
error CP0008: Type 'AssemblyB.MyClass' does not implement interface 'TestNs.IPublicInterface'
on lib/net11.0/AssemblyB.dll but it does on [Baseline] lib/net11.0/AssemblyB.dll
```

API Compat reports CP0008 even though both versions implement identical interfaces.

## Real-World Impact

This issue affects .NET source-build infrastructure (dotnet/source-build-assets) where GenAPI generates reference source from baseline NuGet packages. When validating these packages:

**Microsoft.Build.Utilities.Core 17.11.48:**
- `IMetadataContainer` is **internal** in Microsoft.Build.Framework
- Microsoft.Build.Framework grants `InternalsVisibleTo("Microsoft.Build.Utilities.Core")`
- GenAPI correctly omits this internal interface (not public API)
- API Compat incorrectly reports CP0008

Currently requires manual suppressions in:
- microsoft.build.utilities.core/17.11.48 (IMetadataContainer)
- microsoft.build/17.11.48 (ITranslatable on 22 types, IBuildCheckResult)

## Environment

- .NET SDK: 11.0.100-preview.5.26227.104
- OS: Linux

## Related Issues

- #45995 - CP0008 false positive for generic interfaces where the type argument is internal

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.