Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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,7 +598,7 @@ 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))
{
Expand All @@ -613,6 +613,7 @@ bool checkSymbol(Symbol sym, TextSpan memberSpan, SymbolKind kind, out Symbol re
{
MethodSymbol method => (Symbol)method.PartialImplementationPart,
SourcePropertySymbol property => property.PartialImplementationPart,
SourceEventSymbol ev => ev.PartialImplementationPart,
_ => throw ExceptionUtilities.UnexpectedValue(sym)
}
#pragma warning restore format
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 identifier, 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
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,7 +573,9 @@ 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 };
}

internal static bool ContainsTupleNames(this Symbol member)
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols
/// </summary>
internal abstract class SourceEventSymbol : EventSymbol, IAttributeTargetSymbol
{
private SourceEventSymbol? _otherPartOfPartial;

private readonly Location _location;
private readonly SyntaxReference _syntaxRef;
private readonly DeclarationModifiers _modifiers;
Expand Down Expand Up @@ -55,6 +57,11 @@ internal SourceEventSymbol(
bool modifierErrors;
_modifiers = MakeModifiers(modifiers, isExplicitInterfaceImplementation, isFieldLike, _location, diagnostics, out modifierErrors);
this.CheckAccessibility(_location, diagnostics, isExplicitInterfaceImplementation);

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

public Location Location
Expand All @@ -72,6 +79,8 @@ internal sealed override bool HasComplete(CompletionPart part)

internal override void ForceComplete(SourceLocation? locationOpt, Predicate<Symbol>? filter, CancellationToken cancellationToken)
{
SourcePartialImplementationPart?.ForceComplete(locationOpt, filter, cancellationToken);

if (filter?.Invoke(this) == false)
{
return;
Expand Down Expand Up @@ -370,11 +379,13 @@ public sealed override bool IsAbstract
get { return (_modifiers & DeclarationModifiers.Abstract) != 0; }
}

public sealed override bool IsExtern
private bool HasExternModifier
{
get { return (_modifiers & DeclarationModifiers.Extern) != 0; }
}

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

public sealed override bool IsStatic
{
get { return (_modifiers & DeclarationModifiers.Static) != 0; }
Expand Down Expand Up @@ -449,7 +460,7 @@ private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, bool expli
var defaultInterfaceImplementationModifiers = DeclarationModifiers.None;

// Check that the set of modifiers is allowed
var allowedModifiers = DeclarationModifiers.Unsafe;
var allowedModifiers = DeclarationModifiers.Partial | DeclarationModifiers.Unsafe;
if (!explicitInterfaceImplementation)
{
allowedModifiers |= DeclarationModifiers.New |
Expand Down Expand Up @@ -554,6 +565,18 @@ protected void CheckModifiersAndType(BindingDiagnosticBag diagnostics)
// '{0}' cannot be sealed because it is not an override
diagnostics.Add(ErrorCode.ERR_SealedNonOverride, location, this);
}
else if (IsPartial && !ContainingType.IsPartial())
{
diagnostics.Add(ErrorCode.ERR_PartialMemberOnlyInPartialClass, location);
}
else if (IsPartial && IsExplicitInterfaceImplementation)
{
diagnostics.Add(ErrorCode.ERR_PartialMemberNotExplicit, location);
}
else if (IsPartial && IsAbstract)
{
diagnostics.Add(ErrorCode.ERR_PartialMemberCannotBeAbstract, location);
}
else if (IsAbstract && ContainingType.TypeKind == TypeKind.Struct)
{
// The modifier '{0}' is not valid for this item
Expand Down Expand Up @@ -780,5 +803,42 @@ private void CheckExplicitImplementationAccessor(MethodSymbol? thisAccessor, Met
diagnostics.Add(ErrorCode.ERR_ExplicitPropertyAddingAccessor, thisAccessor.GetFirstLocation(), thisAccessor, explicitlyImplementedEvent);
}
}

internal bool IsPartial => (this.Modifiers & DeclarationModifiers.Partial) != 0;

/// <summary>
/// <see langword="true"/> if this symbol corresponds to a semi-colon body declaration.
/// <see langword="false"/> if this symbol corresponds to a declaration with custom <see langword="add"/> and <see langword="remove"/> accessors.
/// </summary>
protected abstract bool IsFieldLike { get; }

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

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

internal SourceEventSymbol? OtherPartOfPartial => _otherPartOfPartial;

internal SourceEventSymbol? SourcePartialDefinitionPart => IsPartialImplementation ? OtherPartOfPartial : null;

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

internal override EventSymbol? PartialDefinitionPart => SourcePartialDefinitionPart;

internal override EventSymbol? PartialImplementationPart => SourcePartialImplementationPart;

internal static void InitializePartialEventParts(SourceEventSymbol definition, SourceEventSymbol 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);
}
}
}
Loading