Skip to content

Commit 7446164

Browse files
committed
Add missing case for including interface members.
Add tests to verify interface members get included. Add test for interface type constraints, but needs more investigation.
1 parent 8eaa8c9 commit 7446164

File tree

2 files changed

+57
-1
lines changed

2 files changed

+57
-1
lines changed

src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/MemoryOutputDiffGenerator.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ private async Task<string> ProcessAfterAssemblyAsync(IAssemblySymbol afterAssemb
176176
MetadataReferences = loader.MetadataReferences,
177177
DiagnosticOptions = _diagnosticOptions,
178178
SyntaxRewriters = [
179+
// IMPORTANT: The order of these elements matters!
179180
new TypeDeclarationCSharpSyntaxRewriter(_addPartialModifier), // This must be visited BEFORE GlobalPrefixRemover as it depends on the 'global::' prefix to be found
180181
GlobalPrefixRemover.Singleton, // And then call this ASAP afterwards so there are fewer identifiers to visit
181182
PrimitiveSimplificationRewriter.Singleton,
@@ -643,7 +644,11 @@ m is EnumMemberDeclarationSyntax ||
643644

644645
private static IEnumerable<T> GetMembersOfType<T>(SyntaxNode node) where T : MemberDeclarationSyntax => node
645646
.ChildNodes()
646-
.Where(n => n is T m && IsEnumMemberOrHasPublicOrProtectedModifierOrIsDestructor(m))
647+
.Where(n => n is T m &&
648+
// Interface members have no visibility modifiers
649+
(node.IsKind(SyntaxKind.InterfaceDeclaration) ||
650+
// For the rest of the types, analyze each member directly
651+
IsEnumMemberOrHasPublicOrProtectedModifierOrIsDestructor(m)))
647652
.Cast<T>();
648653

649654
private string GetAttributeDocId(IMethodSymbol attributeConstructorSymbol, AttributeSyntax attribute, SemanticModel model)

test/Microsoft.DotNet.ApiDiff.Tests/Diff.Interface.Tests.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public struct MyStruct
2525
}
2626
public interface IMyInterface
2727
{
28+
int MyMethod();
29+
long MyProperty { get; }
2830
}
2931
}
3032
""",
@@ -33,6 +35,8 @@ namespace MyNamespace
3335
{
3436
+ public interface IMyInterface
3537
+ {
38+
+ int MyMethod();
39+
+ long MyProperty { get; }
3640
+ }
3741
}
3842
""");
@@ -44,6 +48,8 @@ namespace MyNamespace
4448
{
4549
public interface IMyBeforeInterface
4650
{
51+
int MyMethod();
52+
long MyProperty { get; }
4753
}
4854
}
4955
""",
@@ -52,6 +58,8 @@ namespace MyNamespace
5258
{
5359
public interface IMyAfterInterface
5460
{
61+
int MyMethod();
62+
long MyProperty { get; }
5563
}
5664
}
5765
""",
@@ -60,9 +68,13 @@ namespace MyNamespace
6068
{
6169
- public interface IMyBeforeInterface
6270
- {
71+
- int MyMethod();
72+
- long MyProperty { get; }
6373
- }
6474
+ public interface IMyAfterInterface
6575
+ {
76+
+ int MyMethod();
77+
+ long MyProperty { get; }
6678
+ }
6779
}
6880
""");
@@ -77,6 +89,8 @@ public struct MyStruct
7789
}
7890
public interface IMyInterface
7991
{
92+
int MyMethod();
93+
long MyProperty { get; }
8094
}
8195
}
8296
""",
@@ -93,9 +107,46 @@ namespace MyNamespace
93107
{
94108
- public interface IMyInterface
95109
- {
110+
+ int MyMethod();
111+
+ long MyProperty { get; }
96112
- }
97113
}
98114
""");
99115

116+
[Fact(Skip = "The resulting inheritance shows more than expected but not wrong, and does not show the nullability constraing")]
117+
// Shows: public interface IMyInterface<TKey, TValue> : System.Collections.Generic.IDictionary<TKey, TValue>, System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey, TValue>>, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue>>, System.Collections.IEnumerable, System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>, System.Collections.Generic.IReadOnlyCollection<System.Collections.Generic.KeyValuePair<TKey, TValue>>
118+
public Task InterfaceAddWithTypeConstraints() => RunTestAsync(
119+
beforeCode: """
120+
using System.Collections.Generic;
121+
namespace MyNamespace
122+
{
123+
public struct MyStruct
124+
{
125+
}
126+
}
127+
""",
128+
afterCode: """
129+
using System.Collections.Generic;
130+
namespace MyNamespace
131+
{
132+
public struct MyStruct
133+
{
134+
}
135+
public interface IMyInterface<TKey, TValue> : IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue> where TKey : notnull
136+
{
137+
bool ContainsValue(TValue value);
138+
}
139+
}
140+
""",
141+
expectedCode: """
142+
namespace MyNamespace
143+
{
144+
+ public interface IMyInterface<TKey, TValue> : System.Collections.Generic.IDictionary<TKey, TValue>, System.Collections.Generic.IReadOnlyDictionary<TKey, TValue> where TKey : notnull
145+
+ {
146+
+ bool ContainsValue(TValue value);
147+
+ }
148+
}
149+
""");
150+
100151
#endregion
101152
}

0 commit comments

Comments
 (0)