From 23320722a624d512173e6011057af54594f7c70f Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Mon, 15 Apr 2024 14:03:46 -0700 Subject: [PATCH] Partial properties 1 --- .../BinderFactory.BinderFactoryVisitor.cs | 22 + .../CSharp/Portable/CSharpResources.resx | 20 +- .../CSharp/Portable/Errors/ErrorCode.cs | 7 + .../CSharp/Portable/Errors/ErrorFacts.cs | 6 + .../Symbols/MemberSymbolExtensions.cs | 22 +- .../Symbols/Source/IPartialMemberSymbol.cs | 23 + .../Source/SourceMemberContainerSymbol.cs | 198 +++++++-- .../SourceMethodSymbolWithAttributes.cs | 2 +- .../Symbols/Source/SourceParameterSymbol.cs | 2 +- .../Source/SourcePropertyAccessorSymbol.cs | 14 +- .../Symbols/Source/SourcePropertySymbol.cs | 54 ++- .../Source/SourcePropertySymbolBase.cs | 16 +- ...nthesizedRecordEqualityContractProperty.cs | 1 + .../SynthesizedRecordPropertySymbol.cs | 1 + .../Portable/xlf/CSharpResources.cs.xlf | 36 +- .../Portable/xlf/CSharpResources.de.xlf | 36 +- .../Portable/xlf/CSharpResources.es.xlf | 36 +- .../Portable/xlf/CSharpResources.fr.xlf | 36 +- .../Portable/xlf/CSharpResources.it.xlf | 36 +- .../Portable/xlf/CSharpResources.ja.xlf | 36 +- .../Portable/xlf/CSharpResources.ko.xlf | 36 +- .../Portable/xlf/CSharpResources.pl.xlf | 36 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 36 +- .../Portable/xlf/CSharpResources.ru.xlf | 36 +- .../Portable/xlf/CSharpResources.tr.xlf | 36 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 36 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 36 +- .../Emit2/Diagnostics/GetDiagnosticsTests.cs | 2 + .../DefaultInterfaceImplementationTests.cs | 12 +- .../Symbol/Symbols/PartialPropertiesTests.cs | 407 ++++++++++++++++++ 30 files changed, 1173 insertions(+), 104 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Symbols/Source/IPartialMemberSymbol.cs create mode 100644 src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs index 39e48112f4f15..bc122e64dd2d8 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs @@ -288,6 +288,7 @@ public override Binder VisitAccessorDeclaration(AccessorDeclarationSyntax parent if ((object)propertySymbol != null) { accessor = (parent.Kind() == SyntaxKind.GetAccessorDeclaration) ? propertySymbol.GetMethod : propertySymbol.SetMethod; + Debug.Assert(accessor is not null); // '_position' was within the accessor body, why weren't we able to get a symbol for the accessor? } break; } @@ -605,6 +606,27 @@ bool checkSymbol(Symbol sym, TextSpan memberSpan, SymbolKind kind, out Symbol re } } } + else if (sym.Kind == SymbolKind.Property) + { + if (InSpan(sym.GetFirstLocation(), this.syntaxTree, memberSpan)) + { + return true; + } + + // If this is a partial property, the property represents the defining part, + // not the implementation (property.Locations includes both parts). If the + // span is in fact in the implementation, return that property instead. + var property = (SourcePropertySymbol)sym; + var implementation = property.IsPartialDefinition ? property.OtherPartOfPartial : property; + if ((object)implementation != null) + { + if (InSpan(implementation.GetFirstLocation(), this.syntaxTree, memberSpan)) + { + result = implementation; + return true; + } + } + } else if (InSpan(sym.Locations, this.syntaxTree, memberSpan)) { return true; diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index cd8a8755867fc..5ebe12c4f91a6 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 return type. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. Imported type '{0}' is invalid. It contains a circular base type dependency. @@ -7923,4 +7923,22 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ The data argument to InterceptsLocationAttribute refers to an invalid position in file '{0}'. + + Partial property '{0}' must have an implementation part. + + + Partial property '{0}' must have an definition part. + + + A partial property may not have multiple defining declarations + + + A partial property may not have multiple implementing declarations + + + Property accessor '{0}' must be implemented because it is declared on the definition part + + + Property accessor '{0}' does not implement any accessor declared on the definition part + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 15fee745c96a3..1b612b5c4c311 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2309,6 +2309,13 @@ internal enum ErrorCode ERR_InterceptsLocationFileNotFound = 9234, ERR_InterceptsLocationDataInvalidPosition = 9235, + ERR_PartialPropertyMissingImplementation = 9300, + ERR_PartialPropertyMissingDefinition = 9301, + ERR_PartialPropertyDuplicateDefinition = 9302, + ERR_PartialPropertyDuplicateImplementation = 9303, + ERR_PartialPropertyMissingAccessor = 9304, + ERR_PartialPropertyUnexpectedAccessor = 9305, + #endregion // Note: you will need to do the following after adding errors: diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index e634deae8b73d..e0d260514f28c 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2437,6 +2437,12 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_InterceptsLocationDuplicateFile: case ErrorCode.ERR_InterceptsLocationFileNotFound: case ErrorCode.ERR_InterceptsLocationDataInvalidPosition: + case ErrorCode.ERR_PartialPropertyMissingImplementation: + case ErrorCode.ERR_PartialPropertyMissingDefinition: + case ErrorCode.ERR_PartialPropertyDuplicateDefinition: + case ErrorCode.ERR_PartialPropertyDuplicateImplementation: + case ErrorCode.ERR_PartialPropertyMissingAccessor: + case ErrorCode.ERR_PartialPropertyUnexpectedAccessor: return false; default: // NOTE: All error codes must be explicitly handled in this switch statement diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs index 3855f36d82f99..3c26b0badd607 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs @@ -474,7 +474,7 @@ internal static bool ShouldEmit(this MethodSymbol method) } // Don't emit partial methods without an implementation part. - if (method.IsPartialMethod() && method.PartialImplementationPart is null) + if (method.IsPartialMember() && method.PartialImplementationPart is null) { return false; } @@ -545,22 +545,28 @@ internal static bool IsExplicitInterfaceImplementation(this Symbol member) } } - internal static bool IsPartialMethod(this Symbol member) + internal static bool IsPartialMember(this Symbol member) { - var sms = member as SourceMemberMethodSymbol; - return sms?.IsPartial == true; + return member + is SourceOrdinaryMethodSymbol { IsPartial: true } + or SourcePropertySymbol { IsPartial: true } + or SourcePropertyAccessorSymbol { IsPartial: true }; } internal static bool IsPartialImplementation(this Symbol member) { - var sms = member as SourceOrdinaryMethodSymbol; - return sms?.IsPartialImplementation == true; + return member + is SourceOrdinaryMethodSymbol { IsPartialImplementation: true } + or SourcePropertySymbol { IsPartialImplementation: true } + or SourcePropertyAccessorSymbol { IsPartialImplementation: true }; } internal static bool IsPartialDefinition(this Symbol member) { - var sms = member as SourceOrdinaryMethodSymbol; - return sms?.IsPartialDefinition == true; + return member + is SourceOrdinaryMethodSymbol { IsPartialDefinition: true } + or SourcePropertySymbol { IsPartialDefinition: true } + or SourcePropertyAccessorSymbol { IsPartialDefinition: true }; } internal static bool ContainsTupleNames(this Symbol member) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/IPartialMemberSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/IPartialMemberSymbol.cs new file mode 100644 index 0000000000000..99b3c4f3c762a --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Source/IPartialMemberSymbol.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + /// + /// Implemented by non-type symbols that can be declared 'partial', such as methods, properties, and property accessors. + /// Note that property accessors cannot be independently marked 'partial', they inherit it from their containing property declaration. + /// + internal interface IPartialMemberSymbol // PROTOTYPE(partial-properties): not sure yet whether to try to use this abstraction + { + /// + /// If this is a partial declaration, returns the other part. + /// This does not return a valid value until InitializePartialMemberParts has been called. + /// + Symbol? OtherPartOfPartial { get; } + + bool IsPartialDefinition { get; } + + bool IsPartialImplementation { get; } + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 97628686b76fa..4bf85633116bf 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -3574,68 +3574,170 @@ private void MergePartialMembers( memberNames.AddRange(membersByName.Keys); //key and value will be the same object - var methodsBySignature = new Dictionary(MemberSignatureComparer.PartialMethodsComparer); + var membersBySignature = new Dictionary(MemberSignatureComparer.PartialMethodsComparer); foreach (var name in memberNames) { - methodsBySignature.Clear(); + membersBySignature.Clear(); foreach (var symbol in membersByName[name]) { - var method = symbol as SourceMemberMethodSymbol; - if (method is null || !method.IsPartial) + if (!symbol.IsPartialMember()) { - continue; // only partial methods need to be merged + continue; } - if (methodsBySignature.TryGetValue(method, out var prev)) + if (!membersBySignature.TryGetValue(symbol, out var prev)) { - var prevPart = (SourceOrdinaryMethodSymbol)prev; - var methodPart = (SourceOrdinaryMethodSymbol)method; + membersBySignature.Add(symbol, symbol); + continue; + } - if (methodPart.IsPartialImplementation && - (prevPart.IsPartialImplementation || (prevPart.OtherPartOfPartial is MethodSymbol otherImplementation && (object)otherImplementation != methodPart))) - { - // A partial method may not have multiple implementing declarations - diagnostics.Add(ErrorCode.ERR_PartialMethodOnlyOneActual, methodPart.GetFirstLocation()); - } - else if (methodPart.IsPartialDefinition && - (prevPart.IsPartialDefinition || (prevPart.OtherPartOfPartial is MethodSymbol otherDefinition && (object)otherDefinition != methodPart))) - { - // A partial method may not have multiple defining declarations - diagnostics.Add(ErrorCode.ERR_PartialMethodOnlyOneLatent, methodPart.GetFirstLocation()); - } - else - { - if ((object)membersByName == _lazyEarlyAttributeDecodingMembersDictionary) + Debug.Assert(symbol.GetType() == prev.GetType()); + switch (symbol, prev) + { + case (SourceOrdinaryMethodSymbol currentMethod, SourceOrdinaryMethodSymbol prevMethod): + mergePartialMethods(ref membersByName, name, currentMethod, prevMethod); + break; + + case (SourcePropertySymbol currentProperty, SourcePropertySymbol prevProperty): + mergePartialProperties(ref membersByName, name, currentProperty, prevProperty); + break; + + case (SourcePropertyAccessorSymbol, SourcePropertyAccessorSymbol): + continue; // accessor symbols and their diagnostics are handled by processing the associated property + + default: + throw ExceptionUtilities.UnexpectedValue(prev); + } + } + + foreach (var symbol in membersBySignature.Values) + { + switch (symbol) + { + case SourceOrdinaryMethodSymbol method: + // partial implementations not paired with a definition + if (method.IsPartialImplementation && method.OtherPartOfPartial is null) { - // Avoid mutating the cached dictionary and especially avoid doing this possibly on multiple threads in parallel. - membersByName = new Dictionary, ImmutableArray>(membersByName, ReadOnlyMemoryOfCharComparer.Instance); + diagnostics.Add(ErrorCode.ERR_PartialMethodMustHaveLatent, method.GetFirstLocation(), method); } + else if (method is { IsPartialDefinition: true, OtherPartOfPartial: null, HasExplicitAccessModifier: true }) + { + diagnostics.Add(ErrorCode.ERR_PartialMethodWithAccessibilityModsMustHaveImplementation, method.GetFirstLocation(), method); + } + break; - membersByName[name] = FixPartialMember(membersByName[name], prevPart, methodPart); - } + case SourcePropertySymbol property: + if (property.OtherPartOfPartial is null) + { + diagnostics.Add( + property.IsPartialDefinition ? ErrorCode.ERR_PartialPropertyMissingImplementation : ErrorCode.ERR_PartialPropertyMissingDefinition, + property.GetFirstLocation(), + property); + } + break; + + case SourcePropertyAccessorSymbol: + break; // handled by SourcePropertySymbol case + + default: + throw ExceptionUtilities.UnexpectedValue(symbol); } - else + } + } + + memberNames.Free(); + + void mergePartialMethods(ref Dictionary, ImmutableArray> membersByName, ReadOnlyMemory name, SourceOrdinaryMethodSymbol currentMethod, SourceOrdinaryMethodSymbol prevMethod) + { + if (currentMethod.IsPartialImplementation && + (prevMethod.IsPartialImplementation || (prevMethod.OtherPartOfPartial is MethodSymbol otherImplementation && (object)otherImplementation != currentMethod))) + { + // A partial method may not have multiple implementing declarations + diagnostics.Add(ErrorCode.ERR_PartialMethodOnlyOneActual, currentMethod.GetFirstLocation()); + } + else if (currentMethod.IsPartialDefinition && + (prevMethod.IsPartialDefinition || (prevMethod.OtherPartOfPartial is MethodSymbol otherDefinition && (object)otherDefinition != currentMethod))) + { + // A partial method may not have multiple defining declarations + diagnostics.Add(ErrorCode.ERR_PartialMethodOnlyOneLatent, currentMethod.GetFirstLocation()); + } + else + { + if ((object)membersByName == _lazyEarlyAttributeDecodingMembersDictionary) { - methodsBySignature.Add(method, method); + // Avoid mutating the cached dictionary and especially avoid doing this possibly on multiple threads in parallel. + membersByName = new Dictionary, ImmutableArray>(membersByName, ReadOnlyMemoryOfCharComparer.Instance); } + + membersByName[name] = FixPartialMember(membersByName[name], prevMethod, currentMethod); } + } - foreach (SourceOrdinaryMethodSymbol method in methodsBySignature.Values) + void mergePartialProperties(ref Dictionary, ImmutableArray> membersByName, ReadOnlyMemory name, SourcePropertySymbol currentProperty, SourcePropertySymbol prevProperty) + { + if (currentProperty.IsPartialImplementation && + (prevProperty.IsPartialImplementation || (prevProperty.OtherPartOfPartial is SourcePropertySymbol otherImplementation && (object)otherImplementation != currentProperty))) + { + // A partial method may not have multiple implementing declarations + diagnostics.Add(ErrorCode.ERR_PartialMethodOnlyOneActual, currentProperty.GetFirstLocation()); + } + else if (currentProperty.IsPartialDefinition && + (prevProperty.IsPartialDefinition || (prevProperty.OtherPartOfPartial is SourcePropertySymbol otherDefinition && (object)otherDefinition != currentProperty))) + { + // A partial method may not have multiple defining declarations + diagnostics.Add(ErrorCode.ERR_PartialMethodOnlyOneLatent, currentProperty.GetFirstLocation()); + } + else { - // partial implementations not paired with a definition - if (method.IsPartialImplementation && method.OtherPartOfPartial is null) + var (currentGet, prevGet) = ((SourcePropertyAccessorSymbol?)currentProperty.GetMethod, (SourcePropertyAccessorSymbol?)prevProperty.GetMethod); + if (currentGet != null || prevGet != null) { - diagnostics.Add(ErrorCode.ERR_PartialMethodMustHaveLatent, method.GetFirstLocation(), method); + var accessorName = (currentGet ?? prevGet)!.Name.AsMemory(); + mergeAccessors(ref membersByName, accessorName, currentGet, prevGet); } - else if (method is { IsPartialDefinition: true, OtherPartOfPartial: null, HasExplicitAccessModifier: true }) + + var (currentSet, prevSet) = ((SourcePropertyAccessorSymbol?)currentProperty.SetMethod, (SourcePropertyAccessorSymbol?)prevProperty.SetMethod); + if (currentSet != null || prevSet != null) { - diagnostics.Add(ErrorCode.ERR_PartialMethodWithAccessibilityModsMustHaveImplementation, method.GetFirstLocation(), method); + var accessorName = (currentSet ?? prevSet)!.Name.AsMemory(); + mergeAccessors(ref membersByName, accessorName, currentSet, prevSet); } + + if ((object)membersByName == _lazyEarlyAttributeDecodingMembersDictionary) + { + // Avoid mutating the cached dictionary and especially avoid doing this possibly on multiple threads in parallel. + membersByName = new Dictionary, ImmutableArray>(membersByName, ReadOnlyMemoryOfCharComparer.Instance); + } + + membersByName[name] = FixPartialMember(membersByName[name], prevProperty, currentProperty); } - } - memberNames.Free(); + void mergeAccessors(ref Dictionary, ImmutableArray> membersByName, ReadOnlyMemory name, SourcePropertyAccessorSymbol? currentAccessor, SourcePropertyAccessorSymbol? prevAccessor) + { + Debug.Assert(currentAccessor != null || prevAccessor != null); + + // When an accessor is present on definition but not on implementation, the accessor is said to be missing on the implementation. + // When an accessor is present on implementation but not on definition, the accessor is said to be unexpected on the implementation. + if (currentAccessor == null) + { + // Partial definition is the source of truth for which accessors should be present. + var (errorCode, propertyToBlame) = prevProperty.IsPartialDefinition ? (ErrorCode.ERR_PartialPropertyMissingAccessor, currentProperty) : (ErrorCode.ERR_PartialPropertyUnexpectedAccessor, prevProperty); + diagnostics.Add(errorCode, propertyToBlame.GetFirstLocation(), prevAccessor!); + } + else if (prevAccessor == null) + { + // Partial definition is the source of truth for which accessors should be present. + var (errorCode, propertyToBlame) = currentProperty.IsPartialDefinition ? (ErrorCode.ERR_PartialPropertyMissingAccessor, prevProperty) : (ErrorCode.ERR_PartialPropertyUnexpectedAccessor, currentProperty); + diagnostics.Add(errorCode, propertyToBlame.GetFirstLocation(), currentAccessor!); + } + else + { + var (definitionAccessor, implementationAccessor) = currentProperty.IsPartialDefinition ? (currentAccessor, prevAccessor) : (prevAccessor, currentAccessor); + membersByName[name] = Remove(membersByName[name], implementationAccessor); + } + } + } } /// @@ -3667,6 +3769,28 @@ private static ImmutableArray FixPartialMember(ImmutableArray sy return Remove(symbols, implementation); } + // PROTOTYPE(partial-properties): is there some abstraction that would make this nice? + private static ImmutableArray FixPartialMember(ImmutableArray symbols, SourcePropertySymbol part1, SourcePropertySymbol part2) + { + SourcePropertySymbol definition; + SourcePropertySymbol implementation; + if (part1.IsPartialDefinition) + { + definition = part1; + implementation = part2; + } + else + { + definition = part2; + implementation = part1; + } + + SourcePropertySymbol.InitializePartialPropertyParts(definition, implementation); + + // a partial method is represented in the member list by its definition part: + return Remove(symbols, implementation); + } + private static ImmutableArray Remove(ImmutableArray symbols, Symbol symbol) { var builder = ArrayBuilder.GetInstance(); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index a4c5ed6fff70e..7bae0f2368e76 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -1450,7 +1450,7 @@ internal override void PostDecodeWellKnownAttributes(ImmutableArray _property is SourcePropertySymbol { IsPartialDefinition: true, OtherPartOfPartial: { } other } + ? (MethodKind == MethodKind.PropertyGet ? other.GetMethod : other.SetMethod) + : null; + + public sealed override MethodSymbol? PartialDefinitionPart => _property is SourcePropertySymbol { IsPartialImplementation: true, OtherPartOfPartial: { } other } + ? (MethodKind == MethodKind.PropertyGet ? other.GetMethod : other.SetMethod) + : null; + + internal bool IsPartialDefinition => _property is SourcePropertySymbol { IsPartialDefinition: true }; + internal bool IsPartialImplementation => _property is SourcePropertySymbol { IsPartialImplementation: true }; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index af93d12c934ff..08356a3534ad6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -13,6 +13,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal sealed class SourcePropertySymbol : SourcePropertySymbolBase { + private SourcePropertySymbol? _otherPartOfPartial; + internal static SourcePropertySymbol Create(SourceMemberContainerTypeSymbol containingType, Binder bodyBinder, PropertyDeclarationSyntax syntax, BindingDiagnosticBag diagnostics) { var nameToken = syntax.Identifier; @@ -37,7 +39,6 @@ private static SourcePropertySymbol Create( GetAccessorDeclarations( syntax, diagnostics, - out bool isAutoProperty, out bool hasAccessorList, out bool accessorsHaveImplementation, out bool isInitOnly, @@ -57,6 +58,8 @@ private static SourcePropertySymbol Create( diagnostics, out _); + // PROTOTYPE(partial-properties): it's unclear if the subtle difference between former 'isAutoProperty' and '!accessorsHaveImplementation' matters + bool isAutoProperty = (modifiers & DeclarationModifiers.Partial) == 0 && !accessorsHaveImplementation; bool isExpressionBodied = !hasAccessorList && GetArrowExpression(syntax) != null; binder = binder.WithUnsafeRegionIfNecessary(modifiersTokenList); @@ -76,6 +79,7 @@ private static SourcePropertySymbol Create( isAutoProperty: isAutoProperty, isExpressionBodied: isExpressionBodied, isInitOnly: isInitOnly, + accessorsHaveImplementation: accessorsHaveImplementation, memberName, location, diagnostics); @@ -93,6 +97,7 @@ private SourcePropertySymbol( bool isAutoProperty, bool isExpressionBodied, bool isInitOnly, + bool accessorsHaveImplementation, string memberName, Location location, BindingDiagnosticBag diagnostics) @@ -109,6 +114,7 @@ private SourcePropertySymbol( isAutoProperty: isAutoProperty, isExpressionBodied: isExpressionBodied, isInitOnly: isInitOnly, + accessorsHaveImplementation: accessorsHaveImplementation, syntax.Type.SkipScoped(out _).GetRefKindInLocalOrReturn(diagnostics), memberName, syntax.AttributeLists, @@ -163,7 +169,6 @@ public override SyntaxList AttributeDeclarationSyntaxList private static void GetAccessorDeclarations( CSharpSyntaxNode syntaxNode, BindingDiagnosticBag diagnostics, - out bool isAutoProperty, out bool hasAccessorList, out bool accessorsHaveImplementation, out bool isInitOnly, @@ -171,7 +176,6 @@ private static void GetAccessorDeclarations( out CSharpSyntaxNode? setSyntax) { var syntax = (BasePropertyDeclarationSyntax)syntaxNode; - isAutoProperty = true; hasAccessorList = syntax.AccessorList != null; getSyntax = null; setSyntax = null; @@ -223,14 +227,12 @@ private static void GetAccessorDeclarations( if (accessor.Body != null || accessor.ExpressionBody != null) { - isAutoProperty = false; accessorsHaveImplementation = true; } } } else { - isAutoProperty = false; accessorsHaveImplementation = GetArrowExpression(syntax) is object; } } @@ -284,6 +286,7 @@ private static DeclarationModifiers MakeModifiers( if (!isExplicitInterfaceImplementation) { allowedModifiers |= DeclarationModifiers.New | + DeclarationModifiers.Partial | DeclarationModifiers.Sealed | DeclarationModifiers.Abstract | DeclarationModifiers.Virtual | @@ -552,9 +555,50 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, } diagnostics.Add(Location, useSiteInfo); + + if (IsPartialDefinition && OtherPartOfPartial is { } implementation) + { + PartialPropertyChecks(implementation, diagnostics); + } + } + + private void PartialPropertyChecks(SourcePropertySymbol implementation, BindingDiagnosticBag diagnostics) + { + Debug.Assert(this.IsPartialDefinition); + Debug.Assert((object)this != implementation); + Debug.Assert((object?)this.OtherPartOfPartial == implementation); + + // PROTOTYPE(partial-properties): check and diagnose all disallowed differences between parts. + // PROTOTYPE(partial-properties): we should likely report a warning when only one of the parts has an explicit access modifier for consistency with methods. + if (DeclaredAccessibility != implementation.DeclaredAccessibility) + { + // PROTOTYPE(partial-properties): we should either generalize the existing diagnostic or introduce new set of diagnostics. + diagnostics.Add(ErrorCode.ERR_PartialMethodAccessibilityDifference, implementation.GetFirstLocation()); + } } private static BaseParameterListSyntax? GetParameterListSyntax(CSharpSyntaxNode syntax) => (syntax as IndexerDeclarationSyntax)?.ParameterList; + + internal bool IsPartial => (_modifiers & DeclarationModifiers.Partial) != 0; + + internal SourcePropertySymbol? OtherPartOfPartial { get => _otherPartOfPartial; } + + internal bool IsPartialDefinition => IsPartial && !AccessorsHaveImplementation && !IsExtern; + + internal bool IsPartialImplementation => IsPartial && (AccessorsHaveImplementation || IsExtern); + + internal static void InitializePartialPropertyParts(SourcePropertySymbol definition, SourcePropertySymbol implementation) + { + Debug.Assert(definition.IsPartialDefinition); + Debug.Assert(implementation.IsPartialImplementation); + + Debug.Assert(definition._otherPartOfPartial is null || definition._otherPartOfPartial == implementation); + Debug.Assert(implementation._otherPartOfPartial is null || implementation._otherPartOfPartial == definition); + + definition._otherPartOfPartial = implementation; + implementation._otherPartOfPartial = definition; + } + } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs index 326d8414791f1..c4476dc8709a6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs @@ -33,6 +33,7 @@ private enum Flags : byte IsAutoProperty = 1 << 1, IsExplicitInterfaceImplementation = 1 << 2, HasInitializer = 1 << 3, + AccessorsHaveImplementation = 1 << 4, } // TODO (tomat): consider splitting into multiple subclasses/rare data. @@ -81,6 +82,7 @@ protected SourcePropertySymbolBase( bool isAutoProperty, bool isExpressionBodied, bool isInitOnly, + bool accessorsHaveImplementation, RefKind refKind, string memberName, SyntaxList indexerNameAttributeLists, @@ -89,6 +91,7 @@ protected SourcePropertySymbolBase( { Debug.Assert(!isExpressionBodied || !isAutoProperty); Debug.Assert(!isExpressionBodied || !hasInitializer); + Debug.Assert(!isExpressionBodied || accessorsHaveImplementation); // PROTOTYPE(partial-properties): further adjust asserts? Debug.Assert((modifiers & DeclarationModifiers.Required) == 0 || this is SourcePropertySymbol); _syntaxRef = syntax.GetReference(); @@ -125,6 +128,11 @@ protected SourcePropertySymbolBase( _propertyFlags |= Flags.IsExpressionBodied; } + if (accessorsHaveImplementation) + { + _propertyFlags |= Flags.AccessorsHaveImplementation; + } + if (isIndexer) { if (indexerNameAttributeLists.Count == 0 || isExplicitInterfaceImplementation) @@ -479,11 +487,6 @@ public override bool IsStatic get { return (_modifiers & DeclarationModifiers.Static) != 0; } } - internal bool IsFixed - { - get { return false; } - } - /// /// Even though it is declared with an IndexerDeclarationSyntax, an explicit /// interface implementation is not an indexer because it will not cause the @@ -621,6 +624,9 @@ internal bool IsAutoPropertyWithGetAccessor protected bool IsAutoProperty => (_propertyFlags & Flags.IsAutoProperty) != 0; + protected bool AccessorsHaveImplementation + => (_propertyFlags & Flags.AccessorsHaveImplementation) != 0; + /// /// Backing field for automatically implemented property, or /// for a property with an initializer. diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs index c64029e821ca8..65bde2a1f8843 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs @@ -34,6 +34,7 @@ public SynthesizedRecordEqualityContractProperty(SourceMemberContainerTypeSymbol isAutoProperty: false, isExpressionBodied: false, isInitOnly: false, + accessorsHaveImplementation: true, RefKind.None, PropertyName, indexerNameAttributeLists: new SyntaxList(), diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs index b3b268b23fe80..b276a81425b6f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs @@ -31,6 +31,7 @@ public SynthesizedRecordPropertySymbol( isAutoProperty: true, isExpressionBodied: false, isInitOnly: ShouldUseInit(containingType), + accessorsHaveImplementation: true, RefKind.None, backingParameter.Name, indexerNameAttributeLists: new SyntaxList(), diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 9cb6c6666dea2..3cdada48f142b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1652,6 +1652,36 @@ Částečná metoda {0} musí mít modifikátory přístupnosti, protože má parametry out. + + A partial property may not have multiple defining declarations + A partial property may not have multiple defining declarations + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be implemented because it is declared on the definition part + Property accessor '{0}' must be implemented because it is declared on the definition part + + + + Partial property '{0}' must have an definition part. + Partial property '{0}' must have an definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Property accessor '{0}' does not implement any accessor declared on the definition part + Property accessor '{0}' does not implement any accessor declared on the definition part + + A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. Konstanta řetězce null není podporována jako vzor pro {0}. Místo toho použijte prázdný řetězec. @@ -6439,8 +6469,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method 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. + 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. @@ -13036,4 +13066,4 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 5350d1c59acab..b19c182ad4dc3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1652,6 +1652,36 @@ Die partielle Methode "{0}" muss Zugriffsmodifizierer aufweisen, weil sie out-Parameter verwendet. + + A partial property may not have multiple defining declarations + A partial property may not have multiple defining declarations + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be implemented because it is declared on the definition part + Property accessor '{0}' must be implemented because it is declared on the definition part + + + + Partial property '{0}' must have an definition part. + Partial property '{0}' must have an definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Property accessor '{0}' does not implement any accessor declared on the definition part + Property accessor '{0}' does not implement any accessor declared on the definition part + + A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. Eine Nullkonstante der Zeichenfolge wird nicht als Muster für "{0}" unterstützt. Verwenden Sie stattdessen eine leere Zeichenfolge. @@ -6439,8 +6469,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. - Der partial-Modifizierer kann nur unmittelbar vor "class", "record", "struct", "interface" oder einem Methodenrückgabetyp verwendet werden. + 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 Methodenrückgabetyp verwendet werden. @@ -13036,4 +13066,4 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 2a449abc1f16c..e626199201e3f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1652,6 +1652,36 @@ El método parcial "{0}" debe tener modificadores de accesibilidad porque tiene parámetros "out". + + A partial property may not have multiple defining declarations + A partial property may not have multiple defining declarations + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be implemented because it is declared on the definition part + Property accessor '{0}' must be implemented because it is declared on the definition part + + + + Partial property '{0}' must have an definition part. + Partial property '{0}' must have an definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Property accessor '{0}' does not implement any accessor declared on the definition part + Property accessor '{0}' does not implement any accessor declared on the definition part + + A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. No se admite una constante de cadena 'null' como patrón para '{0}'. Use una cadena vacía en su lugar. @@ -6439,8 +6469,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. - El modificador "partial" solo puede aparecer inmediatamente antes de "class", "record", "struct", "interface" o de un tipo de valor devuelto del método. + 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 de un tipo de valor devuelto del método. @@ -13036,4 +13066,4 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index e39c57737603f..611284b3dcdd1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1652,6 +1652,36 @@ La méthode partielle '{0}' doit avoir des modificateurs d'accessibilité, car elle a des paramètres 'out'. + + A partial property may not have multiple defining declarations + A partial property may not have multiple defining declarations + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be implemented because it is declared on the definition part + Property accessor '{0}' must be implemented because it is declared on the definition part + + + + Partial property '{0}' must have an definition part. + Partial property '{0}' must have an definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Property accessor '{0}' does not implement any accessor declared on the definition part + Property accessor '{0}' does not implement any accessor declared on the definition part + + A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. Une constante « null » de chaîne n’est pas prise en charge en tant que modèle pour «{0}». Utilisez plutôt une chaîne vide. @@ -6439,8 +6469,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. - Le modificateur 'partial' peut apparaître uniquement juste avant 'class', 'record', 'struct', 'interface' ou un type de retour de méthode. + 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. @@ -13036,4 +13066,4 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index d3378e3228a1f..a94ae727e21fb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1652,6 +1652,36 @@ Il metodo parziale '{0}' deve contenere modificatori di accessibilità perché include parametri 'out'. + + A partial property may not have multiple defining declarations + A partial property may not have multiple defining declarations + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be implemented because it is declared on the definition part + Property accessor '{0}' must be implemented because it is declared on the definition part + + + + Partial property '{0}' must have an definition part. + Partial property '{0}' must have an definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Property accessor '{0}' does not implement any accessor declared on the definition part + Property accessor '{0}' does not implement any accessor declared on the definition part + + A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. Una costante di tipo stringa 'null' non è supportata come criterio per ?{0}'. Usare invece una stringa vuota. @@ -6439,8 +6469,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 return type. - Il modificatore 'partial' può trovarsi solo immediatamente prima di 'class', 'record', 'struct', 'interface' o il tipo restituito di un metodo. + 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 il tipo restituito di un metodo. @@ -13036,4 +13066,4 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 88b8066444024..f2e54b6e9905b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1652,6 +1652,36 @@ 部分メソッド '{0}' には、'out' パラメーターが指定されているため、アクセシビリティ修飾子が必要です。 + + A partial property may not have multiple defining declarations + A partial property may not have multiple defining declarations + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be implemented because it is declared on the definition part + Property accessor '{0}' must be implemented because it is declared on the definition part + + + + Partial property '{0}' must have an definition part. + Partial property '{0}' must have an definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Property accessor '{0}' does not implement any accessor declared on the definition part + Property accessor '{0}' does not implement any accessor declared on the definition part + + A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. 文字列 'null' 定数は、'{0}' のパターンとしてサポートされていません。代わりに空の文字列を使用してください。 @@ -6439,8 +6469,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. - 'partial' 修飾子は、'class'、'record'、'struct'、'interface'、またはメソッドの戻り値の型の直前にのみ指定できます。 + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + 'partial' 修飾子は、'class'、'record'、'struct'、'interface'、またはメソッドの戻り値の型の直前にのみ指定できます。 @@ -13036,4 +13066,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index b964b1a1d24fd..ec1bff289faaa 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1652,6 +1652,36 @@ 부분 메서드 '{0}'에는 'out' 매개 변수가 있으므로 접근성 한정자가 있어야 합니다. + + A partial property may not have multiple defining declarations + A partial property may not have multiple defining declarations + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be implemented because it is declared on the definition part + Property accessor '{0}' must be implemented because it is declared on the definition part + + + + Partial property '{0}' must have an definition part. + Partial property '{0}' must have an definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Property accessor '{0}' does not implement any accessor declared on the definition part + Property accessor '{0}' does not implement any accessor declared on the definition part + + A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. 문자열 'Null' 상수는 '{0}'에 대한 패턴으로 지원되지 않습니다. 대신 빈 문자열을 사용하세요. @@ -6439,8 +6469,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. - 'partial' 한정자는 'class', 'record', 'struct', 'interface' 또는 메서드 반환 형식 바로 앞에만 올 수 있습니다. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + 'partial' 한정자는 'class', 'record', 'struct', 'interface' 또는 메서드 반환 형식 바로 앞에만 올 수 있습니다. @@ -13036,4 +13066,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index c7aceef3e3fc0..28884d20ed5af 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1652,6 +1652,36 @@ Metoda częściowa „{0}” musi mieć modyfikatory dostępności, ponieważ ma parametry „out”. + + A partial property may not have multiple defining declarations + A partial property may not have multiple defining declarations + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be implemented because it is declared on the definition part + Property accessor '{0}' must be implemented because it is declared on the definition part + + + + Partial property '{0}' must have an definition part. + Partial property '{0}' must have an definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Property accessor '{0}' does not implement any accessor declared on the definition part + Property accessor '{0}' does not implement any accessor declared on the definition part + + A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. Stała ciągu „null” nie jest obsługiwana jako wzorzec dla „{0}”. Zamiast tego użyj pustego ciągu. @@ -6439,8 +6469,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. - Modyfikator „partial” może pojawić się tylko bezpośrednio przed słowem kluczowym „class”, „record” „struct”, „interface” lub zwracanym typem metody. + 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. @@ -13036,4 +13066,4 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index eca4a0f9e327d..9a363141b11b5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1652,6 +1652,36 @@ O método parcial '{0}' precisa ter modificadores de acessibilidade porque ele tem parâmetros 'out'. + + A partial property may not have multiple defining declarations + A partial property may not have multiple defining declarations + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be implemented because it is declared on the definition part + Property accessor '{0}' must be implemented because it is declared on the definition part + + + + Partial property '{0}' must have an definition part. + Partial property '{0}' must have an definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Property accessor '{0}' does not implement any accessor declared on the definition part + Property accessor '{0}' does not implement any accessor declared on the definition part + + A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. Uma constante cadeia de caracteres 'null' não é suportada como padrão para '{0}'. Use uma cadeia de caracteres vazia em seu lugar. @@ -6439,8 +6469,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method 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. + 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. @@ -13036,4 +13066,4 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index f00022bc0283a..7e95fe6477774 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1652,6 +1652,36 @@ Разделяемый метод "{0}" должен иметь модификаторы доступа, так как он содержит параметры "out". + + A partial property may not have multiple defining declarations + A partial property may not have multiple defining declarations + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be implemented because it is declared on the definition part + Property accessor '{0}' must be implemented because it is declared on the definition part + + + + Partial property '{0}' must have an definition part. + Partial property '{0}' must have an definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Property accessor '{0}' does not implement any accessor declared on the definition part + Property accessor '{0}' does not implement any accessor declared on the definition part + + A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. Строковая константа "null" не поддерживается в качестве шаблона для "{0}". Используйте вместо этого пустую строку. @@ -6440,8 +6470,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. - Модификатор "partial" может использоваться только перед ключевыми словами "class", "record", "struct" и "interface" и перед возвращаемым типом метода. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + Модификатор "partial" может использоваться только перед ключевыми словами "class", "record", "struct" и "interface" и перед возвращаемым типом метода. @@ -13037,4 +13067,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 3104df55006e0..45e36455f68bb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1652,6 +1652,36 @@ '{0}' kısmi metodunun 'out' parametreleri olduğundan erişilebilirlik değiştiricileri olmalıdır. + + A partial property may not have multiple defining declarations + A partial property may not have multiple defining declarations + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be implemented because it is declared on the definition part + Property accessor '{0}' must be implemented because it is declared on the definition part + + + + Partial property '{0}' must have an definition part. + Partial property '{0}' must have an definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Property accessor '{0}' does not implement any accessor declared on the definition part + Property accessor '{0}' does not implement any accessor declared on the definition part + + A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. Dize 'null' sabiti, '{0}' için desen olarak desteklenmiyor. Bunun yerine boş bir dize kullanın. @@ -6439,8 +6469,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. - 'partial' değiştiricisi yalnızca 'class', 'record', 'struct', 'interface' ifadelerinden veya metot dönüş türünden hemen önce gelebilir. + 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 dönüş türünden hemen önce gelebilir. @@ -13036,4 +13066,4 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 4a501f24b6625..37c2149eee537 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1652,6 +1652,36 @@ 分部方法“{0}”必须具有可访问性修饰符,因为它具有 "out" 参数。 + + A partial property may not have multiple defining declarations + A partial property may not have multiple defining declarations + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be implemented because it is declared on the definition part + Property accessor '{0}' must be implemented because it is declared on the definition part + + + + Partial property '{0}' must have an definition part. + Partial property '{0}' must have an definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Property accessor '{0}' does not implement any accessor declared on the definition part + Property accessor '{0}' does not implement any accessor declared on the definition part + + A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. 不支持将字符串 'null' 常量作为 '{0}' 的模式。请改用空字符串。 @@ -6439,8 +6469,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. - "partial" 修饰符的后面只能紧跟 "class"、"record"、"struct"、"interface" 或方法返回类型。 + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + "partial" 修饰符的后面只能紧跟 "class"、"record"、"struct"、"interface" 或方法返回类型。 @@ -13036,4 +13066,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 94d77f41046f6..f2143affd0c41 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1652,6 +1652,36 @@ 因為部分方法 '{0}' 有 'out' 參數,所以其必須有存取範圍修飾詞。 + + A partial property may not have multiple defining declarations + A partial property may not have multiple defining declarations + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be implemented because it is declared on the definition part + Property accessor '{0}' must be implemented because it is declared on the definition part + + + + Partial property '{0}' must have an definition part. + Partial property '{0}' must have an definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Property accessor '{0}' does not implement any accessor declared on the definition part + Property accessor '{0}' does not implement any accessor declared on the definition part + + A string 'null' constant is not supported as a pattern for '{0}'. Use an empty string instead. 不支援字串 'null' 常數做為 '{0}' 的模式。請改為使用空字串。 @@ -6439,8 +6469,8 @@ strument:TestCoverage 產生檢測要收集 - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. - 'partial' 修飾元只可緊接在 'class'、'record'、'struct'、'interface' 或方法傳回型別之前。 + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + 'partial' 修飾元只可緊接在 'class'、'record'、'struct'、'interface' 或方法傳回型別之前。 @@ -13036,4 +13066,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/Emit2/Diagnostics/GetDiagnosticsTests.cs b/src/Compilers/CSharp/Test/Emit2/Diagnostics/GetDiagnosticsTests.cs index 0d264412df26c..4fcd0bf2fe66c 100644 --- a/src/Compilers/CSharp/Test/Emit2/Diagnostics/GetDiagnosticsTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Diagnostics/GetDiagnosticsTests.cs @@ -186,6 +186,8 @@ private void NonPartialMethod2() { } Assert.True(completedCompilationUnits.Contains(tree1.FilePath)); } + // PROTOTYPE(partial-properties): also test compilation events for complete and incomplete partial properties and their accessors + [Fact, WorkItem(7477, "https://github.com/dotnet/roslyn/issues/7477")] public void TestCompilationEventsForPartialMethod() { diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs index bf23e1d535416..6a4cc790b55c5 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs @@ -10889,7 +10889,7 @@ public Test2(int x) {} Assert.False(m1.IsAsync); Assert.False(m1.IsOverride); Assert.Equal(Accessibility.Private, m1.DeclaredAccessibility); - Assert.True(m1.IsPartialMethod()); + Assert.True(m1.IsPartialMember()); Assert.Null(m1.PartialImplementationPart); var m2 = i1.GetMember("M2"); @@ -10903,7 +10903,7 @@ public Test2(int x) {} Assert.False(m2.IsAsync); Assert.False(m2.IsOverride); Assert.Equal(Accessibility.Private, m2.DeclaredAccessibility); - Assert.True(m2.IsPartialMethod()); + Assert.True(m2.IsPartialMember()); Assert.Equal(2, m2.GetAttributes().Length); Assert.Equal("Test2(1)", m2.GetAttributes()[0].ToString()); @@ -10920,7 +10920,7 @@ public Test2(int x) {} Assert.False(m2Impl.IsAsync); Assert.False(m2Impl.IsOverride); Assert.Equal(Accessibility.Private, m2Impl.DeclaredAccessibility); - Assert.True(m2Impl.IsPartialMethod()); + Assert.True(m2Impl.IsPartialMember()); Assert.Same(m2, m2Impl.PartialDefinitionPart); Assert.Equal(2, m2Impl.GetAttributes().Length); @@ -10996,7 +10996,7 @@ public Test2(int x) {} Assert.False(m1.IsAsync); Assert.False(m1.IsOverride); Assert.Equal(Accessibility.Private, m1.DeclaredAccessibility); - Assert.True(m1.IsPartialMethod()); + Assert.True(m1.IsPartialMember()); Assert.Null(m1.PartialImplementationPart); var m2 = i1.GetMember("M2"); @@ -11010,7 +11010,7 @@ public Test2(int x) {} Assert.False(m2.IsAsync); Assert.False(m2.IsOverride); Assert.Equal(Accessibility.Private, m2.DeclaredAccessibility); - Assert.True(m2.IsPartialMethod()); + Assert.True(m2.IsPartialMember()); Assert.Equal(2, m2.GetAttributes().Length); Assert.Equal("Test2(1)", m2.GetAttributes()[0].ToString()); @@ -11027,7 +11027,7 @@ public Test2(int x) {} Assert.False(m2Impl.IsAsync); Assert.False(m2Impl.IsOverride); Assert.Equal(Accessibility.Private, m2Impl.DeclaredAccessibility); - Assert.True(m2Impl.IsPartialMethod()); + Assert.True(m2Impl.IsPartialMember()); Assert.Same(m2, m2Impl.PartialDefinitionPart); Assert.Equal(2, m2Impl.GetAttributes().Length); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs new file mode 100644 index 0000000000000..57bb106a7e43a --- /dev/null +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs @@ -0,0 +1,407 @@ +// Licensed to the .NET Foundation under one or more agreements. +// 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; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols +{ + public class PartialPropertiesTests : CSharpTestBase + { + [Fact] + public void MissingDeclaration_01() + { + // definition without implementation + var source = """ + partial class C + { + partial int P { get; set; } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,17): error CS9300: Partial property 'C.P' must have an implementation part. + // partial int P { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "P").WithArguments("C.P").WithLocation(3, 17) + ); + + var cClass = comp.GetMember("C"); + AssertEx.Equal([ + "System.Int32 C.P { get; set; }", + "System.Int32 C.P.get", + "void C.P.set", + "C..ctor()" + ], + cClass.GetMembers().SelectAsArray(m => m.ToTestDisplayString())); + } + + [Fact] + public void MissingDeclaration_02() + { + // implementation without definition + var source = """ + partial class C + { + partial int P { get => throw null!; set { } } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,17): error CS9301: Partial property 'C.P' must have an definition part. + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "P").WithArguments("C.P").WithLocation(3, 17) + ); + } + + [Fact] + public void DuplicateDeclaration_01() + { + // duplicate definition + var source = """ + partial class C + { + partial int P { get; set; } + partial int P { get; set; } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,17): error CS9300: Partial property 'C.P' must have an implementation part. + // partial int P { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "P").WithArguments("C.P").WithLocation(3, 17), + // (4,17): error CS0756: A partial method may not have multiple defining declarations + // partial int P { get; set; } + Diagnostic(ErrorCode.ERR_PartialMethodOnlyOneLatent, "P").WithLocation(4, 17), + // (4,17): error CS0102: The type 'C' already contains a definition for 'P' + // partial int P { get; set; } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "P").WithLocation(4, 17) + ); + } + + [Fact] + public void DuplicateDeclaration_02() + { + // duplicate definition with single implementation + var source = """ + partial class C + { + partial int P { get; set; } + partial int P { get; set; } + partial int P { get => throw null!; set { } } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,17): error CS0756: A partial method may not have multiple defining declarations + // partial int P { get; set; } + Diagnostic(ErrorCode.ERR_PartialMethodOnlyOneLatent, "P").WithLocation(4, 17), + // (4,17): error CS0102: The type 'C' already contains a definition for 'P' + // partial int P { get; set; } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "P").WithLocation(4, 17) + ); + } + + [Fact] + public void DuplicateDeclaration_03() + { + // duplicate implementation + var source = """ + partial class C + { + partial int P { get => throw null!; set { } } + partial int P { get => throw null!; set { } } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,17): error CS9301: Partial property 'C.P' must have an definition part. + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "P").WithArguments("C.P").WithLocation(3, 17), + // (4,17): error CS0757: A partial method may not have multiple implementing declarations + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialMethodOnlyOneActual, "P").WithLocation(4, 17), + // (4,17): error CS0102: The type 'C' already contains a definition for 'P' + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "P").WithLocation(4, 17) + ); + } + + [Fact] + public void DuplicateDeclaration_04() + { + // duplicate implementation with single definition + var source = """ + partial class C + { + partial int P { get; set; } + partial int P { get => throw null!; set { } } + partial int P { get => throw null!; set { } } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,17): error CS0757: A partial method may not have multiple implementing declarations + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialMethodOnlyOneActual, "P").WithLocation(5, 17), + // (5,17): error CS0102: The type 'C' already contains a definition for 'P' + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "P").WithLocation(5, 17) + ); + } + + [Fact] + public void DuplicateDeclaration_05() + { + // duplicate implementation and duplicate definition + var source = """ + partial class C + { + partial int P { get; set; } + partial int P { get; set; } + partial int P { get => throw null!; set { } } + partial int P { get => throw null!; set { } } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,17): error CS0756: A partial method may not have multiple defining declarations + // partial int P { get; set; } + Diagnostic(ErrorCode.ERR_PartialMethodOnlyOneLatent, "P").WithLocation(4, 17), + // (4,17): error CS0102: The type 'C' already contains a definition for 'P' + // partial int P { get; set; } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "P").WithLocation(4, 17), + // (6,17): error CS0757: A partial method may not have multiple implementing declarations + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialMethodOnlyOneActual, "P").WithLocation(6, 17), + // (6,17): error CS0102: The type 'C' already contains a definition for 'P' + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "P").WithLocation(6, 17) + ); + } + + [Fact] + public void MissingAccessor_01() + { + // implementation missing setter + var source = """ + partial class C + { + partial int P { get; set; } + partial int P { get => throw null!; } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,17): error CS9304: Property accessor 'C.P.set' must be implemented because it is declared on the definition part + // partial int P { get => throw null!; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingAccessor, "P").WithArguments("C.P.set").WithLocation(4, 17) + ); + } + + [Fact] + public void MissingAccessor_02() + { + // implementation missing getter + var source = """ + partial class C + { + partial int P { get; set; } + partial int P { get => throw null!; } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,17): error CS9304: Property accessor 'C.P.set' must be implemented because it is declared on the definition part + // partial int P { get => throw null!; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingAccessor, "P").WithArguments("C.P.set").WithLocation(4, 17) + ); + } + + [Fact] + public void MissingAccessor_03() + { + // implementation missing init + var source = """ + partial class C + { + partial int P { get; init; } + partial int P { get => throw null!; } + } + """; + var comp = CreateCompilation([source, IsExternalInitTypeDefinition]); + comp.VerifyEmitDiagnostics( + // (4,17): error CS9304: Property accessor 'C.P.init' must be implemented because it is declared on the definition part + // partial int P { get => throw null!; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingAccessor, "P").WithArguments("C.P.init").WithLocation(4, 17) + ); + } + + [Fact] + public void UnexpectedAccessor_01() + { + // implementation unexpected setter + var source = """ + partial class C + { + partial int P { get; } + partial int P { get => throw null!; set { } } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,17): error CS9305: Property accessor 'C.P.set' does not implement any accessor declared on the definition part + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyUnexpectedAccessor, "P").WithArguments("C.P.set").WithLocation(4, 17) + ); + } + + [Fact] + public void UnexpectedAccessor_02() + { + // implementation unexpected getter + var source = """ + partial class C + { + partial int P { set; } + partial int P { get => throw null!; set { } } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,17): error CS9305: Property accessor 'C.P.get' does not implement any accessor declared on the definition part + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyUnexpectedAccessor, "P").WithArguments("C.P.get").WithLocation(4, 17) + ); + } + + [Fact] + public void UnexpectedAccessor_03() + { + // implementation unexpected init + var source = """ + partial class C + { + partial int P { get; } + partial int P { get => throw null!; init { } } + } + """; + var comp = CreateCompilation([source, IsExternalInitTypeDefinition]); + comp.VerifyEmitDiagnostics( + // (4,17): error CS9305: Property accessor 'C.P.init' does not implement any accessor declared on the definition part + // partial int P { get => throw null!; init { } } + Diagnostic(ErrorCode.ERR_PartialPropertyUnexpectedAccessor, "P").WithArguments("C.P.init").WithLocation(4, 17) + ); + } + + [Fact] + public void Semantics_01() + { + // happy definition + implementation case + var source = """ + using System; + + var c = new C { P = 1 }; + Console.Write(c.P); + + partial class C + { + public partial int P { get; set; } + } + + partial class C + { + private int _p; + public partial int P { get => _p; set => _p = value; } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "1"); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.P.get", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldfld "int C._p" + IL_0006: ret + } + """); + verifier.VerifyIL("C.P.set", """ + { + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld "int C._p" + IL_0007: ret + } + """); + + var comp = (CSharpCompilation)verifier.Compilation; + var cClass = comp.GetMember("C"); + var members = cClass.GetMembers().SelectAsArray(m => m.ToTestDisplayString()); + AssertEx.Equal([ + "System.Int32 C.P { get; set; }", + "System.Int32 C.P.get", + "void C.P.set", + "System.Int32 C._p", + "C..ctor()" + ], members); + } + + [Fact] + public void ModifierDifference_01() + { + // access modifier on declaration but not implementation + var source = """ + partial class C + { + public partial int P { get; set; } + } + + partial class C + { + partial int P { get => throw null!; set { } } + } + """; + + // PROTOTYPE(partial-properties): diagnostic message should be generalized for properties as well. + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,17): error CS8799: Both partial method declarations must have identical accessibility modifiers. + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialMethodAccessibilityDifference, "P").WithLocation(8, 17)); + } + + [Fact] + public void ModifierDifference_02() + { + // access modifier on declaration but not implementation + var source = """ + partial class C + { + public partial int P { get; private set; } + } + + partial class C + { + public partial int P { get => throw null!; set { } } + } + """; + + var comp = CreateCompilation(source); + + // PROTOTYPE(partial-properties): missing diagnostic + comp.VerifyEmitDiagnostics(); + } + + // PROTOTYPE(partial-properties): test more mismatching scenarios + } +}