Skip to content

Commit 65f15ca

Browse files
authored
Partial events and constructors: public API (#77202)
* Partial events and constructors: public API * Simplify code * Add more `sealed` * Add more asserts
1 parent 818fe86 commit 65f15ca

File tree

13 files changed

+259
-14
lines changed

13 files changed

+259
-14
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/EventSymbol.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ internal virtual bool IsExplicitInterfaceImplementation
230230

231231
internal virtual EventSymbol? PartialImplementationPart => null;
232232
internal virtual EventSymbol? PartialDefinitionPart => null;
233+
internal virtual bool IsPartialDefinition => false;
233234

234235
/// <summary>
235236
/// Gets the kind of this symbol.

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.PartialDefinitionPart.GetPublicSymbol();
92+
93+
IEventSymbol? IEventSymbol.PartialImplementationPart => _underlying.PartialImplementationPart.GetPublicSymbol();
94+
95+
bool IEventSymbol.IsPartialDefinition => _underlying.IsPartialDefinition;
96+
9197
#region ISymbol Members
9298

9399
protected override void Accept(SymbolVisitor visitor)

src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,9 +340,9 @@ private void PartialConstructorChecks(SourceConstructorSymbol implementation, Bi
340340

341341
internal SourceConstructorSymbol? SourcePartialImplementationPart => IsPartialDefinition ? OtherPartOfPartial : null;
342342

343-
public override MethodSymbol? PartialDefinitionPart => SourcePartialDefinitionPart;
343+
public sealed override MethodSymbol? PartialDefinitionPart => SourcePartialDefinitionPart;
344344

345-
public override MethodSymbol? PartialImplementationPart => SourcePartialImplementationPart;
345+
public sealed override MethodSymbol? PartialImplementationPart => SourcePartialImplementationPart;
346346

347347
internal static void InitializePartialConstructorParts(SourceConstructorSymbol definition, SourceConstructorSymbol implementation)
348348
{

src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,7 @@ private void PartialEventChecks(SourceEventSymbol implementation, BindingDiagnos
911911
/// </summary>
912912
protected abstract bool AccessorsHaveImplementation { get; }
913913

914-
internal bool IsPartialDefinition => IsPartial && !AccessorsHaveImplementation && !HasExternModifier;
914+
internal sealed override bool IsPartialDefinition => IsPartial && !AccessorsHaveImplementation && !HasExternModifier;
915915

916916
internal bool IsPartialImplementation => IsPartial && (AccessorsHaveImplementation || HasExternModifier);
917917

@@ -921,9 +921,9 @@ private void PartialEventChecks(SourceEventSymbol implementation, BindingDiagnos
921921

922922
internal SourceEventSymbol? SourcePartialImplementationPart => IsPartialDefinition ? OtherPartOfPartial : null;
923923

924-
internal override EventSymbol? PartialDefinitionPart => SourcePartialDefinitionPart;
924+
internal sealed override EventSymbol? PartialDefinitionPart => SourcePartialDefinitionPart;
925925

926-
internal override EventSymbol? PartialImplementationPart => SourcePartialImplementationPart;
926+
internal sealed override EventSymbol? PartialImplementationPart => SourcePartialImplementationPart;
927927

928928
internal static void InitializePartialEventParts(SourceEventSymbol definition, SourceEventSymbol implementation)
929929
{

src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -791,8 +791,8 @@ private void PartialPropertyChecks(SourcePropertySymbol implementation, BindingD
791791
internal SourcePropertySymbol? SourcePartialDefinitionPart => IsPartialImplementation ? OtherPartOfPartial : null;
792792
internal SourcePropertySymbol? SourcePartialImplementationPart => IsPartialDefinition ? OtherPartOfPartial : null;
793793

794-
internal override PropertySymbol? PartialDefinitionPart => SourcePartialDefinitionPart;
795-
internal override PropertySymbol? PartialImplementationPart => SourcePartialImplementationPart;
794+
internal sealed override PropertySymbol? PartialDefinitionPart => SourcePartialDefinitionPart;
795+
internal sealed override PropertySymbol? PartialImplementationPart => SourcePartialImplementationPart;
796796

797797
internal static void InitializePartialPropertyParts(SourcePropertySymbol definition, SourcePropertySymbol implementation)
798798
{

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

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Reflection;
1010
using System.Runtime.InteropServices;
1111
using Microsoft.CodeAnalysis.CSharp.Symbols;
12+
using Microsoft.CodeAnalysis.CSharp.Syntax;
1213
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
1314
using Microsoft.CodeAnalysis.Test.Utilities;
1415
using Roslyn.Test.Utilities;
@@ -1656,6 +1657,198 @@ static void verifyMetadata(ModuleSymbol module)
16561657
}
16571658
}
16581659

1660+
[Fact]
1661+
public void GetDeclaredSymbol()
1662+
{
1663+
var source = ("""
1664+
partial class C
1665+
{
1666+
public partial event System.Action E, F;
1667+
public partial event System.Action E { add { } remove { } }
1668+
public partial event System.Action F { add { } remove { } }
1669+
1670+
public partial C();
1671+
public partial C() { }
1672+
}
1673+
""", "Program.cs");
1674+
1675+
var comp = CreateCompilation(source).VerifyDiagnostics();
1676+
var tree = comp.SyntaxTrees.Single();
1677+
var model = comp.GetSemanticModel(tree);
1678+
1679+
var eventDefs = tree.GetRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>().ToImmutableArray();
1680+
Assert.Equal(2, eventDefs.Length);
1681+
1682+
var eventImpls = tree.GetRoot().DescendantNodes().OfType<EventDeclarationSyntax>().ToImmutableArray();
1683+
Assert.Equal(2, eventImpls.Length);
1684+
1685+
{
1686+
var defSymbol = (IEventSymbol)model.GetDeclaredSymbol(eventDefs[0])!;
1687+
Assert.Equal("event System.Action C.E", defSymbol.ToTestDisplayString());
1688+
1689+
IEventSymbol implSymbol = model.GetDeclaredSymbol(eventImpls[0])!;
1690+
Assert.Equal("event System.Action C.E", implSymbol.ToTestDisplayString());
1691+
1692+
Assert.NotEqual(defSymbol, implSymbol);
1693+
Assert.Same(implSymbol, defSymbol.PartialImplementationPart);
1694+
Assert.Same(defSymbol, implSymbol.PartialDefinitionPart);
1695+
Assert.Null(implSymbol.PartialImplementationPart);
1696+
Assert.Null(defSymbol.PartialDefinitionPart);
1697+
Assert.True(defSymbol.IsPartialDefinition);
1698+
Assert.False(implSymbol.IsPartialDefinition);
1699+
1700+
Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single());
1701+
}
1702+
1703+
{
1704+
var defSymbol = (IEventSymbol)model.GetDeclaredSymbol(eventDefs[1])!;
1705+
Assert.Equal("event System.Action C.F", defSymbol.ToTestDisplayString());
1706+
1707+
IEventSymbol implSymbol = model.GetDeclaredSymbol(eventImpls[1])!;
1708+
Assert.Equal("event System.Action C.F", implSymbol.ToTestDisplayString());
1709+
1710+
Assert.NotEqual(defSymbol, implSymbol);
1711+
Assert.Same(implSymbol, defSymbol.PartialImplementationPart);
1712+
Assert.Same(defSymbol, implSymbol.PartialDefinitionPart);
1713+
Assert.Null(implSymbol.PartialImplementationPart);
1714+
Assert.Null(defSymbol.PartialDefinitionPart);
1715+
Assert.True(defSymbol.IsPartialDefinition);
1716+
Assert.False(implSymbol.IsPartialDefinition);
1717+
1718+
Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single());
1719+
}
1720+
1721+
{
1722+
var ctors = tree.GetRoot().DescendantNodes().OfType<ConstructorDeclarationSyntax>().ToImmutableArray();
1723+
Assert.Equal(2, ctors.Length);
1724+
1725+
IMethodSymbol defSymbol = model.GetDeclaredSymbol(ctors[0])!;
1726+
Assert.Equal("C..ctor()", defSymbol.ToTestDisplayString());
1727+
1728+
IMethodSymbol implSymbol = model.GetDeclaredSymbol(ctors[1])!;
1729+
Assert.Equal("C..ctor()", implSymbol.ToTestDisplayString());
1730+
1731+
Assert.NotEqual(defSymbol, implSymbol);
1732+
Assert.Same(implSymbol, defSymbol.PartialImplementationPart);
1733+
Assert.Same(defSymbol, implSymbol.PartialDefinitionPart);
1734+
Assert.Null(implSymbol.PartialImplementationPart);
1735+
Assert.Null(defSymbol.PartialDefinitionPart);
1736+
Assert.True(defSymbol.IsPartialDefinition);
1737+
Assert.False(implSymbol.IsPartialDefinition);
1738+
1739+
Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single());
1740+
}
1741+
}
1742+
1743+
[Fact]
1744+
public void GetDeclaredSymbol_GenericContainer()
1745+
{
1746+
var source = ("""
1747+
partial class C<T>
1748+
{
1749+
public partial event System.Action E;
1750+
public partial event System.Action E { add { } remove { } }
1751+
1752+
public partial C();
1753+
public partial C() { }
1754+
}
1755+
""", "Program.cs");
1756+
1757+
var comp = CreateCompilation(source).VerifyDiagnostics();
1758+
var tree = comp.SyntaxTrees.Single();
1759+
var model = comp.GetSemanticModel(tree);
1760+
1761+
{
1762+
var defSymbol = (IEventSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>().Single())!;
1763+
Assert.Equal("event System.Action C<T>.E", defSymbol.ToTestDisplayString());
1764+
1765+
IEventSymbol implSymbol = model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType<EventDeclarationSyntax>().Single())!;
1766+
Assert.Equal("event System.Action C<T>.E", implSymbol.ToTestDisplayString());
1767+
1768+
Assert.NotEqual(defSymbol, implSymbol);
1769+
Assert.Same(implSymbol, defSymbol.PartialImplementationPart);
1770+
Assert.Same(defSymbol, implSymbol.PartialDefinitionPart);
1771+
Assert.Null(implSymbol.PartialImplementationPart);
1772+
Assert.Null(defSymbol.PartialDefinitionPart);
1773+
Assert.True(defSymbol.IsPartialDefinition);
1774+
Assert.False(implSymbol.IsPartialDefinition);
1775+
1776+
Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single());
1777+
1778+
var intSymbol = comp.GetSpecialType(SpecialType.System_Int32);
1779+
var cOfTSymbol = defSymbol.ContainingType!;
1780+
var cOfIntSymbol = cOfTSymbol.Construct([intSymbol]);
1781+
1782+
var defOfIntSymbol = (IEventSymbol)cOfIntSymbol.GetMember("E");
1783+
Assert.Equal("event System.Action C<System.Int32>.E", defOfIntSymbol.ToTestDisplayString());
1784+
Assert.Null(defOfIntSymbol.PartialImplementationPart);
1785+
Assert.Null(defOfIntSymbol.PartialDefinitionPart);
1786+
Assert.False(defOfIntSymbol.IsPartialDefinition);
1787+
}
1788+
1789+
{
1790+
var ctors = tree.GetRoot().DescendantNodes().OfType<ConstructorDeclarationSyntax>().ToImmutableArray();
1791+
Assert.Equal(2, ctors.Length);
1792+
1793+
IMethodSymbol defSymbol = model.GetDeclaredSymbol(ctors[0])!;
1794+
Assert.Equal("C<T>..ctor()", defSymbol.ToTestDisplayString());
1795+
1796+
IMethodSymbol implSymbol = model.GetDeclaredSymbol(ctors[1])!;
1797+
Assert.Equal("C<T>..ctor()", implSymbol.ToTestDisplayString());
1798+
1799+
Assert.NotEqual(defSymbol, implSymbol);
1800+
Assert.Same(implSymbol, defSymbol.PartialImplementationPart);
1801+
Assert.Same(defSymbol, implSymbol.PartialDefinitionPart);
1802+
Assert.Null(implSymbol.PartialImplementationPart);
1803+
Assert.Null(defSymbol.PartialDefinitionPart);
1804+
Assert.True(defSymbol.IsPartialDefinition);
1805+
Assert.False(implSymbol.IsPartialDefinition);
1806+
1807+
Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single());
1808+
1809+
var intSymbol = comp.GetSpecialType(SpecialType.System_Int32);
1810+
var cOfTSymbol = defSymbol.ContainingType!;
1811+
var cOfIntSymbol = cOfTSymbol.Construct([intSymbol]);
1812+
1813+
var defOfIntSymbol = (IMethodSymbol)cOfIntSymbol.GetMember(".ctor");
1814+
Assert.Equal("C<System.Int32>..ctor()", defOfIntSymbol.ToTestDisplayString());
1815+
Assert.Null(defOfIntSymbol.PartialImplementationPart);
1816+
Assert.Null(defOfIntSymbol.PartialDefinitionPart);
1817+
Assert.False(defOfIntSymbol.IsPartialDefinition);
1818+
}
1819+
}
1820+
1821+
[Fact]
1822+
public void GetDeclaredSymbol_ConstructorParameter()
1823+
{
1824+
var source = ("""
1825+
partial class C
1826+
{
1827+
public partial C(int i);
1828+
public partial C(int i) { }
1829+
}
1830+
""", "Program.cs");
1831+
1832+
var comp = CreateCompilation(source).VerifyDiagnostics();
1833+
var tree = comp.SyntaxTrees.Single();
1834+
var model = comp.GetSemanticModel(tree);
1835+
1836+
var parameters = tree.GetRoot().DescendantNodes().OfType<ParameterSyntax>().ToArray();
1837+
Assert.Equal(2, parameters.Length);
1838+
1839+
IParameterSymbol defSymbol = model.GetDeclaredSymbol(parameters[0])!;
1840+
Assert.Equal("System.Int32 i", defSymbol.ToTestDisplayString());
1841+
1842+
IParameterSymbol implSymbol = model.GetDeclaredSymbol(parameters[1])!;
1843+
Assert.Equal("System.Int32 i", implSymbol.ToTestDisplayString());
1844+
1845+
Assert.NotEqual(defSymbol, implSymbol);
1846+
Assert.Same(implSymbol, ((IMethodSymbol)defSymbol.ContainingSymbol).PartialImplementationPart!.Parameters.Single());
1847+
Assert.Same(defSymbol, ((IMethodSymbol)implSymbol.ContainingSymbol).PartialDefinitionPart!.Parameters.Single());
1848+
1849+
Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single());
1850+
}
1851+
16591852
[Fact]
16601853
public void SequencePoints()
16611854
{

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

0 commit comments

Comments
 (0)