diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs index 84dd51c1cd3b0..79dd556bcad5d 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs @@ -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)) { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 13c398af16501..81ac6b2cd484f 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -1171,7 +1171,7 @@ Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. Imported type '{0}' is invalid. It contains a circular base type dependency. @@ -8047,4 +8047,22 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 'InterceptsLocationAttribute(string, int, int)' is not supported. Move to 'InterceptableLocation'-based generation of these attributes instead. (https://github.com/dotnet/roslyn/issues/72133) + + partial events and constructors + + + Partial member '{0}' must have an implementation part. + + + Partial member '{0}' must have a definition part. + + + Partial member '{0}' may not have multiple defining declarations. + + + Partial member '{0}' may not have multiple implementing declarations. + + + '{0}': partial event cannot have initializer + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 83f128759bf41..ba53a2c019d7d 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -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) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index f6e520514814b..ef9a601688203 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -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. diff --git a/src/Compilers/CSharp/Portable/Symbols/EventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/EventSymbol.cs index 42664a4724719..0217f7daae6c8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/EventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/EventSymbol.cs @@ -228,6 +228,9 @@ internal virtual bool IsExplicitInterfaceImplementation /// public abstract ImmutableArray ExplicitInterfaceImplementations { get; } + internal virtual EventSymbol? PartialImplementationPart => null; + internal virtual EventSymbol? PartialDefinitionPart => null; + /// /// Gets the kind of this symbol. /// diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs index 638734bf2cdf4..49e7dd616eec0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs @@ -551,7 +551,10 @@ 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 } + or SourceEventAccessorSymbol { IsPartial: true }; } internal static bool IsPartialImplementation(this Symbol member) @@ -560,7 +563,10 @@ 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 } + or SourceEventAccessorSymbol { IsPartialImplementation: true }; } internal static bool IsPartialDefinition(this Symbol member) @@ -569,9 +575,26 @@ 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 } + or SourceEventAccessorSymbol { IsPartialDefinition: true }; } +#nullable enable + internal static Symbol? GetPartialImplementationPart(this Symbol member) + { + Debug.Assert(member.IsDefinition); + 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) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs index efbe13b19594e..d0d2dc9440f10 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs @@ -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) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs index 512755dd22f60..7f67fd81d3f9d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs @@ -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; @@ -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, @@ -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); @@ -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); @@ -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); } @@ -176,6 +183,10 @@ 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); + } } internal override OneOrMany> GetAttributeDeclarations() @@ -213,5 +224,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 || ReferenceEquals(alreadySetImplPart, implementation)); + Debug.Assert(implementation._otherPartOfPartial is not { } alreadySetDefPart || ReferenceEquals(alreadySetDefPart, definition)); + + definition._otherPartOfPartial = implementation; + implementation._otherPartOfPartial = definition; + + Debug.Assert(ReferenceEquals(definition._otherPartOfPartial, implementation)); + Debug.Assert(ReferenceEquals(implementation._otherPartOfPartial, definition)); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventSymbol.cs index cc0b0475d0b68..b5faa9b49e15d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventSymbol.cs @@ -170,6 +170,8 @@ internal SourceCustomEventSymbol(SourceMemberContainerTypeSymbol containingType, ImmutableArray.Create(explicitlyImplementedEvent); } + protected override bool AccessorsHaveImplementation => true; + public override TypeWithAnnotations TypeWithAnnotations { get { return _type; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs index 7a3aa3b89b479..087678ce7f3dc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs @@ -233,5 +233,20 @@ internal sealed override int TryGetOverloadResolutionPriority() { return 0; } + +#nullable enable + public sealed override MethodSymbol? PartialImplementationPart => _event is { IsPartialDefinition: true, OtherPartOfPartial: { } other } + ? (MethodKind == MethodKind.EventAdd ? other.AddMethod : other.RemoveMethod) + : null; + + public sealed override MethodSymbol? PartialDefinitionPart => _event is { IsPartialImplementation: true, OtherPartOfPartial: { } other } + ? (MethodKind == MethodKind.EventAdd ? other.AddMethod : other.RemoveMethod) + : null; + + internal bool IsPartialDefinition => _event.IsPartialDefinition; + + internal bool IsPartialImplementation => _event.IsPartialImplementation; + + public sealed override bool IsExtern => PartialImplementationPart is { } implementation ? implementation.IsExtern : base.IsExtern; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs index f59aa15ba20fc..d788294087d3d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs @@ -22,6 +22,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// internal abstract class SourceEventSymbol : EventSymbol, IAttributeTargetSymbol { + private SourceEventSymbol? _otherPartOfPartial; + private readonly Location _location; private readonly SyntaxReference _syntaxRef; private readonly DeclarationModifiers _modifiers; @@ -72,6 +74,8 @@ internal sealed override bool HasComplete(CompletionPart part) internal override void ForceComplete(SourceLocation? locationOpt, Predicate? filter, CancellationToken cancellationToken) { + SourcePartialImplementationPart?.ForceComplete(locationOpt, filter, cancellationToken); + if (filter?.Invoke(this) == false) { return; @@ -370,11 +374,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; } @@ -449,7 +455,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 | @@ -554,6 +560,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 @@ -611,6 +629,11 @@ protected void CheckModifiersAndType(BindingDiagnosticBag diagnostics) diagnostics.Add(ErrorCode.ERR_NewVirtualInSealed, location, this, ContainingType); } + if (IsPartial) + { + ModifierUtils.CheckFeatureAvailabilityForPartialEventsAndConstructors(_location, diagnostics); + } + diagnostics.Add(location, useSiteInfo); } @@ -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; + + /// + /// if this symbol corresponds to a semi-colon body declaration. + /// if this symbol corresponds to a declaration with custom and accessors. + /// + protected abstract bool AccessorsHaveImplementation { get; } + + internal bool IsPartialDefinition => IsPartial && !AccessorsHaveImplementation && !HasExternModifier; + + internal bool IsPartialImplementation => IsPartial && (AccessorsHaveImplementation || 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 || ReferenceEquals(alreadySetImplPart, implementation)); + Debug.Assert(implementation._otherPartOfPartial is not { } alreadySetDefPart || ReferenceEquals(alreadySetDefPart, definition)); + + definition._otherPartOfPartial = implementation; + implementation._otherPartOfPartial = definition; + + Debug.Assert(ReferenceEquals(definition._otherPartOfPartial, implementation)); + Debug.Assert(ReferenceEquals(implementation._otherPartOfPartial, definition)); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldLikeEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldLikeEventSymbol.cs index be6a53393884a..0210b69146cf6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldLikeEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldLikeEventSymbol.cs @@ -6,24 +6,22 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Threading; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Symbols { /// /// This class represents an event declared in source without explicit accessors. /// It implicitly has thread safe accessors and an associated field (of the same - /// name), unless it does not have an initializer and is either extern or inside + /// name), unless it does not have an initializer and is extern, partial, or inside /// an interface, in which case it only has accessors. /// internal sealed class SourceFieldLikeEventSymbol : SourceEventSymbol { private readonly string _name; private readonly TypeWithAnnotations _type; - private readonly SynthesizedEventAccessorSymbol _addMethod; - private readonly SynthesizedEventAccessorSymbol _removeMethod; + private readonly SourceEventAccessorSymbol _addMethod; + private readonly SourceEventAccessorSymbol _removeMethod; internal SourceFieldLikeEventSymbol(SourceMemberContainerTypeSymbol containingType, Binder binder, SyntaxTokenList modifiers, VariableDeclaratorSyntax declaratorSyntax, BindingDiagnosticBag diagnostics) : base(containingType, declaratorSyntax, modifiers, isFieldLike: true, interfaceSpecifierSyntaxOpt: null, @@ -77,11 +75,15 @@ internal SourceFieldLikeEventSymbol(SourceMemberContainerTypeSymbol containingTy { diagnostics.Add(ErrorCode.ERR_ExternEventInitializer, this.GetFirstLocation(), this); } + else if (this.IsPartial) + { + diagnostics.Add(ErrorCode.ERR_PartialEventInitializer, this.GetFirstLocation(), this); + } } // NOTE: if there's an initializer in source, we'd better create a backing field, regardless of // whether or not the initializer is legal. - if (hasInitializer || !(this.IsExtern || this.IsAbstract)) + if (hasInitializer || !(this.IsExtern || this.IsAbstract || this.IsPartial)) { AssociatedEventField = MakeAssociatedField(declaratorSyntax); // Don't initialize this.type - we'll just use the type of the field (which is lazy and handles var) @@ -108,14 +110,22 @@ internal SourceFieldLikeEventSymbol(SourceMemberContainerTypeSymbol containingTy diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, this.GetFirstLocation()); } } - else if (!this.IsAbstract) + else if (!this.IsAbstract && !this.IsPartialDefinition) { diagnostics.Add(ErrorCode.ERR_EventNeedsBothAccessors, this.GetFirstLocation(), this); } } - _addMethod = new SynthesizedEventAccessorSymbol(this, isAdder: true, isExpressionBodied: false); - _removeMethod = new SynthesizedEventAccessorSymbol(this, isAdder: false, isExpressionBodied: false); + if (this.IsPartialDefinition) + { + _addMethod = new SourceEventDefinitionAccessorSymbol(this, isAdder: true, diagnostics); + _removeMethod = new SourceEventDefinitionAccessorSymbol(this, isAdder: false, diagnostics); + } + else + { + _addMethod = new SynthesizedEventAccessorSymbol(this, isAdder: true, isExpressionBodied: false); + _removeMethod = new SynthesizedEventAccessorSymbol(this, isAdder: false, isExpressionBodied: false); + } if (declarationSyntax.Variables[0] == declaratorSyntax) { @@ -126,6 +136,8 @@ internal SourceFieldLikeEventSymbol(SourceMemberContainerTypeSymbol containingTy declaratorDiagnostics.Free(); } + protected override bool AccessorsHaveImplementation => false; + /// /// Backing field for field-like event. Will be null if the event /// has no initializer and is either extern or inside an interface. @@ -191,5 +203,42 @@ internal override void ForceComplete(SourceLocation? locationOpt, Predicate + /// Accessor of a which is a partial definition. + /// + private sealed class SourceEventDefinitionAccessorSymbol : SourceEventAccessorSymbol + { + internal SourceEventDefinitionAccessorSymbol( + SourceFieldLikeEventSymbol ev, + bool isAdder, + BindingDiagnosticBag diagnostics) + : base( + @event: ev, + syntaxReference: ev.SyntaxReference, + location: ev.Location, + explicitlyImplementedEventOpt: null, + aliasQualifierOpt: null, + isAdder: isAdder, + isIterator: false, + isNullableAnalysisEnabled: ev.DeclaringCompilation.IsNullableAnalysisEnabledIn(ev.CSharpSyntaxNode), + isExpressionBodied: false) + { + Debug.Assert(ev.IsPartialDefinition); + + CheckFeatureAvailabilityAndRuntimeSupport(ev.CSharpSyntaxNode, ev.Location, hasBody: false, diagnostics: diagnostics); + } + + public override Accessibility DeclaredAccessibility => AssociatedEvent.DeclaredAccessibility; + + public override bool IsImplicitlyDeclared => true; + + internal override bool GenerateDebugInfo => true; + + internal override ExecutableCodeBinder? TryGetBodyBinder(BinderFactory? binderFactoryOpt = null, bool ignoreAccessibility = false) + { + return null; + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index abb58e2d51de5..033112cbd791d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -1751,6 +1751,9 @@ static bool isMemberInCompleteMemberList(MembersAndInitializers? membersAndIniti case PropertySymbol property: member = property.PartialDefinitionPart ?? property; break; + case EventSymbol ev: + member = ev.PartialDefinitionPart ?? ev; + break; } return membersAndInitializers?.NonTypeMembers.Contains(m => m == (object)member) == true; @@ -3701,14 +3704,24 @@ private static void MergePartialMembers( mergePartialProperties(nonTypeMembers, currentProperty, prevProperty, diagnostics); break; + case (SourceConstructorSymbol { IsStatic: false } currentConstructor, SourceConstructorSymbol { IsStatic: false } prevConstructor): + Debug.Assert(pair.Key.Equals(WellKnownMemberNames.InstanceConstructorName.AsMemory())); + mergePartialConstructors(nonTypeMembers, currentConstructor, prevConstructor, diagnostics); + break; + + case (SourceEventSymbol currentEvent, SourceEventSymbol prevEvent): + mergePartialEvents(nonTypeMembers, currentEvent, prevEvent, diagnostics); + break; + case (SourcePropertyAccessorSymbol, SourcePropertyAccessorSymbol): - break; // accessor symbols and their diagnostics are handled by processing the associated property + case (SourceEventAccessorSymbol, SourceEventAccessorSymbol): + break; // accessor symbols and their diagnostics are handled by processing the associated member default: // This is an error scenario. We simply don't merge the symbols in this case and a duplicate name diagnostic is reported separately. - // One way this case can be reached is if type contains both `public partial int P { get; }` and `public partial int P_get();`. - Debug.Assert(symbol is SourceOrdinaryMethodSymbol or SourcePropertySymbol or SourcePropertyAccessorSymbol); - Debug.Assert(prev is SourceOrdinaryMethodSymbol or SourcePropertySymbol or SourcePropertyAccessorSymbol); + // One way this case can be reached is if type contains both `public partial int P { get; }` and `public partial int get_P();`. + Debug.Assert(symbol is SourceOrdinaryMethodSymbol or SourcePropertySymbol or SourcePropertyAccessorSymbol or SourceEventAccessorSymbol); + Debug.Assert(prev is SourceOrdinaryMethodSymbol or SourcePropertySymbol or SourcePropertyAccessorSymbol or SourceEventAccessorSymbol); break; } } @@ -3746,8 +3759,29 @@ private static void MergePartialMembers( } break; + case SourceConstructorSymbol constructor: + if (constructor.OtherPartOfPartial is null) + { + diagnostics.Add( + constructor.IsPartialDefinition ? ErrorCode.ERR_PartialMemberMissingImplementation : ErrorCode.ERR_PartialMemberMissingDefinition, + constructor.GetFirstLocation(), + constructor); + } + break; + + case SourceEventSymbol ev: + if (ev.OtherPartOfPartial is null) + { + diagnostics.Add( + ev.IsPartialDefinition ? ErrorCode.ERR_PartialMemberMissingImplementation : ErrorCode.ERR_PartialMemberMissingDefinition, + ev.GetFirstLocation(), + ev); + } + break; + + case SourceEventAccessorSymbol: case SourcePropertyAccessorSymbol: - break; // diagnostics for missing partial accessors are handled in 'mergePartialProperties'. + break; // diagnostics for missing partial accessors are handled in 'mergePartialProperties'/'mergePartialEvents'. default: throw ExceptionUtilities.UnexpectedValue(symbol); @@ -3847,6 +3881,60 @@ static bool hasInitializer(SourcePropertySymbol property) return property.DeclaredBackingField?.HasInitializer == true; } } + + static void mergePartialConstructors(ArrayBuilder nonTypeMembers, SourceConstructorSymbol currentConstructor, SourceConstructorSymbol prevConstructor, BindingDiagnosticBag diagnostics) + { + if (currentConstructor.IsPartialImplementation && + (prevConstructor.IsPartialImplementation || (prevConstructor.OtherPartOfPartial is { } otherImplementation && !ReferenceEquals(otherImplementation, currentConstructor)))) + { + // A partial constructor may not have multiple implementing declarations + diagnostics.Add(ErrorCode.ERR_PartialMemberDuplicateImplementation, currentConstructor.GetFirstLocation(), currentConstructor); + } + else if (currentConstructor.IsPartialDefinition && + (prevConstructor.IsPartialDefinition || (prevConstructor.OtherPartOfPartial is { } otherDefinition && !ReferenceEquals(otherDefinition, currentConstructor)))) + { + // A partial constructor may not have multiple defining declarations + diagnostics.Add(ErrorCode.ERR_PartialMemberDuplicateDefinition, currentConstructor.GetFirstLocation(), currentConstructor); + } + else + { + FixPartialConstructor(nonTypeMembers, prevConstructor, currentConstructor); + } + } + + static void mergePartialEvents(ArrayBuilder nonTypeMembers, SourceEventSymbol currentEvent, SourceEventSymbol prevEvent, BindingDiagnosticBag diagnostics) + { + if (currentEvent.IsPartialImplementation && + (prevEvent.IsPartialImplementation || (prevEvent.OtherPartOfPartial is { } otherImplementation && !ReferenceEquals(otherImplementation, currentEvent)))) + { + // A partial event may not have multiple implementing declarations + diagnostics.Add(ErrorCode.ERR_PartialMemberDuplicateImplementation, currentEvent.GetFirstLocation(), currentEvent); + } + else if (currentEvent.IsPartialDefinition && + (prevEvent.IsPartialDefinition || (prevEvent.OtherPartOfPartial is { } otherDefinition && !ReferenceEquals(otherDefinition, currentEvent)))) + { + // A partial event may not have multiple defining declarations + diagnostics.Add(ErrorCode.ERR_PartialMemberDuplicateDefinition, currentEvent.GetFirstLocation(), currentEvent); + } + else + { + mergeAccessors(nonTypeMembers, (SourceEventAccessorSymbol?)currentEvent.AddMethod, (SourceEventAccessorSymbol?)prevEvent.AddMethod); + mergeAccessors(nonTypeMembers, (SourceEventAccessorSymbol?)currentEvent.RemoveMethod, (SourceEventAccessorSymbol?)prevEvent.RemoveMethod); + FixPartialEvent(nonTypeMembers, prevEvent, currentEvent); + } + + static void mergeAccessors(ArrayBuilder nonTypeMembers, SourceEventAccessorSymbol? currentAccessor, SourceEventAccessorSymbol? prevAccessor) + { + if (currentAccessor?.IsPartialImplementation == true) + { + Remove(nonTypeMembers, currentAccessor); + } + else if (prevAccessor?.IsPartialImplementation == true) + { + Remove(nonTypeMembers, prevAccessor); + } + } + } } /// Links together the definition and implementation parts of a partial method. Removes implementation part from . @@ -3899,6 +3987,50 @@ private static void FixPartialProperty(ArrayBuilder nonTypeMembers, Sour Remove(nonTypeMembers, implementation); } + /// Links together the definition and implementation parts of a partial constructor. Removes implementation part from . + private static void FixPartialConstructor(ArrayBuilder nonTypeMembers, SourceConstructorSymbol part1, SourceConstructorSymbol part2) + { + SourceConstructorSymbol definition; + SourceConstructorSymbol implementation; + if (part1.IsPartialDefinition) + { + definition = part1; + implementation = part2; + } + else + { + definition = part2; + implementation = part1; + } + + SourceConstructorSymbol.InitializePartialConstructorParts(definition, implementation); + + // a partial constructor is represented in the member list by its definition part: + Remove(nonTypeMembers, implementation); + } + + /// Links together the definition and implementation parts of a partial event. Removes implementation part from . + private static void FixPartialEvent(ArrayBuilder nonTypeMembers, SourceEventSymbol part1, SourceEventSymbol part2) + { + SourceEventSymbol definition; + SourceEventSymbol implementation; + if (part1.IsPartialDefinition) + { + definition = part1; + implementation = part2; + } + else + { + definition = part2; + implementation = part1; + } + + SourceEventSymbol.InitializePartialEventParts(definition, implementation); + + // a partial event is represented in the member list by its definition part: + Remove(nonTypeMembers, implementation); + } + private static void Remove(ArrayBuilder symbols, Symbol symbol) { for (int i = 0; i < symbols.Count; i++) @@ -5195,8 +5327,8 @@ private void AddNonTypeMembers( } } - Debug.Assert((object)@event.AddMethod != null); - Debug.Assert((object)@event.RemoveMethod != null); + Debug.Assert(@event.IsPartial || @event.AddMethod is not null); + Debug.Assert(@event.IsPartial || @event.RemoveMethod is not null); AddAccessorIfAvailable(builder.NonTypeMembersWithPartialImplementations, @event.AddMethod); AddAccessorIfAvailable(builder.NonTypeMembersWithPartialImplementations, @event.RemoveMethod); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs index c0df4ffcad913..60f1757b19408 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs @@ -679,6 +679,17 @@ protected override void MethodChecks(BindingDiagnosticBag diagnostics) CheckModifiers(MethodKind == MethodKind.ExplicitInterfaceImplementation, _location, diagnostics); } + + internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, BindingDiagnosticBag diagnostics) + { + base.AfterAddingTypeMembersChecks(conversions, diagnostics); + + if (this.ReturnType?.IsErrorType() == true && GetSyntax().ReturnType is IdentifierNameSyntax { Identifier.RawContextualKind: (int)SyntaxKind.PartialKeyword }) + { + var available = MessageID.IDS_FeaturePartialEventsAndConstructors.CheckFeatureAvailability(diagnostics, DeclaringCompilation, ReturnTypeLocation); + Debug.Assert(!available, "Should have been parsed as partial constructor."); + } + } #nullable disable // Consider moving this to flags to save space diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index f9beb8f61f97b..6d1368f53458a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1667,6 +1667,11 @@ Parametr params musí mít platný typ kolekce. + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. Obě deklarace částečných členů musí mít shodné modifikátory přístupnosti. @@ -1677,11 +1682,31 @@ Částečný člen nemůže mít modifikátor abstract. + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. V deklaracích částečných členů, {0} a {1} se musí používat stejné názvy prvků řazené kolekce členů. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Částečný člen musí být deklarován v rámci částečného typu. @@ -2532,6 +2557,11 @@ kolekce parametrů + + partial events and constructors + partial events and constructors + + positional fields in records pozice polí v záznamech @@ -6704,8 +6734,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - Modifikátor partial se může objevit jen bezprostředně před klíčovými slovy class, record, struct, interface nebo návratovým typem metody nebo vlastnosti. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + Modifikátor partial se může objevit jen bezprostředně před klíčovými slovy class, record, struct, interface nebo návratovým typem metody nebo vlastnosti. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index f67d28a8bb90d..f9db1edebf616 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1667,6 +1667,11 @@ Der Params-Parameter muss einen gültigen Sammlungstyp aufweisen + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. Beide Deklarationen des partiellen Mitglieds müssen identische Zugriffsmodifizierer aufweisen. @@ -1677,11 +1682,31 @@ Ein partielles Mitglied darf nicht den Modifizierer "abstract" aufweisen. + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Beide Deklarationen des partiellen Mitglieds ("{0}" und "{1}") müssen die gleichen Tupelelementnamen verwenden. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Ein partielles Mitglied muss innerhalb eines partiellen Typs deklariert sein. @@ -2532,6 +2557,11 @@ Params-Sammlungen + + partial events and constructors + partial events and constructors + + positional fields in records Positionsfelder in Datensätzen @@ -6704,8 +6734,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - Der partial-Modifizierer kann nur unmittelbar vor "class", "record", "struct", "interface" oder einem Methoden- Eigenschaftsrückgabetyp verwendet werden. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + Der partial-Modifizierer kann nur unmittelbar vor "class", "record", "struct", "interface" oder einem Methoden- Eigenschaftsrückgabetyp verwendet werden. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 97ad7e39a3a67..2db46ddd2c07c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1667,6 +1667,11 @@ El parámetro params debe tener un tipo de colección válido + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. Ambas declaraciones de miembro parcial deben tener modificadores de accesibilidad idénticos. @@ -1677,11 +1682,31 @@ Un miembro parcial no puede tener el modificador 'abstract' + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Ambas declaraciones de miembro parcial, '{0}' y '{1}', deben usar los mismos nombres de elemento de tupla. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Un miembro parcial debe declararse dentro de un tipo parcial @@ -2532,6 +2557,11 @@ colecciones params + + partial events and constructors + partial events and constructors + + positional fields in records campos posicionales en registros @@ -6704,8 +6734,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - El modificador 'partial' solo puede aparecer inmediatamente antes de 'class', 'record', 'struct', 'interface' o un método o tipo de valor devuelto de propiedad. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + El modificador 'partial' solo puede aparecer inmediatamente antes de 'class', 'record', 'struct', 'interface' o un método o tipo de valor devuelto de propiedad. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 6c4bc747be0fc..8ef14ce29ffee 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1667,6 +1667,11 @@ Le paramètre params doit avoir un type de collection valide + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. Les deux déclarations de membres partiels doivent avoir des modificateurs d'accessibilité identiques. @@ -1677,11 +1682,31 @@ Un membre partiel ne peut pas avoir le modificateur « abstract » + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Les deux déclarations de membres partiels, « {0} » et « {1} », doivent utiliser les mêmes noms d'éléments tuple. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Un membre partiel doit être déclaré dans un type partiel @@ -2532,6 +2557,11 @@ collections de params + + partial events and constructors + partial events and constructors + + positional fields in records champs positionnels dans les enregistrements @@ -6704,8 +6734,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - Le modificateur 'partial' peut apparaître uniquement juste avant 'class', 'record', 'struct', 'interface' ou un type de retour de méthode ou de propriété. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + Le modificateur 'partial' peut apparaître uniquement juste avant 'class', 'record', 'struct', 'interface' ou un type de retour de méthode ou de propriété. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 8ac91c37499e7..f5c5e4a365693 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1667,6 +1667,11 @@ Il parametro params deve avere un tipo di raccolta valido + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. I modificatori di accessibilità devono essere identici in entrambe le dichiarazioni di membro parziale. @@ -1677,11 +1682,31 @@ Un membro parziale non può contenere il modificatore 'abstract' + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Entrambe le dichiarazioni di membro parziale '{0}' e '{1}' devono usare gli stessi nomi di elementi di tupla. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Un membro parziale deve essere dichiarato in un tipo parziale @@ -2532,6 +2557,11 @@ raccolte parametri + + partial events and constructors + partial events and constructors + + positional fields in records campi posizionali nei record @@ -6704,8 +6734,8 @@ target:module Compila un modulo che può essere aggiunto ad altro - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - Il modificatore 'partial' può trovarsi solo immediatamente prima di 'class', 'record', 'struct', 'interface' o del tipo restituito di un metodo o di una proprietà. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + Il modificatore 'partial' può trovarsi solo immediatamente prima di 'class', 'record', 'struct', 'interface' o del tipo restituito di un metodo o di una proprietà. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 1435728a114fc..1f1f0df203762 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1667,6 +1667,11 @@ params パラメーターには有効なコレクションの種類が必要です + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. 両方の部分メンバー宣言には、同じアクセシビリティ修飾子を指定する必要があります。 @@ -1677,11 +1682,31 @@ 部分メンバーに 'abstract' 修飾子を指定することはできません + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. 部分メンバー宣言 '{0}' および '{1}' は、どちらも同じタプル要素名を使用する必要があります。 + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type 部分メンバーは、部分型内で宣言される必要があります @@ -2532,6 +2557,11 @@ params コレクション + + partial events and constructors + partial events and constructors + + positional fields in records レコード内の位置指定フィールド @@ -6704,8 +6734,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - 'partial' 修飾子は、'class'、'record'、'struct'、'interface'、またはメソッドまだはプロパティの戻り値の型の直前にのみ指定できます。 + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + 'partial' 修飾子は、'class'、'record'、'struct'、'interface'、またはメソッドまだはプロパティの戻り値の型の直前にのみ指定できます。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index d4c4a339c7929..bcaf9f9d405d7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1667,6 +1667,11 @@ params 매개 변수는 유효한 컬렉션 형식이어야 합니다. + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. 두 부분 멤버 선언에는 동일한 접근성 한정자를 갖어야 합니다. @@ -1677,11 +1682,31 @@ 부분 멤버는 'abstract' 한정자를 갖을 수 없습니다. + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. 두 부분 멤버 선언은 '{0}' 및 '{1}' 모두에서 동일한 튜플 요소 이름을 사용해야 합니다. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type 부분 멤버 내에 부분 메서드가 선언되어야 함 @@ -2532,6 +2557,11 @@ params 컬렉션 + + partial events and constructors + partial events and constructors + + positional fields in records 레코드의 위치 필드 @@ -6704,8 +6734,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - 'partial' 한정자는 'class', 'record', 'struct', 'interface' 또는 메서드 또는 속성 반환 형식 바로 앞에만 올 수 있습니다. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + 'partial' 한정자는 'class', 'record', 'struct', 'interface' 또는 메서드 또는 속성 반환 형식 바로 앞에만 올 수 있습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 242fa39cf7703..9ca4664ae0c67 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1667,6 +1667,11 @@ Parametr params musi mieć prawidłowy typ kolekcji + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. Obie częściowe deklaracje elementów członkowskich muszą mieć identyczne modyfikatory dostępności. @@ -1677,11 +1682,31 @@ Częściowy element członkowski nie może mieć modyfikatora „abstract” + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Obie deklaracje częściowych elementów członkowskich, „{0}” i „{1}”, muszą korzystać z tych samych nazw elementów krotki. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Częściowy element członkowski musi być zadeklarowany w ramach częściowego typu @@ -2532,6 +2557,11 @@ kolekcje params + + partial events and constructors + partial events and constructors + + positional fields in records pola pozycyjne w rekordach @@ -6704,8 +6734,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - Modyfikator „partial” może pojawić się tylko bezpośrednio przed słowem kluczowym „class”, „record” „struct”, „interface” lub zwracanym typem metody lub właściwości. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + Modyfikator „partial” może pojawić się tylko bezpośrednio przed słowem kluczowym „class”, „record” „struct”, „interface” lub zwracanym typem metody lub właściwości. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index aecc0953a73cf..c09c10bb82469 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1667,6 +1667,11 @@ O parâmetro params deve ter um tipo de coleção válido + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. As duas declarações de membros parciais precisam ter modificadores de acessibilidade idênticos. @@ -1677,11 +1682,31 @@ Um membro parcial não pode ter o modificador "abstract" + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. As duas declarações de membros parciais, "{0}" e "{1}", precisam usar os mesmos nomes de elementos de tupla. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Um membro parcial precisa ser declarado dentro de um tipo parcial @@ -2532,6 +2557,11 @@ coleções de params + + partial events and constructors + partial events and constructors + + positional fields in records campos posicionais nos registros @@ -6704,8 +6734,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - O modificador "partial" só pode aparecer imediatamente antes de "class", de "record", de "struct", de "interface" ou de um tipo de retorno de método ou propriedade. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + O modificador "partial" só pode aparecer imediatamente antes de "class", de "record", de "struct", de "interface" ou de um tipo de retorno de método ou propriedade. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index f01a4b78a48da..dfceabd6724a0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1667,6 +1667,11 @@ Параметр params должен иметь допустимый тип коллекции. + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. У обоих объявлений частичного члена должны быть идентичные модификаторы доступа. @@ -1677,11 +1682,31 @@ У частичного члена не может быть модификатора "abstract" + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Оба определения частичного члена "{0}" и "{1}" должны использовать одинаковые имена элементов кортежа. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Частичный член должен быть объявлен из частичного типа @@ -2532,6 +2557,11 @@ коллекции params + + partial events and constructors + partial events and constructors + + positional fields in records позиционные поля в записях @@ -6705,8 +6735,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - Модификатор "partial" может использоваться только перед ключевыми словами "class", "record", "struct" и "interface", а также перед возвращаемым типом метода или свойства. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + Модификатор "partial" может использоваться только перед ключевыми словами "class", "record", "struct" и "interface", а также перед возвращаемым типом метода или свойства. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 96e2fa7558ec1..9a4ee34560ab4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1667,6 +1667,11 @@ Params parametresi geçerli bir koleksiyon türüne sahip olmalıdır + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. Kısmi üye bildirimlerinin ikisi de aynı erişilebilirlik değiştiricilerine sahip olmalıdır. @@ -1677,11 +1682,31 @@ Kısmi üyede 'abstract' değiştiricisi olamaz + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Kısmi üye bildirimlerinin ikisi de ('{0}' ve '{1}') aynı tanımlama grubu adını kullanmalıdır. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Kısmi metot, kısmi tür içinde bildirilmelidir @@ -2532,6 +2557,11 @@ params koleksiyonları + + partial events and constructors + partial events and constructors + + positional fields in records kayıtlardaki konumsal alanlar @@ -6704,8 +6734,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - 'partial' değiştiricisi yalnızca 'class', 'record', 'struct', 'interface' ifadelerinden veya metot ya da özellik dönüş türünden hemen önce gelebilir. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + 'partial' değiştiricisi yalnızca 'class', 'record', 'struct', 'interface' ifadelerinden veya metot ya da özellik dönüş türünden hemen önce gelebilir. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 2ff45997e4afb..9be0dbcc47b74 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1667,6 +1667,11 @@ Params 参数必须具有有效的集合类型 + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. 这两个分部成员声明必须具有相同的可访问性修饰符。 @@ -1677,11 +1682,31 @@ 分部成员不能具有 "abstract" 修饰符 + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. 这两个分部成员声明(“{0}”和“{1}”)都必须使用相同的元组元素名称。 + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type 分部成员必须在分部类型内声明 @@ -2532,6 +2557,11 @@ Params 集合 + + partial events and constructors + partial events and constructors + + positional fields in records 记录中的位置字段 @@ -6704,8 +6734,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - "partial" 修饰符的后面只能紧跟 "class"、"record"、"struct"、"interface" 或者方法或属性返回类型。 + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + "partial" 修饰符的后面只能紧跟 "class"、"record"、"struct"、"interface" 或者方法或属性返回类型。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index ec8fad7250765..0aa9373876f89 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1667,6 +1667,11 @@ params 參數必須具有有效的集合型別 + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. 兩個部分成員宣告都必須具有完全相同的協助工具修飾元。 @@ -1677,11 +1682,31 @@ 部分成員不能有 'abstract' 修飾元 + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. 兩個部份成員宣告 '{0}' 和 '{1}' 都必須使用相同的 Tuple 元素名稱。 + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type 部分成員必須在部分型別內宣告 @@ -2532,6 +2557,11 @@ 參數集合 + + partial events and constructors + partial events and constructors + + positional fields in records 記錄中的位置欄位 @@ -6704,8 +6734,8 @@ strument:TestCoverage 產生檢測要收集 - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - 'partial' 修飾元只能直接出現在 'class'、'record'、'struct'、'interface' 或方法或屬性傳回型別之前。 + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + 'partial' 修飾元只能直接出現在 'class'、'record'、'struct'、'interface' 或方法或屬性傳回型別之前。 diff --git a/src/Compilers/CSharp/Test/Emit3/Diagnostics/GetDiagnosticsTests.cs b/src/Compilers/CSharp/Test/Emit3/Diagnostics/GetDiagnosticsTests.cs index da6e2ff2751c5..3a7a679a1ab01 100644 --- a/src/Compilers/CSharp/Test/Emit3/Diagnostics/GetDiagnosticsTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Diagnostics/GetDiagnosticsTests.cs @@ -175,7 +175,7 @@ private void NonPartialMethod2() { } Assert.True(eventQueue.Count > 0); bool compilationStartedFired; HashSet declaredSymbolNames, completedCompilationUnits; - Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out completedCompilationUnits)); + Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out _, out completedCompilationUnits)); // Verify symbol declared events fired for all symbols declared in the first source file. Assert.True(compilationStartedFired); @@ -224,7 +224,7 @@ partial void PartialMethod() { } Assert.True(eventQueue.Count > 0); bool compilationStartedFired; HashSet declaredSymbolNames, completedCompilationUnits; - Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out completedCompilationUnits)); + Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out _, out completedCompilationUnits)); // Verify symbol declared events fired for all symbols declared in the first source file. Assert.True(compilationStartedFired); @@ -276,7 +276,7 @@ partial class Class Assert.True(eventQueue.Count > 0); bool compilationStartedFired; HashSet declaredSymbolNames, completedCompilationUnits; - Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out completedCompilationUnits)); + Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out _, out completedCompilationUnits)); // Verify symbol declared events fired for all symbols declared in the first source file. Assert.True(compilationStartedFired); @@ -303,6 +303,130 @@ partial class Class AssertEx.Equal(["file1"], completedCompilationUnits.OrderBy(name => name)); } + [Fact] + public void TestCompilationEventsForPartialEvent() + { + var source1 = @" +namespace N1 +{ + partial class Class + { + event System.Action NonPartialEvent1 { add { } remove { } } + partial event System.Action DefOnlyPartialEvent; + partial event System.Action ImplOnlyPartialEvent { add { } remove { } } + partial event System.Action PartialEvent1; + partial event System.Action PartialEvent2 { add { } remove { } } + } +} +"; + var source2 = @" +namespace N1 +{ + partial class Class + { + event System.Action NonPartialEvent2 { add { } remove { } } + partial event System.Action PartialEvent1 { add { } remove { } } + partial event System.Action PartialEvent2; + } +} +"; + + var tree1 = CSharpSyntaxTree.ParseText(source1, path: "file1", options: TestOptions.RegularPreview); + var tree2 = CSharpSyntaxTree.ParseText(source2, path: "file2", options: TestOptions.RegularPreview); + var eventQueue = new AsyncQueue(); + var compilation = CreateCompilationWithMscorlib461(new[] { tree1, tree2 }).WithEventQueue(eventQueue); + + // Invoke SemanticModel.GetDiagnostics to force populate the event queue for symbols in the first source file. + var model = compilation.GetSemanticModel(tree1); + model.GetDiagnostics(tree1.GetRoot().FullSpan); + + Assert.True(eventQueue.Count > 0); + bool compilationStartedFired; + HashSet declaredSymbolNames, completedCompilationUnits; + Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out _, out completedCompilationUnits)); + + // Verify symbol declared events fired for all symbols declared in the first source file. + Assert.True(compilationStartedFired); + + // NB: NonPartialEvent2 is missing here because we only asked for diagnostics in tree1. + // PartialEvent2 is missing because it is the implementation part and that is removed (only the definition part is kept). + AssertEx.Equal([ + "", + "add_ImplOnlyPartialEvent", + "add_NonPartialEvent1", + "add_PartialEvent1", + "Class", + "DefOnlyPartialEvent", + "ImplOnlyPartialEvent", + "N1", + "NonPartialEvent1", + "PartialEvent1", + "remove_ImplOnlyPartialEvent", + "remove_NonPartialEvent1", + "remove_PartialEvent1", + ], declaredSymbolNames.OrderBy(name => name)); + + AssertEx.Equal(["file1"], completedCompilationUnits.OrderBy(name => name)); + } + + [Fact] + public void TestCompilationEventsForPartialConstructor() + { + var source1 = @" +namespace N1 +{ + partial class Class + { + partial C(int a) { } // not partial 1 + partial C(int a, int b); // def only + partial C(int a, int b, int c) { } // impl only + partial C(int a, int b, int c, int d); // full partial with def first + partial C(int a, int b, int c, int d, int e) { } // full partial with impl first + } +} +"; + var source2 = @" +namespace N1 +{ + partial class Class + { + partial C(string a) { } // not partial 2 + partial C(int a, int b, int c, int d) { } // full partial with def first + partial C(int a, int b, int c, int d, int e); // full partial with impl first + } +} +"; + + var tree1 = CSharpSyntaxTree.ParseText(source1, path: "file1", options: TestOptions.RegularPreview); + var tree2 = CSharpSyntaxTree.ParseText(source2, path: "file2", options: TestOptions.RegularPreview); + var eventQueue = new AsyncQueue(); + var compilation = CreateCompilationWithMscorlib461(new[] { tree1, tree2 }).WithEventQueue(eventQueue); + + // Invoke SemanticModel.GetDiagnostics to force populate the event queue for symbols in the first source file. + var model = compilation.GetSemanticModel(tree1); + model.GetDiagnostics(tree1.GetRoot().FullSpan); + + Assert.True(eventQueue.Count > 0); + bool compilationStartedFired; + HashSet declaredSymbols, completedCompilationUnits; + Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out _, out declaredSymbols, out completedCompilationUnits)); + + // Verify symbol declared events fired for all symbols declared in the first source file. + Assert.True(compilationStartedFired); + + // NB: non partial 2 is missing here because we only asked for diagnostics in tree1 + AssertEx.Equal([ + "", + "N1", + "N1.Class", + "N1.Class..ctor(System.Int32 a)", + "N1.Class..ctor(System.Int32 a, System.Int32 b, System.Int32 c)", + "N1.Class..ctor(System.Int32 a, System.Int32 b, System.Int32 c, System.Int32 d)", + ], declaredSymbols.OrderBy(name => name, StringComparer.Ordinal)); + + AssertEx.Equal(["file1"], completedCompilationUnits.OrderBy(name => name)); + } + [Fact, WorkItem(8178, "https://github.com/dotnet/roslyn/issues/8178")] public void TestEarlyCancellation() { @@ -324,10 +448,11 @@ private void NonPartialMethod1() { } model.GetDiagnostics(tree.GetRoot().FullSpan); } - private static bool DequeueCompilationEvents(AsyncQueue eventQueue, out bool compilationStartedFired, out HashSet declaredSymbolNames, out HashSet completedCompilationUnits) + private static bool DequeueCompilationEvents(AsyncQueue eventQueue, out bool compilationStartedFired, out HashSet declaredSymbolNames, out HashSet declaredSymbols, out HashSet completedCompilationUnits) { compilationStartedFired = false; declaredSymbolNames = new HashSet(); + declaredSymbols = new HashSet(); completedCompilationUnits = new HashSet(); if (eventQueue.Count == 0) { @@ -348,7 +473,7 @@ private static bool DequeueCompilationEvents(AsyncQueue eventQ if (symbolDeclaredEvent != null) { var symbol = symbolDeclaredEvent.Symbol; - var added = declaredSymbolNames.Add(symbol.Name); + var added = declaredSymbolNames.Add(symbol.Name) & declaredSymbols.Add(symbol.ToTestDisplayString()); if (!added) { Assert.True(symbol.GetSymbol().IsPartialMember(), "Unexpected multiple symbol declared events for symbol " + symbol); diff --git a/src/Compilers/CSharp/Test/Emit3/PartialEventsAndConstructorsTests.cs b/src/Compilers/CSharp/Test/Emit3/PartialEventsAndConstructorsTests.cs index a3a76c94790a7..94746d9fc5858 100644 --- a/src/Compilers/CSharp/Test/Emit3/PartialEventsAndConstructorsTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/PartialEventsAndConstructorsTests.cs @@ -2,7 +2,14 @@ // 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.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests; @@ -32,12 +39,15 @@ static void Main() // (5,50): error CS1513: } expected // System.Console.Write(F().GetType().Name); Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 50), - // (6,9): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - // partial F() => new(); - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(6, 9), // (6,17): error CS1520: Method must have a return type // partial F() => new(); Diagnostic(ErrorCode.ERR_MemberNeedsType, "F").WithLocation(6, 17), + // (6,17): error CS0751: A partial member must be declared within a partial type + // partial F() => new(); + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "F").WithLocation(6, 17), + // (6,17): error CS9401: Partial member 'partial.partial()' must have a definition part. + // partial F() => new(); + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "F").WithArguments("partial.partial()").WithLocation(6, 17), // (6,24): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // partial F() => new(); Diagnostic(ErrorCode.ERR_IllegalStatement, "new()").WithLocation(6, 24), @@ -95,12 +105,15 @@ class @partial; var expectedDiagnostics = new[] { - // (3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - // partial F() => new(); - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), // (3,13): error CS1520: Method must have a return type // partial F() => new(); Diagnostic(ErrorCode.ERR_MemberNeedsType, "F").WithLocation(3, 13), + // (3,13): error CS0751: A partial member must be declared within a partial type + // partial F() => new(); + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "F").WithLocation(3, 13), + // (3,13): error CS9401: Partial member 'C.C()' must have a definition part. + // partial F() => new(); + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "F").WithArguments("C.C()").WithLocation(3, 13), // (3,20): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // partial F() => new(); Diagnostic(ErrorCode.ERR_IllegalStatement, "new()").WithLocation(3, 20), @@ -112,4 +125,1290 @@ class @partial; CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); } + + [Fact] + public void ReturningPartialType_Method_CouldBePartialConstructor() + { + var source = """ + class C + { + partial F() { } + partial C() { } + } + """; + CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (3,5): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // partial F() { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(3, 5), + // (3,5): error CS8652: The feature 'partial events and constructors' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // partial F() { } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "partial").WithArguments("partial events and constructors").WithLocation(3, 5), + // (3,13): error CS0161: 'C.F()': not all code paths return a value + // partial F() { } + Diagnostic(ErrorCode.ERR_ReturnExpected, "F").WithArguments("C.F()").WithLocation(3, 13), + // (4,5): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // partial C() { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(4, 5), + // (4,5): error CS8652: The feature 'partial events and constructors' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // partial C() { } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "partial").WithArguments("partial events and constructors").WithLocation(4, 5), + // (4,13): error CS0542: 'C': member names cannot be the same as their enclosing type + // partial C() { } + Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "C").WithArguments("C").WithLocation(4, 13), + // (4,13): error CS0161: 'C.C()': not all code paths return a value + // partial C() { } + Diagnostic(ErrorCode.ERR_ReturnExpected, "C").WithArguments("C.C()").WithLocation(4, 13)); + } + + [Fact] + public void ReturningPartialType_Method_Escaped() + { + var source = """ + class C + { + @partial F() { } + @partial C() { } + } + """; + + var expectedDiagnostics = new[] + { + // (3,5): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // @partial F() { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "@partial").WithArguments("partial").WithLocation(3, 5), + // (3,14): error CS0161: 'C.F()': not all code paths return a value + // @partial F() { } + Diagnostic(ErrorCode.ERR_ReturnExpected, "F").WithArguments("C.F()").WithLocation(3, 14), + // (4,5): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // @partial C() { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "@partial").WithArguments("partial").WithLocation(4, 5), + // (4,14): error CS0542: 'C': member names cannot be the same as their enclosing type + // @partial C() { } + Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "C").WithArguments("C").WithLocation(4, 14), + // (4,14): error CS0161: 'C.C()': not all code paths return a value + // @partial C() { } + Diagnostic(ErrorCode.ERR_ReturnExpected, "C").WithArguments("C.C()").WithLocation(4, 14) + }; + + CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void LangVersion() + { + var source = """ + partial class C + { + partial event System.Action E; + partial event System.Action E { add { } remove { } } + partial C(); + partial C() { } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (3,33): error CS8703: The modifier 'partial' is not valid for this item in C# 13.0. Please use language version 'preview' or greater. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "E").WithArguments("partial", "13.0", "preview").WithLocation(3, 33), + // (5,5): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // partial C(); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(5, 5), + // (5,5): error CS8652: The feature 'partial events and constructors' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // partial C(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "partial").WithArguments("partial events and constructors").WithLocation(5, 5), + // (5,13): error CS0501: 'C.C()' must declare a body because it is not marked abstract, extern, or partial + // partial C(); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "C").WithArguments("C.C()").WithLocation(5, 13), + // (5,13): error CS0542: 'C': member names cannot be the same as their enclosing type + // partial C(); + Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "C").WithArguments("C").WithLocation(5, 13), + // (6,5): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // partial C() { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(6, 5), + // (6,5): error CS8652: The feature 'partial events and constructors' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // partial C() { } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "partial").WithArguments("partial events and constructors").WithLocation(6, 5), + // (6,13): error CS0542: 'C': member names cannot be the same as their enclosing type + // partial C() { } + Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "C").WithArguments("C").WithLocation(6, 13), + // (6,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C() { } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(6, 13), + // (6,13): error CS0161: 'C.C()': not all code paths return a value + // partial C() { } + Diagnostic(ErrorCode.ERR_ReturnExpected, "C").WithArguments("C.C()").WithLocation(6, 13)); + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(); + CreateCompilation(source).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void PartialLast([CombinatorialValues("", "public")] string modifier) + { + var source = $$""" + partial class C + { + {{modifier}} + partial event System.Action E; + {{modifier}} + partial event System.Action E { add { } remove { } } + {{modifier}} + partial C(); + {{modifier}} + partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics(); + } + + [Fact] + public void PartialNotLast() + { + var source = """ + partial class C + { + partial public event System.Action E; + partial public event System.Action E { add { } remove { } } + partial public C(); + partial public C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial public event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), + // (4,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial public event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(4, 5), + // (5,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial public C(); + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(5, 5), + // (6,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial public C() { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(6, 5)); + } + + [Fact] + public void PartialAsType() + { + var source = """ + partial class C + { + partial C() => new partial(); + } + + class @partial; + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,13): error CS9401: Partial member 'C.C()' must have a definition part. + // partial C() => new partial(); + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "C").WithArguments("C.C()").WithLocation(3, 13)); + } + + [Fact] + public void MissingImplementation() + { + var source = """ + partial class C + { + partial event System.Action E; + partial C(); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS9400: Partial member 'C.E' must have an implementation part. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "E").WithArguments("C.E").WithLocation(3, 33), + // (4,13): error CS9400: Partial member 'C.C()' must have an implementation part. + // partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "C").WithArguments("C.C()").WithLocation(4, 13)); + } + + [Fact] + public void MissingDefinition() + { + var source = """ + partial class C + { + partial event System.Action E { add { } remove { } } + partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS9401: Partial member 'C.E' must have a definition part. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "E").WithArguments("C.E").WithLocation(3, 33), + // (4,13): error CS9401: Partial member 'C.C()' must have a definition part. + // partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "C").WithArguments("C.C()").WithLocation(4, 13)); + } + + [Fact] + public void DuplicateDefinition() + { + var source = """ + partial class C + { + partial event System.Action E, F; + partial event System.Action E; + partial event System.Action F; + partial C(); + partial C(); + + partial event System.Action E { add { } remove { } } + partial event System.Action F { add { } remove { } } + partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,33): error CS9402: Partial member 'C.E' may not have multiple defining declarations. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "E").WithArguments("C.E").WithLocation(4, 33), + // (4,33): error CS0102: The type 'C' already contains a definition for 'E' + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(4, 33), + // (5,33): error CS9402: Partial member 'C.F' may not have multiple defining declarations. + // partial event System.Action F; + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "F").WithArguments("C.F").WithLocation(5, 33), + // (5,33): error CS0102: The type 'C' already contains a definition for 'F' + // partial event System.Action F; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "F").WithArguments("C", "F").WithLocation(5, 33), + // (7,13): error CS9402: Partial member 'C.C()' may not have multiple defining declarations. + // partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "C").WithArguments("C.C()").WithLocation(7, 13), + // (7,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(7, 13)); + } + + [Fact] + public void DuplicateImplementation() + { + var source = """ + partial class C + { + partial event System.Action E { add { } remove { } } + partial event System.Action E { add { } remove { } } + partial C() { } + partial C() { } + + partial event System.Action E; + partial C(); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,33): error CS9403: Partial member 'C.E' may not have multiple implementing declarations. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "E").WithArguments("C.E").WithLocation(4, 33), + // (6,13): error CS9403: Partial member 'C.C()' may not have multiple implementing declarations. + // partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "C").WithArguments("C.C()").WithLocation(6, 13), + // (8,33): error CS0102: The type 'C' already contains a definition for 'E' + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(8, 33), + // (9,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(9, 13)); + } + + [Fact] + public void DuplicateDeclarations_01() + { + var source = """ + partial class C + { + partial event System.Action E { add { } remove { } } + partial event System.Action E { add { } remove { } } + partial C() { } + partial C() { } + + partial event System.Action E; + partial event System.Action E; + partial C(); + partial C(); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,33): error CS9403: Partial member 'C.E' may not have multiple implementing declarations. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "E").WithArguments("C.E").WithLocation(4, 33), + // (6,13): error CS9403: Partial member 'C.C()' may not have multiple implementing declarations. + // partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "C").WithArguments("C.C()").WithLocation(6, 13), + // (8,33): error CS0102: The type 'C' already contains a definition for 'E' + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(8, 33), + // (9,33): error CS9402: Partial member 'C.E' may not have multiple defining declarations. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "E").WithArguments("C.E").WithLocation(9, 33), + // (9,33): error CS0102: The type 'C' already contains a definition for 'E' + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(9, 33), + // (10,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(10, 13), + // (11,13): error CS9402: Partial member 'C.C()' may not have multiple defining declarations. + // partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "C").WithArguments("C.C()").WithLocation(11, 13), + // (11,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(11, 13)); + } + + [Fact] + public void DuplicateDeclarations_02() + { + var source = """ + partial class C + { + partial event System.Action E; + partial void add_E(System.Action value); + partial void remove_E(System.Action value); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS9400: Partial member 'C.E' must have an implementation part. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "E").WithArguments("C.E").WithLocation(3, 33), + // (3,33): error CS0082: Type 'C' already reserves a member called 'add_E' with the same parameter types + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_MemberReserved, "E").WithArguments("add_E", "C").WithLocation(3, 33), + // (3,33): error CS0082: Type 'C' already reserves a member called 'remove_E' with the same parameter types + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_MemberReserved, "E").WithArguments("remove_E", "C").WithLocation(3, 33)); + } + + [Fact] + public void DuplicateDeclarations_03() + { + var source = """ + partial class C + { + partial event System.Action E; + partial event System.Action E; + partial C(); + partial C(); + + partial event System.Action E { add { } remove { } } + partial event System.Action E { add { } remove { } } + partial C() { } + partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,33): error CS9402: Partial member 'C.E' may not have multiple defining declarations. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "E").WithArguments("C.E").WithLocation(4, 33), + // (4,33): error CS0102: The type 'C' already contains a definition for 'E' + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(4, 33), + // (6,13): error CS9402: Partial member 'C.C()' may not have multiple defining declarations. + // partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "C").WithArguments("C.C()").WithLocation(6, 13), + // (6,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(6, 13), + // (9,33): error CS9403: Partial member 'C.E' may not have multiple implementing declarations. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "E").WithArguments("C.E").WithLocation(9, 33), + // (9,33): error CS0102: The type 'C' already contains a definition for 'E' + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(9, 33), + // (11,13): error CS9403: Partial member 'C.C()' may not have multiple implementing declarations. + // partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "C").WithArguments("C.C()").WithLocation(11, 13), + // (11,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C() { } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(11, 13)); + } + + [Fact] + public void EventInitializer_Single() + { + var source = """ + partial class C + { + partial event System.Action E = null; + partial event System.Action E { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS9404: 'C.E': partial event cannot have initializer + // partial event System.Action E = null; + Diagnostic(ErrorCode.ERR_PartialEventInitializer, "E").WithArguments("C.E").WithLocation(3, 33), + // (3,33): warning CS0414: The field 'C.E' is assigned but its value is never used + // partial event System.Action E = null; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "E").WithArguments("C.E").WithLocation(3, 33)); + } + + [Fact] + public void EventInitializer_Multiple_01() + { + var source = """ + partial class C + { + partial event System.Action E, F = null; + partial event System.Action E { add { } remove { } } + partial event System.Action F { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,36): error CS9404: 'C.F': partial event cannot have initializer + // partial event System.Action E, F = null; + Diagnostic(ErrorCode.ERR_PartialEventInitializer, "F").WithArguments("C.F").WithLocation(3, 36), + // (3,36): warning CS0414: The field 'C.F' is assigned but its value is never used + // partial event System.Action E, F = null; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "F").WithArguments("C.F").WithLocation(3, 36)); + } + + [Fact] + public void EventInitializer_Multiple_02() + { + var source = """ + partial class C + { + partial event System.Action E = null, F = null; + partial event System.Action E { add { } remove { } } + partial event System.Action F { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS9404: 'C.E': partial event cannot have initializer + // partial event System.Action E = null, F = null; + Diagnostic(ErrorCode.ERR_PartialEventInitializer, "E").WithArguments("C.E").WithLocation(3, 33), + // (3,33): warning CS0414: The field 'C.E' is assigned but its value is never used + // partial event System.Action E = null, F = null; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "E").WithArguments("C.E").WithLocation(3, 33), + // (3,43): error CS9404: 'C.F': partial event cannot have initializer + // partial event System.Action E = null, F = null; + Diagnostic(ErrorCode.ERR_PartialEventInitializer, "F").WithArguments("C.F").WithLocation(3, 43), + // (3,43): warning CS0414: The field 'C.F' is assigned but its value is never used + // partial event System.Action E = null, F = null; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "F").WithArguments("C.F").WithLocation(3, 43)); + } + + [Fact] + public void EventAccessorMissing() + { + var source = """ + partial class C + { + partial event System.Action E, F; + partial event System.Action E { add { } } + partial event System.Action F { remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,33): error CS0065: 'C.E': event property must have both add and remove accessors + // partial event System.Action E { add { } } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "E").WithArguments("C.E").WithLocation(4, 33), + // (5,33): error CS0065: 'C.F': event property must have both add and remove accessors + // partial event System.Action F { remove { } } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "F").WithArguments("C.F").WithLocation(5, 33)); + } + + [Fact] + public void StaticPartialConstructor() + { + var source = """ + partial class C + { + static partial C(); + static partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // static partial C(); + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12), + // (4,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // static partial C() { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(4, 12), + // (4,20): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // static partial C() { } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(4, 20)); + } + + [Fact] + public void NotInPartialType() + { + var source = """ + class C + { + partial event System.Action E; + partial event System.Action E { add { } remove { } } + partial event System.Action F { add { } remove { } } + partial C(); + partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS0751: A partial member must be declared within a partial type + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "E").WithLocation(3, 33), + // (5,33): error CS9401: Partial event 'C.F' must have a definition part. + // partial event System.Action F { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "F").WithArguments("C.F").WithLocation(5, 33), + // (5,33): error CS0751: A partial member must be declared within a partial type + // partial event System.Action F { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "F").WithLocation(5, 33), + // (6,13): error CS0751: A partial member must be declared within a partial type + // partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "C").WithLocation(6, 13), + // (7,13): error CS0751: A partial member must be declared within a partial type + // partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "C").WithLocation(7, 13)); + } + + [Fact] + public void InInterface() + { + var source = """ + partial interface I + { + partial event System.Action E; + partial event System.Action E { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,37): error CS8701: Target runtime doesn't support default interface implementation. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, "add").WithLocation(4, 37), + // (4,45): error CS8701: Target runtime doesn't support default interface implementation. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, "remove").WithLocation(4, 45)); + + CreateCompilation(source, targetFramework: TargetFramework.Net60).VerifyDiagnostics(); + + CreateCompilation(source, targetFramework: TargetFramework.Net60, parseOptions: TestOptions.Regular7).VerifyDiagnostics( + // (3,33): error CS8703: The modifier 'partial' is not valid for this item in C# 7.0. Please use language version 'preview' or greater. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "E").WithArguments("partial", "7.0", "preview").WithLocation(3, 33), + // (4,37): error CS8107: Feature 'default interface implementation' is not available in C# 7.0. Please use language version 8.0 or greater. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "add").WithArguments("default interface implementation", "8.0").WithLocation(4, 37), + // (4,45): error CS8107: Feature 'default interface implementation' is not available in C# 7.0. Please use language version 8.0 or greater. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "remove").WithArguments("default interface implementation", "8.0").WithLocation(4, 45)); + } + + [Fact] + public void Abstract() + { + var source = """ + abstract partial class C + { + protected abstract partial event System.Action E; + protected abstract partial event System.Action E { add { } remove { } } + protected abstract partial C(); + protected abstract partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,52): error CS0750: A partial member cannot have the 'abstract' modifier + // protected abstract partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "E").WithLocation(3, 52), + // (4,54): error CS8712: 'C.E': abstract event cannot use event accessor syntax + // protected abstract partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("C.E").WithLocation(4, 54), + // (5,32): error CS0106: The modifier 'abstract' is not valid for this item + // protected abstract partial C(); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "C").WithArguments("abstract").WithLocation(5, 32), + // (6,32): error CS0106: The modifier 'abstract' is not valid for this item + // protected abstract partial C() { } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "C").WithArguments("abstract").WithLocation(6, 32)); + } + + [Fact] + public void ExplicitInterfaceImplementation() + { + var source = """ + interface I + { + event System.Action E; + } + partial class C : I + { + partial event System.Action I.E; + partial event System.Action I.E { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (5,15): error CS8646: 'I.E' is explicitly implemented more than once. + // partial class C : I + Diagnostic(ErrorCode.ERR_DuplicateExplicitImpl, "C").WithArguments("I.E").WithLocation(5, 15), + // (7,35): error CS0071: An explicit interface implementation of an event must use event accessor syntax + // partial event System.Action I.E; + Diagnostic(ErrorCode.ERR_ExplicitEventFieldImpl, "E").WithLocation(7, 35), + // (7,35): error CS9401: Partial member 'C.I.E' must have a definition part. + // partial event System.Action I.E; + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "E").WithArguments("C.I.E").WithLocation(7, 35), + // (7,35): error CS0754: A partial member may not explicitly implement an interface member + // partial event System.Action I.E; + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "E").WithLocation(7, 35), + // (8,35): error CS9403: Partial member 'C.I.E' may not have multiple implementing declarations. + // partial event System.Action I.E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "E").WithArguments("C.I.E").WithLocation(8, 35), + // (8,35): error CS0102: The type 'C' already contains a definition for 'I.E' + // partial event System.Action I.E { add { } remove { } } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "I.E").WithLocation(8, 35), + // (8,35): error CS0754: A partial member may not explicitly implement an interface member + // partial event System.Action I.E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "E").WithLocation(8, 35)); + } + + [Fact] + public void Extern_01() + { + var source = """ + partial class C + { + partial event System.Action E; + extern partial event System.Action E; + + partial C(); + extern partial C(); + } + """; + CompileAndVerifyWithMscorlib46(source, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + sourceSymbolValidator: verifySource, + symbolValidator: verifyMetadata, + // PEVerify fails when extern methods lack an implementation + verify: Verification.FailsPEVerify with + { + PEVerifyMessage = """ + Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. + Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. + Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. + Type load failed. + """, + }) + .VerifyDiagnostics() + .VerifyTypeIL("C", """ + .class private auto ansi beforefieldinit C + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname + instance void add_E ( + class [mscorlib]System.Action 'value' + ) cil managed + { + } // end of method C::add_E + .method private hidebysig specialname + instance void remove_E ( + class [mscorlib]System.Action 'value' + ) cil managed + { + } // end of method C::remove_E + .method private hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + } // end of method C::.ctor + // Events + .event [mscorlib]System.Action E + { + .addon instance void C::add_E(class [mscorlib]System.Action) + .removeon instance void C::remove_E(class [mscorlib]System.Action) + } + } // end of class C + """); + + static void verifySource(ModuleSymbol module) + { + var ev = module.GlobalNamespace.GetMember("C.E"); + Assert.True(ev.IsPartialDefinition); + Assert.True(ev.GetPublicSymbol().IsExtern); + Assert.True(ev.AddMethod!.GetPublicSymbol().IsExtern); + Assert.True(ev.RemoveMethod!.GetPublicSymbol().IsExtern); + Assert.True(ev.PartialImplementationPart!.GetPublicSymbol().IsExtern); + Assert.True(ev.PartialImplementationPart!.AddMethod!.GetPublicSymbol().IsExtern); + Assert.True(ev.PartialImplementationPart!.RemoveMethod!.GetPublicSymbol().IsExtern); + + var c = module.GlobalNamespace.GetMember("C..ctor"); + Assert.True(c.IsPartialDefinition); + Assert.True(c.GetPublicSymbol().IsExtern); + Assert.True(c.PartialImplementationPart!.GetPublicSymbol().IsExtern); + + var members = module.GlobalNamespace.GetTypeMember("C").GetMembers().Select(s => s.ToTestDisplayString()).Join("\n"); + AssertEx.AssertEqualToleratingWhitespaceDifferences(""" + void C.E.add + void C.E.remove + event System.Action C.E + C..ctor() + """, members); + } + + static void verifyMetadata(ModuleSymbol module) + { + // IsExtern doesn't round trip from metadata when DllImportAttribute is missing. + // This is consistent with the behavior of partial methods and properties. + + var ev = module.GlobalNamespace.GetMember("C.E"); + Assert.False(ev.GetPublicSymbol().IsExtern); + Assert.False(ev.AddMethod!.GetPublicSymbol().IsExtern); + Assert.False(ev.RemoveMethod!.GetPublicSymbol().IsExtern); + + var c = module.GlobalNamespace.GetMember("C..ctor"); + Assert.False(c.GetPublicSymbol().IsExtern); + + var members = module.GlobalNamespace.GetTypeMember("C").GetMembers().Select(s => s.ToTestDisplayString()).Join("\n"); + AssertEx.AssertEqualToleratingWhitespaceDifferences(""" + void C.E.add + void C.E.remove + C..ctor() + event System.Action C.E + """, members); + } + } + + [Fact] + public void Extern_02() + { + var source = """ + partial class C + { + partial event System.Action E; + extern event System.Action E; + + partial C(); + extern C(); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS9400: Partial member 'C.E' must have an implementation part. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "E").WithArguments("C.E").WithLocation(3, 33), + // (4,32): error CS0102: The type 'C' already contains a definition for 'E' + // extern event System.Action E; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(4, 32), + // (4,32): warning CS0626: Method, operator, or accessor 'C.E.remove' is marked external and has no attributes on it. Consider adding a DllImport attribute to specify the external implementation. + // extern event System.Action E; + Diagnostic(ErrorCode.WRN_ExternMethodNoImplementation, "E").WithArguments("C.E.remove").WithLocation(4, 32), + // (6,13): error CS9400: Partial member 'C.C()' must have an implementation part. + // partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "C").WithArguments("C.C()").WithLocation(6, 13), + // (7,12): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // extern C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(7, 12), + // (7,12): warning CS0824: Constructor 'C.C()' is marked external + // extern C(); + Diagnostic(ErrorCode.WRN_ExternCtorNoImplementation, "C").WithArguments("C.C()").WithLocation(7, 12)); + } + + [Fact] + public void Extern_03() + { + var source = """ + partial class C + { + extern partial event System.Action E; + partial event System.Action E { add { } remove { } } + + extern partial C(); + partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,40): error CS9401: Partial member 'C.E' must have a definition part. + // extern partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "E").WithArguments("C.E").WithLocation(3, 40), + // (4,33): error CS9403: Partial member 'C.E' may not have multiple implementing declarations. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "E").WithArguments("C.E").WithLocation(4, 33), + // (4,33): error CS0102: The type 'C' already contains a definition for 'E' + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(4, 33), + // (6,20): error CS9401: Partial member 'C.C()' must have a definition part. + // extern partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "C").WithArguments("C.C()").WithLocation(6, 20), + // (7,13): error CS9403: Partial member 'C.C()' may not have multiple implementing declarations. + // partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "C").WithArguments("C.C()").WithLocation(7, 13), + // (7,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C() { } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(7, 13)); + } + + [Fact(Skip = "PROTOTYPE: needs attribute merging")] + public void Extern_DllImport() + { + var source = """ + using System; + using System.Runtime.InteropServices; + public partial class C + { + public static partial event Action E; + [method: DllImport("something.dll")] + public static extern partial event Action E; + } + """; + CompileAndVerify(source, + sourceSymbolValidator: verify, + symbolValidator: verify) + .VerifyDiagnostics(); + + static void verify(ModuleSymbol module) + { + var e = module.GlobalNamespace.GetMember("C.E"); + Assert.True(e.GetPublicSymbol().IsExtern); + // unexpected mismatch between metadata and entrypoint name: https://github.com/dotnet/roslyn/issues/76882 + verifyAccessor(e.AddMethod!, "add_E", "remove_E"); + verifyAccessor(e.RemoveMethod!, "remove_E", "remove_E"); + + if (module is SourceModuleSymbol) + { + var eImpl = ((SourceEventSymbol)e).PartialImplementationPart!; + Assert.True(eImpl.GetPublicSymbol().IsExtern); + // unexpected mismatch between metadata and entrypoint name: https://github.com/dotnet/roslyn/issues/76882 + verifyAccessor(eImpl.AddMethod!, "add_E", "remove_E"); + verifyAccessor(eImpl.RemoveMethod!, "remove_E", "remove_E"); + } + } + + static void verifyAccessor(MethodSymbol accessor, string expectedMetadataName, string expectedEntryPointName) + { + Assert.True(accessor.GetPublicSymbol().IsExtern); + Assert.Equal(expectedMetadataName, accessor.MetadataName); + + var importData = accessor.GetDllImportData()!; + Assert.Equal("something.dll", importData.ModuleName); + Assert.Equal(expectedEntryPointName, importData.EntryPointName); + Assert.Equal(CharSet.None, importData.CharacterSet); + Assert.False(importData.SetLastError); + Assert.False(importData.ExactSpelling); + Assert.Equal(MethodImplAttributes.PreserveSig, accessor.ImplementationAttributes); + Assert.Equal(CallingConvention.Winapi, importData.CallingConvention); + Assert.Null(importData.BestFitMapping); + Assert.Null(importData.ThrowOnUnmappableCharacter); + } + } + + [Fact(Skip = "PROTOTYPE: needs attribute merging")] + public void Extern_InternalCall() + { + var source = """ + using System; + using System.Runtime.CompilerServices; + public partial class C + { + public partial C(); + [MethodImpl(MethodImplOptions.InternalCall)] + public extern partial C(); + + public partial event Action E; + [method: MethodImpl(MethodImplOptions.InternalCall)] + public extern partial event Action E; + } + """; + CompileAndVerify(source, + sourceSymbolValidator: verifySource, + symbolValidator: verifyMetadata, + // PEVerify fails when extern methods lack an implementation + verify: Verification.FailsPEVerify with + { + PEVerifyMessage = """ + Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. + Type load failed. + """, + }) + .VerifyDiagnostics(); + + static void verifySource(ModuleSymbol module) + { + var ev = module.GlobalNamespace.GetMember("C.E"); + Assert.True(ev.GetPublicSymbol().IsExtern); + Assert.True(ev.AddMethod!.GetPublicSymbol().IsExtern); + Assert.Null(ev.AddMethod!.GetDllImportData()); + Assert.Equal(MethodImplAttributes.InternalCall, ev.AddMethod.ImplementationAttributes); + Assert.True(ev.RemoveMethod!.GetPublicSymbol().IsExtern); + Assert.Null(ev.RemoveMethod!.GetDllImportData()); + Assert.Equal(MethodImplAttributes.InternalCall, ev.RemoveMethod.ImplementationAttributes); + + var c = module.GlobalNamespace.GetMember("C..ctor"); + Assert.True(c.GetPublicSymbol().IsExtern); + Assert.Null(c.GetDllImportData()); + Assert.Equal(MethodImplAttributes.InternalCall, c.ImplementationAttributes); + } + + static void verifyMetadata(ModuleSymbol module) + { + var ev = module.GlobalNamespace.GetMember("C.E"); + Assert.False(ev.GetPublicSymbol().IsExtern); + Assert.False(ev.AddMethod!.GetPublicSymbol().IsExtern); + Assert.Null(ev.AddMethod!.GetDllImportData()); + Assert.Equal(MethodImplAttributes.InternalCall, ev.AddMethod.ImplementationAttributes); + Assert.False(ev.RemoveMethod!.GetPublicSymbol().IsExtern); + Assert.Null(ev.RemoveMethod!.GetDllImportData()); + Assert.Equal(MethodImplAttributes.InternalCall, ev.RemoveMethod.ImplementationAttributes); + + var c = module.GlobalNamespace.GetMember("C..ctor"); + Assert.False(c.GetPublicSymbol().IsExtern); + Assert.Null(c.GetDllImportData()); + Assert.Equal(MethodImplAttributes.InternalCall, c.ImplementationAttributes); + } + } + + [Fact] + public void Metadata() + { + var source = """ + public partial class C + { + public partial event System.Action E; + public partial event System.Action E { add { } remove { } } + public partial C(); + public partial C() { } + } + """; + CompileAndVerify(source, + sourceSymbolValidator: verifySource, + symbolValidator: verifyMetadata) + .VerifyDiagnostics(); + + static void verifySource(ModuleSymbol module) + { + var e = module.GlobalNamespace.GetMember("C.E"); + Assert.True(e.IsPartialDefinition); + Assert.False(e.IsPartialImplementation); + Assert.False(e.HasAssociatedField); + Assert.Null(e.PartialDefinitionPart); + Assert.True(e.SourcePartialImplementationPart!.IsPartialImplementation); + Assert.False(e.SourcePartialImplementationPart.IsPartialDefinition); + Assert.False(e.SourcePartialImplementationPart.HasAssociatedField); + + var addMethod = e.AddMethod!; + Assert.Equal("add_E", addMethod.Name); + Assert.NotSame(addMethod, e.SourcePartialImplementationPart.AddMethod); + Assert.Same(e, addMethod.AssociatedSymbol); + Assert.Same(e.PartialImplementationPart, addMethod.PartialImplementationPart.AssociatedSymbol); + var removeMethod = e.RemoveMethod!; + Assert.Equal("remove_E", removeMethod.Name); + Assert.NotSame(removeMethod, e.SourcePartialImplementationPart.RemoveMethod); + Assert.Same(e, removeMethod.AssociatedSymbol); + Assert.Same(e.PartialImplementationPart, removeMethod.PartialImplementationPart.AssociatedSymbol); + + var c = module.GlobalNamespace.GetMember("C..ctor"); + Assert.True(c.IsPartialDefinition); + Assert.False(c.IsPartialImplementation); + Assert.Null(c.PartialDefinitionPart); + var cImpl = (SourceConstructorSymbol)c.PartialImplementationPart!; + Assert.True(cImpl.IsPartialImplementation); + Assert.False(cImpl.IsPartialDefinition); + } + + static void verifyMetadata(ModuleSymbol module) + { + var e = module.GlobalNamespace.GetMember("C.E"); + Assert.False(e.HasAssociatedField); + + var addMethod = e.AddMethod!; + Assert.Equal("add_E", addMethod.Name); + var removeMethod = e.RemoveMethod!; + Assert.Equal("remove_E", removeMethod.Name); + } + } + + [Fact] + public void SequencePoints() + { + var source = """ + partial class C + { + partial C(int i); + partial C(int i) + { + System.Console.Write(i); + } + partial event System.Action E; + partial event System.Action E + { + add + { + System.Console.Write(value); + } + remove + { + value(); + } + } + } + """; + CompileAndVerify(source) + .VerifyDiagnostics() + .VerifyMethodBody("C..ctor", """ + { + // Code size 13 (0xd) + .maxstack 1 + // sequence point: partial C(int i) + IL_0000: ldarg.0 + IL_0001: call "object..ctor()" + // sequence point: System.Console.Write(i); + IL_0006: ldarg.1 + IL_0007: call "void System.Console.Write(int)" + // sequence point: } + IL_000c: ret + } + """) + .VerifyMethodBody("C.E.add", """ + { + // Code size 7 (0x7) + .maxstack 1 + // sequence point: System.Console.Write(value); + IL_0000: ldarg.1 + IL_0001: call "void System.Console.Write(object)" + // sequence point: } + IL_0006: ret + } + """) + .VerifyMethodBody("C.E.remove", """ + { + // Code size 7 (0x7) + .maxstack 1 + // sequence point: value(); + IL_0000: ldarg.1 + IL_0001: callvirt "void System.Action.Invoke()" + // sequence point: } + IL_0006: ret + } + """); + } + + [Fact] + public void EmitOrder_01() + { + verify(""" + partial class C + { + partial event System.Action E; + partial event System.Action E { add { } remove { } } + partial C(); + partial C() { } + } + """); + + verify(""" + partial class C + { + partial event System.Action E { add { } remove { } } + partial event System.Action E; + partial C() { } + partial C(); + } + """); + + verify(""" + partial class C + { + partial event System.Action E { add { } remove { } } + partial C() { } + } + """, """ + partial class C + { + partial event System.Action E; + partial C(); + } + """); + + verify(""" + partial class C + { + partial event System.Action E; + partial C(); + } + """, """ + partial class C + { + partial event System.Action E { add { } remove { } } + partial C() { } + } + """); + + void verify(params CSharpTestSource sources) + { + CompileAndVerify(sources, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: validate) + .VerifyDiagnostics(); + } + + static void validate(ModuleSymbol module) + { + var members = module.GlobalNamespace.GetTypeMember("C").GetMembers().Select(s => s.ToTestDisplayString()).Join("\n"); + AssertEx.AssertEqualToleratingWhitespaceDifferences(""" + void C.E.add + void C.E.remove + C..ctor() + event System.Action C.E + """, members); + } + } + + [Fact] + public void EmitOrder_02() + { + verify(""" + partial class C + { + partial C(); + partial C() { } + partial event System.Action E; + partial event System.Action E { add { } remove { } } + } + """); + + verify(""" + partial class C + { + partial C() { } + partial C(); + partial event System.Action E { add { } remove { } } + partial event System.Action E; + } + """); + + verify(""" + partial class C + { + partial C(); + partial event System.Action E; + } + """, """ + partial class C + { + partial C() { } + partial event System.Action E { add { } remove { } } + } + """); + + verify(""" + partial class C + { + partial C() { } + partial event System.Action E { add { } remove { } } + } + """, """ + partial class C + { + partial C(); + partial event System.Action E; + } + """); + + void verify(params CSharpTestSource sources) + { + CompileAndVerify(sources, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: validate) + .VerifyDiagnostics(); + } + + static void validate(ModuleSymbol module) + { + var members = module.GlobalNamespace.GetTypeMember("C").GetMembers().Select(s => s.ToTestDisplayString()).Join("\n"); + AssertEx.AssertEqualToleratingWhitespaceDifferences(""" + C..ctor() + void C.E.add + void C.E.remove + event System.Action C.E + """, members); + } + } + + [Fact] + public void Use_Valid() + { + var source = """ + using System; + + var c = new C(); + c.E += () => Console.Write(1); + c.E -= () => Console.Write(2); + + partial class C + { + public partial event Action E; + public partial event Action E + { + add { Console.Write(3); value(); } + remove { Console.Write(4); value(); } + } + public partial C(); + public partial C() { Console.Write(5); } + } + """; + CompileAndVerify(source, expectedOutput: "53142").VerifyDiagnostics(); + } + + [Fact] + public void Use_EventAsValue() + { + var source = """ + using System; + + var c = new C(); + Action a = c.E; + c.E(); + + partial class C + { + public partial event Action E; + public partial event Action E { add { } remove { } } + + void M() + { + Action a = this.E; + this.E(); + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,14): error CS0079: The event 'C.E' can only appear on the left hand side of += or -= + // Action a = c.E; + Diagnostic(ErrorCode.ERR_BadEventUsageNoField, "E").WithArguments("C.E").WithLocation(4, 14), + // (5,3): error CS0079: The event 'C.E' can only appear on the left hand side of += or -= + // c.E(); + Diagnostic(ErrorCode.ERR_BadEventUsageNoField, "E").WithArguments("C.E").WithLocation(5, 3), + // (14,25): error CS0079: The event 'C.E' can only appear on the left hand side of += or -= + // Action a = this.E; + Diagnostic(ErrorCode.ERR_BadEventUsageNoField, "E").WithArguments("C.E").WithLocation(14, 25), + // (15,14): error CS0079: The event 'C.E' can only appear on the left hand side of += or -= + // this.E(); + Diagnostic(ErrorCode.ERR_BadEventUsageNoField, "E").WithArguments("C.E").WithLocation(15, 14)); + } + + [Fact] + public void Use_EventAccessorsInaccessible() + { + var source = """ + using System; + + var c = new C(); + c.E += () => { }; + c.E -= () => { }; + + partial class C + { + partial event Action E; + partial event Action E { add { } remove { } } + + void M() + { + this.E += () => { }; + this.E -= () => { }; + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,3): error CS0122: 'C.E' is inaccessible due to its protection level + // c.E += () => { }; + Diagnostic(ErrorCode.ERR_BadAccess, "E").WithArguments("C.E").WithLocation(4, 3), + // (5,3): error CS0122: 'C.E' is inaccessible due to its protection level + // c.E -= () => { }; + Diagnostic(ErrorCode.ERR_BadAccess, "E").WithArguments("C.E").WithLocation(5, 3)); + } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs index 28b8ca9a92740..e27380e19f546 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs @@ -44585,30 +44585,36 @@ interface I19 // (62,20): error CS0106: The modifier 'virtual' is not valid for this item // virtual static I13() => throw null; Diagnostic(ErrorCode.ERR_BadMemberFlag, "I13").WithArguments("virtual").WithLocation(62, 20), - // (66,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (66,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I14(); Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(66, 5), - // (66,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (66,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I14(); Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(66, 5), // (70,12): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) // static partial I15(); Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(70, 12), + // (70,12): error CS8652: The feature 'partial events and constructors' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static partial I15(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "partial").WithArguments("partial events and constructors").WithLocation(70, 12), // (70,20): error CS0501: 'I15.I15()' must declare a body because it is not marked abstract, extern, or partial // static partial I15(); Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "I15").WithArguments("I15.I15()").WithLocation(70, 20), // (70,20): error CS0542: 'I15': member names cannot be the same as their enclosing type // static partial I15(); Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "I15").WithArguments("I15").WithLocation(70, 20), - // (74,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (74,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I16() {} Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(74, 5), - // (74,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (74,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I16() {} Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(74, 5), // (78,12): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) // static partial I17() => throw null; Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(78, 12), + // (78,12): error CS8652: The feature 'partial events and constructors' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static partial I17() => throw null; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "partial").WithArguments("partial events and constructors").WithLocation(78, 12), // (78,20): error CS0542: 'I17': member names cannot be the same as their enclosing type // static partial I17() => throw null; Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "I17").WithArguments("I17").WithLocation(78, 20), diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index 3354802d99db8..6835b9282356f 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -819,34 +819,34 @@ partial sealed static I3() {} // (4,19): error CS0106: The modifier 'sealed' is not valid for this item // sealed static I1() {} Diagnostic(ErrorCode.ERR_BadMemberFlag, "I1").WithArguments("sealed").WithLocation(4, 19), - // (9,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (9,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial sealed static I2(); Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(9, 5), - // (9,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (9,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial sealed static I2(); Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(9, 5), // (9,27): error CS0106: The modifier 'sealed' is not valid for this item // partial sealed static I2(); Diagnostic(ErrorCode.ERR_BadMemberFlag, "I2").WithArguments("sealed").WithLocation(9, 27), - // (14,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (14,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I2() {} Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(14, 5), - // (14,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (14,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I2() {} Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(14, 5), // (14,20): error CS0111: Type 'I2' already defines a member called 'I2' with the same parameter types // partial static I2() {} Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "I2").WithArguments("I2", "I2").WithLocation(14, 20), - // (19,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (19,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I3(); Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(19, 5), - // (19,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (19,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I3(); Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(19, 5), - // (24,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (24,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial sealed static I3() {} Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(24, 5), - // (24,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (24,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial sealed static I3() {} Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(24, 5), // (24,27): error CS0106: The modifier 'sealed' is not valid for this item @@ -901,22 +901,22 @@ sealed static partial I3() {} targetFramework: _supportingFramework); compilation1.VerifyDiagnostics( - // (4,19): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // (4,19): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // sealed static partial I2(); Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(4, 19), // (4,27): error CS0106: The modifier 'sealed' is not valid for this item // sealed static partial I2(); Diagnostic(ErrorCode.ERR_BadMemberFlag, "I2").WithArguments("sealed").WithLocation(4, 27), - // (9,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // (9,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // static partial I2() {} Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(9, 12), // (9,20): error CS0111: Type 'I2' already defines a member called 'I2' with the same parameter types // static partial I2() {} Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "I2").WithArguments("I2", "I2").WithLocation(9, 20), - // (14,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // (14,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // static partial I3(); Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(14, 12), - // (19,19): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // (19,19): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // sealed static partial I3() {} Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(19, 19), // (19,27): error CS0106: The modifier 'sealed' is not valid for this item diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs index 5be5485639018..fbb9ac8795a1c 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs @@ -22115,7 +22115,6 @@ string featureName [Fact] public void PartialConstructor() { - // PROTOTYPE: some of these should be allowed CreateCompilation(new[] { """ @@ -22137,18 +22136,27 @@ partial public PartialPublicCtor() { } } """ }).VerifyDiagnostics( - // 0.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - // partial PartialCtor() { } - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // 2.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // 2.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial public PartialPublicCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // 2.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // 0.cs(3,13): error CS0751: A partial member must be declared within a partial type + // partial PartialCtor() { } + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "PartialCtor").WithLocation(3, 13), + // 0.cs(3,13): error CS9401: Partial member 'PartialCtor.PartialCtor()' must have a definition part. + // partial PartialCtor() { } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "PartialCtor").WithArguments("PartialCtor.PartialCtor()").WithLocation(3, 13), + // 2.cs(3,20): error CS0751: A partial member must be declared within a partial type // partial public PartialPublicCtor() { } - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // 1.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "PartialPublicCtor").WithLocation(3, 20), + // 1.cs(3,20): error CS0751: A partial member must be declared within a partial type + // public partial PublicPartialCtor() { } + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "PublicPartialCtor").WithLocation(3, 20), + // 1.cs(3,20): error CS9401: Partial member 'PublicPartialCtor.PublicPartialCtor()' must have a definition part. // public partial PublicPartialCtor() { } - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12)); + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "PublicPartialCtor").WithArguments("PublicPartialCtor.PublicPartialCtor()").WithLocation(3, 20), + // 2.cs(3,20): error CS9401: Partial member 'PartialPublicCtor.PartialPublicCtor()' must have a definition part. + // partial public PartialPublicCtor() { } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "PartialPublicCtor").WithArguments("PartialPublicCtor.PartialPublicCtor()").WithLocation(3, 20)); } [Fact] @@ -22205,63 +22213,63 @@ partial public static PartialPublicStaticCtor() { } } """, }).VerifyDiagnostics( - // 0.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // 6.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial static public PartialStaticPublicCtor() { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), + // 6.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial static public PartialStaticPublicCtor() { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), + // 0.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static PartialStaticCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // 0.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // 0.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static PartialStaticCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // 7.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // 7.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial public static PartialPublicStaticCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // 7.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // 7.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial public static PartialPublicStaticCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // 6.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - // partial static public PartialStaticPublicCtor() { } - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // 6.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - // partial static public PartialStaticPublicCtor() { } - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // 1.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // 3.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // public partial static PublicPartialStaticCtor() { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12), + // 3.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // public partial static PublicPartialStaticCtor() { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12), + // 1.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // static partial StaticPartialCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12), - // 5.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // 5.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // static partial public StaticPartialPublicCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12), - // 5.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // 5.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // static partial public StaticPartialPublicCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12), - // 3.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - // public partial static PublicPartialStaticCtor() { } - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12), - // 3.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - // public partial static PublicPartialStaticCtor() { } - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12), - // 4.cs(3,19): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // 2.cs(3,19): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // public static partial PublicStaticPartialCtor() { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 19), + // 4.cs(3,19): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // static public partial StaticPublicPartialCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 19), - // 2.cs(3,19): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + // 3.cs(3,27): error CS0515: 'PublicPartialStaticCtor.PublicPartialStaticCtor()': access modifiers are not allowed on static constructors + // public partial static PublicPartialStaticCtor() { } + Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "PublicPartialStaticCtor").WithArguments("PublicPartialStaticCtor.PublicPartialStaticCtor()").WithLocation(3, 27), + // 6.cs(3,27): error CS0515: 'PartialStaticPublicCtor.PartialStaticPublicCtor()': access modifiers are not allowed on static constructors + // partial static public PartialStaticPublicCtor() { } + Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "PartialStaticPublicCtor").WithArguments("PartialStaticPublicCtor.PartialStaticPublicCtor()").WithLocation(3, 27), + // 2.cs(3,27): error CS0515: 'PublicStaticPartialCtor.PublicStaticPartialCtor()': access modifiers are not allowed on static constructors // public static partial PublicStaticPartialCtor() { } - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 19), + Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "PublicStaticPartialCtor").WithArguments("PublicStaticPartialCtor.PublicStaticPartialCtor()").WithLocation(3, 27), // 4.cs(3,27): error CS0515: 'StaticPublicPartialCtor.StaticPublicPartialCtor()': access modifiers are not allowed on static constructors // static public partial StaticPublicPartialCtor() { } Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "StaticPublicPartialCtor").WithArguments("StaticPublicPartialCtor.StaticPublicPartialCtor()").WithLocation(3, 27), // 7.cs(3,27): error CS0515: 'PartialPublicStaticCtor.PartialPublicStaticCtor()': access modifiers are not allowed on static constructors // partial public static PartialPublicStaticCtor() { } Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "PartialPublicStaticCtor").WithArguments("PartialPublicStaticCtor.PartialPublicStaticCtor()").WithLocation(3, 27), - // 6.cs(3,27): error CS0515: 'PartialStaticPublicCtor.PartialStaticPublicCtor()': access modifiers are not allowed on static constructors - // partial static public PartialStaticPublicCtor() { } - Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "PartialStaticPublicCtor").WithArguments("PartialStaticPublicCtor.PartialStaticPublicCtor()").WithLocation(3, 27), // 5.cs(3,27): error CS0515: 'StaticPartialPublicCtor.StaticPartialPublicCtor()': access modifiers are not allowed on static constructors // static partial public StaticPartialPublicCtor() { } - Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "StaticPartialPublicCtor").WithArguments("StaticPartialPublicCtor.StaticPartialPublicCtor()").WithLocation(3, 27), - // 3.cs(3,27): error CS0515: 'PublicPartialStaticCtor.PublicPartialStaticCtor()': access modifiers are not allowed on static constructors - // public partial static PublicPartialStaticCtor() { } - Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "PublicPartialStaticCtor").WithArguments("PublicPartialStaticCtor.PublicPartialStaticCtor()").WithLocation(3, 27), - // 2.cs(3,27): error CS0515: 'PublicStaticPartialCtor.PublicStaticPartialCtor()': access modifiers are not allowed on static constructors - // public static partial PublicStaticPartialCtor() { } - Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "PublicStaticPartialCtor").WithArguments("PublicStaticPartialCtor.PublicStaticPartialCtor()").WithLocation(3, 27)); + Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "StaticPartialPublicCtor").WithArguments("StaticPartialPublicCtor.StaticPartialPublicCtor()").WithLocation(3, 27)); } } } diff --git a/src/Compilers/Test/Core/CompilationVerifier.cs b/src/Compilers/Test/Core/CompilationVerifier.cs index c709948406117..b8fb0762efd44 100644 --- a/src/Compilers/Test/Core/CompilationVerifier.cs +++ b/src/Compilers/Test/Core/CompilationVerifier.cs @@ -633,7 +633,8 @@ internal string VisualizeIL(CompilationTestData.MethodData methodData, bool real throw new Exception($"Failed to extract PDB information. PdbToXmlConverter returned:{Environment.NewLine}{actualPdbXml}"); } - var methodDef = (Cci.IMethodDefinition)methodData.Method.GetCciAdapter(); + var method = methodData.Method.PartialDefinitionPart ?? methodData.Method; + var methodDef = (Cci.IMethodDefinition)method.GetCciAdapter(); var methodToken = MetadataTokens.GetToken(_testData.MetadataWriter.GetMethodDefinitionOrReferenceHandle(methodDef)); var xmlDocument = XElement.Parse(actualPdbXml); var xmlMethod = ILValidation.GetMethodElement(xmlDocument, methodToken);