From b8010925d91086e63952e4a53439db7eaf7c2bd3 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Mon, 20 May 2024 14:41:14 -0700 Subject: [PATCH 01/18] Add public API --- .../Portable/Symbols/PublicModel/PropertySymbol.cs | 6 ++++++ .../Core/Portable/PublicAPI.Unshipped.txt | 2 ++ .../Core/Portable/Symbols/IPropertySymbol.cs | 12 ++++++++++++ .../VisualBasic/Portable/Symbols/PropertySymbol.vb | 14 ++++++++++++++ ...etadataAsSourceService.WrappedPropertySymbol.cs | 4 ++++ .../Symbols/CodeGenerationPropertySymbol.cs | 4 ++++ 6 files changed, 42 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PropertySymbol.cs index 90182156c74f3..6f969a9dae145 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PropertySymbol.cs @@ -109,6 +109,12 @@ ImmutableArray IPropertySymbol.RefCustomModifiers RefKind IPropertySymbol.RefKind => _underlying.RefKind; +#nullable enable + IPropertySymbol? IPropertySymbol.PartialDefinitionPart => (_underlying as SourcePropertySymbol)?.PartialDefinitionPart.GetPublicSymbol(); + + IPropertySymbol? IPropertySymbol.PartialImplementationPart => (_underlying as SourcePropertySymbol)?.PartialImplementationPart.GetPublicSymbol(); +#nullable disable + #region ISymbol Members protected override void Accept(SymbolVisitor visitor) diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index e5fe82d74f013..1f8ef22613f47 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -1,5 +1,7 @@ Microsoft.CodeAnalysis.IParameterSymbol.IsParamsCollection.get -> bool Microsoft.CodeAnalysis.IParameterSymbol.IsParamsArray.get -> bool +Microsoft.CodeAnalysis.IPropertySymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IPropertySymbol? +Microsoft.CodeAnalysis.IPropertySymbol.PartialImplementationPart.get -> Microsoft.CodeAnalysis.IMethodSymbol? Microsoft.CodeAnalysis.Operations.ArgumentKind.ParamCollection = 4 -> Microsoft.CodeAnalysis.Operations.ArgumentKind Microsoft.CodeAnalysis.Diagnostics.SuppressionInfo.ProgrammaticSuppressions.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Emit.InstrumentationKind.ModuleCancellation = 3 -> Microsoft.CodeAnalysis.Emit.InstrumentationKind diff --git a/src/Compilers/Core/Portable/Symbols/IPropertySymbol.cs b/src/Compilers/Core/Portable/Symbols/IPropertySymbol.cs index 177e264f5aaa6..df699e7e8efe1 100644 --- a/src/Compilers/Core/Portable/Symbols/IPropertySymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/IPropertySymbol.cs @@ -110,5 +110,17 @@ public interface IPropertySymbol : ISymbol /// The list of custom modifiers, if any, associated with the type of the property. /// ImmutableArray TypeCustomModifiers { get; } + + /// + /// If this is a partial property implementation part, returns the corresponding + /// definition part. Otherwise null. + /// + IPropertySymbol? PartialDefinitionPart { get; } + + /// + /// If this is a partial property definition part, returns the corresponding + /// implementation part. Otherwise null. + /// + IPropertySymbol? PartialImplementationPart { get; } } } diff --git a/src/Compilers/VisualBasic/Portable/Symbols/PropertySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/PropertySymbol.vb index 22cdc43eb0c50..8fc082a76b298 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/PropertySymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/PropertySymbol.vb @@ -621,6 +621,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Private ReadOnly Property IPropertySymbol_PartialDefinitionPart As IPropertySymbol Implements IPropertySymbol.PartialDefinitionPart + Get + ' Feature not supported in VB + Return Nothing + End Get + End Property + + Private ReadOnly Property IPropertySymbol_PartialImplementationPart As IPropertySymbol Implements IPropertySymbol.PartialImplementationPart + Get + ' Feature not supported in VB + Return Nothing + End Get + End Property + Public Overrides Sub Accept(visitor As SymbolVisitor) visitor.VisitProperty(Me) End Sub diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedPropertySymbol.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedPropertySymbol.cs index b29838a199da9..26909af1aed0e 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedPropertySymbol.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedPropertySymbol.cs @@ -66,5 +66,9 @@ public ImmutableArray ExplicitInterfaceImplementations return this; } } + + public IPropertySymbol PartialDefinitionPart => _symbol.PartialDefinitionPart; + + public IPropertySymbol PartialImplementationPart => _symbol.PartialImplementationPart; } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationPropertySymbol.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationPropertySymbol.cs index 242f6901f374b..13fbe0c46657b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationPropertySymbol.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationPropertySymbol.cs @@ -85,4 +85,8 @@ public override TResult Accept(SymbolVisitor RefCustomModifiers => []; public ImmutableArray TypeCustomModifiers => []; + + public IPropertySymbol PartialImplementationPart => null; + + public IPropertySymbol PartialDefinitionPart => null; } From 7079e746401e188f0435e514e97b682a555a13a8 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Mon, 20 May 2024 16:41:46 -0700 Subject: [PATCH 02/18] fix public API file --- src/Compilers/Core/Portable/PublicAPI.Unshipped.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 1f8ef22613f47..b8aee812cc609 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -1,7 +1,7 @@ Microsoft.CodeAnalysis.IParameterSymbol.IsParamsCollection.get -> bool Microsoft.CodeAnalysis.IParameterSymbol.IsParamsArray.get -> bool Microsoft.CodeAnalysis.IPropertySymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IPropertySymbol? -Microsoft.CodeAnalysis.IPropertySymbol.PartialImplementationPart.get -> Microsoft.CodeAnalysis.IMethodSymbol? +Microsoft.CodeAnalysis.IPropertySymbol.PartialImplementationPart.get -> Microsoft.CodeAnalysis.IPropertySymbol? Microsoft.CodeAnalysis.Operations.ArgumentKind.ParamCollection = 4 -> Microsoft.CodeAnalysis.Operations.ArgumentKind Microsoft.CodeAnalysis.Diagnostics.SuppressionInfo.ProgrammaticSuppressions.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Emit.InstrumentationKind.ModuleCancellation = 3 -> Microsoft.CodeAnalysis.Emit.InstrumentationKind From b148d3f6152c1ad079c74b23b8555732d8af452e Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Mon, 20 May 2024 18:13:39 -0700 Subject: [PATCH 03/18] go to def --- .../CSharpTest/NavigateTo/NavigateToTests.cs | 14 ++++++++ .../CSharpGoToDefinitionTests.vb | 33 +++++++++++++++++++ .../GoToDefinitionFeatureHelpers.cs | 6 ++-- .../Definitions/GoToDefinitionTests.cs | 24 ++++++++++++++ 4 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs index 14f570ec10efa..eb158639b3394 100644 --- a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs +++ b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs @@ -610,6 +610,20 @@ public async Task FindPartialMethods(TestHost testHost, Composition composition) }); } + [Theory, CombinatorialData] + public async Task FindPartialProperties(TestHost testHost, Composition composition) + { + await TestAsync(testHost, composition, "partial class Goo { partial int Prop { get; set; } } partial class Goo { partial int Prop { get => 1; set { } } }", async w => + { + var expecteditem1 = new NavigateToItem("Prop", NavigateToItemKind.Property, "csharp", null, null, s_emptyExactPatternMatch, null); + var expecteditems = new List { expecteditem1, expecteditem1 }; + + var items = await _aggregator.GetItemsAsync("Prop"); + + VerifyNavigateToResultItems(expecteditems, items); + }); + } + [Theory, CombinatorialData] public async Task FindPartialMethodDefinitionOnly(TestHost testHost, Composition composition) { diff --git a/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb b/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb index af34b5fed0fbe..0c890ed6a17b2 100644 --- a/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb +++ b/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb @@ -277,6 +277,39 @@ class Program Await TestAsync(workspace) End Function + + Public Async Function TestCSharpGotoDefinitionPartialProperty() As Task + Dim workspace = + + + + partial class Test + { + public partial int Prop { get; set; } + } + + + partial class Test + { + void Goo() + { + var t = new Test(); + int i = t.Prop$$; + } + + public partial void [|Prop|] + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + } + + + + + Await TestAsync(workspace) + End Function + Public Async Function TestCSharpGotoDefinitionOnMethodCall1() As Task Dim workspace = diff --git a/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs b/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs index a28e87d1ad407..7dd952ed4105a 100644 --- a/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs +++ b/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs @@ -48,11 +48,13 @@ internal static class GoToDefinitionFeatureHelpers symbol = definition ?? symbol; - // If it is a partial method declaration with no body, choose to go to the implementation - // that has a method body. + // If symbol has a partial implementation part, prefer to go to it, since that is where the body is. if (symbol is IMethodSymbol method) symbol = method.PartialImplementationPart ?? symbol; + if (symbol is IPropertySymbol property) + symbol = property.PartialImplementationPart ?? symbol; + return symbol; } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs index 96b573de9de56..b80b425c49eb1 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs @@ -181,6 +181,30 @@ public partial class C AssertLocationsEqual(testLspServer.GetLocations("definition"), results); } + [Theory, CombinatorialData] + [WorkItem("https://github.com/dotnet/vscode-csharp/issues/5740")] + public async Task TestGotoDefinitionPartialProperties(bool mutatingLspWorkspace) + { + var markup = + """ + using System; + + public partial class C + { + partial int {|caret:|}Prop { get; set; } + } + + public partial class C + { + partial int {|definition:Prop|} { get => 1; set { } } + } + """; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + + var results = await RunGotoDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); + AssertLocationsEqual(testLspServer.GetLocations("definition"), results); + } + [Theory] [InlineData("ValueTuple valueTuple1;")] [InlineData("ValueTuple valueTuple2;")] From 8bf8b4d80f04a17614a5d835d935fa09afa73703 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Mon, 20 May 2024 18:13:57 -0700 Subject: [PATCH 04/18] find references (wip) --- .../Compilation/SyntaxTreeSemanticModel.cs | 12 +++++--- .../FindReferencesTests.PropertySymbols.vb | 29 ++++++++++++++++++- .../Finders/PropertySymbolReferenceFinder.cs | 14 +++++++++ 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 602946b93212e..2b85e7a878ed7 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -1799,10 +1799,14 @@ private Symbol GetDeclaredMember(NamespaceOrTypeSymbol container, TextSpan decla zeroWidthMatch = symbol; } - // Handle the case of the implementation of a partial method. - var partial = symbol.Kind == SymbolKind.Method - ? ((MethodSymbol)symbol).PartialImplementationPart - : null; + // Handle the case of the implementation of a partial member. + Symbol? partial = symbol switch + { + MethodSymbol method => method.PartialImplementationPart, + SourcePropertySymbol property => property.PartialImplementationPart, + _ => null + }; + if ((object)partial != null) { var loc = partial.GetFirstLocation(); diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb index 31e2400d4036f..5c60b1446be04 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb @@ -1547,7 +1547,7 @@ record Goo(int x, int {|Definition:$$y|}) class P { static void Main() - { +{ var f = new Goo(y: 1); Console.WriteLine(f.[|y|]); } @@ -1558,5 +1558,32 @@ class P Await TestAPIAndFeature(input, kind, host) End Function + + + + Public Async Function TestCSharp_PartialProperty1(kind As TestKind, host As TestHost) As Task + Dim input = + + + +using System; +namespace ConsoleApplication22 +{ + class Program + { + public static partial int {|Definition:Prop|} { get; } + public static partial int {|Definition:P$$rop|} => 1; + + static void Main(string[] args) + { + int temp = Program.[|Prop|]; + } + } +} + + + + Await TestAPIAndFeature(input, kind, host) + End Function End Class End Namespace diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs index d5fed2c94c5aa..132c2870c0f71 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs @@ -30,6 +30,7 @@ protected override ValueTask> DetermineCascadedSymbolsAs { using var _ = ArrayBuilder.GetInstance(out var result); + CascadeToOtherPartOfPartial(symbol, result); CascadeToBackingFields(symbol, result); CascadeToAccessors(symbol, result); CascadeToPrimaryConstructorParameters(symbol, result, cancellationToken); @@ -37,6 +38,19 @@ protected override ValueTask> DetermineCascadedSymbolsAs return new(result.ToImmutable()); } + private static void CascadeToOtherPartOfPartial(IPropertySymbol symbol, ArrayBuilder result) + { + if (symbol.PartialDefinitionPart is { } definitionPart) + { + result.Add(definitionPart); + } + + if (symbol.PartialImplementationPart is { } implementationPart) + { + result.Add(implementationPart); + } + } + private static void CascadeToBackingFields(IPropertySymbol symbol, ArrayBuilder result) { foreach (var member in symbol.ContainingType.GetMembers()) From 90e9e4c7e6d568f1deb8533c0b4fe79e6b35ca51 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Wed, 22 May 2024 10:33:18 -0700 Subject: [PATCH 05/18] fix whitespace --- .../Test2/FindReferences/FindReferencesTests.PropertySymbols.vb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb index 5c60b1446be04..0a9a3db21b813 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb @@ -1547,7 +1547,7 @@ record Goo(int x, int {|Definition:$$y|}) class P { static void Main() -{ + { var f = new Goo(y: 1); Console.WriteLine(f.[|y|]); } From f588b1d6ba28e7433bfbf9bc8d8a2634da67461b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 22 May 2024 11:40:59 -0700 Subject: [PATCH 06/18] Revert --- .../Test2/FindReferences/FindReferencesTests.PropertySymbols.vb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb index 5c60b1446be04..0a9a3db21b813 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb @@ -1547,7 +1547,7 @@ record Goo(int x, int {|Definition:$$y|}) class P { static void Main() -{ + { var f = new Goo(y: 1); Console.WriteLine(f.[|y|]); } From cbbe29305ba81344bad364d091920326fb99b87e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 22 May 2024 11:42:41 -0700 Subject: [PATCH 07/18] Simplify --- .../Finders/PropertySymbolReferenceFinder.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs index 132c2870c0f71..7dd7974883d91 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs @@ -40,15 +40,8 @@ protected override ValueTask> DetermineCascadedSymbolsAs private static void CascadeToOtherPartOfPartial(IPropertySymbol symbol, ArrayBuilder result) { - if (symbol.PartialDefinitionPart is { } definitionPart) - { - result.Add(definitionPart); - } - - if (symbol.PartialImplementationPart is { } implementationPart) - { - result.Add(implementationPart); - } + result.AddIfNotNull(symbol.PartialDefinitionPart); + result.AddIfNotNull(symbol.PartialImplementationPart); } private static void CascadeToBackingFields(IPropertySymbol symbol, ArrayBuilder result) From ab009632a040059ff5f637c947c693842bf1dee2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 22 May 2024 12:02:15 -0700 Subject: [PATCH 08/18] Fix find-refs and partial properties --- .../Core/SymbolKey/SymbolKey.MethodSymbolKey.cs | 10 ++++------ .../Core/SymbolKey/SymbolKey.PropertySymbolKey.cs | 11 ++++++++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.MethodSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.MethodSymbolKey.cs index 04727e0303788..785458b4296dc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.MethodSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.MethodSymbolKey.cs @@ -164,7 +164,7 @@ protected sealed override SymbolKeyResolution Resolve( var containingType = reader.ReadSymbolKey(contextualSymbol?.ContainingSymbol, out var containingTypeFailureReason); var arity = reader.ReadInteger(); - var isPartialMethodImplementationPart = reader.ReadBoolean(); + var isPartialImplementationPart = reader.ReadBoolean(); using var parameterRefKinds = reader.ReadRefKindArray(); // For each method that we look at, we'll have to resolve the parameter list and @@ -195,7 +195,7 @@ protected sealed override SymbolKeyResolution Resolve( // resolving this method. using (reader.PushMethod(candidate)) { - method = Resolve(reader, isPartialMethodImplementationPart, candidate); + method = Resolve(reader, isPartialImplementationPart, candidate); } // Note: after finding the first method that matches we stop. That's necessary as we cache results @@ -242,7 +242,7 @@ protected sealed override SymbolKeyResolution Resolve( } private static IMethodSymbol? Resolve( - SymbolKeyReader reader, bool isPartialMethodImplementationPart, IMethodSymbol method) + SymbolKeyReader reader, bool isPartialImplementationPart, IMethodSymbol method) { var returnType = (ITypeSymbol?)reader.ReadSymbolKey(contextualSymbol: method.ReturnType, out _).GetAnySymbol(); if (returnType != null && @@ -257,10 +257,8 @@ protected sealed override SymbolKeyResolution Resolve( getContextualType: static (method, i) => SafeGet(method.Parameters, i)?.Type, method.OriginalDefinition.Parameters)) { - if (isPartialMethodImplementationPart) - { + if (isPartialImplementationPart) method = method.PartialImplementationPart ?? method; - } Debug.Assert(method != null); return method; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PropertySymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PropertySymbolKey.cs index 151548b7951ff..49b92a73b2224 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PropertySymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PropertySymbolKey.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics; + namespace Microsoft.CodeAnalysis; internal partial struct SymbolKey @@ -15,6 +17,7 @@ public sealed override void Create(IPropertySymbol symbol, SymbolKeyWriter visit visitor.WriteString(symbol.MetadataName); visitor.WriteSymbolKey(symbol.ContainingSymbol); visitor.WriteBoolean(symbol.IsIndexer); + visitor.WriteBoolean(symbol.PartialDefinitionPart != null); visitor.WriteRefKindArray(symbol.Parameters); visitor.WriteParameterTypesArray(symbol.OriginalDefinition.Parameters); } @@ -27,6 +30,7 @@ protected sealed override SymbolKeyResolution Resolve( var containingTypeResolution = reader.ReadSymbolKey(contextualSymbol?.ContainingSymbol, out var containingTypeFailureReason); var isIndexer = reader.ReadBoolean(); + var isPartialImplementationPart = reader.ReadBoolean(); using var refKinds = reader.ReadRefKindArray(); using var properties = GetMembersOfNamedType(containingTypeResolution, metadataName: null); @@ -51,7 +55,7 @@ protected sealed override SymbolKeyResolution Resolve( continue; } - property = Resolve(reader, candidate); + property = Resolve(reader, isPartialImplementationPart, candidate); if (property != null) break; @@ -86,6 +90,7 @@ protected sealed override SymbolKeyResolution Resolve( private static IPropertySymbol? Resolve( SymbolKeyReader reader, + bool isPartialImplementationPart, IPropertySymbol property) { if (reader.ParameterTypesMatch( @@ -93,6 +98,10 @@ protected sealed override SymbolKeyResolution Resolve( getContextualType: static (property, i) => SafeGet(property.OriginalDefinition.Parameters, i)?.Type, property.OriginalDefinition.Parameters)) { + if (isPartialImplementationPart) + property = property.PartialImplementationPart ?? property; + + Debug.Assert(property != null); return property; } From eddd6f9de4370236dc9f81833823fb1a4038012f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 22 May 2024 12:05:59 -0700 Subject: [PATCH 09/18] Fix symbol equivalence --- .../SymbolEquivalenceComparer.EquivalenceVisitor.cs | 2 ++ .../SymbolEquivalenceComparer.GetHashCodeVisitor.cs | 4 +++- .../Compiler/Core/Utilities/SymbolEquivalenceComparer.cs | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs index a4efca7badcac..64c2f3e0110d5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs @@ -582,6 +582,8 @@ private bool PropertiesAreEquivalent(IPropertySymbol x, IPropertySymbol y, Dicti x.IsIndexer == y.IsIndexer && x.MetadataName == y.MetadataName && x.Parameters.Length == y.Parameters.Length && + IsPartialMethodDefinitionPart(x) == IsPartialMethodDefinitionPart(y) && + IsPartialMethodImplementationPart(x) == IsPartialMethodImplementationPart(y) && ParametersAreEquivalent(x.Parameters, y.Parameters, equivalentTypesWithDifferingAssemblies) && AreEquivalent(x.ContainingSymbol, y.ContainingSymbol, equivalentTypesWithDifferingAssemblies); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.GetHashCodeVisitor.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.GetHashCodeVisitor.cs index c436b4b95d895..f54b8bc87d787 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.GetHashCodeVisitor.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.GetHashCodeVisitor.cs @@ -245,7 +245,9 @@ private int CombineHashCodes(IPropertySymbol x, int currentHash) Hash.Combine(x.IsIndexer, Hash.Combine(x.Name, Hash.Combine(x.Parameters.Length, - GetHashCode(x.ContainingSymbol, currentHash)))); + Hash.Combine(IsPartialMethodImplementationPart(x), + Hash.Combine(IsPartialMethodDefinitionPart(x), + GetHashCode(x.ContainingSymbol, currentHash)))))); return CombineHashCodes(x.Parameters, currentHash, _parameterAggregator); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs index a8bb3f5bc68ae..9a5192089d6a1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs @@ -217,6 +217,12 @@ private static bool IsPartialMethodDefinitionPart(IMethodSymbol symbol) private static bool IsPartialMethodImplementationPart(IMethodSymbol symbol) => symbol.PartialDefinitionPart != null; + private static bool IsPartialMethodDefinitionPart(IPropertySymbol symbol) + => symbol.PartialImplementationPart != null; + + private static bool IsPartialMethodImplementationPart(IPropertySymbol symbol) + => symbol.PartialDefinitionPart != null; + private static TypeKind GetTypeKind(INamedTypeSymbol x) => x.TypeKind switch { From 2cb66a9dd87a46ce8a4e4413c6aad3254dab39f9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 22 May 2024 12:07:13 -0700 Subject: [PATCH 10/18] Simplify --- .../GoToDefinition/GoToDefinitionFeatureHelpers.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs b/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs index 7dd952ed4105a..10896277c8c50 100644 --- a/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs +++ b/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs @@ -49,11 +49,8 @@ internal static class GoToDefinitionFeatureHelpers symbol = definition ?? symbol; // If symbol has a partial implementation part, prefer to go to it, since that is where the body is. - if (symbol is IMethodSymbol method) - symbol = method.PartialImplementationPart ?? symbol; - - if (symbol is IPropertySymbol property) - symbol = property.PartialImplementationPart ?? symbol; + symbol = (symbol as IMethodSymbol)?.PartialImplementationPart ?? symbol; + symbol = (symbol as IPropertySymbol)?.PartialImplementationPart ?? symbol; return symbol; } From 1ddc59e5e5f116bec51041ffbe166847c1087f0e Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Wed, 22 May 2024 12:23:59 -0700 Subject: [PATCH 11/18] more --- .../Compilation/SyntaxTreeSemanticModel.cs | 2 +- .../Symbol/Symbols/PartialPropertiesTests.cs | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 2b85e7a878ed7..da2bf44f27b3e 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -1800,7 +1800,7 @@ private Symbol GetDeclaredMember(NamespaceOrTypeSymbol container, TextSpan decla } // Handle the case of the implementation of a partial member. - Symbol? partial = symbol switch + Symbol partial = symbol switch { MethodSymbol method => method.PartialImplementationPart, SourcePropertySymbol property => property.PartialImplementationPart, diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs index fa727c8878268..23549e9666e7e 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs @@ -4450,6 +4450,39 @@ partial class C Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "this").WithArguments("partial", "12.0", "preview").WithLocation(7, 24)); } + [Fact] + public void GetDeclaredSymbol_01() + { + var source = (""" + partial class C + { + public partial int Prop { get; } + public partial int Prop { get => 1; } + } + """, "Program.cs"); + + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees[0]; + + var model = comp.GetSemanticModel(tree); + var properties = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(2, properties.Length); + + var defSymbol = model.GetDeclaredSymbol(properties[0])!; + Assert.Equal("System.Int32 C.Prop { get; }", defSymbol.ToTestDisplayString()); + + var implSymbol = model.GetDeclaredSymbol(properties[1])!; + Assert.Equal("System.Int32 C.Prop { get; }", implSymbol.ToTestDisplayString()); + + Assert.NotEqual(defSymbol, implSymbol); + Assert.Same(implSymbol, defSymbol.PartialImplementationPart); + Assert.Same(defSymbol, implSymbol.PartialDefinitionPart); + + // This is consistent with partial methods. + Assert.Equal("SourceFile(Program.cs[43..47))", defSymbol.Locations.Single().ToString()); + Assert.Equal("SourceFile(Program.cs[81..85))", implSymbol.Locations.Single().ToString()); + } + // PROTOTYPE(partial-properties): override partial property where base has modopt } } From 5f82d3468b36cebcdb7632bfa5d152116accc78f Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Wed, 22 May 2024 13:54:55 -0700 Subject: [PATCH 12/18] fix test --- .../CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs index 23549e9666e7e..8065f0e9dbb79 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs @@ -4459,7 +4459,7 @@ partial class C public partial int Prop { get; } public partial int Prop { get => 1; } } - """, "Program.cs"); + """.NormalizeLineEndings(), "Program.cs"); var comp = CreateCompilation(source); var tree = comp.SyntaxTrees[0]; From 3222d83f86f6d5b95f6d44d45132c34d0cbfe6e6 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Thu, 23 May 2024 14:04:21 -0700 Subject: [PATCH 13/18] Add IsPartialDefinition --- .../Symbols/PublicModel/PropertySymbol.cs | 2 + .../Symbol/Symbols/PartialPropertiesTests.cs | 48 +++++++++++++++++++ .../Core/Portable/PublicAPI.Unshipped.txt | 1 + .../Core/Portable/Symbols/IPropertySymbol.cs | 5 ++ .../Portable/Symbols/PropertySymbol.vb | 7 +++ ...taAsSourceService.WrappedPropertySymbol.cs | 2 + .../Symbols/CodeGenerationPropertySymbol.cs | 2 + 7 files changed, 67 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PropertySymbol.cs index 6f969a9dae145..3072bfb1875da 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PropertySymbol.cs @@ -113,6 +113,8 @@ ImmutableArray IPropertySymbol.RefCustomModifiers IPropertySymbol? IPropertySymbol.PartialDefinitionPart => (_underlying as SourcePropertySymbol)?.PartialDefinitionPart.GetPublicSymbol(); IPropertySymbol? IPropertySymbol.PartialImplementationPart => (_underlying as SourcePropertySymbol)?.PartialImplementationPart.GetPublicSymbol(); + + bool IPropertySymbol.IsPartialDefinition => (_underlying as SourcePropertySymbol)?.IsPartialDefinition ?? false; #nullable disable #region ISymbol Members diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs index 8065f0e9dbb79..fc5ed602774f0 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs @@ -4477,12 +4477,60 @@ partial class C Assert.NotEqual(defSymbol, implSymbol); Assert.Same(implSymbol, defSymbol.PartialImplementationPart); Assert.Same(defSymbol, implSymbol.PartialDefinitionPart); + Assert.True(defSymbol.IsPartialDefinition); + Assert.False(implSymbol.IsPartialDefinition); // This is consistent with partial methods. Assert.Equal("SourceFile(Program.cs[43..47))", defSymbol.Locations.Single().ToString()); Assert.Equal("SourceFile(Program.cs[81..85))", implSymbol.Locations.Single().ToString()); } + [Fact] + public void GetDeclaredSymbol_02() + { + var source = (""" + partial class C + { + public partial int Prop { get; } + public partial int Prop { get => 1; } + } + """.NormalizeLineEndings(), "Program.cs"); + + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees[0]; + + var model = comp.GetSemanticModel(tree); + var properties = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(2, properties.Length); + + var defSymbol = model.GetDeclaredSymbol(properties[0])!; + Assert.Equal("System.Int32 C.Prop { get; }", defSymbol.ToTestDisplayString()); + + var implSymbol = model.GetDeclaredSymbol(properties[1])!; + Assert.Equal("System.Int32 C.Prop { get; }", implSymbol.ToTestDisplayString()); + + Assert.NotEqual(defSymbol, implSymbol); + Assert.Same(implSymbol, defSymbol.PartialImplementationPart); + Assert.Same(defSymbol, implSymbol.PartialDefinitionPart); + + Assert.True(defSymbol.IsPartialDefinition); + Assert.False(implSymbol.IsPartialDefinition); + + // This is consistent with partial methods. + Assert.Equal("SourceFile(Program.cs[46..50))", defSymbol.Locations.Single().ToString()); + Assert.Equal("SourceFile(Program.cs[84..88))", implSymbol.Locations.Single().ToString()); + + var intSymbol = comp.GetSpecialType(SpecialType.System_Int32); + var cOfTSymbol = defSymbol.ContainingType!; + var cOfIntSymbol = cOfTSymbol.Construct([intSymbol]); + + // Constructed symbols always return null/false from the partial-related public APIs + var defOfIntSymbol = (IPropertySymbol)cOfIntSymbol.GetMember("Prop"); + Assert.Equal("System.Int32 C.Prop { get; }", defOfIntSymbol.ToTestDisplayString()); + Assert.Null(defOfIntSymbol.PartialImplementationPart); + Assert.False(defOfIntSymbol.IsPartialDefinition); + } + // PROTOTYPE(partial-properties): override partial property where base has modopt } } diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index b8aee812cc609..9773ff609d0d7 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -2,6 +2,7 @@ Microsoft.CodeAnalysis.IParameterSymbol.IsParamsCollection.get -> bool Microsoft.CodeAnalysis.IParameterSymbol.IsParamsArray.get -> bool Microsoft.CodeAnalysis.IPropertySymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IPropertySymbol? Microsoft.CodeAnalysis.IPropertySymbol.PartialImplementationPart.get -> Microsoft.CodeAnalysis.IPropertySymbol? +Microsoft.CodeAnalysis.IPropertySymbol.IsPartialDefinition.get -> bool Microsoft.CodeAnalysis.Operations.ArgumentKind.ParamCollection = 4 -> Microsoft.CodeAnalysis.Operations.ArgumentKind Microsoft.CodeAnalysis.Diagnostics.SuppressionInfo.ProgrammaticSuppressions.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Emit.InstrumentationKind.ModuleCancellation = 3 -> Microsoft.CodeAnalysis.Emit.InstrumentationKind diff --git a/src/Compilers/Core/Portable/Symbols/IPropertySymbol.cs b/src/Compilers/Core/Portable/Symbols/IPropertySymbol.cs index df699e7e8efe1..12735dd442946 100644 --- a/src/Compilers/Core/Portable/Symbols/IPropertySymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/IPropertySymbol.cs @@ -122,5 +122,10 @@ public interface IPropertySymbol : ISymbol /// implementation part. Otherwise null. /// IPropertySymbol? PartialImplementationPart { get; } + + /// + /// Returns true if this is a partial definition part. Otherwise false. + /// + bool IsPartialDefinition { get; } } } diff --git a/src/Compilers/VisualBasic/Portable/Symbols/PropertySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/PropertySymbol.vb index 8fc082a76b298..c3e8138ec3749 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/PropertySymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/PropertySymbol.vb @@ -635,6 +635,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Private ReadOnly Property IPropertySymbol_IsPartialDefinition As Boolean Implements IPropertySymbol.IsPartialDefinition + Get + ' Feature not supported in VB + Return False + End Get + End Property + Public Overrides Sub Accept(visitor As SymbolVisitor) visitor.VisitProperty(Me) End Sub diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedPropertySymbol.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedPropertySymbol.cs index 26909af1aed0e..e2cfb6aefba23 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedPropertySymbol.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedPropertySymbol.cs @@ -70,5 +70,7 @@ public ImmutableArray ExplicitInterfaceImplementations public IPropertySymbol PartialDefinitionPart => _symbol.PartialDefinitionPart; public IPropertySymbol PartialImplementationPart => _symbol.PartialImplementationPart; + + public bool IsPartialDefinition => _symbol.IsPartialDefinition; } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationPropertySymbol.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationPropertySymbol.cs index 13fbe0c46657b..361647a0027d9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationPropertySymbol.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationPropertySymbol.cs @@ -89,4 +89,6 @@ public override TResult Accept(SymbolVisitor null; public IPropertySymbol PartialDefinitionPart => null; + + public bool IsPartialDefinition => false; } From bccb280d61a408e3632de1329e60cd06dfb51798 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 May 2024 13:46:32 -0700 Subject: [PATCH 14/18] Add test for partial indexer parameter --- .../Symbol/Symbols/PartialPropertiesTests.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs index fc5ed602774f0..74500cb2a4ff4 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs @@ -4488,6 +4488,7 @@ partial class C [Fact] public void GetDeclaredSymbol_02() { + // Property contained in generic type. Check original definition and constructed symbols. var source = (""" partial class C { @@ -4531,6 +4532,77 @@ partial class C Assert.False(defOfIntSymbol.IsPartialDefinition); } + [Fact] + public void GetDeclaredSymbol_03() + { + // Indexer + var source = (""" + partial class C + { + public partial int this[int i] { get; } + public partial int this[int i] { get => 1; } + } + """.NormalizeLineEndings(), "Program.cs"); + + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees[0]; + + var model = comp.GetSemanticModel(tree); + var indexers = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(2, indexers.Length); + + var defSymbol = model.GetDeclaredSymbol(indexers[0])!; + Assert.Equal("System.Int32 C.this[System.Int32 i] { get; }", defSymbol.ToTestDisplayString()); + + var implSymbol = model.GetDeclaredSymbol(indexers[1])!; + Assert.Equal("System.Int32 C.this[System.Int32 i] { get; }", implSymbol.ToTestDisplayString()); + + Assert.NotEqual(defSymbol, implSymbol); + Assert.Same(implSymbol, defSymbol.PartialImplementationPart); + Assert.Same(defSymbol, implSymbol.PartialDefinitionPart); + + Assert.True(defSymbol.IsPartialDefinition); + Assert.False(implSymbol.IsPartialDefinition); + + // This is consistent with partial methods. + Assert.Equal("SourceFile(Program.cs[43..47))", defSymbol.Locations.Single().ToString()); + Assert.Equal("SourceFile(Program.cs[88..92))", implSymbol.Locations.Single().ToString()); + } + + [Fact] + public void GetDeclaredSymbol_04() + { + // Indexer parameter + var source = (""" + partial class C + { + public partial int this[int i] { get; } + public partial int this[int i] { get => 1; } + } + """.NormalizeLineEndings(), "Program.cs"); + + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees[0]; + + var model = comp.GetSemanticModel(tree); + var parameters = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(2, parameters.Length); + + var defSymbol = model.GetDeclaredSymbol(parameters[0])!; + Assert.Equal("System.Int32 i", defSymbol.ToTestDisplayString()); + + var implSymbol = model.GetDeclaredSymbol(parameters[1])!; + Assert.Equal("System.Int32 i", implSymbol.ToTestDisplayString()); + + Assert.NotEqual(defSymbol, implSymbol); + Assert.Same(implSymbol, ((IPropertySymbol)defSymbol.ContainingSymbol).PartialImplementationPart!.Parameters[0]); + Assert.Same(defSymbol, ((IPropertySymbol)implSymbol.ContainingSymbol).PartialDefinitionPart!.Parameters[0]); + + // This is consistent with partial methods. + Assert.Equal("SourceFile(Program.cs[52..53))", defSymbol.Locations.Single().ToString()); + Assert.Equal("SourceFile(Program.cs[97..98))", implSymbol.Locations.Single().ToString()); + } + // PROTOTYPE(partial-properties): override partial property where base has modopt } } From c0a3817a1d1e3a712c4cbfaed729d54c66fa6053 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 May 2024 15:21:46 -0700 Subject: [PATCH 15/18] Remove unnecessary check of 'method.PartialDefinitionPart' --- .../CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index da2bf44f27b3e..9f817b9a898d4 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -2037,9 +2037,7 @@ private ParameterSymbol GetMethodParameterSymbol( return null; } - return - GetParameterSymbol(method.Parameters, parameter, cancellationToken) ?? - ((object)method.PartialDefinitionPart == null ? null : GetParameterSymbol(method.PartialDefinitionPart.Parameters, parameter, cancellationToken)); + return GetParameterSymbol(method.Parameters, parameter, cancellationToken); } private ParameterSymbol GetIndexerParameterSymbol( From ea8494427d86c0df362c5a1164e5e6116dd502b3 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 May 2024 15:32:02 -0700 Subject: [PATCH 16/18] cleanup doc comment --- .../CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 9f817b9a898d4..d25cf144130ec 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -2129,11 +2129,8 @@ private ParameterSymbol GetDelegateParameterSymbol( } /// - /// Given a type parameter declaration (field or method), get the corresponding symbol + /// Given a type parameter declaration (on a type or method), get the corresponding symbol /// - /// - /// The cancellation token. - /// public override ITypeParameterSymbol GetDeclaredSymbol(TypeParameterSyntax typeParameter, CancellationToken cancellationToken = default(CancellationToken)) { if (typeParameter == null) From d29b2058bdff2f890269e46d541df04166b110f9 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 May 2024 15:33:35 -0700 Subject: [PATCH 17/18] Remove another check of PartialDefinitionPart which doesn't make sense --- .../CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index d25cf144130ec..223eb20eea3bc 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -2164,10 +2164,7 @@ private ParameterSymbol GetDelegateParameterSymbol( return this.GetTypeParameterSymbol(typeSymbol.TypeParameters, typeParameter).GetPublicSymbol(); case MethodSymbol methodSymbol: - return (this.GetTypeParameterSymbol(methodSymbol.TypeParameters, typeParameter) ?? - ((object)methodSymbol.PartialDefinitionPart == null - ? null - : this.GetTypeParameterSymbol(methodSymbol.PartialDefinitionPart.TypeParameters, typeParameter))).GetPublicSymbol(); + return this.GetTypeParameterSymbol(methodSymbol.TypeParameters, typeParameter); } } From 5fc7fc909596bd342ad9299635b97464aef25388 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 May 2024 15:34:07 -0700 Subject: [PATCH 18/18] fix --- .../CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 223eb20eea3bc..10b7050bbd0b3 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -2164,7 +2164,7 @@ private ParameterSymbol GetDelegateParameterSymbol( return this.GetTypeParameterSymbol(typeSymbol.TypeParameters, typeParameter).GetPublicSymbol(); case MethodSymbol methodSymbol: - return this.GetTypeParameterSymbol(methodSymbol.TypeParameters, typeParameter); + return this.GetTypeParameterSymbol(methodSymbol.TypeParameters, typeParameter).GetPublicSymbol(); } }