Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -598,25 +598,16 @@ bool checkSymbol(Symbol sym, TextSpan memberSpan, SymbolKind kind, out Symbol re
return false;
}

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

// 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 this is a partial member, the member represents the defining part, not the implementation.
// If the span is in fact in the implementation, return that member instead.
if (sym.GetPartialImplementationPart() is { } implementation)
{
if (InSpan(implementation.GetFirstLocation(), this.syntaxTree, memberSpan))
{
Expand Down
20 changes: 19 additions & 1 deletion 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 or property return type.</value>
<value>The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, 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 @@ -8047,4 +8047,22 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="WRN_InterceptsLocationAttributeUnsupportedSignature_Title" xml:space="preserve">
<value>'InterceptsLocationAttribute(string, int, int)' is not supported. Move to 'InterceptableLocation'-based generation of these attributes instead. (https://github.com/dotnet/roslyn/issues/72133)</value>
</data>
<data name="IDS_FeaturePartialEventsAndConstructors" xml:space="preserve">
<value>partial events and constructors</value>
</data>
<data name="ERR_PartialMemberMissingImplementation" xml:space="preserve">
<value>Partial member '{0}' must have an implementation part.</value>
</data>
<data name="ERR_PartialMemberMissingDefinition" xml:space="preserve">
<value>Partial member '{0}' must have a definition part.</value>
</data>
<data name="ERR_PartialMemberDuplicateDefinition" xml:space="preserve">
<value>Partial member '{0}' may not have multiple defining declarations.</value>
</data>
<data name="ERR_PartialMemberDuplicateImplementation" xml:space="preserve">
<value>Partial member '{0}' may not have multiple implementing declarations.</value>
</data>
<data name="ERR_PartialEventInitializer" xml:space="preserve">
<value>'{0}': partial event cannot have initializer</value>
</data>
</root>
7 changes: 7 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2360,6 +2360,13 @@ internal enum ErrorCode
ERR_ImplicitlyTypedParamsParameter = 9272,
ERR_VariableDeclarationNamedField = 9273,

// PROTOTYPE: compact
ERR_PartialMemberMissingImplementation = 9400,
ERR_PartialMemberMissingDefinition = 9401,
ERR_PartialMemberDuplicateDefinition = 9402,
ERR_PartialMemberDuplicateImplementation = 9403,
ERR_PartialEventInitializer = 9404,

// Note: you will need to do the following after adding errors:
// 1) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs)

Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2479,6 +2479,11 @@ or ErrorCode.WRN_UnscopedRefAttributeOldRules
or ErrorCode.WRN_InterceptsLocationAttributeUnsupportedSignature
or ErrorCode.ERR_ImplicitlyTypedParamsParameter
or ErrorCode.ERR_VariableDeclarationNamedField
or ErrorCode.ERR_PartialMemberMissingImplementation
or ErrorCode.ERR_PartialMemberMissingDefinition
or ErrorCode.ERR_PartialMemberDuplicateDefinition
or ErrorCode.ERR_PartialMemberDuplicateImplementation
or ErrorCode.ERR_PartialEventInitializer
=> false,
};
#pragma warning restore CS8524 // The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value.
Expand Down
3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/Symbols/EventSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@ internal virtual bool IsExplicitInterfaceImplementation
/// </remarks>
public abstract ImmutableArray<EventSymbol> ExplicitInterfaceImplementations { get; }

internal virtual EventSymbol? PartialImplementationPart => null;
internal virtual EventSymbol? PartialDefinitionPart => null;

/// <summary>
/// Gets the kind of this symbol.
/// </summary>
Expand Down
25 changes: 22 additions & 3 deletions src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,9 @@ internal static bool IsPartialMember(this Symbol member)
return member
is SourceOrdinaryMethodSymbol { IsPartial: true }
or SourcePropertySymbol { IsPartial: true }
or SourcePropertyAccessorSymbol { IsPartial: true };
or SourcePropertyAccessorSymbol { IsPartial: true }
or SourceConstructorSymbol { IsPartial: true }
or SourceEventSymbol { IsPartial: true };
}

internal static bool IsPartialImplementation(this Symbol member)
Expand All @@ -560,7 +562,9 @@ internal static bool IsPartialImplementation(this Symbol member)
return member
is SourceOrdinaryMethodSymbol { IsPartialImplementation: true }
or SourcePropertySymbol { IsPartialImplementation: true }
or SourcePropertyAccessorSymbol { IsPartialImplementation: true };
or SourcePropertyAccessorSymbol { IsPartialImplementation: true }
or SourceConstructorSymbol { IsPartialImplementation: true }
or SourceEventSymbol { IsPartialImplementation: true };
}

internal static bool IsPartialDefinition(this Symbol member)
Expand All @@ -569,9 +573,24 @@ internal static bool IsPartialDefinition(this Symbol member)
return member
is SourceOrdinaryMethodSymbol { IsPartialDefinition: true }
or SourcePropertySymbol { IsPartialDefinition: true }
or SourcePropertyAccessorSymbol { IsPartialDefinition: true };
or SourcePropertyAccessorSymbol { IsPartialDefinition: true }
or SourceConstructorSymbol { IsPartialDefinition: true }
or SourceEventSymbol { IsPartialDefinition: true };
}

#nullable enable
internal static Symbol? GetPartialImplementationPart(this Symbol member)
{
return member switch
{
MethodSymbol method => method.PartialImplementationPart,
SourcePropertySymbol property => property.PartialImplementationPart,
SourceEventSymbol ev => ev.PartialImplementationPart,
_ => null,
};
}
#nullable disable

internal static bool ContainsTupleNames(this Symbol member)
{
switch (member.Kind)
Expand Down
20 changes: 20 additions & 0 deletions src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,26 @@ internal static void CheckFeatureAvailabilityForStaticAbstractMembersInInterface
}
}

#nullable enable
internal static void CheckFeatureAvailabilityForPartialEventsAndConstructors(Location location, BindingDiagnosticBag diagnostics)
{
Debug.Assert(location.SourceTree is not null);

LanguageVersion availableVersion = ((CSharpParseOptions)location.SourceTree.Options).LanguageVersion;
LanguageVersion requiredVersion = MessageID.IDS_FeaturePartialEventsAndConstructors.RequiredVersion();
if (availableVersion < requiredVersion)
{
ReportUnsupportedModifiersForLanguageVersion(
DeclarationModifiers.Partial,
DeclarationModifiers.Partial,
location,
diagnostics,
availableVersion,
requiredVersion);
}
}
#nullable disable

internal static DeclarationModifiers AdjustModifiersForAnInterfaceMember(DeclarationModifiers mods, bool hasBody, bool isExplicitInterfaceImplementation)
{
if (isExplicitInterfaceImplementation)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;
Expand All @@ -12,6 +10,9 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal sealed class SourceConstructorSymbol : SourceConstructorSymbolBase
{
private SourceConstructorSymbol? _otherPartOfPartial;

#nullable disable
public static SourceConstructorSymbol CreateConstructorSymbol(
SourceMemberContainerTypeSymbol containingType,
ConstructorDeclarationSyntax syntax,
Expand Down Expand Up @@ -88,10 +89,11 @@ private static (DeclarationModifiers, Flags) MakeModifiersAndFlags(
out bool modifierErrors,
out bool report_ERR_StaticConstructorWithAccessModifiers)
{
DeclarationModifiers declarationModifiers = MakeModifiers(containingType, syntax, methodKind, syntax.HasAnyBody(), location, diagnostics, out modifierErrors, out report_ERR_StaticConstructorWithAccessModifiers);
Flags flags = MakeFlags(
methodKind, RefKind.None, declarationModifiers, returnsVoid: true, returnsVoidIsSet: true,
isExpressionBodied: syntax.IsExpressionBodied(), isExtensionMethod: false, isVarArg: syntax.IsVarArg(),
bool hasAnyBody = syntax.HasAnyBody();
DeclarationModifiers declarationModifiers = MakeModifiers(containingType, syntax, methodKind, hasAnyBody, location, diagnostics, out modifierErrors, out report_ERR_StaticConstructorWithAccessModifiers);
Flags flags = new Flags(
methodKind, RefKind.None, declarationModifiers, returnsVoid: true, returnsVoidIsSet: true, hasAnyBody: hasAnyBody,
isExpressionBodied: syntax.IsExpressionBodied(), isExtensionMethod: false, isVararg: syntax.IsVarArg(),
isNullableAnalysisEnabled: isNullableAnalysisEnabled, isExplicitInterfaceImplementation: false,
hasThisInitializer: hasThisInitializer);

Expand Down Expand Up @@ -126,12 +128,17 @@ private static DeclarationModifiers MakeModifiers(
var defaultAccess = (methodKind == MethodKind.StaticConstructor) ? DeclarationModifiers.None : DeclarationModifiers.Private;

// Check that the set of modifiers is allowed
const DeclarationModifiers allowedModifiers =
DeclarationModifiers allowedModifiers =
DeclarationModifiers.AccessibilityMask |
DeclarationModifiers.Static |
DeclarationModifiers.Extern |
DeclarationModifiers.Unsafe;

if (methodKind == MethodKind.Constructor)
{
allowedModifiers |= DeclarationModifiers.Partial;
}

bool isInterface = containingType.IsInterface;
var mods = ModifierUtils.MakeAndCheckNonTypeMemberModifiers(isOrdinaryMethod: false, isForInterfaceMember: isInterface, syntax.Modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors);

Expand Down Expand Up @@ -164,7 +171,7 @@ private static DeclarationModifiers MakeModifiers(

private void CheckModifiers(MethodKind methodKind, bool hasBody, Location location, BindingDiagnosticBag diagnostics)
{
if (!hasBody && !IsExtern)
if (!hasBody && !IsExtern && !IsPartial)
{
diagnostics.Add(ErrorCode.ERR_ConcreteMissingBody, location, this);
}
Expand All @@ -176,6 +183,15 @@ private void CheckModifiers(MethodKind methodKind, bool hasBody, Location locati
{
diagnostics.Add(ErrorCode.ERR_ConstructorInStaticClass, location);
}
else if (IsPartial && !ContainingType.IsPartial())
{
diagnostics.Add(ErrorCode.ERR_PartialMemberOnlyInPartialClass, location);
}

if (IsPartial)
{
ModifierUtils.CheckFeatureAvailabilityForPartialEventsAndConstructors(location, diagnostics);
}
}

internal override OneOrMany<SyntaxList<AttributeListSyntax>> GetAttributeDeclarations()
Expand Down Expand Up @@ -213,5 +229,35 @@ protected override bool IsWithinExpressionOrBlockBody(int position, out int offs
offset = -1;
return false;
}

#nullable enable
public sealed override bool IsExtern => PartialImplementationPart is { } implementation ? implementation.IsExtern : HasExternModifier;

private bool HasAnyBody => flags.HasAnyBody;

internal bool IsPartialDefinition => IsPartial && !HasAnyBody && !HasExternModifier;

internal bool IsPartialImplementation => IsPartial && (HasAnyBody || HasExternModifier);

internal SourceConstructorSymbol? OtherPartOfPartial => _otherPartOfPartial;

public override MethodSymbol? PartialDefinitionPart => IsPartialImplementation ? OtherPartOfPartial : null;

public override MethodSymbol? PartialImplementationPart => IsPartialDefinition ? OtherPartOfPartial : null;

internal static void InitializePartialConstructorParts(SourceConstructorSymbol definition, SourceConstructorSymbol implementation)
{
Debug.Assert(definition.IsPartialDefinition);
Debug.Assert(implementation.IsPartialImplementation);

Debug.Assert(definition._otherPartOfPartial is not { } alreadySetImplPart || alreadySetImplPart == implementation);
Debug.Assert(implementation._otherPartOfPartial is not { } alreadySetDefPart || alreadySetDefPart == definition);

definition._otherPartOfPartial = implementation;
implementation._otherPartOfPartial = definition;

Debug.Assert(definition._otherPartOfPartial == implementation);
Debug.Assert(implementation._otherPartOfPartial == definition);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ internal SourceCustomEventSymbol(SourceMemberContainerTypeSymbol containingType,
ImmutableArray.Create<EventSymbol>(explicitlyImplementedEvent);
}

protected override bool IsFieldLike => false;

public override TypeWithAnnotations TypeWithAnnotations
{
get { return _type; }
Expand Down
Loading