Skip to content

Commit

Permalink
Merge pull request #73815 from RikkiGibson/partial-properties-to-main
Browse files Browse the repository at this point in the history
Merge partial properties feature to main
  • Loading branch information
RikkiGibson authored May 31, 2024
2 parents 0ace424 + 13fd9e2 commit d847cec
Show file tree
Hide file tree
Showing 103 changed files with 8,735 additions and 1,163 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ SyntaxKind.IndexerDeclaration or
var symbol = model.GetSymbolInfo(invocation.Expression, cancellationToken).Symbol;
if (symbol is not IMethodSymbol method || method.PartialImplementationPart is not null)
{
// https://github.com/dotnet/roslyn/issues/73772: should we also bail out on a partial property?
// We don't handle partial methods yet
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ public override Binder VisitAccessorDeclaration(AccessorDeclarationSyntax parent
if ((object)propertySymbol != null)
{
accessor = (parent.Kind() == SyntaxKind.GetAccessorDeclaration) ? propertySymbol.GetMethod : propertySymbol.SetMethod;
Debug.Assert(accessor is not null || parent.HasErrors);
}
break;
}
Expand Down Expand Up @@ -597,18 +598,25 @@ bool checkSymbol(Symbol sym, TextSpan memberSpan, SymbolKind kind, out Symbol re
return false;
}

if (sym.Kind == SymbolKind.Method)
if (kind is SymbolKind.Method or SymbolKind.Property)
{
if (InSpan(sym.GetFirstLocation(), this.syntaxTree, memberSpan))
{
return true;
}

// If this is a partial method, the method represents the defining part,
// not the implementation (method.Locations includes both parts). If the
// span is in fact in the implementation, return that method instead.
var implementation = ((MethodSymbol)sym).PartialImplementationPart;
if ((object)implementation != null)
// If this is a partial member, the member represents the defining part,
// not the implementation (member.Locations includes both parts). If the
// span is in fact in the implementation, return that member instead.
if (sym switch
#pragma warning disable format
{
MethodSymbol method => (Symbol)method.PartialImplementationPart,
SourcePropertySymbol property => property.PartialImplementationPart,
_ => throw ExceptionUtilities.UnexpectedValue(sym)
}
#pragma warning restore format
is { } implementation)
{
if (InSpan(implementation.GetFirstLocation(), this.syntaxTree, memberSpan))
{
Expand Down
83 changes: 58 additions & 25 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1171,7 +1171,7 @@
<value>Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?)</value>
</data>
<data name="ERR_PartialMisplaced" xml:space="preserve">
<value>The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type.</value>
<value>The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type.</value>
</data>
<data name="ERR_ImportedCircularBase" xml:space="preserve">
<value>Imported type '{0}' is invalid. It contains a circular base type dependency.</value>
Expand Down Expand Up @@ -2207,14 +2207,14 @@ If such a class is used as a base class and if the deriving class defines a dest
<data name="ERR_InconsistentLambdaParameterUsage" xml:space="preserve">
<value>Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit</value>
</data>
<data name="ERR_PartialMethodInvalidModifier" xml:space="preserve">
<value>A partial method cannot have the 'abstract' modifier</value>
<data name="ERR_PartialMemberCannotBeAbstract" xml:space="preserve">
<value>A partial member cannot have the 'abstract' modifier</value>
</data>
<data name="ERR_PartialMethodOnlyInPartialClass" xml:space="preserve">
<value>A partial method must be declared within a partial type</value>
<data name="ERR_PartialMemberOnlyInPartialClass" xml:space="preserve">
<value>A partial member must be declared within a partial type</value>
</data>
<data name="ERR_PartialMethodNotExplicit" xml:space="preserve">
<value>A partial method may not explicitly implement an interface method</value>
<data name="ERR_PartialMemberNotExplicit" xml:space="preserve">
<value>A partial member may not explicitly implement an interface member</value>
</data>
<data name="ERR_PartialMethodExtensionDifference" xml:space="preserve">
<value>Both partial method declarations must be extension methods or neither may be an extension method</value>
Expand All @@ -2225,26 +2225,26 @@ If such a class is used as a base class and if the deriving class defines a dest
<data name="ERR_PartialMethodOnlyOneActual" xml:space="preserve">
<value>A partial method may not have multiple implementing declarations</value>
</data>
<data name="ERR_PartialMethodParamsDifference" xml:space="preserve">
<value>Both partial method declarations must use a params parameter or neither may use a params parameter</value>
<data name="ERR_PartialMemberParamsDifference" xml:space="preserve">
<value>Both partial member declarations must use a params parameter or neither may use a params parameter</value>
</data>
<data name="ERR_PartialMethodMustHaveLatent" xml:space="preserve">
<value>No defining declaration found for implementing declaration of partial method '{0}'</value>
</data>
<data name="ERR_PartialMethodInconsistentTupleNames" xml:space="preserve">
<value>Both partial method declarations, '{0}' and '{1}', must use the same tuple element names.</value>
<data name="ERR_PartialMemberInconsistentTupleNames" xml:space="preserve">
<value>Both partial member declarations, '{0}' and '{1}', must use the same tuple element names.</value>
</data>
<data name="ERR_PartialMethodInconsistentConstraints" xml:space="preserve">
<value>Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}'</value>
</data>
<data name="ERR_PartialMethodToDelegate" xml:space="preserve">
<value>Cannot create delegate from method '{0}' because it is a partial method without an implementing declaration</value>
</data>
<data name="ERR_PartialMethodStaticDifference" xml:space="preserve">
<value>Both partial method declarations must be static or neither may be static</value>
<data name="ERR_PartialMemberStaticDifference" xml:space="preserve">
<value>Both partial member declarations must be static or neither may be static</value>
</data>
<data name="ERR_PartialMethodUnsafeDifference" xml:space="preserve">
<value>Both partial method declarations must be unsafe or neither may be unsafe</value>
<data name="ERR_PartialMemberUnsafeDifference" xml:space="preserve">
<value>Both partial member declarations must be unsafe or neither may be unsafe</value>
</data>
<data name="ERR_PartialMethodInExpressionTree" xml:space="preserve">
<value>Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees</value>
Expand Down Expand Up @@ -5737,8 +5737,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_FieldLikeEventCantBeReadOnly" xml:space="preserve">
<value>Field-like event '{0}' cannot be 'readonly'.</value>
</data>
<data name="ERR_PartialMethodReadOnlyDifference" xml:space="preserve">
<value>Both partial method declarations must be readonly or neither may be readonly</value>
<data name="ERR_PartialMemberReadOnlyDifference" xml:space="preserve">
<value>Both partial member declarations must be readonly or neither may be readonly</value>
</data>
<data name="ERR_ReadOnlyModMissingAccessor" xml:space="preserve">
<value>'{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor</value>
Expand Down Expand Up @@ -6521,17 +6521,17 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_PartialMethodWithExtendedModMustHaveAccessMods" xml:space="preserve">
<value>Partial method '{0}' must have accessibility modifiers because it has a 'virtual', 'override', 'sealed', 'new', or 'extern' modifier.</value>
</data>
<data name="ERR_PartialMethodAccessibilityDifference" xml:space="preserve">
<value>Both partial method declarations must have identical accessibility modifiers.</value>
<data name="ERR_PartialMemberAccessibilityDifference" xml:space="preserve">
<value>Both partial member declarations must have identical accessibility modifiers.</value>
</data>
<data name="ERR_PartialMethodExtendedModDifference" xml:space="preserve">
<value>Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers.</value>
<data name="ERR_PartialMemberExtendedModDifference" xml:space="preserve">
<value>Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers.</value>
</data>
<data name="ERR_PartialMethodReturnTypeDifference" xml:space="preserve">
<value>Both partial method declarations must have the same return type.</value>
</data>
<data name="ERR_PartialMethodRefReturnDifference" xml:space="preserve">
<value>Partial method declarations must have matching ref return values.</value>
<data name="ERR_PartialMemberRefReturnDifference" xml:space="preserve">
<value>Partial member declarations must have matching ref return values.</value>
</data>
<data name="WRN_PartialMethodTypeDifference" xml:space="preserve">
<value>Partial method declarations '{0}' and '{1}' have signature differences.</value>
Expand Down Expand Up @@ -7032,7 +7032,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<value>The 'scoped' modifier of parameter doesn't match target.</value>
</data>
<data name="ERR_ScopedMismatchInParameterOfPartial" xml:space="preserve">
<value>The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration.</value>
<value>The 'scoped' modifier of parameter '{0}' doesn't match partial definition.</value>
</data>
<data name="ERR_FixedFieldMustNotBeRef" xml:space="preserve">
<value>A fixed field must not be a ref field.</value>
Expand Down Expand Up @@ -7956,4 +7956,37 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_BadAllowByRefLikeEnumerator" xml:space="preserve">
<value>foreach statement cannot operate on enumerators of type '{0}' because it is a type parameter that allows ref struct and it is not known at compile time to implement IDisposable.</value>
</data>
</root>
<data name="ERR_PartialPropertyMissingImplementation" xml:space="preserve">
<value>Partial property '{0}' must have an implementation part.</value>
</data>
<data name="ERR_PartialPropertyMissingDefinition" xml:space="preserve">
<value>Partial property '{0}' must have a definition part.</value>
</data>
<data name="ERR_PartialPropertyDuplicateDefinition" xml:space="preserve">
<value>A partial property may not have multiple defining declarations, and cannot be an auto-property.</value>
</data>
<data name="ERR_PartialPropertyDuplicateImplementation" xml:space="preserve">
<value>A partial property may not have multiple implementing declarations</value>
</data>
<data name="ERR_PartialPropertyMissingAccessor" xml:space="preserve">
<value>Property accessor '{0}' must be implemented because it is declared on the definition part</value>
</data>
<data name="ERR_PartialPropertyUnexpectedAccessor" xml:space="preserve">
<value>Property accessor '{0}' does not implement any accessor declared on the definition part</value>
</data>
<data name="ERR_PartialPropertyInitMismatch" xml:space="preserve">
<value>Property accessor '{0}' must be '{1}' to match the definition part</value>
</data>
<data name="ERR_PartialPropertyTypeDifference" xml:space="preserve">
<value>Both partial property declarations must have the same type.</value>
</data>
<data name="WRN_PartialPropertySignatureDifference" xml:space="preserve">
<value>Partial property declarations '{0}' and '{1}' have signature differences.</value>
</data>
<data name="WRN_PartialPropertySignatureDifference_Title" xml:space="preserve">
<value>Partial property declarations have signature differences.</value>
</data>
<data name="ERR_PartialPropertyRequiredDifference" xml:space="preserve">
<value>Both partial property declarations must be required or neither may be required</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -2033,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(
Expand Down Expand Up @@ -2127,11 +2129,8 @@ private ParameterSymbol GetDelegateParameterSymbol(
}

/// <summary>
/// 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
/// </summary>
/// <param name="typeParameter"></param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
public override ITypeParameterSymbol GetDeclaredSymbol(TypeParameterSyntax typeParameter, CancellationToken cancellationToken = default(CancellationToken))
{
if (typeParameter == null)
Expand Down Expand Up @@ -2165,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).GetPublicSymbol();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,14 @@ public override void DefaultVisit(Symbol symbol)
bool shouldSkipPartialDefinitionComments = false;
if (symbol.IsPartialDefinition())
{
if (symbol is MethodSymbol { PartialImplementationPart: MethodSymbol implementationPart })
Symbol? implementationPart = symbol switch
{
MethodSymbol method => method.PartialImplementationPart,
SourcePropertySymbol property => property.PartialImplementationPart,
_ => null
};

if (implementationPart is not null)
{
Visit(implementationPart);

Expand Down
Loading

0 comments on commit d847cec

Please sign in to comment.