Skip to content

Commit 0c6c9aa

Browse files
committed
Partial events and constructors: public API
1 parent e7ce8c5 commit 0c6c9aa

File tree

9 files changed

+242
-7
lines changed

9 files changed

+242
-7
lines changed

src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1800,13 +1800,7 @@ private Symbol GetDeclaredMember(NamespaceOrTypeSymbol container, TextSpan decla
18001800
}
18011801

18021802
// Handle the case of the implementation of a partial member.
1803-
Symbol partial = symbol switch
1804-
{
1805-
MethodSymbol method => method.PartialImplementationPart,
1806-
SourcePropertySymbol property => property.PartialImplementationPart,
1807-
_ => null
1808-
};
1809-
1803+
Symbol partial = symbol.GetPartialImplementationPart();
18101804
if ((object)partial != null)
18111805
{
18121806
var loc = partial.GetFirstLocation();

src/Compilers/CSharp/Portable/Symbols/PublicModel/EventSymbol.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ ImmutableArray<IEventSymbol> IEventSymbol.ExplicitInterfaceImplementations
8888

8989
bool IEventSymbol.IsWindowsRuntimeEvent => _underlying.IsWindowsRuntimeEvent;
9090

91+
IEventSymbol? IEventSymbol.PartialDefinitionPart => (_underlying as SourceEventSymbol)?.PartialDefinitionPart.GetPublicSymbol();
92+
93+
IEventSymbol? IEventSymbol.PartialImplementationPart => (_underlying as SourceEventSymbol)?.PartialImplementationPart.GetPublicSymbol();
94+
95+
bool IEventSymbol.IsPartialDefinition => (_underlying as SourceEventSymbol)?.IsPartialDefinition ?? false;
96+
9197
#region ISymbol Members
9298

9399
protected override void Accept(SymbolVisitor visitor)

src/Compilers/CSharp/Test/Emit3/PartialEventsAndConstructorsTests.cs

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Collections.Immutable;
56
using System.Linq;
67
using System.Reflection;
78
using System.Runtime.InteropServices;
89
using Microsoft.CodeAnalysis.CSharp.Symbols;
10+
using Microsoft.CodeAnalysis.CSharp.Syntax;
911
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
1012
using Microsoft.CodeAnalysis.Test.Utilities;
1113
using Roslyn.Test.Utilities;
@@ -1131,6 +1133,188 @@ static void verifyMetadata(ModuleSymbol module)
11311133
}
11321134
}
11331135

1136+
[Fact]
1137+
public void GetDeclaredSymbol()
1138+
{
1139+
var source = ("""
1140+
partial class C
1141+
{
1142+
public partial event System.Action E, F;
1143+
public partial event System.Action E { add { } remove { } }
1144+
public partial event System.Action F { add { } remove { } }
1145+
1146+
public partial C();
1147+
public partial C() { }
1148+
}
1149+
""", "Program.cs");
1150+
1151+
var comp = CreateCompilation(source).VerifyDiagnostics();
1152+
var tree = comp.SyntaxTrees.Single();
1153+
var model = comp.GetSemanticModel(tree);
1154+
1155+
var eventDefs = tree.GetRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>().ToImmutableArray();
1156+
Assert.Equal(2, eventDefs.Length);
1157+
1158+
var eventImpls = tree.GetRoot().DescendantNodes().OfType<EventDeclarationSyntax>().ToImmutableArray();
1159+
Assert.Equal(2, eventImpls.Length);
1160+
1161+
{
1162+
var defSymbol = (IEventSymbol)model.GetDeclaredSymbol(eventDefs[0])!;
1163+
Assert.Equal("event System.Action C.E", defSymbol.ToTestDisplayString());
1164+
1165+
IEventSymbol implSymbol = model.GetDeclaredSymbol(eventImpls[0])!;
1166+
Assert.Equal("event System.Action C.E", implSymbol.ToTestDisplayString());
1167+
1168+
Assert.NotEqual(defSymbol, implSymbol);
1169+
Assert.Same(implSymbol, defSymbol.PartialImplementationPart);
1170+
Assert.Same(defSymbol, implSymbol.PartialDefinitionPart);
1171+
Assert.True(defSymbol.IsPartialDefinition);
1172+
Assert.False(implSymbol.IsPartialDefinition);
1173+
1174+
Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single());
1175+
}
1176+
1177+
{
1178+
var defSymbol = (IEventSymbol)model.GetDeclaredSymbol(eventDefs[1])!;
1179+
Assert.Equal("event System.Action C.F", defSymbol.ToTestDisplayString());
1180+
1181+
IEventSymbol implSymbol = model.GetDeclaredSymbol(eventImpls[1])!;
1182+
Assert.Equal("event System.Action C.F", implSymbol.ToTestDisplayString());
1183+
1184+
Assert.NotEqual(defSymbol, implSymbol);
1185+
Assert.Same(implSymbol, defSymbol.PartialImplementationPart);
1186+
Assert.Same(defSymbol, implSymbol.PartialDefinitionPart);
1187+
Assert.True(defSymbol.IsPartialDefinition);
1188+
Assert.False(implSymbol.IsPartialDefinition);
1189+
1190+
Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single());
1191+
}
1192+
1193+
{
1194+
var ctors = tree.GetRoot().DescendantNodes().OfType<ConstructorDeclarationSyntax>().ToImmutableArray();
1195+
Assert.Equal(2, ctors.Length);
1196+
1197+
IMethodSymbol defSymbol = model.GetDeclaredSymbol(ctors[0])!;
1198+
Assert.Equal("C..ctor()", defSymbol.ToTestDisplayString());
1199+
1200+
IMethodSymbol implSymbol = model.GetDeclaredSymbol(ctors[1])!;
1201+
Assert.Equal("C..ctor()", implSymbol.ToTestDisplayString());
1202+
1203+
Assert.NotEqual(defSymbol, implSymbol);
1204+
Assert.Same(implSymbol, defSymbol.PartialImplementationPart);
1205+
Assert.Same(defSymbol, implSymbol.PartialDefinitionPart);
1206+
Assert.True(defSymbol.IsPartialDefinition);
1207+
Assert.False(implSymbol.IsPartialDefinition);
1208+
1209+
Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single());
1210+
}
1211+
}
1212+
1213+
[Fact]
1214+
public void GetDeclaredSymbol_GenericContainer()
1215+
{
1216+
var source = ("""
1217+
partial class C<T>
1218+
{
1219+
public partial event System.Action E;
1220+
public partial event System.Action E { add { } remove { } }
1221+
1222+
public partial C();
1223+
public partial C() { }
1224+
}
1225+
""", "Program.cs");
1226+
1227+
var comp = CreateCompilation(source).VerifyDiagnostics();
1228+
var tree = comp.SyntaxTrees.Single();
1229+
var model = comp.GetSemanticModel(tree);
1230+
1231+
{
1232+
var defSymbol = (IEventSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>().Single())!;
1233+
Assert.Equal("event System.Action C<T>.E", defSymbol.ToTestDisplayString());
1234+
1235+
IEventSymbol implSymbol = model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType<EventDeclarationSyntax>().Single())!;
1236+
Assert.Equal("event System.Action C<T>.E", implSymbol.ToTestDisplayString());
1237+
1238+
Assert.NotEqual(defSymbol, implSymbol);
1239+
Assert.Same(implSymbol, defSymbol.PartialImplementationPart);
1240+
Assert.Same(defSymbol, implSymbol.PartialDefinitionPart);
1241+
1242+
Assert.True(defSymbol.IsPartialDefinition);
1243+
Assert.False(implSymbol.IsPartialDefinition);
1244+
1245+
Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single());
1246+
1247+
var intSymbol = comp.GetSpecialType(SpecialType.System_Int32);
1248+
var cOfTSymbol = defSymbol.ContainingType!;
1249+
var cOfIntSymbol = cOfTSymbol.Construct([intSymbol]);
1250+
1251+
var defOfIntSymbol = (IEventSymbol)cOfIntSymbol.GetMember("E");
1252+
Assert.Equal("event System.Action C<System.Int32>.E", defOfIntSymbol.ToTestDisplayString());
1253+
Assert.Null(defOfIntSymbol.PartialImplementationPart);
1254+
Assert.False(defOfIntSymbol.IsPartialDefinition);
1255+
}
1256+
1257+
{
1258+
var ctors = tree.GetRoot().DescendantNodes().OfType<ConstructorDeclarationSyntax>().ToImmutableArray();
1259+
Assert.Equal(2, ctors.Length);
1260+
1261+
IMethodSymbol defSymbol = model.GetDeclaredSymbol(ctors[0])!;
1262+
Assert.Equal("C<T>..ctor()", defSymbol.ToTestDisplayString());
1263+
1264+
IMethodSymbol implSymbol = model.GetDeclaredSymbol(ctors[1])!;
1265+
Assert.Equal("C<T>..ctor()", implSymbol.ToTestDisplayString());
1266+
1267+
Assert.NotEqual(defSymbol, implSymbol);
1268+
Assert.Same(implSymbol, defSymbol.PartialImplementationPart);
1269+
Assert.Same(defSymbol, implSymbol.PartialDefinitionPart);
1270+
1271+
Assert.True(defSymbol.IsPartialDefinition);
1272+
Assert.False(implSymbol.IsPartialDefinition);
1273+
1274+
Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single());
1275+
1276+
var intSymbol = comp.GetSpecialType(SpecialType.System_Int32);
1277+
var cOfTSymbol = defSymbol.ContainingType!;
1278+
var cOfIntSymbol = cOfTSymbol.Construct([intSymbol]);
1279+
1280+
var defOfIntSymbol = (IMethodSymbol)cOfIntSymbol.GetMember(".ctor");
1281+
Assert.Equal("C<System.Int32>..ctor()", defOfIntSymbol.ToTestDisplayString());
1282+
Assert.Null(defOfIntSymbol.PartialImplementationPart);
1283+
Assert.False(defOfIntSymbol.IsPartialDefinition);
1284+
}
1285+
}
1286+
1287+
[Fact]
1288+
public void GetDeclaredSymbol_ConstructorParameter()
1289+
{
1290+
var source = ("""
1291+
partial class C
1292+
{
1293+
public partial C(int i);
1294+
public partial C(int i) { }
1295+
}
1296+
""", "Program.cs");
1297+
1298+
var comp = CreateCompilation(source).VerifyDiagnostics();
1299+
var tree = comp.SyntaxTrees.Single();
1300+
var model = comp.GetSemanticModel(tree);
1301+
1302+
var parameters = tree.GetRoot().DescendantNodes().OfType<ParameterSyntax>().ToArray();
1303+
Assert.Equal(2, parameters.Length);
1304+
1305+
IParameterSymbol defSymbol = model.GetDeclaredSymbol(parameters[0])!;
1306+
Assert.Equal("System.Int32 i", defSymbol.ToTestDisplayString());
1307+
1308+
IParameterSymbol implSymbol = model.GetDeclaredSymbol(parameters[1])!;
1309+
Assert.Equal("System.Int32 i", implSymbol.ToTestDisplayString());
1310+
1311+
Assert.NotEqual(defSymbol, implSymbol);
1312+
Assert.Same(implSymbol, ((IMethodSymbol)defSymbol.ContainingSymbol).PartialImplementationPart!.Parameters.Single());
1313+
Assert.Same(defSymbol, ((IMethodSymbol)implSymbol.ContainingSymbol).PartialDefinitionPart!.Parameters.Single());
1314+
1315+
Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single());
1316+
}
1317+
11341318
[Fact]
11351319
public void SequencePoints()
11361320
{

src/Compilers/Core/Portable/PublicAPI.Unshipped.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ Microsoft.CodeAnalysis.GeneratorFilterContext.CancellationToken.get -> System.Th
1111
Microsoft.CodeAnalysis.GeneratorFilterContext.Generator.get -> Microsoft.CodeAnalysis.ISourceGenerator!
1212
Microsoft.CodeAnalysis.GeneratorFilterContext.GeneratorFilterContext() -> void
1313
Microsoft.CodeAnalysis.GeneratorRunResult.HostOutputs.get -> System.Collections.Immutable.ImmutableDictionary<string!, object!>!
14+
Microsoft.CodeAnalysis.IEventSymbol.IsPartialDefinition.get -> bool
15+
Microsoft.CodeAnalysis.IEventSymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IEventSymbol?
16+
Microsoft.CodeAnalysis.IEventSymbol.PartialImplementationPart.get -> Microsoft.CodeAnalysis.IEventSymbol?
1417
Microsoft.CodeAnalysis.IncrementalGeneratorOutputKind.Host = 8 -> Microsoft.CodeAnalysis.IncrementalGeneratorOutputKind
1518
Microsoft.CodeAnalysis.IncrementalGeneratorPostInitializationContext.AddEmbeddedAttributeDefinition() -> void
1619
Microsoft.CodeAnalysis.IPropertySymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IPropertySymbol?

src/Compilers/Core/Portable/Symbols/IEventSymbol.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,20 @@ public interface IEventSymbol : ISymbol
6565
/// Properties imported from metadata can explicitly implement more than one event.
6666
/// </remarks>
6767
ImmutableArray<IEventSymbol> ExplicitInterfaceImplementations { get; }
68+
69+
/// <summary>
70+
/// If this is a partial event implementation part, returns the corresponding definition part, otherwise <see langword="null"/>.
71+
/// </summary>
72+
IEventSymbol? PartialDefinitionPart { get; }
73+
74+
/// <summary>
75+
/// If this is a partial event definition part, returns the corresponding implementation part, otherwise <see langword="null"/>.
76+
/// </summary>
77+
IEventSymbol? PartialImplementationPart { get; }
78+
79+
/// <summary>
80+
/// Returns <see langword="true"/> if this is a partial definition part, otherwise <see langword="false"/>.
81+
/// </summary>
82+
bool IsPartialDefinition { get; }
6883
}
6984
}

src/Compilers/VisualBasic/Portable/Symbols/EventSymbol.vb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
323323
End Get
324324
End Property
325325

326+
Private ReadOnly Property IEventSymbol_PartialDefinitionPart As IEventSymbol Implements IEventSymbol.PartialDefinitionPart
327+
Get
328+
' Feature not supported in VB
329+
Return Nothing
330+
End Get
331+
End Property
332+
333+
Private ReadOnly Property IEventSymbol_PartialImplementationPart As IEventSymbol Implements IEventSymbol.PartialImplementationPart
334+
Get
335+
' Feature not supported in VB
336+
Return Nothing
337+
End Get
338+
End Property
339+
340+
Private ReadOnly Property IEventSymbol_IsPartialDefinition As Boolean Implements IEventSymbol.IsPartialDefinition
341+
Get
342+
' Feature not supported in VB
343+
Return False
344+
End Get
345+
End Property
346+
326347
Public Overrides Sub Accept(visitor As SymbolVisitor)
327348
visitor.VisitEvent(Me)
328349
End Sub

src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedEventSymbol.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,8 @@ public ImmutableArray<IEventSymbol> ExplicitInterfaceImplementations
3232
public IMethodSymbol? RemoveMethod => _symbol.RemoveMethod;
3333
public ITypeSymbol Type => _symbol.Type;
3434
public NullableAnnotation NullableAnnotation => _symbol.NullableAnnotation;
35+
public IEventSymbol? PartialDefinitionPart => _symbol.PartialDefinitionPart;
36+
public IEventSymbol? PartialImplementationPart => _symbol.PartialImplementationPart;
37+
public bool IsPartialDefinition => _symbol.IsPartialDefinition;
3538
}
3639
}

src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,10 +1168,13 @@ Microsoft.CodeAnalysis.IErrorTypeSymbol.get_CandidateSymbols
11681168
Microsoft.CodeAnalysis.IEventSymbol
11691169
Microsoft.CodeAnalysis.IEventSymbol.get_AddMethod
11701170
Microsoft.CodeAnalysis.IEventSymbol.get_ExplicitInterfaceImplementations
1171+
Microsoft.CodeAnalysis.IEventSymbol.get_IsPartialDefinition
11711172
Microsoft.CodeAnalysis.IEventSymbol.get_IsWindowsRuntimeEvent
11721173
Microsoft.CodeAnalysis.IEventSymbol.get_NullableAnnotation
11731174
Microsoft.CodeAnalysis.IEventSymbol.get_OriginalDefinition
11741175
Microsoft.CodeAnalysis.IEventSymbol.get_OverriddenEvent
1176+
Microsoft.CodeAnalysis.IEventSymbol.get_PartialDefinitionPart
1177+
Microsoft.CodeAnalysis.IEventSymbol.get_PartialImplementationPart
11751178
Microsoft.CodeAnalysis.IEventSymbol.get_RaiseMethod
11761179
Microsoft.CodeAnalysis.IEventSymbol.get_RemoveMethod
11771180
Microsoft.CodeAnalysis.IEventSymbol.get_Type

src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationEventSymbol.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,11 @@ public override TResult Accept<TArgument, TResult>(SymbolVisitor<TArgument, TRes
5959

6060
public IEventSymbol? OverriddenEvent => null;
6161

62+
public IEventSymbol? PartialImplementationPart => null;
63+
64+
public IEventSymbol? PartialDefinitionPart => null;
65+
66+
public bool IsPartialDefinition => false;
67+
6268
public static ImmutableArray<CustomModifier> TypeCustomModifiers => [];
6369
}

0 commit comments

Comments
 (0)