diff --git a/src/Analyzers/CSharp/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs index 1576ed85a8762..3b49e503aec70 100644 --- a/src/Analyzers/CSharp/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs @@ -216,6 +216,7 @@ SyntaxKind.IndexerDeclaration or var symbol = model.GetSymbolInfo(invocation.Expression, cancellationToken).Symbol; if (symbol is not IMethodSymbol method || method.PartialImplementationPart is not null) { + // https://github.com/dotnet/roslyn/issues/73772: should we also bail out on a partial property? // We don't handle partial methods yet return null; } diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs index 86c3bbd550cf6..84dd51c1cd3b0 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs @@ -290,6 +290,7 @@ public override Binder VisitAccessorDeclaration(AccessorDeclarationSyntax parent if ((object)propertySymbol != null) { accessor = (parent.Kind() == SyntaxKind.GetAccessorDeclaration) ? propertySymbol.GetMethod : propertySymbol.SetMethod; + Debug.Assert(accessor is not null || parent.HasErrors); } break; } @@ -597,18 +598,25 @@ bool checkSymbol(Symbol sym, TextSpan memberSpan, SymbolKind kind, out Symbol re return false; } - if (sym.Kind == SymbolKind.Method) + if (kind is SymbolKind.Method or SymbolKind.Property) { if (InSpan(sym.GetFirstLocation(), this.syntaxTree, memberSpan)) { return true; } - // If this is a partial method, the method represents the defining part, - // not the implementation (method.Locations includes both parts). If the - // span is in fact in the implementation, return that method instead. - var implementation = ((MethodSymbol)sym).PartialImplementationPart; - if ((object)implementation != null) + // If this is a partial member, the member represents the defining part, + // not the implementation (member.Locations includes both parts). If the + // span is in fact in the implementation, return that member instead. + if (sym switch +#pragma warning disable format + { + MethodSymbol method => (Symbol)method.PartialImplementationPart, + SourcePropertySymbol property => property.PartialImplementationPart, + _ => throw ExceptionUtilities.UnexpectedValue(sym) + } +#pragma warning restore format + is { } implementation) { if (InSpan(implementation.GetFirstLocation(), this.syntaxTree, memberSpan)) { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 4c09a80debc1a..70558cc8ba160 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. @@ -2207,14 +2207,14 @@ If such a class is used as a base class and if the deriving class defines a dest Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit - - A partial method cannot have the 'abstract' modifier + + A partial member cannot have the 'abstract' modifier - - A partial method must be declared within a partial type + + A partial member must be declared within a partial type - - A partial method may not explicitly implement an interface method + + A partial member may not explicitly implement an interface member Both partial method declarations must be extension methods or neither may be an extension method @@ -2225,14 +2225,14 @@ If such a class is used as a base class and if the deriving class defines a dest A partial method may not have multiple implementing declarations - - Both partial method declarations must use a params parameter or neither may use a params parameter + + Both partial member declarations must use a params parameter or neither may use a params parameter No defining declaration found for implementing declaration of partial method '{0}' - - Both partial method declarations, '{0}' and '{1}', must use the same tuple element names. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}' @@ -2240,11 +2240,11 @@ If such a class is used as a base class and if the deriving class defines a dest Cannot create delegate from method '{0}' because it is a partial method without an implementing declaration - - Both partial method declarations must be static or neither may be static + + Both partial member declarations must be static or neither may be static - - Both partial method declarations must be unsafe or neither may be unsafe + + Both partial member declarations must be unsafe or neither may be unsafe Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees @@ -5737,8 +5737,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Field-like event '{0}' cannot be 'readonly'. - - Both partial method declarations must be readonly or neither may be readonly + + Both partial member declarations must be readonly or neither may be readonly '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor @@ -6521,17 +6521,17 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Partial method '{0}' must have accessibility modifiers because it has a 'virtual', 'override', 'sealed', 'new', or 'extern' modifier. - - Both partial method declarations must have identical accessibility modifiers. + + Both partial member declarations must have identical accessibility modifiers. - - Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + + Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. Both partial method declarations must have the same return type. - - Partial method declarations must have matching ref return values. + + Partial member declarations must have matching ref return values. Partial method declarations '{0}' and '{1}' have signature differences. @@ -7032,7 +7032,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ The 'scoped' modifier of parameter doesn't match target. - The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration. + The 'scoped' modifier of parameter '{0}' doesn't match partial definition. A fixed field must not be a ref field. @@ -7956,4 +7956,37 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ foreach statement cannot operate on enumerators of type '{0}' because it is a type parameter that allows ref struct and it is not known at compile time to implement IDisposable. - \ No newline at end of file + + Partial property '{0}' must have an implementation part. + + + Partial property '{0}' must have a definition part. + + + A partial property may not have multiple defining declarations, and cannot be an auto-property. + + + 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 + + + Property accessor '{0}' must be '{1}' to match the definition part + + + Both partial property declarations must have the same type. + + + Partial property declarations '{0}' and '{1}' have signature differences. + + + Partial property declarations have signature differences. + + + Both partial property declarations must be required or neither may be required + + diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 602946b93212e..10b7050bbd0b3 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -1799,10 +1799,14 @@ private Symbol GetDeclaredMember(NamespaceOrTypeSymbol container, TextSpan decla zeroWidthMatch = symbol; } - // Handle the case of the implementation of a partial method. - var partial = symbol.Kind == SymbolKind.Method - ? ((MethodSymbol)symbol).PartialImplementationPart - : null; + // Handle the case of the implementation of a partial member. + Symbol partial = symbol switch + { + MethodSymbol method => method.PartialImplementationPart, + SourcePropertySymbol property => property.PartialImplementationPart, + _ => null + }; + if ((object)partial != null) { var loc = partial.GetFirstLocation(); @@ -2033,9 +2037,7 @@ private ParameterSymbol GetMethodParameterSymbol( return null; } - return - GetParameterSymbol(method.Parameters, parameter, cancellationToken) ?? - ((object)method.PartialDefinitionPart == null ? null : GetParameterSymbol(method.PartialDefinitionPart.Parameters, parameter, cancellationToken)); + return GetParameterSymbol(method.Parameters, parameter, cancellationToken); } private ParameterSymbol GetIndexerParameterSymbol( @@ -2127,11 +2129,8 @@ private ParameterSymbol GetDelegateParameterSymbol( } /// - /// Given a type parameter declaration (field or method), get the corresponding symbol + /// Given a type parameter declaration (on a type or method), get the corresponding symbol /// - /// - /// The cancellation token. - /// public override ITypeParameterSymbol GetDeclaredSymbol(TypeParameterSyntax typeParameter, CancellationToken cancellationToken = default(CancellationToken)) { if (typeParameter == null) @@ -2165,10 +2164,7 @@ private ParameterSymbol GetDelegateParameterSymbol( return this.GetTypeParameterSymbol(typeSymbol.TypeParameters, typeParameter).GetPublicSymbol(); case MethodSymbol methodSymbol: - return (this.GetTypeParameterSymbol(methodSymbol.TypeParameters, typeParameter) ?? - ((object)methodSymbol.PartialDefinitionPart == null - ? null - : this.GetTypeParameterSymbol(methodSymbol.PartialDefinitionPart.TypeParameters, typeParameter))).GetPublicSymbol(); + return this.GetTypeParameterSymbol(methodSymbol.TypeParameters, typeParameter).GetPublicSymbol(); } } diff --git a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs index 56852488935b3..dcae3e0001579 100644 --- a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs @@ -258,7 +258,14 @@ public override void DefaultVisit(Symbol symbol) bool shouldSkipPartialDefinitionComments = false; if (symbol.IsPartialDefinition()) { - if (symbol is MethodSymbol { PartialImplementationPart: MethodSymbol implementationPart }) + Symbol? implementationPart = symbol switch + { + MethodSymbol method => method.PartialImplementationPart, + SourcePropertySymbol property => property.PartialImplementationPart, + _ => null + }; + + if (implementationPart is not null) { Visit(implementationPart); diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 4f8a380ef0913..b4ba341c47edb 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -529,20 +529,20 @@ internal enum ErrorCode ERR_InvalidAnonymousTypeMemberDeclarator = 746, ERR_InvalidInitializerElementInitializer = 747, ERR_InconsistentLambdaParameterUsage = 748, - ERR_PartialMethodInvalidModifier = 750, - ERR_PartialMethodOnlyInPartialClass = 751, + ERR_PartialMemberCannotBeAbstract = 750, + ERR_PartialMemberOnlyInPartialClass = 751, // ERR_PartialMethodCannotHaveOutParameters = 752, Removed as part of 'extended partial methods' feature // ERR_PartialMethodOnlyMethods = 753, Removed as it is subsumed by ERR_PartialMisplaced - ERR_PartialMethodNotExplicit = 754, + ERR_PartialMemberNotExplicit = 754, ERR_PartialMethodExtensionDifference = 755, ERR_PartialMethodOnlyOneLatent = 756, ERR_PartialMethodOnlyOneActual = 757, - ERR_PartialMethodParamsDifference = 758, + ERR_PartialMemberParamsDifference = 758, ERR_PartialMethodMustHaveLatent = 759, ERR_PartialMethodInconsistentConstraints = 761, ERR_PartialMethodToDelegate = 762, - ERR_PartialMethodStaticDifference = 763, - ERR_PartialMethodUnsafeDifference = 764, + ERR_PartialMemberStaticDifference = 763, + ERR_PartialMemberUnsafeDifference = 764, ERR_PartialMethodInExpressionTree = 765, // ERR_PartialMethodMustReturnVoid = 766, Removed as part of 'extended partial methods' feature ERR_ExplicitImplCollisionOnRefOut = 767, @@ -1392,7 +1392,7 @@ internal enum ErrorCode ERR_CantChangeTupleNamesOnOverride = 8139, ERR_DuplicateInterfaceWithTupleNamesInBaseList = 8140, ERR_ImplBadTupleNames = 8141, - ERR_PartialMethodInconsistentTupleNames = 8142, + ERR_PartialMemberInconsistentTupleNames = 8142, ERR_ExpressionTreeContainsTupleLiteral = 8143, ERR_ExpressionTreeContainsTupleConversion = 8144, #endregion tuple diagnostics introduced in C# 7 @@ -1709,7 +1709,7 @@ internal enum ErrorCode ERR_InvalidPropertyReadOnlyMods = 8660, ERR_DuplicatePropertyReadOnlyMods = 8661, ERR_FieldLikeEventCantBeReadOnly = 8662, - ERR_PartialMethodReadOnlyDifference = 8663, + ERR_PartialMemberReadOnlyDifference = 8663, ERR_ReadOnlyModMissingAccessor = 8664, ERR_OverrideRefConstraintNotSatisfied = 8665, ERR_OverrideValConstraintNotSatisfied = 8666, @@ -1809,8 +1809,8 @@ internal enum ErrorCode ERR_PartialMethodWithNonVoidReturnMustHaveAccessMods = 8796, ERR_PartialMethodWithOutParamMustHaveAccessMods = 8797, ERR_PartialMethodWithExtendedModMustHaveAccessMods = 8798, - ERR_PartialMethodAccessibilityDifference = 8799, - ERR_PartialMethodExtendedModDifference = 8800, + ERR_PartialMemberAccessibilityDifference = 8799, + ERR_PartialMemberExtendedModDifference = 8800, ERR_SimpleProgramLocalIsReferencedOutsideOfTopLevelStatement = 8801, ERR_SimpleProgramMultipleUnitsWithTopLevelStatements = 8802, @@ -1832,7 +1832,7 @@ internal enum ErrorCode ERR_ModuleInitializerMethodAndContainingTypesMustNotBeGeneric = 8816, ERR_PartialMethodReturnTypeDifference = 8817, - ERR_PartialMethodRefReturnDifference = 8818, + ERR_PartialMemberRefReturnDifference = 8818, WRN_NullabilityMismatchInReturnTypeOnPartial = 8819, ERR_StaticAnonymousFunctionCannotCaptureVariable = 8820, @@ -2325,6 +2325,17 @@ internal enum ErrorCode ERR_BadNonVirtualInterfaceMemberAccessOnAllowsRefLike = 9246, ERR_BadAllowByRefLikeEnumerator = 9247, + ERR_PartialPropertyMissingImplementation = 9248, + ERR_PartialPropertyMissingDefinition = 9249, + ERR_PartialPropertyDuplicateDefinition = 9250, + ERR_PartialPropertyDuplicateImplementation = 9251, + ERR_PartialPropertyMissingAccessor = 9252, + ERR_PartialPropertyUnexpectedAccessor = 9253, + ERR_PartialPropertyInitMismatch = 9254, + ERR_PartialPropertyTypeDifference = 9255, + WRN_PartialPropertySignatureDifference = 9256, + ERR_PartialPropertyRequiredDifference = 9257, + // 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 03a75fda22f79..e7c10d9f6e241 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -561,6 +561,7 @@ internal static int GetWarningLevel(ErrorCode code) case ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod: case ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer: case ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor: + case ErrorCode.WRN_PartialPropertySignatureDifference: return 1; default: @@ -1076,18 +1077,18 @@ or ErrorCode.ERR_ExpectedContextualKeywordBy or ErrorCode.ERR_InvalidAnonymousTypeMemberDeclarator or ErrorCode.ERR_InvalidInitializerElementInitializer or ErrorCode.ERR_InconsistentLambdaParameterUsage - or ErrorCode.ERR_PartialMethodInvalidModifier - or ErrorCode.ERR_PartialMethodOnlyInPartialClass - or ErrorCode.ERR_PartialMethodNotExplicit + or ErrorCode.ERR_PartialMemberCannotBeAbstract + or ErrorCode.ERR_PartialMemberOnlyInPartialClass + or ErrorCode.ERR_PartialMemberNotExplicit or ErrorCode.ERR_PartialMethodExtensionDifference or ErrorCode.ERR_PartialMethodOnlyOneLatent or ErrorCode.ERR_PartialMethodOnlyOneActual - or ErrorCode.ERR_PartialMethodParamsDifference + or ErrorCode.ERR_PartialMemberParamsDifference or ErrorCode.ERR_PartialMethodMustHaveLatent or ErrorCode.ERR_PartialMethodInconsistentConstraints or ErrorCode.ERR_PartialMethodToDelegate - or ErrorCode.ERR_PartialMethodStaticDifference - or ErrorCode.ERR_PartialMethodUnsafeDifference + or ErrorCode.ERR_PartialMemberStaticDifference + or ErrorCode.ERR_PartialMemberUnsafeDifference or ErrorCode.ERR_PartialMethodInExpressionTree or ErrorCode.ERR_ExplicitImplCollisionOnRefOut or ErrorCode.ERR_IndirectRecursiveConstructorCall @@ -1750,7 +1751,7 @@ or ErrorCode.ERR_ExplicitTupleElementNamesAttribute or ErrorCode.ERR_CantChangeTupleNamesOnOverride or ErrorCode.ERR_DuplicateInterfaceWithTupleNamesInBaseList or ErrorCode.ERR_ImplBadTupleNames - or ErrorCode.ERR_PartialMethodInconsistentTupleNames + or ErrorCode.ERR_PartialMemberInconsistentTupleNames or ErrorCode.ERR_ExpressionTreeContainsTupleLiteral or ErrorCode.ERR_ExpressionTreeContainsTupleConversion or ErrorCode.ERR_AutoPropertyCannotBeRefReturning @@ -1986,7 +1987,7 @@ or ErrorCode.ERR_AutoPropertyWithSetterCantBeReadOnly or ErrorCode.ERR_InvalidPropertyReadOnlyMods or ErrorCode.ERR_DuplicatePropertyReadOnlyMods or ErrorCode.ERR_FieldLikeEventCantBeReadOnly - or ErrorCode.ERR_PartialMethodReadOnlyDifference + or ErrorCode.ERR_PartialMemberReadOnlyDifference or ErrorCode.ERR_ReadOnlyModMissingAccessor or ErrorCode.ERR_OverrideRefConstraintNotSatisfied or ErrorCode.ERR_OverrideValConstraintNotSatisfied @@ -2056,8 +2057,8 @@ or ErrorCode.ERR_PartialMethodWithAccessibilityModsMustHaveImplementation or ErrorCode.ERR_PartialMethodWithNonVoidReturnMustHaveAccessMods or ErrorCode.ERR_PartialMethodWithOutParamMustHaveAccessMods or ErrorCode.ERR_PartialMethodWithExtendedModMustHaveAccessMods - or ErrorCode.ERR_PartialMethodAccessibilityDifference - or ErrorCode.ERR_PartialMethodExtendedModDifference + or ErrorCode.ERR_PartialMemberAccessibilityDifference + or ErrorCode.ERR_PartialMemberExtendedModDifference or ErrorCode.ERR_SimpleProgramLocalIsReferencedOutsideOfTopLevelStatement or ErrorCode.ERR_SimpleProgramMultipleUnitsWithTopLevelStatements or ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType @@ -2075,7 +2076,7 @@ or ErrorCode.ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType or ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid or ErrorCode.ERR_ModuleInitializerMethodAndContainingTypesMustNotBeGeneric or ErrorCode.ERR_PartialMethodReturnTypeDifference - or ErrorCode.ERR_PartialMethodRefReturnDifference + or ErrorCode.ERR_PartialMemberRefReturnDifference or ErrorCode.WRN_NullabilityMismatchInReturnTypeOnPartial or ErrorCode.ERR_StaticAnonymousFunctionCannotCaptureVariable or ErrorCode.ERR_StaticAnonymousFunctionCannotCaptureThis @@ -2450,6 +2451,16 @@ or ErrorCode.ERR_NotRefStructConstraintNotSatisfied or ErrorCode.ERR_RefStructDoesNotSupportDefaultInterfaceImplementationForMember or ErrorCode.ERR_BadNonVirtualInterfaceMemberAccessOnAllowsRefLike or ErrorCode.ERR_BadAllowByRefLikeEnumerator + or ErrorCode.ERR_PartialPropertyMissingImplementation + or ErrorCode.ERR_PartialPropertyMissingDefinition + or ErrorCode.ERR_PartialPropertyDuplicateDefinition + or ErrorCode.ERR_PartialPropertyDuplicateImplementation + or ErrorCode.ERR_PartialPropertyMissingAccessor + or ErrorCode.ERR_PartialPropertyUnexpectedAccessor + or ErrorCode.ERR_PartialPropertyInitMismatch + or ErrorCode.ERR_PartialPropertyTypeDifference + or ErrorCode.WRN_PartialPropertySignatureDifference + or ErrorCode.ERR_PartialPropertyRequiredDifference => 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/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 18fa9dbb51d6e..9c5288962c68b 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -286,6 +286,8 @@ internal enum MessageID IDS_FeatureRefUnsafeInIteratorAsync = MessageBase + 12843, IDS_FeatureRefStructInterfaces = MessageBase + 12844, + + IDS_FeaturePartialProperties = MessageBase + 12845, } // Message IDs may refer to strings that need to be localized. @@ -472,6 +474,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureParamsCollections: case MessageID.IDS_FeatureRefUnsafeInIteratorAsync: case MessageID.IDS_FeatureRefStructInterfaces: + case MessageID.IDS_FeaturePartialProperties: return LanguageVersion.Preview; // C# 12.0 features. diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.Variables.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.Variables.cs index 4eb4abe18b34c..dd3ed1448840d 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.Variables.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.Variables.cs @@ -396,6 +396,7 @@ internal Variables GetRootScope() internal Variables? GetVariablesForMethodScope(MethodSymbol method) { + // https://github.com/dotnet/roslyn/issues/73772: is this needed if we also delete the weird cascading in EnterParameters? method = method.PartialImplementationPart ?? method; var variables = this; while (true) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 81109ab290733..faf378f8cfdaa 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -2764,6 +2764,7 @@ private void EnterParameters() } // The partial definition part may include optional parameters whose default values we want to simulate assigning at the beginning of the method + // https://github.com/dotnet/roslyn/issues/73772: is this actually used/meaningful? methodSymbol = methodSymbol.PartialDefinitionPart ?? methodSymbol; var methodParameters = methodSymbol.Parameters; diff --git a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs index ac6ebf95db21f..6e81f32c0f79e 100644 --- a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs @@ -342,6 +342,7 @@ public static bool IsWarning(ErrorCode code) case ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer: case ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor: case ErrorCode.WRN_BadYieldInLock: + case ErrorCode.WRN_PartialPropertySignatureDifference: return true; default: return false; diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs index 3855f36d82f99..b55fd58855437 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,31 @@ 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; + Debug.Assert(member.IsDefinition); + 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; + Debug.Assert(member.IsDefinition); + 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; + Debug.Assert(member.IsDefinition); + 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/PropertySymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/PropertySymbolExtensions.cs index 2de2705522cc4..5f3f9ce715734 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PropertySymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PropertySymbolExtensions.cs @@ -10,6 +10,11 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal static class PropertySymbolExtensions { + public static bool IsParams(this PropertySymbol property) + { + return property.ParameterCount != 0 && property.Parameters[property.ParameterCount - 1].IsParams; + } + /// /// If the property has a GetMethod, return that. Otherwise check the overridden /// property, if any. Repeat for each overridden property. diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/MethodSymbol.cs index e66a1da90da85..c3c9b8c4b1d61 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/MethodSymbol.cs @@ -298,7 +298,7 @@ IMethodSymbol IMethodSymbol.PartialDefinitionPart } } - bool IMethodSymbol.IsPartialDefinition => _underlying.IsPartialDefinition(); + bool IMethodSymbol.IsPartialDefinition => _underlying.IsDefinition && _underlying.IsPartialDefinition(); INamedTypeSymbol IMethodSymbol.AssociatedAnonymousDelegate { diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PropertySymbol.cs index 90182156c74f3..3072bfb1875da 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PropertySymbol.cs @@ -109,6 +109,14 @@ ImmutableArray IPropertySymbol.RefCustomModifiers RefKind IPropertySymbol.RefKind => _underlying.RefKind; +#nullable enable + IPropertySymbol? IPropertySymbol.PartialDefinitionPart => (_underlying as SourcePropertySymbol)?.PartialDefinitionPart.GetPublicSymbol(); + + IPropertySymbol? IPropertySymbol.PartialImplementationPart => (_underlying as SourcePropertySymbol)?.PartialImplementationPart.GetPublicSymbol(); + + bool IPropertySymbol.IsPartialDefinition => (_underlying as SourcePropertySymbol)?.IsPartialDefinition ?? false; +#nullable disable + #region ISymbol Members protected override void Accept(SymbolVisitor visitor) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs index 67d199cf3ff56..bfec737594640 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs @@ -26,7 +26,7 @@ internal abstract class FieldSymbolWithAttributesAndModifiers : FieldSymbol, IAt /// /// Gets the syntax list of custom attributes applied on the symbol. /// - protected abstract SyntaxList AttributeDeclarationSyntaxList { get; } + protected abstract OneOrMany> GetAttributeDeclarations(); protected abstract IAttributeTargetSymbol AttributeOwner { get; } @@ -86,7 +86,7 @@ private CustomAttributesBag GetAttributesBag() return bag; } - if (LoadAndValidateAttributes(OneOrMany.Create(this.AttributeDeclarationSyntaxList), ref _lazyCustomAttributesBag)) + if (LoadAndValidateAttributes(this.GetAttributeDeclarations(), ref _lazyCustomAttributesBag)) { var completed = state.NotePartComplete(CompletionPart.Attributes); Debug.Assert(completed); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs index 64b9a72c48268..3403885c3079f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs @@ -56,7 +56,7 @@ internal static GlobalExpressionVariable Create( : new GlobalExpressionVariable(containingType, modifiers, typeSyntax, name, syntaxReference, locationSpan); } - protected override SyntaxList AttributeDeclarationSyntaxList => default(SyntaxList); + protected override OneOrMany> GetAttributeDeclarations() => OneOrMany>.Empty; protected override TypeSyntax TypeSyntax => (TypeSyntax)_typeSyntaxOpt?.GetSyntax(); protected override SyntaxTokenList ModifiersTokenList => default(SyntaxTokenList); public override bool HasInitializer => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index f3a9a363d034d..fad54f4249416 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -454,30 +454,57 @@ AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations } } +#nullable enable /// /// Symbol to copy bound attributes from, or null if the attributes are not shared among multiple source parameter symbols. /// /// - /// Used for parameters of partial implementation. We bind the attributes only on the definition - /// part and copy them over to the implementation. + /// This is inconsistent with analogous 'BoundAttributesSource' on other symbols. + /// Usually the definition part is the source, but for parameters the implementation part is the source. + /// This affects the location of diagnostics among other things. /// - private SourceParameterSymbol BoundAttributesSource + private SourceParameterSymbol? BoundAttributesSource + => PartialImplementationPart; + + protected SourceParameterSymbol? PartialImplementationPart { get { - var sourceMethod = this.ContainingSymbol as SourceOrdinaryMethodSymbol; - if ((object)sourceMethod == null) + ImmutableArray implParameters = this.ContainingSymbol switch + { + SourceMemberMethodSymbol { PartialImplementationPart.Parameters: { } parameters } => parameters, + SourcePropertySymbol { PartialImplementationPart.Parameters: { } parameters } => parameters, + _ => default + }; + + if (implParameters.IsDefault) { return null; } - var impl = sourceMethod.SourcePartialImplementation; - if ((object)impl == null) + Debug.Assert(!this.ContainingSymbol.IsPartialImplementation()); + return (SourceParameterSymbol)implParameters[this.Ordinal]; + } + } + + protected SourceParameterSymbol? PartialDefinitionPart + { + get + { + ImmutableArray defParameters = this.ContainingSymbol switch + { + SourceMemberMethodSymbol { PartialDefinitionPart.Parameters: { } parameters } => parameters, + SourcePropertySymbol { PartialDefinitionPart.Parameters: { } parameters } => parameters, + _ => default + }; + + if (defParameters.IsDefault) { return null; } - return (SourceParameterSymbol)impl.Parameters[this.Ordinal]; + Debug.Assert(!this.ContainingSymbol.IsPartialDefinition()); + return (SourceParameterSymbol)defParameters[this.Ordinal]; } } @@ -495,44 +522,22 @@ internal sealed override SyntaxList AttributeDeclarationLis /// internal virtual OneOrMany> GetAttributeDeclarations() { - // C# spec: - // The attributes on the parameters of the resulting method declaration - // are the combined attributes of the corresponding parameters of the defining - // and the implementing partial method declaration in unspecified order. - // Duplicates are not removed. - - SyntaxList attributes = AttributeDeclarationList; - - var sourceMethod = this.ContainingSymbol as SourceOrdinaryMethodSymbol; - if ((object)sourceMethod == null) - { - return OneOrMany.Create(attributes); - } + // Attributes on parameters in partial members are owned by the parameter in the implementation part. + // If this symbol has a non-null PartialImplementationPart, we should have accessed this method through that implementation symbol. + Debug.Assert(PartialImplementationPart is null); - SyntaxList otherAttributes; - - // if this is a definition get the implementation and vice versa - SourceOrdinaryMethodSymbol otherPart = sourceMethod.OtherPartOfPartial; - if ((object)otherPart != null) + if (PartialDefinitionPart is { } definitionPart) { - otherAttributes = ((SourceParameterSymbol)otherPart.Parameters[this.Ordinal]).AttributeDeclarationList; + return OneOrMany.Create( + AttributeDeclarationList, + definitionPart.AttributeDeclarationList); } else { - otherAttributes = default(SyntaxList); - } - - if (attributes.Equals(default(SyntaxList))) - { - return OneOrMany.Create(otherAttributes); - } - else if (otherAttributes.Equals(default(SyntaxList))) - { - return OneOrMany.Create(attributes); + return OneOrMany.Create(AttributeDeclarationList); } - - return OneOrMany.Create(ImmutableArray.Create(attributes, otherAttributes)); } +#nullable disable /// /// Returns data decoded from well-known attributes applied to the symbol or null if there are no applied attributes. @@ -1030,33 +1035,26 @@ private bool IsValidCallerInfoContext(AttributeSyntax node) => !ContainingSymbol && !ContainingSymbol.IsOperator() && !IsOnPartialImplementation(node); +#nullable enable /// /// Is the attribute syntax appearing on a parameter of a partial method implementation part? /// Since attributes are merged between the parts of a partial, we need to look at the syntax where the /// attribute appeared in the source to see if it corresponds to a partial method implementation part. /// - /// - /// private bool IsOnPartialImplementation(AttributeSyntax node) { - var method = ContainingSymbol as MethodSymbol; - if ((object)method == null) return false; - var impl = method.IsPartialImplementation() ? method : method.PartialImplementationPart; - if ((object)impl == null) return false; - var paramList = - node // AttributeSyntax - .Parent // AttributeListSyntax - .Parent // ParameterSyntax - .Parent as ParameterListSyntax; // ParameterListSyntax - if (paramList == null) return false; - var methDecl = paramList.Parent as MethodDeclarationSyntax; - if (methDecl == null) return false; - foreach (var r in impl.DeclaringSyntaxReferences) - { - if (r.GetSyntax() == methDecl) return true; - } - return false; + // If we are asking this, the candidate attribute had better be contained in *some* attribute associated with this parameter syntactically + Debug.Assert(this.GetAttributeDeclarations().Any(attrLists => attrLists.Any(attrList => attrList.Contains(node)))); + + var implParameter = this.ContainingSymbol.IsPartialImplementation() ? this : PartialImplementationPart; + if (implParameter?.AttributeDeclarationList is not { } implParameterAttributeList) + { + return false; + } + + return implParameterAttributeList.Any(attrList => attrList.Attributes.Contains(node)); } +#nullable disable private void ValidateCallerLineNumberAttribute(AttributeSyntax node, BindingDiagnosticBag diagnostics) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEnumConstantSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEnumConstantSymbol.cs index 4216680ce2790..a2f5dd94a71f9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEnumConstantSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEnumConstantSymbol.cs @@ -86,17 +86,14 @@ protected sealed override DeclarationModifiers Modifiers } } - protected override SyntaxList AttributeDeclarationSyntaxList + protected override OneOrMany> GetAttributeDeclarations() { - get + if (this.containingType.AnyMemberHasAttributes) { - if (this.containingType.AnyMemberHasAttributes) - { - return this.SyntaxNode.AttributeLists; - } - - return default(SyntaxList); + return OneOrMany.Create(this.SyntaxNode.AttributeLists); } + + return OneOrMany>.Empty; } #nullable enable diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 97628686b76fa..40e7f04cc0ebf 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -3574,78 +3574,169 @@ 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) + switch (symbol, prev) + { + case (SourceOrdinaryMethodSymbol currentMethod, SourceOrdinaryMethodSymbol prevMethod): + mergePartialMethods(ref membersByName, name, currentMethod, prevMethod, diagnostics); + break; + + case (SourcePropertySymbol currentProperty, SourcePropertySymbol prevProperty): + mergePartialProperties(ref membersByName, name, currentProperty, prevProperty, diagnostics); + break; + + case (SourcePropertyAccessorSymbol, SourcePropertyAccessorSymbol): + break; // accessor symbols and their diagnostics are handled by processing the associated property + + 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); + break; + } + } + + 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) + { + diagnostics.Add(ErrorCode.ERR_PartialMethodMustHaveLatent, method.GetFirstLocation(), method); + } + else if (method is { IsPartialDefinition: true, OtherPartOfPartial: null, HasExplicitAccessModifier: true }) { - // 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_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; // diagnostics for missing partial accessors are handled in 'mergePartialProperties'. + + default: + throw ExceptionUtilities.UnexpectedValue(symbol); } - else + } + } + + memberNames.Free(); + + void mergePartialMethods(ref Dictionary, ImmutableArray> membersByName, ReadOnlyMemory name, SourceOrdinaryMethodSymbol currentMethod, SourceOrdinaryMethodSymbol prevMethod, BindingDiagnosticBag diagnostics) + { + 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, BindingDiagnosticBag diagnostics) + { + if (currentProperty.IsPartialImplementation && + (prevProperty.IsPartialImplementation || (prevProperty.OtherPartOfPartial is SourcePropertySymbol otherImplementation && (object)otherImplementation != currentProperty))) + { + diagnostics.Add(ErrorCode.ERR_PartialPropertyDuplicateImplementation, currentProperty.GetFirstLocation()); + } + else if (currentProperty.IsPartialDefinition && + (prevProperty.IsPartialDefinition || (prevProperty.OtherPartOfPartial is SourcePropertySymbol otherDefinition && (object)otherDefinition != currentProperty))) + { + diagnostics.Add(ErrorCode.ERR_PartialPropertyDuplicateDefinition, 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) + { + var accessorName = (currentSet ?? prevSet)!.Name.AsMemory(); + mergeAccessors(ref membersByName, accessorName, currentSet, prevSet); + } + + if ((object)membersByName == _lazyEarlyAttributeDecodingMembersDictionary) { - diagnostics.Add(ErrorCode.ERR_PartialMethodWithAccessibilityModsMustHaveImplementation, method.GetFirstLocation(), 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], prevProperty, currentProperty); } - } - memberNames.Free(); + void mergeAccessors(ref Dictionary, ImmutableArray> membersByName, ReadOnlyMemory name, SourcePropertyAccessorSymbol? currentAccessor, SourcePropertyAccessorSymbol? prevAccessor) + { + Debug.Assert(currentAccessor != null || prevAccessor != null); + if (currentAccessor != null && prevAccessor != null) + { + var implementationAccessor = currentProperty.IsPartialDefinition ? prevAccessor : currentAccessor; + membersByName[name] = Remove(membersByName[name], implementationAccessor); + } + else + { + var (foundAccessor, containingProperty, otherProperty) = prevAccessor != null ? (prevAccessor, prevProperty, currentProperty) : (currentAccessor!, currentProperty, prevProperty); + // 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. + var (errorCode, propertyToBlame) = foundAccessor.IsPartialDefinition + ? (ErrorCode.ERR_PartialPropertyMissingAccessor, otherProperty) + : (ErrorCode.ERR_PartialPropertyUnexpectedAccessor, containingProperty); + diagnostics.Add(errorCode, propertyToBlame.GetFirstLocation(), foundAccessor); + } + } + } } - /// - /// Fix up a partial method by combining its defining and implementing declarations, updating the array of symbols (by name), - /// and returning the combined symbol. - /// - /// The symbols array containing both the latent and implementing declaration - /// One of the two declarations - /// The other declaration - /// An updated symbols array containing only one method symbol representing the two parts + /// Links together the definition and implementation parts of a partial method. Returns a member list which has the implementation part removed. private static ImmutableArray FixPartialMember(ImmutableArray symbols, SourceOrdinaryMethodSymbol part1, SourceOrdinaryMethodSymbol part2) { SourceOrdinaryMethodSymbol definition; @@ -3667,6 +3758,28 @@ private static ImmutableArray FixPartialMember(ImmutableArray sy return Remove(symbols, implementation); } + /// Links together the definition and implementation parts of a partial property. Returns a member list which has the implementation part removed. + 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 property 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/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs index 8f635798af17d..c5366989a30bc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs @@ -408,17 +408,14 @@ private static BaseFieldDeclarationSyntax GetFieldDeclaration(CSharpSyntaxNode d return (BaseFieldDeclarationSyntax)declarator.Parent.Parent; } - protected override SyntaxList AttributeDeclarationSyntaxList + protected override OneOrMany> GetAttributeDeclarations() { - get + if (this.containingType.AnyMemberHasAttributes) { - if (this.containingType.AnyMemberHasAttributes) - { - return GetFieldDeclaration(this.SyntaxNode).AttributeLists; - } - - return default(SyntaxList); + return OneOrMany.Create(GetFieldDeclaration(this.SyntaxNode).AttributeLists); } + + return OneOrMany>.Empty; } public sealed override RefKind RefKind => GetTypeAndRefKind(ConsList.Empty).RefKind; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index e5765d5060ace..70a019b8eb217 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -1457,7 +1457,7 @@ internal override void PostDecodeWellKnownAttributes(ImmutableArray - /// Returns the implementation part of a partial method definition, - /// or null if this is not a partial method or it is the definition part. - /// internal SourceOrdinaryMethodSymbol SourcePartialDefinition { get @@ -347,10 +343,6 @@ internal SourceOrdinaryMethodSymbol SourcePartialDefinition } } - /// - /// Returns the definition part of a partial method implementation, - /// or null if this is not a partial method or it is the implementation part. - /// internal SourceOrdinaryMethodSymbol SourcePartialImplementation { get @@ -401,6 +393,10 @@ protected sealed override SourceMemberMethodSymbol BoundAttributesSource internal sealed override OneOrMany> GetAttributeDeclarations() { + // Attributes on partial methods are owned by the definition part. + // If this symbol has a non-null PartialDefinitionPart, we should have accessed this method through that definition symbol instead + Debug.Assert(PartialDefinitionPart is null); + if ((object)this.SourcePartialImplementation != null) { return OneOrMany.Create(ImmutableArray.Create(AttributeDeclarationSyntaxList, this.SourcePartialImplementation.AttributeDeclarationSyntaxList)); @@ -471,6 +467,10 @@ protected sealed override void PartialMethodChecks(BindingDiagnosticBag diagnost /// parts of a partial method. Diagnostics are reported on the /// implementing part, matching Dev10 behavior. /// + /// + /// This method is analogous to . + /// Whenever new checks are added to this method, the other method should also have those checks added, if applicable. + /// private static void PartialMethodChecks(SourceOrdinaryMethodSymbol definition, SourceOrdinaryMethodSymbol implementation, BindingDiagnosticBag diagnostics) { Debug.Assert(!ReferenceEquals(definition, implementation)); @@ -484,22 +484,22 @@ private static void PartialMethodChecks(SourceOrdinaryMethodSymbol definition, S else if (MemberSignatureComparer.ConsideringTupleNamesCreatesDifference(definition, implementation)) { hasTypeDifferences = true; - diagnostics.Add(ErrorCode.ERR_PartialMethodInconsistentTupleNames, implementation.GetFirstLocation(), definition, implementation); + diagnostics.Add(ErrorCode.ERR_PartialMemberInconsistentTupleNames, implementation.GetFirstLocation(), definition, implementation); } if (definition.RefKind != implementation.RefKind) { - diagnostics.Add(ErrorCode.ERR_PartialMethodRefReturnDifference, implementation.GetFirstLocation()); + diagnostics.Add(ErrorCode.ERR_PartialMemberRefReturnDifference, implementation.GetFirstLocation()); } if (definition.IsStatic != implementation.IsStatic) { - diagnostics.Add(ErrorCode.ERR_PartialMethodStaticDifference, implementation.GetFirstLocation()); + diagnostics.Add(ErrorCode.ERR_PartialMemberStaticDifference, implementation.GetFirstLocation()); } if (definition.IsDeclaredReadOnly != implementation.IsDeclaredReadOnly) { - diagnostics.Add(ErrorCode.ERR_PartialMethodReadOnlyDifference, implementation.GetFirstLocation()); + diagnostics.Add(ErrorCode.ERR_PartialMemberReadOnlyDifference, implementation.GetFirstLocation()); } if (definition.IsExtensionMethod != implementation.IsExtensionMethod) @@ -509,18 +509,18 @@ private static void PartialMethodChecks(SourceOrdinaryMethodSymbol definition, S if (definition.IsUnsafe != implementation.IsUnsafe && definition.CompilationAllowsUnsafe()) // Don't cascade. { - diagnostics.Add(ErrorCode.ERR_PartialMethodUnsafeDifference, implementation.GetFirstLocation()); + diagnostics.Add(ErrorCode.ERR_PartialMemberUnsafeDifference, implementation.GetFirstLocation()); } if (definition.IsParams() != implementation.IsParams()) { - diagnostics.Add(ErrorCode.ERR_PartialMethodParamsDifference, implementation.GetFirstLocation()); + diagnostics.Add(ErrorCode.ERR_PartialMemberParamsDifference, implementation.GetFirstLocation()); } if (definition.HasExplicitAccessModifier != implementation.HasExplicitAccessModifier || definition.DeclaredAccessibility != implementation.DeclaredAccessibility) { - diagnostics.Add(ErrorCode.ERR_PartialMethodAccessibilityDifference, implementation.GetFirstLocation()); + diagnostics.Add(ErrorCode.ERR_PartialMemberAccessibilityDifference, implementation.GetFirstLocation()); } if (definition.IsVirtual != implementation.IsVirtual @@ -528,7 +528,7 @@ private static void PartialMethodChecks(SourceOrdinaryMethodSymbol definition, S || definition.IsSealed != implementation.IsSealed || definition.IsNew != implementation.IsNew) { - diagnostics.Add(ErrorCode.ERR_PartialMethodExtendedModDifference, implementation.GetFirstLocation()); + diagnostics.Add(ErrorCode.ERR_PartialMemberExtendedModDifference, implementation.GetFirstLocation()); } PartialMethodConstraintsChecks(definition, implementation, diagnostics); @@ -806,7 +806,7 @@ private void CheckModifiers(bool isExplicitInterfaceImplementation, Location loc if (IsPartial && IsAbstract) { - diagnostics.Add(ErrorCode.ERR_PartialMethodInvalidModifier, location); + diagnostics.Add(ErrorCode.ERR_PartialMemberCannotBeAbstract, location); } else if (IsPartial && !HasExplicitAccessModifier && !ReturnsVoid) { @@ -1000,11 +1000,14 @@ internal static void InitializePartialMethodParts(SourceOrdinaryMethodSymbolComp 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); + Debug.Assert(definition._otherPartOfPartial is not { } alreadySetImplPart || alreadySetImplPart == implementation); + Debug.Assert(implementation._otherPartOfPartial is not { } alreadySetDefPart || alreadySetDefPart == definition); definition._otherPartOfPartial = implementation; implementation._otherPartOfPartial = definition; + + Debug.Assert(definition._otherPartOfPartial == implementation); + Debug.Assert(implementation._otherPartOfPartial == definition); } protected sealed override MethodSymbol FindExplicitlyImplementedMethod(BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs index 15f02aa85bb9e..15c514be372ef 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs @@ -79,7 +79,7 @@ public static SourceParameterSymbol Create( !isExtensionMethodThis && (syntax.Default == null) && (syntax.AttributeLists.Count == 0) && - !owner.IsPartialMethod() && + !owner.IsPartialMember() && scope == ScopedKind.None) { return new SourceSimpleParameterSymbol(owner, parameterType, ordinal, refKind, name, location); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs index d61cec7b4c355..c625366d197a9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs @@ -145,7 +145,18 @@ private SourcePropertyAccessorSymbol( bool isNullableAnalysisEnabled, BindingDiagnosticBag diagnostics) : base(containingType, syntax.GetReference(), location, isIterator: false, - MakeModifiersAndFlags(property, propertyModifiers, isNullableAnalysisEnabled)) + MakeModifiersAndFlags( + containingType, + property, + propertyModifiers, + location, + hasBlockBody: false, + hasExpressionBody: true, + modifiers: [], + methodKind: MethodKind.PropertyGet, + isNullableAnalysisEnabled, + diagnostics, + out var modifierErrors)) { _property = property; _isAutoPropertyAccessor = false; @@ -158,21 +169,6 @@ private SourcePropertyAccessorSymbol( this.CheckModifiers(location, hasBody: true, isAutoPropertyOrExpressionBodied: true, diagnostics: diagnostics); } - private static (DeclarationModifiers, Flags) MakeModifiersAndFlags(SourcePropertySymbol property, DeclarationModifiers propertyModifiers, bool isNullableAnalysisEnabled) - { - // The modifiers for the accessor are the same as the modifiers for the property, - // minus the indexer and readonly bit - var declarationModifiers = GetAccessorModifiers(propertyModifiers); - - // ReturnsVoid property is overridden in this class so - // returnsVoid argument to MakeFlags is ignored. - Flags flags = MakeFlags(MethodKind.PropertyGet, property.RefKind, declarationModifiers, returnsVoid: false, returnsVoidIsSet: false, - isExpressionBodied: true, isExtensionMethod: false, isNullableAnalysisEnabled: isNullableAnalysisEnabled, - isVarArg: false, isExplicitInterfaceImplementation: property.IsExplicitInterfaceImplementation, hasThisInitializer: false); - - return (declarationModifiers, flags); - } - #nullable enable protected SourcePropertyAccessorSymbol( NamedTypeSymbol containingType, @@ -538,7 +534,7 @@ private void CheckModifiers(Location location, bool hasBody, bool isAutoProperty // '{0}' is a new virtual member in sealed type '{1}' diagnostics.Add(ErrorCode.ERR_NewVirtualInSealed, location, this, ContainingType); } - else if (!hasBody && !IsExtern && !IsAbstract && !isAutoPropertyOrExpressionBodied) + else if (!hasBody && !IsExtern && !IsAbstract && !isAutoPropertyOrExpressionBodied && !IsPartial) { diagnostics.Add(ErrorCode.ERR_ConcreteMissingBody, location, this); } @@ -635,16 +631,38 @@ public sealed override ImmutableArray ExplicitInterfaceImplementat internal sealed override OneOrMany> GetAttributeDeclarations() { - var syntax = this.GetSyntax(); - switch (syntax.Kind()) + if (PartialImplementationPart is { } implementation) { - case SyntaxKind.GetAccessorDeclaration: - case SyntaxKind.SetAccessorDeclaration: - case SyntaxKind.InitAccessorDeclaration: - return OneOrMany.Create(((AccessorDeclarationSyntax)syntax).AttributeLists); + return OneOrMany.Create(AttributeDeclarationList, ((SourcePropertyAccessorSymbol)implementation).AttributeDeclarationList); } - return base.GetAttributeDeclarations(); + // If we are asking this question on a partial implementation symbol, + // it must be from a context which prefers to order implementation attributes before definition attributes. + // For example, the 'value' parameter of a set accessor. + if (PartialDefinitionPart is { } definition) + { + Debug.Assert(MethodKind == MethodKind.PropertySet); + return OneOrMany.Create(AttributeDeclarationList, ((SourcePropertyAccessorSymbol)definition).AttributeDeclarationList); + } + + return OneOrMany.Create(AttributeDeclarationList); + } + + private SyntaxList AttributeDeclarationList + { + get + { + var syntax = this.GetSyntax(); + switch (syntax.Kind()) + { + case SyntaxKind.GetAccessorDeclaration: + case SyntaxKind.SetAccessorDeclaration: + case SyntaxKind.InitAccessorDeclaration: + return ((AccessorDeclarationSyntax)syntax).AttributeLists; + } + + return default; + } } #nullable enable @@ -807,5 +825,42 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r } } } + +#nullable enable + protected sealed override SourceMemberMethodSymbol? BoundAttributesSource => (SourceMemberMethodSymbol?)PartialDefinitionPart; + + public sealed override MethodSymbol? PartialImplementationPart => _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 }; + + public sealed override bool IsExtern => PartialImplementationPart is { } implementation ? implementation.IsExtern : base.IsExtern; + + internal void PartialAccessorChecks(SourcePropertyAccessorSymbol implementationAccessor, BindingDiagnosticBag diagnostics) + { + Debug.Assert(IsPartialDefinition); + + if (LocalAccessibility != implementationAccessor.LocalAccessibility) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberAccessibilityDifference, implementationAccessor.GetFirstLocation()); + } + + if (LocalDeclaredReadOnly != implementationAccessor.LocalDeclaredReadOnly) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberReadOnlyDifference, implementationAccessor.GetFirstLocation()); + } + + if (_usesInit != implementationAccessor._usesInit) + { + var accessorName = _usesInit ? "init" : "set"; + diagnostics.Add(ErrorCode.ERR_PartialPropertyInitMismatch, implementationAccessor.GetFirstLocation(), implementationAccessor, accessorName); + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index eb6c3ff553bfa..2deeefb254519 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -2,10 +2,11 @@ // 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.Collections.Generic; +using System; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; @@ -13,6 +14,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 +40,6 @@ private static SourcePropertySymbol Create( GetAccessorDeclarations( syntax, diagnostics, - out bool isAutoProperty, out bool hasAccessorList, out bool accessorsHaveImplementation, out bool isInitOnly, @@ -47,7 +49,7 @@ private static SourcePropertySymbol Create( var explicitInterfaceSpecifier = GetExplicitInterfaceSpecifier(syntax); SyntaxTokenList modifiersTokenList = GetModifierTokensSyntax(syntax); bool isExplicitInterfaceImplementation = explicitInterfaceSpecifier is object; - var modifiers = MakeModifiers( + var (modifiers, hasExplicitAccessMod) = MakeModifiers( containingType, modifiersTokenList, isExplicitInterfaceImplementation, @@ -57,6 +59,7 @@ private static SourcePropertySymbol Create( diagnostics, out _); + bool isAutoProperty = (modifiers & DeclarationModifiers.Partial) == 0 && !accessorsHaveImplementation; bool isExpressionBodied = !hasAccessorList && GetArrowExpression(syntax) != null; binder = binder.SetOrClearUnsafeRegionIfNecessary(modifiersTokenList); @@ -73,9 +76,11 @@ private static SourcePropertySymbol Create( explicitInterfaceType, aliasQualifierOpt, modifiers, + hasExplicitAccessMod: hasExplicitAccessMod, isAutoProperty: isAutoProperty, isExpressionBodied: isExpressionBodied, isInitOnly: isInitOnly, + accessorsHaveImplementation: accessorsHaveImplementation, memberName, location, diagnostics); @@ -90,9 +95,11 @@ private SourcePropertySymbol( TypeSymbol? explicitInterfaceType, string? aliasQualifierOpt, DeclarationModifiers modifiers, + bool hasExplicitAccessMod, bool isAutoProperty, bool isExpressionBodied, bool isInitOnly, + bool accessorsHaveImplementation, string memberName, Location location, BindingDiagnosticBag diagnostics) @@ -106,9 +113,11 @@ private SourcePropertySymbol( aliasQualifierOpt, modifiers, hasInitializer: HasInitializer(syntax), + hasExplicitAccessMod: hasExplicitAccessMod, isAutoProperty: isAutoProperty, isExpressionBodied: isExpressionBodied, isInitOnly: isInitOnly, + accessorsHaveImplementation: accessorsHaveImplementation, syntax.Type.SkipScoped(out _).GetRefKindInLocalOrReturn(diagnostics), memberName, syntax.AttributeLists, @@ -136,6 +145,12 @@ private SourcePropertySymbol( MessageID.IDS_FeatureAutoPropertyInitializer.CheckFeatureAvailability(diagnostics, initializer.EqualsToken); } + internal override void ForceComplete(SourceLocation? locationOpt, Predicate? filter, CancellationToken cancellationToken) + { + PartialImplementationPart?.ForceComplete(locationOpt, filter, cancellationToken); + base.ForceComplete(locationOpt, filter, cancellationToken); + } + private TypeSyntax GetTypeSyntax(SyntaxNode syntax) => ((BasePropertyDeclarationSyntax)syntax).Type; protected override Location TypeLocation @@ -155,15 +170,34 @@ private static SyntaxTokenList GetModifierTokensSyntax(SyntaxNode syntax) private static bool HasInitializer(SyntaxNode syntax) => syntax is PropertyDeclarationSyntax { Initializer: { } }; - public override SyntaxList AttributeDeclarationSyntaxList - => ((BasePropertyDeclarationSyntax)CSharpSyntaxNode).AttributeLists; + public override OneOrMany> GetAttributeDeclarations() + { + // Attributes on partial properties are owned by the definition part. + // If this symbol has a non-null PartialDefinitionPart, we should have accessed this method through that definition symbol instead + Debug.Assert(PartialDefinitionPart is null + // We might still get here when asking for the attributes on a backing field. + // This is an error scenario (requires using a property initializer and field-targeted attributes on partial property implementation part). + || this.BackingField is not null); + + if (PartialImplementationPart is { } implementationPart) + { + return OneOrMany.Create( + ((BasePropertyDeclarationSyntax)CSharpSyntaxNode).AttributeLists, + ((BasePropertyDeclarationSyntax)implementationPart.CSharpSyntaxNode).AttributeLists); + } + else + { + return OneOrMany.Create(((BasePropertyDeclarationSyntax)CSharpSyntaxNode).AttributeLists); + } + } + + protected override SourcePropertySymbolBase? BoundAttributesSource => PartialDefinitionPart; public override IAttributeTargetSymbol AttributesOwner => this; private static void GetAccessorDeclarations( CSharpSyntaxNode syntaxNode, BindingDiagnosticBag diagnostics, - out bool isAutoProperty, out bool hasAccessorList, out bool accessorsHaveImplementation, out bool isInitOnly, @@ -171,7 +205,6 @@ private static void GetAccessorDeclarations( out CSharpSyntaxNode? setSyntax) { var syntax = (BasePropertyDeclarationSyntax)syntaxNode; - isAutoProperty = true; hasAccessorList = syntax.AccessorList != null; getSyntax = null; setSyntax = null; @@ -223,15 +256,14 @@ private static void GetAccessorDeclarations( if (accessor.Body != null || accessor.ExpressionBody != null) { - isAutoProperty = false; accessorsHaveImplementation = true; } } } else { - isAutoProperty = false; accessorsHaveImplementation = GetArrowExpression(syntax) is object; + Debug.Assert(accessorsHaveImplementation); // it's not clear how this even parsed as a property if it has no accessor list and no arrow expression. } } @@ -264,7 +296,7 @@ private static AccessorDeclarationSyntax GetSetAccessorDeclaration(BasePropertyD throw ExceptionUtilities.Unreachable(); } - private static DeclarationModifiers MakeModifiers( + private static (DeclarationModifiers modifiers, bool hasExplicitAccessMod) MakeModifiers( NamedTypeSymbol containingType, SyntaxTokenList modifiers, bool isExplicitInterfaceImplementation, @@ -278,7 +310,7 @@ private static DeclarationModifiers MakeModifiers( var defaultAccess = isInterface && !isExplicitInterfaceImplementation ? DeclarationModifiers.Public : DeclarationModifiers.Private; // Check that the set of modifiers is allowed - var allowedModifiers = DeclarationModifiers.Unsafe; + var allowedModifiers = DeclarationModifiers.Partial | DeclarationModifiers.Unsafe; var defaultInterfaceImplementationModifiers = DeclarationModifiers.None; if (!isExplicitInterfaceImplementation) @@ -339,8 +371,32 @@ private static DeclarationModifiers MakeModifiers( allowedModifiers |= DeclarationModifiers.Extern; + // In order to detect whether explicit accessibility mods were provided, we pass the default value + // for 'defaultAccess' and manually add in the 'defaultAccess' flags after the call. + bool hasExplicitAccessMod; var mods = ModifierUtils.MakeAndCheckNonTypeMemberModifiers(isOrdinaryMethod: false, isForInterfaceMember: isInterface, - modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors); + modifiers, defaultAccess: DeclarationModifiers.None, allowedModifiers, location, diagnostics, out modifierErrors); + if ((mods & DeclarationModifiers.AccessibilityMask) == 0) + { + hasExplicitAccessMod = false; + mods |= defaultAccess; + } + else + { + hasExplicitAccessMod = true; + } + + if ((mods & DeclarationModifiers.Partial) != 0) + { + Debug.Assert(location.SourceTree is not null); + + LanguageVersion availableVersion = ((CSharpParseOptions)location.SourceTree.Options).LanguageVersion; + LanguageVersion requiredVersion = MessageID.IDS_FeaturePartialProperties.RequiredVersion(); + if (availableVersion < requiredVersion) + { + ModifierUtils.ReportUnsupportedModifiersForLanguageVersion(mods, DeclarationModifiers.Partial, location, diagnostics, availableVersion, requiredVersion); + } + } ModifierUtils.CheckFeatureAvailabilityForStaticAbstractMembersInInterfacesIfNeeded(mods, isExplicitInterfaceImplementation, location, diagnostics); @@ -369,7 +425,7 @@ private static DeclarationModifiers MakeModifiers( mods &= ~DeclarationModifiers.Required; } - return mods; + return (mods, hasExplicitAccessMod); } protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) @@ -552,9 +608,142 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, } diagnostics.Add(Location, useSiteInfo); + + if (IsPartialDefinition && OtherPartOfPartial is { } implementation) + { + PartialPropertyChecks(implementation, diagnostics); + implementation.CheckInitializerIfNeeded(diagnostics); + } + } + + /// + /// This method is analogous to . + /// Whenever new checks are added to this method, the other method should also have those checks added, if applicable. + /// + private void PartialPropertyChecks(SourcePropertySymbol implementation, BindingDiagnosticBag diagnostics) + { + Debug.Assert(this.IsPartialDefinition); + Debug.Assert((object)this != implementation); + Debug.Assert((object?)this.OtherPartOfPartial == implementation); + + // The purpose of this flag is to avoid cascading a type difference error with an additional redundant warning. + bool hasTypeDifferences = !TypeWithAnnotations.Equals(implementation.TypeWithAnnotations, TypeCompareKind.AllIgnoreOptions); + + if (hasTypeDifferences) + { + diagnostics.Add(ErrorCode.ERR_PartialPropertyTypeDifference, implementation.GetFirstLocation()); + } + else if (MemberSignatureComparer.ConsideringTupleNamesCreatesDifference(this, implementation)) + { + hasTypeDifferences = true; + diagnostics.Add(ErrorCode.ERR_PartialMemberInconsistentTupleNames, implementation.GetFirstLocation(), this, implementation); + } + + if (RefKind != implementation.RefKind) + { + hasTypeDifferences = true; + diagnostics.Add(ErrorCode.ERR_PartialMemberRefReturnDifference, implementation.GetFirstLocation()); + } + + if ((!hasTypeDifferences && !MemberSignatureComparer.PartialMethodsStrictComparer.Equals(this, implementation)) + || !Parameters.SequenceEqual(implementation.Parameters, (a, b) => a.Name == b.Name)) + { + diagnostics.Add(ErrorCode.WRN_PartialPropertySignatureDifference, implementation.GetFirstLocation(), + new FormattedSymbol(this, SymbolDisplayFormat.MinimallyQualifiedFormat), + new FormattedSymbol(implementation, SymbolDisplayFormat.MinimallyQualifiedFormat)); + } + + if (IsRequired != implementation.IsRequired) + { + diagnostics.Add(ErrorCode.ERR_PartialPropertyRequiredDifference, implementation.GetFirstLocation()); + } + + if (IsStatic != implementation.IsStatic) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberStaticDifference, implementation.GetFirstLocation()); + } + + if (HasReadOnlyModifier != implementation.HasReadOnlyModifier) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberReadOnlyDifference, implementation.GetFirstLocation()); + } + + if ((_modifiers & DeclarationModifiers.Unsafe) != (implementation._modifiers & DeclarationModifiers.Unsafe) && this.CompilationAllowsUnsafe()) // Don't cascade. + { + diagnostics.Add(ErrorCode.ERR_PartialMemberUnsafeDifference, implementation.GetFirstLocation()); + } + + if (this.IsParams() != implementation.IsParams()) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberParamsDifference, implementation.GetFirstLocation()); + } + + if (DeclaredAccessibility != implementation.DeclaredAccessibility + || HasExplicitAccessModifier != implementation.HasExplicitAccessModifier) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberAccessibilityDifference, implementation.GetFirstLocation()); + } + + if (IsVirtual != implementation.IsVirtual + || IsOverride != implementation.IsOverride + || IsSealed != implementation.IsSealed + || IsNew != implementation.IsNew) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberExtendedModDifference, implementation.GetFirstLocation()); + } + + Debug.Assert(this.ParameterCount == implementation.ParameterCount); + for (var i = 0; i < this.ParameterCount; i++) + { + // An error is only reported for a modifier difference here, regardless of whether the difference is safe or not. + // Presence of UnscopedRefAttribute is also not considered when checking partial signatures, because when the attribute is used, it will affect both parts the same way. + var definitionParameter = (SourceParameterSymbol)this.Parameters[i]; + var implementationParameter = (SourceParameterSymbol)implementation.Parameters[i]; + if (definitionParameter.DeclaredScope != implementationParameter.DeclaredScope) + { + diagnostics.Add(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, implementation.GetFirstLocation(), new FormattedSymbol(implementation.Parameters[i], SymbolDisplayFormat.ShortFormat)); + } + } + + if (this.GetMethod is { } definitionGetAccessor && implementation.GetMethod is { } implementationGetAccessor) + { + ((SourcePropertyAccessorSymbol)definitionGetAccessor).PartialAccessorChecks((SourcePropertyAccessorSymbol)implementationGetAccessor, diagnostics); + } + + if (this.SetMethod is { } definitionSetAccessor && implementation.SetMethod is { } implementationSetAccessor) + { + ((SourcePropertyAccessorSymbol)definitionSetAccessor).PartialAccessorChecks((SourcePropertyAccessorSymbol)implementationSetAccessor, diagnostics); + } } private static BaseParameterListSyntax? GetParameterListSyntax(CSharpSyntaxNode syntax) => (syntax as IndexerDeclarationSyntax)?.ParameterList; + + public sealed override bool IsExtern => PartialImplementationPart is { } implementation ? implementation.IsExtern : HasExternModifier; + + internal SourcePropertySymbol? OtherPartOfPartial => _otherPartOfPartial; + + internal bool IsPartialDefinition => IsPartial && !AccessorsHaveImplementation && !HasExternModifier; + + internal bool IsPartialImplementation => IsPartial && (AccessorsHaveImplementation || HasExternModifier); + + internal SourcePropertySymbol? PartialDefinitionPart => IsPartialImplementation ? OtherPartOfPartial : null; + + internal SourcePropertySymbol? PartialImplementationPart => IsPartialDefinition ? OtherPartOfPartial : null; + + internal static void InitializePartialPropertyParts(SourcePropertySymbol definition, SourcePropertySymbol implementation) + { + Debug.Assert(definition.IsPartialDefinition); + Debug.Assert(implementation.IsPartialImplementation); + + Debug.Assert(definition._otherPartOfPartial is not { } alreadySetImplPart || alreadySetImplPart == implementation); + Debug.Assert(implementation._otherPartOfPartial is not { } alreadySetDefPart || alreadySetDefPart == definition); + + definition._otherPartOfPartial = implementation; + implementation._otherPartOfPartial = definition; + + Debug.Assert(definition._otherPartOfPartial == implementation); + Debug.Assert(implementation._otherPartOfPartial == definition); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs index 78c9c04f76aae..d4fbc4a85099d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs @@ -5,6 +5,7 @@ #nullable disable using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; @@ -12,6 +13,7 @@ using System.Runtime.CompilerServices; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Emit; +using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -33,6 +35,8 @@ private enum Flags : byte IsAutoProperty = 1 << 1, IsExplicitInterfaceImplementation = 1 << 2, HasInitializer = 1 << 3, + AccessorsHaveImplementation = 1 << 4, + HasExplicitAccessModifier = 1 << 5, } // TODO (tomat): consider splitting into multiple subclasses/rare data. @@ -78,9 +82,11 @@ protected SourcePropertySymbolBase( string? aliasQualifierOpt, DeclarationModifiers modifiers, bool hasInitializer, + bool hasExplicitAccessMod, bool isAutoProperty, bool isExpressionBodied, bool isInitOnly, + bool accessorsHaveImplementation, RefKind refKind, string memberName, SyntaxList indexerNameAttributeLists, @@ -89,6 +95,7 @@ protected SourcePropertySymbolBase( { Debug.Assert(!isExpressionBodied || !isAutoProperty); Debug.Assert(!isExpressionBodied || !hasInitializer); + Debug.Assert(!isExpressionBodied || accessorsHaveImplementation); Debug.Assert((modifiers & DeclarationModifiers.Required) == 0 || this is SourcePropertySymbol); _syntaxRef = syntax.GetReference(); @@ -110,6 +117,11 @@ protected SourcePropertySymbolBase( bool isIndexer = IsIndexer; isAutoProperty = isAutoProperty && !(containingType.IsInterface && !IsStatic) && !IsAbstract && !IsExtern && !isIndexer; + if (hasExplicitAccessMod) + { + _propertyFlags |= Flags.HasExplicitAccessModifier; + } + if (isAutoProperty) { _propertyFlags |= Flags.IsAutoProperty; @@ -125,6 +137,11 @@ protected SourcePropertySymbolBase( _propertyFlags |= Flags.IsExpressionBodied; } + if (accessorsHaveImplementation) + { + _propertyFlags |= Flags.AccessorsHaveImplementation; + } + if (isIndexer) { if (indexerNameAttributeLists.Count == 0 || isExplicitInterfaceImplementation) @@ -262,20 +279,20 @@ explicitlyImplementedProperty is null ? internal bool IsExpressionBodied => (_propertyFlags & Flags.IsExpressionBodied) != 0; - private void CheckInitializer( - bool isAutoProperty, - bool isInterface, - bool isStatic, - Location location, - BindingDiagnosticBag diagnostics) + protected void CheckInitializerIfNeeded(BindingDiagnosticBag diagnostics) { - if (isInterface && !isStatic) + if ((_propertyFlags & Flags.HasInitializer) == 0) + { + return; + } + + if (ContainingType.IsInterface && !IsStatic) { - diagnostics.Add(ErrorCode.ERR_InstancePropertyInitializerInInterface, location); + diagnostics.Add(ErrorCode.ERR_InstancePropertyInitializerInInterface, Location); } - else if (!isAutoProperty) + else if (!IsAutoProperty) { - diagnostics.Add(ErrorCode.ERR_InitializerOnNonAutoProperty, location); + diagnostics.Add(ErrorCode.ERR_InitializerOnNonAutoProperty, Location); } } @@ -469,19 +486,25 @@ public override bool IsAbstract get { return (_modifiers & DeclarationModifiers.Abstract) != 0; } } - public override bool IsExtern + protected bool HasExternModifier { - get { return (_modifiers & DeclarationModifiers.Extern) != 0; } + get + { + return (_modifiers & DeclarationModifiers.Extern) != 0; + } } - public override bool IsStatic + public override bool IsExtern { - get { return (_modifiers & DeclarationModifiers.Static) != 0; } + get + { + return HasExternModifier; + } } - internal bool IsFixed + public override bool IsStatic { - get { return false; } + get { return (_modifiers & DeclarationModifiers.Static) != 0; } } /// @@ -618,9 +641,15 @@ public bool HasSkipLocalsInitAttribute internal bool IsAutoPropertyWithGetAccessor => IsAutoProperty && _getMethod is object; + protected bool HasExplicitAccessModifier + => (_propertyFlags & Flags.HasExplicitAccessModifier) != 0; + 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. @@ -663,11 +692,7 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, this.CheckAccessibility(Location, diagnostics, isExplicitInterfaceImplementation); this.CheckModifiers(isExplicitInterfaceImplementation, Location, IsIndexer, diagnostics); - bool hasInitializer = (_propertyFlags & Flags.HasInitializer) != 0; - if (hasInitializer) - { - CheckInitializer(IsAutoProperty, ContainingType.IsInterface, IsStatic, Location, diagnostics); - } + CheckInitializerIfNeeded(diagnostics); if (RefKind != RefKind.None && IsRequired) { @@ -870,6 +895,18 @@ private void CheckModifiers(bool isExplicitInterfaceImplementation, Location loc // '{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 @@ -1030,7 +1067,13 @@ private SynthesizedSealedPropertyAccessor MakeSynthesizedSealedAccessor() #region Attributes - public abstract SyntaxList AttributeDeclarationSyntaxList { get; } + public abstract OneOrMany> GetAttributeDeclarations(); + + /// + /// Symbol to copy bound attributes from, or null if the attributes are not shared among multiple source property symbols. + /// Analogous to . + /// + protected abstract SourcePropertySymbolBase BoundAttributesSource { get; } public abstract IAttributeTargetSymbol AttributesOwner { get; } @@ -1057,10 +1100,32 @@ private CustomAttributesBag GetAttributesBag() return bag; } + var copyFrom = this.BoundAttributesSource; + + // prevent infinite recursion: + Debug.Assert(!ReferenceEquals(copyFrom, this)); + // The property is responsible for completion of the backing field + // NB: when the **field keyword feature** is implemented, it's possible that synthesized field symbols will also be merged or shared between partial property parts. + // If we do that then this check should possibly be moved, and asserts adjusted accordingly. _ = BackingField?.GetAttributes(); - if (LoadAndValidateAttributes(OneOrMany.Create(AttributeDeclarationSyntaxList), ref _lazyCustomAttributesBag)) + bool bagCreatedOnThisThread; + if (copyFrom is not null) + { + // When partial properties get the ability to have a backing field, + // the implementer will have to decide how the BackingField symbol works in 'copyFrom' scenarios. + Debug.Assert(!IsAutoProperty); + + var attributesBag = copyFrom.GetAttributesBag(); + bagCreatedOnThisThread = Interlocked.CompareExchange(ref _lazyCustomAttributesBag, attributesBag, null) == null; + } + else + { + bagCreatedOnThisThread = LoadAndValidateAttributes(GetAttributeDeclarations(), ref _lazyCustomAttributesBag); + } + + if (bagCreatedOnThisThread) { var completed = _state.NotePartComplete(CompletionPart.Attributes); Debug.Assert(completed); @@ -1437,6 +1502,8 @@ internal sealed override bool RequiresCompletion get { return true; } } + internal bool IsPartial => (_modifiers & DeclarationModifiers.Partial) != 0; + internal sealed override bool HasComplete(CompletionPart part) { return _state.HasComplete(part); diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedPrimaryConstructorParameterBackingFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedPrimaryConstructorParameterBackingFieldSymbol.cs index 6552a867d6ae9..5f3d127451425 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedPrimaryConstructorParameterBackingFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedPrimaryConstructorParameterBackingFieldSymbol.cs @@ -33,8 +33,8 @@ protected override IAttributeTargetSymbol AttributeOwner internal override Location ErrorLocation => ParameterSymbol.TryGetFirstLocation() ?? NoLocation.Singleton; - protected override SyntaxList AttributeDeclarationSyntaxList - => default; + protected override OneOrMany> GetAttributeDeclarations() + => OneOrMany>.Empty; public override Symbol? AssociatedSymbol => null; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs index c64029e821ca8..1dc7444d03efb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs @@ -31,9 +31,11 @@ public SynthesizedRecordEqualityContractProperty(SourceMemberContainerTypeSymbol (_, false) => DeclarationModifiers.Protected | DeclarationModifiers.Override }, hasInitializer: false, + hasExplicitAccessMod: false, isAutoProperty: false, isExpressionBodied: false, isInitOnly: false, + accessorsHaveImplementation: true, RefKind.None, PropertyName, indexerNameAttributeLists: new SyntaxList(), @@ -47,8 +49,10 @@ public SynthesizedRecordEqualityContractProperty(SourceMemberContainerTypeSymbol public override ImmutableArray DeclaringSyntaxReferences => ImmutableArray.Empty; - public override SyntaxList AttributeDeclarationSyntaxList - => new SyntaxList(); + public override OneOrMany> GetAttributeDeclarations() + => OneOrMany>.Empty; + + protected override SourcePropertySymbolBase? BoundAttributesSource => null; public override IAttributeTargetSymbol AttributesOwner => this; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs index b3b268b23fe80..2d905f6daf0f8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs @@ -5,6 +5,7 @@ using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols { @@ -28,9 +29,11 @@ public SynthesizedRecordPropertySymbol( aliasQualifierOpt: null, modifiers: DeclarationModifiers.Public | (isOverride ? DeclarationModifiers.Override : DeclarationModifiers.None), hasInitializer: true, // Synthesized record properties always have a synthesized initializer + hasExplicitAccessMod: false, isAutoProperty: true, isExpressionBodied: false, isInitOnly: ShouldUseInit(containingType), + accessorsHaveImplementation: true, RefKind.None, backingParameter.Name, indexerNameAttributeLists: new SyntaxList(), @@ -40,13 +43,15 @@ public SynthesizedRecordPropertySymbol( BackingParameter = (SourceParameterSymbol)backingParameter; } + protected override SourcePropertySymbolBase? BoundAttributesSource => null; + public override IAttributeTargetSymbol AttributesOwner => BackingParameter as IAttributeTargetSymbol ?? this; protected override Location TypeLocation => ((ParameterSyntax)CSharpSyntaxNode).Type!.Location; - public override SyntaxList AttributeDeclarationSyntaxList - => BackingParameter.AttributeDeclarationList; + public override OneOrMany> GetAttributeDeclarations() + => OneOrMany.Create(BackingParameter.AttributeDeclarationList); protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs index cf1ddb35d1427..323447d1aa226 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs @@ -100,8 +100,8 @@ protected override IAttributeTargetSymbol AttributeOwner internal override Location ErrorLocation => _property.Location; - protected override SyntaxList AttributeDeclarationSyntaxList - => _property.AttributeDeclarationSyntaxList; + protected override OneOrMany> GetAttributeDeclarations() + => _property.GetAttributeDeclarations(); public override Symbol AssociatedSymbol => _property; @@ -163,15 +163,18 @@ private void CheckForFieldTargetedAttribute(BindingDiagnosticBag diagnostics) return; } - foreach (var attribute in AttributeDeclarationSyntaxList) + foreach (var attributeList in GetAttributeDeclarations()) { - if (attribute.Target?.GetAttributeLocation() == AttributeLocation.Field) + foreach (var attribute in attributeList) { - diagnostics.Add( - new CSDiagnosticInfo(ErrorCode.WRN_AttributesOnBackingFieldsNotAvailable, - languageVersion.ToDisplayString(), - new CSharpRequiredLanguageVersion(MessageID.IDS_FeatureAttributesOnBackingFields.RequiredVersion())), - attribute.Target.Location); + if (attribute.Target?.GetAttributeLocation() == AttributeLocation.Field) + { + diagnostics.Add( + new CSDiagnosticInfo(ErrorCode.WRN_AttributesOnBackingFieldsNotAvailable, + languageVersion.ToDisplayString(), + new CSharpRequiredLanguageVersion(MessageID.IDS_FeatureAttributesOnBackingFields.RequiredVersion())), + attribute.Target.Location); + } } } } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 482f74b8c1b7e..358a573db20a3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1642,26 +1642,51 @@ Parametr params musí mít platný typ kolekce. - - Both partial method declarations must have identical accessibility modifiers. - Obě deklarace částečných metod musí mít shodné modifikátory přístupnosti. + + Both partial member declarations must have identical accessibility modifiers. + Both partial member declarations must have identical accessibility modifiers. - - Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. - Obě deklarace částečných metod musí mít shodné kombinace modifikátorů virtual, override, sealed a new. + + A partial member cannot have the 'abstract' modifier + A partial member cannot have the 'abstract' modifier + + + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. - - Both partial method declarations must be readonly or neither may be readonly - Obě deklarace částečné metody musí mít modifikátor readonly, nebo nesmí mít modifikátor readonly žádná z nich. + + A partial member must be declared within a partial type + A partial member must be declared within a partial type - - Partial method declarations must have matching ref return values. + + Both partial member declarations must use a params parameter or neither may use a params parameter + Both partial member declarations must use a params parameter or neither may use a params parameter + + + + Both partial member declarations must be readonly or neither may be readonly + Both partial member declarations must be readonly or neither may be readonly + + + + Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + Obě deklarace částečných metod musí mít shodné kombinace modifikátorů virtual, override, sealed a new. + + + + Partial member declarations must have matching ref return values. Deklarace částečných metod musí mít odpovídající referenční návratové hodnoty. + + Both partial member declarations must be unsafe or neither may be unsafe + Both partial member declarations must be unsafe or neither may be unsafe + + Both partial method declarations must have the same return type. Obě deklarace částečných metod musí mít stejný návratový typ. @@ -1687,6 +1712,51 @@ Čá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, and cannot be an auto-property. + A partial property may not have multiple defining declarations, and cannot be an auto-property. + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be '{1}' to match the definition part + Property accessor '{0}' must be '{1}' to match the definition part + + + + 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 a definition part. + Partial property '{0}' must have a definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Both partial property declarations must be required or neither may be required + Both partial property declarations must be required or neither may be required + + + + Both partial property declarations must have the same type. + Both partial property declarations must have the same type. + + + + 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. @@ -1953,8 +2023,8 @@ - The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration. - Modifikátor scoped parametru {0} neodpovídá částečné deklaraci metody. + The 'scoped' modifier of parameter '{0}' doesn't match partial definition. + Modifikátor scoped parametru {0} neodpovídá částečné deklaraci metody. @@ -3047,6 +3117,16 @@ Parametr má modifikátor params ve výrazu lambda, ale ne v cílovém typu delegáta. + + Partial property declarations '{0}' and '{1}' have signature differences. + Partial property declarations '{0}' and '{1}' have signature differences. + + + + Partial property declarations have signature differences. + Partial property declarations have signature differences. + + Primary constructor parameter '{0}' is shadowed by a member from base. Parametr primárního konstruktoru {0} je stínován členem z báze. @@ -4765,12 +4845,12 @@ Nullability of reference types in type of parameter '{0}' doesn't match partial method declaration. - Typ odkazu s možnou hodnotou null v typu parametru {0} neodpovídá deklaraci částečné metody. + Typ odkazu s možnou hodnotou null v typu parametru {0} neodpovídá deklaraci částečné metody. Nullability of reference types in type of parameter doesn't match partial method declaration. - Typ odkazu s možnou hodnotou null v typu parametru neodpovídá deklaraci částečné metody. + Typ odkazu s možnou hodnotou null v typu parametru neodpovídá deklaraci částečné metody. @@ -4815,12 +4895,12 @@ Nullability of reference types in return type doesn't match partial method declaration. - Typ odkazu s možnou hodnotou null v návratovém typu neodpovídá deklaraci částečné metody. + Typ odkazu s možnou hodnotou null v návratovém typu neodpovídá deklaraci částečné metody. Nullability of reference types in return type doesn't match partial method declaration. - Typ odkazu s možnou hodnotou null v návratovém typu neodpovídá deklaraci částečné metody + Typ odkazu s možnou hodnotou null v návratovém typu neodpovídá deklaraci částečné metody @@ -6524,8 +6604,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. @@ -8132,18 +8212,8 @@ Pokud se taková třída používá jako základní třída a pokud odvozující Nekonzistentní použití parametru lambda. Typy parametrů musí být buď všechny explicitní, nebo všechny implicitní. - - A partial method cannot have the 'abstract' modifier - Částečná metoda nemůže mít modifikátor abstract. - - - - A partial method must be declared within a partial type - Částečná metoda musí být deklarovaná uvnitř částečného typu. - - - - A partial method may not explicitly implement an interface method + + A partial member may not explicitly implement an interface member Částečná metoda nesmí explicitně implementovat metodu rozhraní. @@ -8162,21 +8232,11 @@ Pokud se taková třída používá jako základní třída a pokud odvozující Částečná metoda nesmí mít víc implementujících deklarací. - - Both partial method declarations must use a params parameter or neither may use a params parameter - Obě deklarace částečné metody musí používat parametr params nebo ho nepoužívat. - - No defining declaration found for implementing declaration of partial method '{0}' Nenašla se žádná definující deklarace pro implementující deklaraci částečné metody {0}. - - Both partial method declarations, '{0}' and '{1}', must use the same tuple element names. - V deklaracích metod, {0} a {1} se musí používat stejné názvy prvků řazené kolekce členů. - - Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}' Částečné deklarace metod {0} mají nekonzistentní omezení parametru typu {1}. @@ -8187,16 +8247,11 @@ Pokud se taková třída používá jako základní třída a pokud odvozující Nejde vytvořit delegáta z metody {0}, protože se jedná o částečnou metodu bez implementující deklarace. - - Both partial method declarations must be static or neither may be static + + Both partial member declarations must be static or neither may be static Obě deklarace částečné metody musí být statické, nebo nesmí být statická žádná z nich. - - Both partial method declarations must be unsafe or neither may be unsafe - Obě deklarace částečné metody musí být nezabezpečené, nebo nesmí být nezabezpečená žádná z nich. - - Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees Ve stromech výrazů nejde používat částečné metody, pro které existuje jenom definující deklarace, nebo odebrané podmíněné metody. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 458820646f0bd..e70454ee1c2f7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1642,26 +1642,51 @@ Der Params-Parameter muss einen gültigen Sammlungstyp aufweisen - - Both partial method declarations must have identical accessibility modifiers. - Beide partiellen Methodendeklarationen müssen identische Zugriffsmodifizierer aufweisen. + + Both partial member declarations must have identical accessibility modifiers. + Both partial member declarations must have identical accessibility modifiers. - - Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. - Beide partiellen Methodendeklarationen müssen identische Kombinationen der Modifizierer "virtual", "override", "sealed" und "new" verwenden. + + A partial member cannot have the 'abstract' modifier + A partial member cannot have the 'abstract' modifier + + + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. - - Both partial method declarations must be readonly or neither may be readonly - Entweder beide oder keine der partiellen Methodendeklarationen müssen als "readonly" festgelegt werden. + + A partial member must be declared within a partial type + A partial member must be declared within a partial type - - Partial method declarations must have matching ref return values. + + Both partial member declarations must use a params parameter or neither may use a params parameter + Both partial member declarations must use a params parameter or neither may use a params parameter + + + + Both partial member declarations must be readonly or neither may be readonly + Both partial member declarations must be readonly or neither may be readonly + + + + Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + Beide partiellen Methodendeklarationen müssen identische Kombinationen der Modifizierer "virtual", "override", "sealed" und "new" verwenden. + + + + Partial member declarations must have matching ref return values. Deklarationen partieller Methoden müssen übereinstimmende Ref-Rückgabewerte aufweisen. + + Both partial member declarations must be unsafe or neither may be unsafe + Both partial member declarations must be unsafe or neither may be unsafe + + Both partial method declarations must have the same return type. Beide Deklarationen der partiellen Methode müssen den gleichen Rückgabetyp aufweisen. @@ -1687,6 +1712,51 @@ Die partielle Methode "{0}" muss Zugriffsmodifizierer aufweisen, weil sie out-Parameter verwendet. + + A partial property may not have multiple defining declarations, and cannot be an auto-property. + A partial property may not have multiple defining declarations, and cannot be an auto-property. + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be '{1}' to match the definition part + Property accessor '{0}' must be '{1}' to match the definition part + + + + 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 a definition part. + Partial property '{0}' must have a definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Both partial property declarations must be required or neither may be required + Both partial property declarations must be required or neither may be required + + + + Both partial property declarations must have the same type. + Both partial property declarations must have the same type. + + + + 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. @@ -1953,8 +2023,8 @@ - The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration. - Der Modifikator ‚Scoped‘ des Parameters ‚{0}‘ stimmt nicht mit der partiellen Methodendeklaration überein. + The 'scoped' modifier of parameter '{0}' doesn't match partial definition. + Der Modifikator ‚Scoped‘ des Parameters ‚{0}‘ stimmt nicht mit der partiellen Methodendeklaration überein. @@ -3047,6 +3117,16 @@ Der Parameter verfügt über den Modifizierer „params“ in der Lambdafunktion, aber nicht im Delegattyp des Ziels. + + Partial property declarations '{0}' and '{1}' have signature differences. + Partial property declarations '{0}' and '{1}' have signature differences. + + + + Partial property declarations have signature differences. + Partial property declarations have signature differences. + + Primary constructor parameter '{0}' is shadowed by a member from base. Der primäre Konstruktorparameter "{0}" wird von einem Member aus der Basis überschatten. @@ -4765,12 +4845,12 @@ Nullability of reference types in type of parameter '{0}' doesn't match partial method declaration. - Die NULL-Zulässigkeit von Verweistypen im Typ des Parameters "{0}" entspricht nicht der Deklaration der partiellen Methode. + Die NULL-Zulässigkeit von Verweistypen im Typ des Parameters "{0}" entspricht nicht der Deklaration der partiellen Methode. Nullability of reference types in type of parameter doesn't match partial method declaration. - Die NULL-Zulässigkeit von Verweistypen im Typ des Parameters entspricht nicht der Deklaration der partiellen Methode. + Die NULL-Zulässigkeit von Verweistypen im Typ des Parameters entspricht nicht der Deklaration der partiellen Methode. @@ -4815,12 +4895,12 @@ Nullability of reference types in return type doesn't match partial method declaration. - Die NULL-Zulässigkeit von Verweistypen im Rückgabetyp entspricht nicht der Deklaration der partiellen Methode. + Die NULL-Zulässigkeit von Verweistypen im Rückgabetyp entspricht nicht der Deklaration der partiellen Methode. Nullability of reference types in return type doesn't match partial method declaration. - Die NULL-Zulässigkeit von Verweistypen im Rückgabetyp entspricht nicht der Deklaration der partiellen Methode. + Die NULL-Zulässigkeit von Verweistypen im Rückgabetyp entspricht nicht der Deklaration der partiellen Methode. @@ -6524,8 +6604,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. @@ -8132,18 +8212,8 @@ Wenn solch eine Klasse als Basisklasse verwendet wird und die ableitende Klasse Inkonsistente Verwendung des lambda-Parameters. Alle Parametertypen müssen entweder explizit oder implizit sein. - - A partial method cannot have the 'abstract' modifier - Eine partielle Methode darf nicht den Modifizierer "abstract" aufweisen. - - - - A partial method must be declared within a partial type - Eine partielle Methode muss innerhalb eines partiellen Typs deklariert sein. - - - - A partial method may not explicitly implement an interface method + + A partial member may not explicitly implement an interface member Eine partielle Methode darf Schnittstellenmethoden nicht explizit implementieren. @@ -8162,21 +8232,11 @@ Wenn solch eine Klasse als Basisklasse verwendet wird und die ableitende Klasse Eine partielle Methode darf nicht über mehrere implementierende Deklarationen verfügen. - - Both partial method declarations must use a params parameter or neither may use a params parameter - Beide partiellen Methodendeklarationen müssen einen params-Parameter verwenden, oder keine von beiden darf einen params-Parameter verwenden. - - No defining declaration found for implementing declaration of partial method '{0}' Für die implementierende Deklaration der partiellen Methode "{0}" wurde keine definierende Deklaration gefunden. - - Both partial method declarations, '{0}' and '{1}', must use the same tuple element names. - Die beiden partiellen Methodendeklarationen ("{0}" und "{1}") müssen die gleichen Tupelelementnamen verwenden. - - Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}' Partielle Methodendeklarationen von "{0}" weisen inkonsistente Einschränkungen für den Typparameter "{1}" auf. @@ -8187,16 +8247,11 @@ Wenn solch eine Klasse als Basisklasse verwendet wird und die ableitende Klasse Aus der {0}-Methode kann kein Delegat erstellt werden, da es sich um eine partielle Methode ohne implementierende Deklaration handelt. - - Both partial method declarations must be static or neither may be static + + Both partial member declarations must be static or neither may be static Beide partiellen Methodendeklarationen müssen statisch sein, oder keine von beiden darf statisch sein. - - Both partial method declarations must be unsafe or neither may be unsafe - Beide partiellen Methodendeklarationen müssen unsicher sein, oder keine von beiden darf unsicher sein. - - Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees In Ausdrucksbäumen dürfen weder partielle Methoden mit nur einer definierenden Deklaration noch entfernte bedingte Methoden verwendet werden. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index de96942b43ca1..9a4a1bed9796f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1642,26 +1642,51 @@ El parámetro params debe tener un tipo de colección válido - - Both partial method declarations must have identical accessibility modifiers. - Ambas declaraciones de método parcial deben tener modificadores de accesibilidad idénticos. + + Both partial member declarations must have identical accessibility modifiers. + Both partial member declarations must have identical accessibility modifiers. - - Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. - Ambas declaraciones de método parcial deben tener combinaciones idénticas de los modificadores "virtual", "override", "sealed" y "new". + + A partial member cannot have the 'abstract' modifier + A partial member cannot have the 'abstract' modifier + + + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. - - Both partial method declarations must be readonly or neither may be readonly - Ambas declaraciones de métodos parciales deben ser de solo lectura o ninguna de ellas puede ser de solo lectura + + A partial member must be declared within a partial type + A partial member must be declared within a partial type - - Partial method declarations must have matching ref return values. + + Both partial member declarations must use a params parameter or neither may use a params parameter + Both partial member declarations must use a params parameter or neither may use a params parameter + + + + Both partial member declarations must be readonly or neither may be readonly + Both partial member declarations must be readonly or neither may be readonly + + + + Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + Ambas declaraciones de método parcial deben tener combinaciones idénticas de los modificadores "virtual", "override", "sealed" y "new". + + + + Partial member declarations must have matching ref return values. Las declaraciones de método parcial deben tener valores devueltos de referencia que coincidan. + + Both partial member declarations must be unsafe or neither may be unsafe + Both partial member declarations must be unsafe or neither may be unsafe + + Both partial method declarations must have the same return type. Ambas declaraciones de método parcial deben tener el mismo tipo de valor devuelto. @@ -1687,6 +1712,51 @@ El método parcial "{0}" debe tener modificadores de accesibilidad porque tiene parámetros "out". + + A partial property may not have multiple defining declarations, and cannot be an auto-property. + A partial property may not have multiple defining declarations, and cannot be an auto-property. + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be '{1}' to match the definition part + Property accessor '{0}' must be '{1}' to match the definition part + + + + 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 a definition part. + Partial property '{0}' must have a definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Both partial property declarations must be required or neither may be required + Both partial property declarations must be required or neither may be required + + + + Both partial property declarations must have the same type. + Both partial property declarations must have the same type. + + + + 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. @@ -1953,8 +2023,8 @@ - The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration. - El modificador 'scoped' del parámetro '{0}' no coincide con la declaración de método parcial. + The 'scoped' modifier of parameter '{0}' doesn't match partial definition. + El modificador 'scoped' del parámetro '{0}' no coincide con la declaración de método parcial. @@ -3047,6 +3117,16 @@ El parámetro tiene modificador de parámetros en lambda, pero no en el tipo delegado de destino. + + Partial property declarations '{0}' and '{1}' have signature differences. + Partial property declarations '{0}' and '{1}' have signature differences. + + + + Partial property declarations have signature differences. + Partial property declarations have signature differences. + + Primary constructor parameter '{0}' is shadowed by a member from base. El parámetro del constructor principal "{0}" está sombreado por un miembro de la base. @@ -4765,12 +4845,12 @@ Nullability of reference types in type of parameter '{0}' doesn't match partial method declaration. - La nulabilidad de los tipos de referencia del tipo de parámetro"{0}" no coincide con la declaración de método parcial. + La nulabilidad de los tipos de referencia del tipo de parámetro"{0}" no coincide con la declaración de método parcial. Nullability of reference types in type of parameter doesn't match partial method declaration. - La nulabilidad de los tipos de referencia del tipo de parámetro no coincide con la declaración de método parcial + La nulabilidad de los tipos de referencia del tipo de parámetro no coincide con la declaración de método parcial @@ -4815,12 +4895,12 @@ Nullability of reference types in return type doesn't match partial method declaration. - La nulabilidad de los tipos de referencia del tipo devuelto no coincide con la declaración de método parcial. + La nulabilidad de los tipos de referencia del tipo devuelto no coincide con la declaración de método parcial. Nullability of reference types in return type doesn't match partial method declaration. - La nulabilidad de los tipos de referencia del tipo devuelto no coincide con la declaración de método parcial. + La nulabilidad de los tipos de referencia del tipo devuelto no coincide con la declaración de método parcial. @@ -6524,8 +6604,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. @@ -8132,18 +8212,8 @@ Si se utiliza una clase de este tipo como clase base y si la clase derivada defi Uso incoherente del parámetro lambda; los tipos de parámetro deben ser todos explícitos o todos implícitos - - A partial method cannot have the 'abstract' modifier - Un método parcial no puede tener el modificador "abstract" - - - - A partial method must be declared within a partial type - Un método parcial debe declararse dentro de un tipo parcial - - - - A partial method may not explicitly implement an interface method + + A partial member may not explicitly implement an interface member Un método parcial no puede implementar explícitamente un método de interfaz @@ -8162,21 +8232,11 @@ Si se utiliza una clase de este tipo como clase base y si la clase derivada defi Un método parcial no puede tener varias declaraciones de implementación - - Both partial method declarations must use a params parameter or neither may use a params parameter - Ambas declaraciones de métodos parciales deben usar un parámetro params; si no, ninguna podrá usarlo - - No defining declaration found for implementing declaration of partial method '{0}' No se encontró ninguna declaración de definición para la declaración de implementación del método parcial '{0}' - - Both partial method declarations, '{0}' and '{1}', must use the same tuple element names. - Ambas declaraciones de método parcial, '{0}' y '{1}', deben usar los mismos nombres de elementos de tupla. - - Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}' Las declaraciones de métodos parciales de "{0}" tienen restricciones incoherentes para el parámetro de tipo "{1}" @@ -8187,16 +8247,11 @@ Si se utiliza una clase de este tipo como clase base y si la clase derivada defi No se puede crear un delegado a partir del método '{0}' porque es un método parcial sin declaración de implementación - - Both partial method declarations must be static or neither may be static + + Both partial member declarations must be static or neither may be static Ambas declaraciones de método parcial deben ser estáticas o ninguna de ellas puede ser estática - - Both partial method declarations must be unsafe or neither may be unsafe - Ambas declaraciones de métodos parciales deben ser no seguras o ninguna de ellas puede ser no segura - - Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees En los árboles de expresión no se pueden usar métodos parciales con solo una declaración de definición ni métodos condicionales quitados diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index cd8e3b4571564..e907385f43c1d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1642,26 +1642,51 @@ Le paramètre params doit avoir un type de collection valide - - Both partial method declarations must have identical accessibility modifiers. - Les deux déclarations de méthodes partielles doivent avoir des modificateurs d'accessibilité identiques. + + Both partial member declarations must have identical accessibility modifiers. + Both partial member declarations must have identical accessibility modifiers. - - Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. - Les deux déclarations de méthodes partielles doivent avoir des combinaisons identiques des modificateurs 'virtual', 'override', 'sealed' et 'new'. + + A partial member cannot have the 'abstract' modifier + A partial member cannot have the 'abstract' modifier + + + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. - - Both partial method declarations must be readonly or neither may be readonly - Soit les deux déclarations de méthodes partielles sont readonly, soit aucune ne l'est + + A partial member must be declared within a partial type + A partial member must be declared within a partial type - - Partial method declarations must have matching ref return values. + + Both partial member declarations must use a params parameter or neither may use a params parameter + Both partial member declarations must use a params parameter or neither may use a params parameter + + + + Both partial member declarations must be readonly or neither may be readonly + Both partial member declarations must be readonly or neither may be readonly + + + + Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + Les deux déclarations de méthodes partielles doivent avoir des combinaisons identiques des modificateurs 'virtual', 'override', 'sealed' et 'new'. + + + + Partial member declarations must have matching ref return values. Les déclarations de méthodes partielles doivent avoir des valeurs de retour ref correspondantes. + + Both partial member declarations must be unsafe or neither may be unsafe + Both partial member declarations must be unsafe or neither may be unsafe + + Both partial method declarations must have the same return type. Les deux déclarations de méthodes partielles doivent avoir le même type de retour. @@ -1687,6 +1712,51 @@ 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, and cannot be an auto-property. + A partial property may not have multiple defining declarations, and cannot be an auto-property. + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be '{1}' to match the definition part + Property accessor '{0}' must be '{1}' to match the definition part + + + + 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 a definition part. + Partial property '{0}' must have a definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Both partial property declarations must be required or neither may be required + Both partial property declarations must be required or neither may be required + + + + Both partial property declarations must have the same type. + Both partial property declarations must have the same type. + + + + 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. @@ -1953,8 +2023,8 @@ - The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration. - Le modificateur 'scoped' du paramètre '{0}' ne correspond pas à la déclaration de méthode partielle. + The 'scoped' modifier of parameter '{0}' doesn't match partial definition. + Le modificateur 'scoped' du paramètre '{0}' ne correspond pas à la déclaration de méthode partielle. @@ -3047,6 +3117,16 @@ Le paramètre a un modificateur de paramètres dans l’expression lambda mais pas dans le type délégué cible. + + Partial property declarations '{0}' and '{1}' have signature differences. + Partial property declarations '{0}' and '{1}' have signature differences. + + + + Partial property declarations have signature differences. + Partial property declarations have signature differences. + + Primary constructor parameter '{0}' is shadowed by a member from base. Le paramètre de constructeur principal « {0} » est ombré par un membre de la base. @@ -4765,12 +4845,12 @@ Nullability of reference types in type of parameter '{0}' doesn't match partial method declaration. - La nullabilité des types référence dans le type de paramètre '{0}' ne correspond pas à la déclaration de méthode partielle. + La nullabilité des types référence dans le type de paramètre '{0}' ne correspond pas à la déclaration de méthode partielle. Nullability of reference types in type of parameter doesn't match partial method declaration. - La nullabilité des types référence dans le type de paramètre ne correspond pas à la déclaration de méthode partielle. + La nullabilité des types référence dans le type de paramètre ne correspond pas à la déclaration de méthode partielle. @@ -4815,12 +4895,12 @@ Nullability of reference types in return type doesn't match partial method declaration. - La nullabilité des types référence dans le type de retour ne correspond pas à la déclaration de méthode partielle. + La nullabilité des types référence dans le type de retour ne correspond pas à la déclaration de méthode partielle. Nullability of reference types in return type doesn't match partial method declaration. - La nullabilité des types référence dans le type de retour ne correspond pas à la déclaration de méthode partielle. + La nullabilité des types référence dans le type de retour ne correspond pas à la déclaration de méthode partielle. @@ -6524,8 +6604,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. @@ -8132,18 +8212,8 @@ Si une telle classe est utilisée en tant que classe de base et si la classe dé Utilisation du paramètre lambda incohérente ; les types de paramètres doivent être tous explicites ou tous implicites - - A partial method cannot have the 'abstract' modifier - Une méthode partielle ne peut pas avoir le modificateur 'abstract' - - - - A partial method must be declared within a partial type - Une méthode partielle doit être déclarée au sein d'un type partiel - - - - A partial method may not explicitly implement an interface method + + A partial member may not explicitly implement an interface member Une méthode partielle ne peut pas implémenter explicitement une méthode d'interface @@ -8162,21 +8232,11 @@ Si une telle classe est utilisée en tant que classe de base et si la classe dé Une méthode partielle ne peut pas avoir plusieurs déclarations d'implémentation - - Both partial method declarations must use a params parameter or neither may use a params parameter - Soit les deux déclarations de méthode partielles utilisent un paramètre params, soit aucune des deux - - No defining declaration found for implementing declaration of partial method '{0}' Aucune déclaration de définition trouvée pour la déclaration d'implémentation de la méthode partielle '{0}' - - Both partial method declarations, '{0}' and '{1}', must use the same tuple element names. - Les deux déclarations de méthodes partielles, '{0}' et '{1}', doivent utiliser les mêmes noms d'éléments tuples. - - Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}' Les déclarations de méthodes partielles de '{0}' ont des contraintes incohérentes pour le paramètre de type '{1}' @@ -8187,16 +8247,11 @@ Si une telle classe est utilisée en tant que classe de base et si la classe dé Impossible de créer un délégué à partir de la méthode '{0}', car il s'agit d'une méthode partielle sans déclaration d'implémentation - - Both partial method declarations must be static or neither may be static + + Both partial member declarations must be static or neither may be static Soit les deux déclarations de méthode partielles sont statiques, soit aucune ne l'est - - Both partial method declarations must be unsafe or neither may be unsafe - Soit les deux déclarations de méthode partielles sont unsafe, soit aucune ne l'est - - Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees Les méthodes partielles avec uniquement une déclaration de définition ou des méthodes conditionnelles supprimées ne peuvent pas être utilisées dans des arborescences d'expressions diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index ee6c972ccf03d..b64ad3c5a9d50 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1642,26 +1642,51 @@ Il parametro params deve avere un tipo di raccolta valido - - Both partial method declarations must have identical accessibility modifiers. - I modificatori di accessibilità devono essere identici in entrambe le dichiarazioni di metodo parziale. + + Both partial member declarations must have identical accessibility modifiers. + Both partial member declarations must have identical accessibility modifiers. - - Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. - Entrambe le dichiarazioni di metodo parziale devono contenere combinazioni identiche di modificatori 'virtual', 'override', 'sealed' e 'new'. + + A partial member cannot have the 'abstract' modifier + A partial member cannot have the 'abstract' modifier + + + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. - - Both partial method declarations must be readonly or neither may be readonly - Nessuna o entrambe le dichiarazioni di metodi parziali devono essere di tipo readonly + + A partial member must be declared within a partial type + A partial member must be declared within a partial type - - Partial method declarations must have matching ref return values. + + Both partial member declarations must use a params parameter or neither may use a params parameter + Both partial member declarations must use a params parameter or neither may use a params parameter + + + + Both partial member declarations must be readonly or neither may be readonly + Both partial member declarations must be readonly or neither may be readonly + + + + Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + Entrambe le dichiarazioni di metodo parziale devono contenere combinazioni identiche di modificatori 'virtual', 'override', 'sealed' e 'new'. + + + + Partial member declarations must have matching ref return values. Le dichiarazioni di metodo parziale devono contenere valori restituiti di riferimento corrispondenti. + + Both partial member declarations must be unsafe or neither may be unsafe + Both partial member declarations must be unsafe or neither may be unsafe + + Both partial method declarations must have the same return type. Il tipo restituito deve essere identico in entrambe le dichiarazioni di metodo parziale. @@ -1687,6 +1712,51 @@ Il metodo parziale '{0}' deve contenere modificatori di accessibilità perché include parametri 'out'. + + A partial property may not have multiple defining declarations, and cannot be an auto-property. + A partial property may not have multiple defining declarations, and cannot be an auto-property. + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be '{1}' to match the definition part + Property accessor '{0}' must be '{1}' to match the definition part + + + + 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 a definition part. + Partial property '{0}' must have a definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Both partial property declarations must be required or neither may be required + Both partial property declarations must be required or neither may be required + + + + Both partial property declarations must have the same type. + Both partial property declarations must have the same type. + + + + 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. @@ -1953,8 +2023,8 @@ - The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration. - Il modificatore 'scoped' del parametro '{0}' non corrisponde alla dichiarazione di metodo parziale. + The 'scoped' modifier of parameter '{0}' doesn't match partial definition. + Il modificatore 'scoped' del parametro '{0}' non corrisponde alla dichiarazione di metodo parziale. @@ -3047,6 +3117,16 @@ Il parametro contiene un modificatore di parametri in lambda ma non nel tipo delegato di destinazione. + + Partial property declarations '{0}' and '{1}' have signature differences. + Partial property declarations '{0}' and '{1}' have signature differences. + + + + Partial property declarations have signature differences. + Partial property declarations have signature differences. + + Primary constructor parameter '{0}' is shadowed by a member from base. Il parametro del costruttore primario '{0}' è ombreggiato da un membro della base. @@ -4765,12 +4845,12 @@ target:module Compila un modulo che può essere aggiunto ad altro Nullability of reference types in type of parameter '{0}' doesn't match partial method declaration. - Il supporto dei valori Null dei tipi riferimento nel tipo di parametro '{0}' non corrisponde alla dichiarazione di metodo parziale. + Il supporto dei valori Null dei tipi riferimento nel tipo di parametro '{0}' non corrisponde alla dichiarazione di metodo parziale. Nullability of reference types in type of parameter doesn't match partial method declaration. - Il supporto dei valori Null dei tipi riferimento nel tipo di parametro non corrisponde alla dichiarazione di metodo parziale. + Il supporto dei valori Null dei tipi riferimento nel tipo di parametro non corrisponde alla dichiarazione di metodo parziale. @@ -4815,12 +4895,12 @@ target:module Compila un modulo che può essere aggiunto ad altro Nullability of reference types in return type doesn't match partial method declaration. - Il supporto dei valori Null dei tipi riferimento nel tipo restituito non corrisponde alla dichiarazione di metodo parziale. + Il supporto dei valori Null dei tipi riferimento nel tipo restituito non corrisponde alla dichiarazione di metodo parziale. Nullability of reference types in return type doesn't match partial method declaration. - Il supporto dei valori Null dei tipi riferimento nel tipo restituito non corrisponde alla dichiarazione di metodo parziale. + Il supporto dei valori Null dei tipi riferimento nel tipo restituito non corrisponde alla dichiarazione di metodo parziale. @@ -6524,8 +6604,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. @@ -8132,18 +8212,8 @@ Se si usa tale classe come classe base e se la classe di derivazione definisce u Utilizzo non coerente dei parametri lambda: i parametri devono essere tutti di tipo esplicito o implicito - - A partial method cannot have the 'abstract' modifier - Un metodo parziale non può contenere il modificatore 'abstract' - - - - A partial method must be declared within a partial type - Un metodo parziale deve essere dichiarato in un tipo parziale - - - - A partial method may not explicitly implement an interface method + + A partial member may not explicitly implement an interface member Un metodo parziale non può implementare in modo esplicito un metodo di interfaccia @@ -8162,21 +8232,11 @@ Se si usa tale classe come classe base e se la classe di derivazione definisce u Un metodo parziale non può avere più dichiarazioni di implementazione - - Both partial method declarations must use a params parameter or neither may use a params parameter - Entrambe le dichiarazioni di metodo parziale devono usare un parametro params, altrimenti nessuna delle due potrà usarla - - No defining declaration found for implementing declaration of partial method '{0}' Non sono state trovate dichiarazioni di definizione per la dichiarazione di implementazione del metodo parziale '{0}' - - Both partial method declarations, '{0}' and '{1}', must use the same tuple element names. - Entrambe le dichiarazioni di metodo parziale '{0}' e '{1}' devono usare gli stessi nomi di elementi di tupla. - - Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}' Le dichiarazioni di metodo parziali di '{0}' contengono vincoli incoerenti per il parametro di tipo '{1}' @@ -8187,16 +8247,11 @@ Se si usa tale classe come classe base e se la classe di derivazione definisce u Non è possibile creare il delegato dal metodo '{0}' perché è un metodo parziale senza una dichiarazione di implementazione - - Both partial method declarations must be static or neither may be static + + Both partial member declarations must be static or neither may be static Entrambe le dichiarazioni di metodo parziale devono essere statiche, altrimenti nessuna delle due potrà esserlo - - Both partial method declarations must be unsafe or neither may be unsafe - Nessuna o entrambe le dichiarazioni di metodi parziali devono essere di tipo unsafe - - Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees Non è possibile usare negli alberi delle espressioni metodi parziali contenenti solo una dichiarazione di definizione o metodi condizionali rimossi diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 049964d81ce3a..0f66f576146bd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1642,26 +1642,51 @@ params パラメーターには有効なコレクションの種類が必要です - - Both partial method declarations must have identical accessibility modifiers. - 両方の部分メソッド宣言には、同じアクセシビリティ修飾子を指定する必要があります。 + + Both partial member declarations must have identical accessibility modifiers. + Both partial member declarations must have identical accessibility modifiers. - - Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. - 両方の部分メソッド宣言には、'virtual'、'override'、'sealed'、'new' 修飾子の同じ組み合わせを指定する必要があります。 + + A partial member cannot have the 'abstract' modifier + A partial member cannot have the 'abstract' modifier + + + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. - - Both partial method declarations must be readonly or neither may be readonly - 部分メソッド宣言は、両方とも readonly であるか、両方とも readonly でないかのいずれかである必要があります + + A partial member must be declared within a partial type + A partial member must be declared within a partial type - - Partial method declarations must have matching ref return values. + + Both partial member declarations must use a params parameter or neither may use a params parameter + Both partial member declarations must use a params parameter or neither may use a params parameter + + + + Both partial member declarations must be readonly or neither may be readonly + Both partial member declarations must be readonly or neither may be readonly + + + + Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + 両方の部分メソッド宣言には、'virtual'、'override'、'sealed'、'new' 修飾子の同じ組み合わせを指定する必要があります。 + + + + Partial member declarations must have matching ref return values. 部分メソッドの宣言には、一致する ref 戻り値が必要です。 + + Both partial member declarations must be unsafe or neither may be unsafe + Both partial member declarations must be unsafe or neither may be unsafe + + Both partial method declarations must have the same return type. 部分メソッドの両方の宣言には、同じ戻り値の型を指定しなければなりません。 @@ -1687,6 +1712,51 @@ 部分メソッド '{0}' には、'out' パラメーターが指定されているため、アクセシビリティ修飾子が必要です。 + + A partial property may not have multiple defining declarations, and cannot be an auto-property. + A partial property may not have multiple defining declarations, and cannot be an auto-property. + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be '{1}' to match the definition part + Property accessor '{0}' must be '{1}' to match the definition part + + + + 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 a definition part. + Partial property '{0}' must have a definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Both partial property declarations must be required or neither may be required + Both partial property declarations must be required or neither may be required + + + + Both partial property declarations must have the same type. + Both partial property declarations must have the same type. + + + + 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}' のパターンとしてサポートされていません。代わりに空の文字列を使用してください。 @@ -1953,8 +2023,8 @@ - The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration. - パラメーター '{0}' の 'scoped' 修飾子が部分メソッド宣言と一致しません。 + The 'scoped' modifier of parameter '{0}' doesn't match partial definition. + パラメーター '{0}' の 'scoped' 修飾子が部分メソッド宣言と一致しません。 @@ -3047,6 +3117,16 @@ パラメーターにラムダの params 修飾子がありますが、ターゲット デリゲート型にはありません。 + + Partial property declarations '{0}' and '{1}' have signature differences. + Partial property declarations '{0}' and '{1}' have signature differences. + + + + Partial property declarations have signature differences. + Partial property declarations have signature differences. + + Primary constructor parameter '{0}' is shadowed by a member from base. プライマリ コンストラクター パラメーター '{0}' は基底のメンバーによってシャドウされます。 @@ -4765,12 +4845,12 @@ Nullability of reference types in type of parameter '{0}' doesn't match partial method declaration. - パラメーター '{0}' の型における参照型の Null 許容性が、部分メソッド宣言と一致しません。 + パラメーター '{0}' の型における参照型の Null 許容性が、部分メソッド宣言と一致しません。 Nullability of reference types in type of parameter doesn't match partial method declaration. - パラメーターの型における参照型の Null 許容性が、部分メソッド宣言と一致しません。 + パラメーターの型における参照型の Null 許容性が、部分メソッド宣言と一致しません。 @@ -4815,12 +4895,12 @@ Nullability of reference types in return type doesn't match partial method declaration. - 戻り値の型における参照型の Null 値の許容が、部分メソッド宣言と一致しません。 + 戻り値の型における参照型の Null 値の許容が、部分メソッド宣言と一致しません。 Nullability of reference types in return type doesn't match partial method declaration. - 戻り値の型における参照型の Null 値の許容が、部分メソッド宣言と一致しません。 + 戻り値の型における参照型の Null 値の許容が、部分メソッド宣言と一致しません。 @@ -6524,8 +6604,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'、またはメソッドの戻り値の型の直前にのみ指定できます。 @@ -8132,18 +8212,8 @@ If such a class is used as a base class and if the deriving class defines a dest ラムダ パラメーターの使用方法に一貫性がありません。パラメーター型はすべて明示的であるか、またはすべて暗黙的である必要があります - - A partial method cannot have the 'abstract' modifier - 部分メソッドに 'abstract' 修飾子を指定することはできません - - - - A partial method must be declared within a partial type - 部分メソッドは、部分型内で宣言される必要があります - - - - A partial method may not explicitly implement an interface method + + A partial member may not explicitly implement an interface member 部分メソッドは、インターフェイス メソッドを明示的に実装できないことがあります @@ -8162,21 +8232,11 @@ If such a class is used as a base class and if the deriving class defines a dest 部分メソッドでは、複数の実装宣言を含むことができない場合があります - - Both partial method declarations must use a params parameter or neither may use a params parameter - 部分メソッド宣言は、両方とも params パラメーターを使用するか、両方とも params パラメーターを使用しないかのいずれかである必要があります - - No defining declaration found for implementing declaration of partial method '{0}' 部分メソッド '{0}' の実装宣言に対する定義宣言が見つかりませんでした - - Both partial method declarations, '{0}' and '{1}', must use the same tuple element names. - 部分メソッド宣言 '{0}' および '{1}' は、どちらも同じタプル要素名を使用する必要があります。 - - Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}' '{0}' の部分メソッド宣言には、型パラメーター '{1}' に対して矛盾する制約が含まれています @@ -8187,16 +8247,11 @@ If such a class is used as a base class and if the deriving class defines a dest メソッド '{0}' は実装宣言がない部分メソッドであるため、このメソッドからデリゲートを作成できません - - Both partial method declarations must be static or neither may be static + + Both partial member declarations must be static or neither may be static 部分メソッド宣言は、両方とも static であるか、両方とも static でないかのいずれかである必要があります - - Both partial method declarations must be unsafe or neither may be unsafe - 部分メソッド宣言は、両方とも unsafe であるか、両方とも unsafe でないかのいずれかである必要があります - - Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees 定義宣言だけを含む部分メソッドまたは削除された条件付きメソッドは、式ツリーで使用できません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index dd8ef848b9a66..682e122e79c06 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1642,26 +1642,51 @@ params 매개 변수는 유효한 컬렉션 형식이어야 합니다. - - Both partial method declarations must have identical accessibility modifiers. - 두 부분 메서드 선언에는 동일한 접근성 한정자가 있어야 합니다. + + Both partial member declarations must have identical accessibility modifiers. + Both partial member declarations must have identical accessibility modifiers. - - Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. - 두 부분 메서드 선언에는 동일한 조합의 'virtual', 'override', 'sealed' 및 'new' 한정자가 있어야 합니다. + + A partial member cannot have the 'abstract' modifier + A partial member cannot have the 'abstract' modifier + + + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. - - Both partial method declarations must be readonly or neither may be readonly - 두 부분 메서드(Partial method) 선언 모두 readonly이거나 readonly가 아니어야 합니다. + + A partial member must be declared within a partial type + A partial member must be declared within a partial type - - Partial method declarations must have matching ref return values. + + Both partial member declarations must use a params parameter or neither may use a params parameter + Both partial member declarations must use a params parameter or neither may use a params parameter + + + + Both partial member declarations must be readonly or neither may be readonly + Both partial member declarations must be readonly or neither may be readonly + + + + Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + 두 부분 메서드 선언에는 동일한 조합의 'virtual', 'override', 'sealed' 및 'new' 한정자가 있어야 합니다. + + + + Partial member declarations must have matching ref return values. 부분 메서드 선언에는 일치하는 참조 반환 값이 있어야 합니다. + + Both partial member declarations must be unsafe or neither may be unsafe + Both partial member declarations must be unsafe or neither may be unsafe + + Both partial method declarations must have the same return type. 두 부분 메서드 선언의 반환 형식이 같아야 합니다. @@ -1687,6 +1712,51 @@ 부분 메서드 '{0}'에는 'out' 매개 변수가 있으므로 접근성 한정자가 있어야 합니다. + + A partial property may not have multiple defining declarations, and cannot be an auto-property. + A partial property may not have multiple defining declarations, and cannot be an auto-property. + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be '{1}' to match the definition part + Property accessor '{0}' must be '{1}' to match the definition part + + + + 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 a definition part. + Partial property '{0}' must have a definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Both partial property declarations must be required or neither may be required + Both partial property declarations must be required or neither may be required + + + + Both partial property declarations must have the same type. + Both partial property declarations must have the same type. + + + + 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}'에 대한 패턴으로 지원되지 않습니다. 대신 빈 문자열을 사용하세요. @@ -1953,8 +2023,8 @@ - The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration. - 매개 변수 '{0}'의 '범위 지정' 한정자가 부분 메서드 선언과 일치하지 않습니다. + The 'scoped' modifier of parameter '{0}' doesn't match partial definition. + 매개 변수 '{0}'의 '범위 지정' 한정자가 부분 메서드 선언과 일치하지 않습니다. @@ -3047,6 +3117,16 @@ 매개 변수에 람다의 매개 변수 한정자가 있지만 대상 대리자 형식에는 없습니다. + + Partial property declarations '{0}' and '{1}' have signature differences. + Partial property declarations '{0}' and '{1}' have signature differences. + + + + Partial property declarations have signature differences. + Partial property declarations have signature differences. + + Primary constructor parameter '{0}' is shadowed by a member from base. 기본의 멤버가 '{0}' 기본 생성자 매개 변수를 그림자처럼 따릅니다. @@ -4765,12 +4845,12 @@ Nullability of reference types in type of parameter '{0}' doesn't match partial method declaration. - '{0}' 매개 변수 형식에 있는 참조 형식 Null 허용 여부가 부분 메서드(Partial Method) 선언과 일치하지 않습니다. + '{0}' 매개 변수 형식에 있는 참조 형식 Null 허용 여부가 부분 메서드(Partial Method) 선언과 일치하지 않습니다. Nullability of reference types in type of parameter doesn't match partial method declaration. - 매개 변수 형식에 있는 참조 형식 Null 허용 여부가 부분 메서드(Partial Method) 선언과 일치하지 않습니다. + 매개 변수 형식에 있는 참조 형식 Null 허용 여부가 부분 메서드(Partial Method) 선언과 일치하지 않습니다. @@ -4815,12 +4895,12 @@ Nullability of reference types in return type doesn't match partial method declaration. - 반환 형식에 있는 참조 형식 Null 허용 여부가 부분 메서드 선언과 일치하지 않습니다. + 반환 형식에 있는 참조 형식 Null 허용 여부가 부분 메서드 선언과 일치하지 않습니다. Nullability of reference types in return type doesn't match partial method declaration. - 반환 형식에 있는 참조 형식 Null 허용 여부가 부분 메서드 선언과 일치하지 않습니다. + 반환 형식에 있는 참조 형식 Null 허용 여부가 부분 메서드 선언과 일치하지 않습니다. @@ -6524,8 +6604,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' 또는 메서드 반환 형식 바로 앞에만 올 수 있습니다. @@ -8132,18 +8212,8 @@ If such a class is used as a base class and if the deriving class defines a dest 람다 매개 변수가 일관성 없이 사용되었습니다. 매개 변수 형식은 모두 명시적이거나 암시적이어야 합니다. - - A partial method cannot have the 'abstract' modifier - 부분 메서드에는 'abstract' 한정자가 있을 수 없습니다. - - - - A partial method must be declared within a partial type - 부분 형식(Partial Type) 내에 부분 메서드가 선언되어야 합니다. - - - - A partial method may not explicitly implement an interface method + + A partial member may not explicitly implement an interface member 부분 메서드(Partial Method)는 인터페이스 메서드를 명시적으로 구현할 수 없습니다. @@ -8162,21 +8232,11 @@ If such a class is used as a base class and if the deriving class defines a dest 부분 메서드(Partial Method)에는 하나의 구현 선언만 사용할 수 있습니다. - - Both partial method declarations must use a params parameter or neither may use a params parameter - 두 부분 메서드(Partial Method) 선언 모두 params 매개 변수를 사용하거나 params 매개 변수를 사용할 수 없습니다. - - No defining declaration found for implementing declaration of partial method '{0}' '{0}' 부분 메서드(Partial Method)의 구현 선언에 대한 정의 선언이 없습니다. - - Both partial method declarations, '{0}' and '{1}', must use the same tuple element names. - 두 부분 메서드(Partial Method) 선언 '{0}' 및 '{1}' 모두에서 동일한 튜플 요소 이름을 사용해야 합니다. - - Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}' '{0}'의 부분 메서드(Partial method) 선언의 '{1}' 형식 매개 변수에 대한 제약 조건이 일관되지 않습니다. @@ -8187,16 +8247,11 @@ If such a class is used as a base class and if the deriving class defines a dest '{0}'은(는) 구현 선언이 없는 부분 메서드(Partial Method)이므로 이 메서드로부터 대리자를 만들 수 없습니다. - - Both partial method declarations must be static or neither may be static + + Both partial member declarations must be static or neither may be static 두 부분 메서드(Partial Method) 선언 모두 static이거나 static이 아니어야 합니다. - - Both partial method declarations must be unsafe or neither may be unsafe - 두 부분 메서드(Partial Method) 선언 모두 unsafe이거나 unsafe가 아니어야 합니다. - - Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees 정의 선언만 있는 부분 메서드(Partial Method) 또는 제거된 조건부 메서드는 식 트리에 사용할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 871593fc5cad5..c9daf489d2411 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1642,26 +1642,51 @@ Parametr params musi mieć prawidłowy typ kolekcji - - Both partial method declarations must have identical accessibility modifiers. - Obie deklaracje metody częściowej muszą mieć identyczne modyfikatory dostępności. + + Both partial member declarations must have identical accessibility modifiers. + Both partial member declarations must have identical accessibility modifiers. - - Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. - Obie deklaracje metody częściowej muszą mieć identyczne kombinacje modyfikatorów „virtual”, „override”, „sealed” i „new”. + + A partial member cannot have the 'abstract' modifier + A partial member cannot have the 'abstract' modifier + + + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. - - Both partial method declarations must be readonly or neither may be readonly - Obie metody częściowe muszą być zadeklarowane jako readonly lub żadna nie może być zadeklarowana jako readonly + + A partial member must be declared within a partial type + A partial member must be declared within a partial type - - Partial method declarations must have matching ref return values. + + Both partial member declarations must use a params parameter or neither may use a params parameter + Both partial member declarations must use a params parameter or neither may use a params parameter + + + + Both partial member declarations must be readonly or neither may be readonly + Both partial member declarations must be readonly or neither may be readonly + + + + Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + Obie deklaracje metody częściowej muszą mieć identyczne kombinacje modyfikatorów „virtual”, „override”, „sealed” i „new”. + + + + Partial member declarations must have matching ref return values. Deklaracje metody częściowej muszą mieć pasujące wartości zwracane przez odwołanie. + + Both partial member declarations must be unsafe or neither may be unsafe + Both partial member declarations must be unsafe or neither may be unsafe + + Both partial method declarations must have the same return type. Obie częściowe deklaracje metod muszą mieć taki sam zwracany typ. @@ -1687,6 +1712,51 @@ Metoda częściowa „{0}” musi mieć modyfikatory dostępności, ponieważ ma parametry „out”. + + A partial property may not have multiple defining declarations, and cannot be an auto-property. + A partial property may not have multiple defining declarations, and cannot be an auto-property. + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be '{1}' to match the definition part + Property accessor '{0}' must be '{1}' to match the definition part + + + + 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 a definition part. + Partial property '{0}' must have a definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Both partial property declarations must be required or neither may be required + Both partial property declarations must be required or neither may be required + + + + Both partial property declarations must have the same type. + Both partial property declarations must have the same type. + + + + 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. @@ -1953,8 +2023,8 @@ - The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration. - Modyfikator „scoped” parametru „{0}” nie jest zgodny z częściową deklaracją metody. + The 'scoped' modifier of parameter '{0}' doesn't match partial definition. + Modyfikator „scoped” parametru „{0}” nie jest zgodny z częściową deklaracją metody. @@ -3047,6 +3117,16 @@ Parametr ma modyfikator params w wyrażeniu lambda, ale nie ma w docelowym typie delegata. + + Partial property declarations '{0}' and '{1}' have signature differences. + Partial property declarations '{0}' and '{1}' have signature differences. + + + + Partial property declarations have signature differences. + Partial property declarations have signature differences. + + Primary constructor parameter '{0}' is shadowed by a member from base. Podstawowy parametr konstruktora „{0}” jest w tle przez składową z bazy. @@ -4765,12 +4845,12 @@ Nullability of reference types in type of parameter '{0}' doesn't match partial method declaration. - Obsługa wartości null dla typów referencyjnych w typie parametru „{0}” jest niezgodna z częściową deklaracją metody. + Obsługa wartości null dla typów referencyjnych w typie parametru „{0}” jest niezgodna z częściową deklaracją metody. Nullability of reference types in type of parameter doesn't match partial method declaration. - Obsługa wartości null dla typów referencyjnych w typie parametru jest niezgodna z częściową deklaracją metody. + Obsługa wartości null dla typów referencyjnych w typie parametru jest niezgodna z częściową deklaracją metody. @@ -4815,12 +4895,12 @@ Nullability of reference types in return type doesn't match partial method declaration. - Nullowalność typów referencyjnych w zwracanym typie jest niezgodna z częściową deklaracją metody. + Nullowalność typów referencyjnych w zwracanym typie jest niezgodna z częściową deklaracją metody. Nullability of reference types in return type doesn't match partial method declaration. - Nullowalność typów referencyjnych w zwracanym typie jest niezgodna z częściową deklaracją metody. + Nullowalność typów referencyjnych w zwracanym typie jest niezgodna z częściową deklaracją metody. @@ -6524,8 +6604,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. @@ -8132,18 +8212,8 @@ Jeśli taka klasa zostanie użyta jako klasa bazowa i klasa pochodna definiuje d Niespójne użycie parametrów lambda. Wszystkie typy parametrów muszą być albo jawne, albo niejawne. - - A partial method cannot have the 'abstract' modifier - Metoda częściowa nie może mieć modyfikatora „abstract” - - - - A partial method must be declared within a partial type - Metoda częściowa musi być zadeklarowana w typie częściowym. - - - - A partial method may not explicitly implement an interface method + + A partial member may not explicitly implement an interface member Metoda częściowa nie może jawnie implementować metody interfejsu. @@ -8162,21 +8232,11 @@ Jeśli taka klasa zostanie użyta jako klasa bazowa i klasa pochodna definiuje d Metoda częściowa nie może mieć wielu deklaracji implementujących. - - Both partial method declarations must use a params parameter or neither may use a params parameter - Obie częściowe deklaracje metody muszą używać parametru params lub żadna nie może go używać - - No defining declaration found for implementing declaration of partial method '{0}' Nie znaleziono deklaracji definiującej na potrzeby implementowania częściowej metody „{0}” - - Both partial method declarations, '{0}' and '{1}', must use the same tuple element names. - Obydwie częściowe deklaracje metody, „{0}” i „{1}”, muszą korzystać z tych samych nazw elementów krotki. - - Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}' Deklaracje metod częściowych elementu „{0}” mają niespójne ograniczenia dla parametru typu „{1}” @@ -8187,16 +8247,11 @@ Jeśli taka klasa zostanie użyta jako klasa bazowa i klasa pochodna definiuje d Nie można utworzyć delegata z metody „{0}”, ponieważ jest to metoda częściowa bez deklaracji implementującej. - - Both partial method declarations must be static or neither may be static + + Both partial member declarations must be static or neither may be static Obie deklaracje metody częściowej muszą być statyczne albo żadna z nich nie może być statyczna. - - Both partial method declarations must be unsafe or neither may be unsafe - Obie deklaracje metody częściowej muszą być niezabezpieczone albo żadna z nich nie może być niezabezpieczona. - - Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees W drzewach wyrażeń nie można używać metod częściowych zawierających tylko deklarację definiującą ani usuniętych metod warunkowych. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index a4f21ff34b491..6a17c009cf8d4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1642,26 +1642,51 @@ O parâmetro params deve ter um tipo de coleção válido - - Both partial method declarations must have identical accessibility modifiers. - As duas declarações de métodos parciais precisam ter modificadores de acessibilidade idênticos. + + Both partial member declarations must have identical accessibility modifiers. + Both partial member declarations must have identical accessibility modifiers. - - Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. - As duas declarações de métodos parciais precisam ter combinações idênticas dos modificadores 'virtual', 'override', 'sealed' e 'new'. + + A partial member cannot have the 'abstract' modifier + A partial member cannot have the 'abstract' modifier + + + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. - - Both partial method declarations must be readonly or neither may be readonly - As duas declarações de métodos parciais precisam ser readonly ou nenhuma deve ser readonly + + A partial member must be declared within a partial type + A partial member must be declared within a partial type - - Partial method declarations must have matching ref return values. + + Both partial member declarations must use a params parameter or neither may use a params parameter + Both partial member declarations must use a params parameter or neither may use a params parameter + + + + Both partial member declarations must be readonly or neither may be readonly + Both partial member declarations must be readonly or neither may be readonly + + + + Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + As duas declarações de métodos parciais precisam ter combinações idênticas dos modificadores 'virtual', 'override', 'sealed' e 'new'. + + + + Partial member declarations must have matching ref return values. As declarações de método parcial precisam ter valores de retorno de referência correspondentes. + + Both partial member declarations must be unsafe or neither may be unsafe + Both partial member declarations must be unsafe or neither may be unsafe + + Both partial method declarations must have the same return type. As duas declarações de método parcial precisam ter o mesmo tipo de retorno. @@ -1687,6 +1712,51 @@ 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, and cannot be an auto-property. + A partial property may not have multiple defining declarations, and cannot be an auto-property. + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be '{1}' to match the definition part + Property accessor '{0}' must be '{1}' to match the definition part + + + + 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 a definition part. + Partial property '{0}' must have a definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Both partial property declarations must be required or neither may be required + Both partial property declarations must be required or neither may be required + + + + Both partial property declarations must have the same type. + Both partial property declarations must have the same type. + + + + 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. @@ -1953,8 +2023,8 @@ - The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration. - O modificador 'scoped' do parâmetro '{0}' não corresponde à declaração de método parcial. + The 'scoped' modifier of parameter '{0}' doesn't match partial definition. + O modificador 'scoped' do parâmetro '{0}' não corresponde à declaração de método parcial. @@ -3047,6 +3117,16 @@ O parâmetro tem modificador de parâmetros em lambda, mas não no tipo delegado de destino. + + Partial property declarations '{0}' and '{1}' have signature differences. + Partial property declarations '{0}' and '{1}' have signature differences. + + + + Partial property declarations have signature differences. + Partial property declarations have signature differences. + + Primary constructor parameter '{0}' is shadowed by a member from base. O parâmetro '{0}' do construtor primário é sombreado por um membro da base. @@ -4765,12 +4845,12 @@ Nullability of reference types in type of parameter '{0}' doesn't match partial method declaration. - A anulabilidade de tipos de referência em tipo de parâmetro '{0}' não corresponde à declaração de método parcial. + A anulabilidade de tipos de referência em tipo de parâmetro '{0}' não corresponde à declaração de método parcial. Nullability of reference types in type of parameter doesn't match partial method declaration. - A anulabilidade de tipos de referência em tipo de parâmetro não corresponde à declaração de método parcial. + A anulabilidade de tipos de referência em tipo de parâmetro não corresponde à declaração de método parcial. @@ -4815,12 +4895,12 @@ Nullability of reference types in return type doesn't match partial method declaration. - A nulidade dos tipos de referência no tipo de retorno não corresponde à declaração de método parcial. + A nulidade dos tipos de referência no tipo de retorno não corresponde à declaração de método parcial. Nullability of reference types in return type doesn't match partial method declaration. - A nulidade dos tipos de referência no tipo de retorno não corresponde à declaração de método parcial. + A nulidade dos tipos de referência no tipo de retorno não corresponde à declaração de método parcial. @@ -6524,8 +6604,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. @@ -8132,18 +8212,8 @@ Se tal classe for usada como uma classe base e se a classe derivada definir um d Utilização inconsistente do parâmetro lambda; todos os tipos de parâmetros devem ser explícitos ou implícitos - - A partial method cannot have the 'abstract' modifier - Um método parcial não pode ter o modificador 'abstract' - - - - A partial method must be declared within a partial type - Um método parcial precisa ser declarado em um tipo parcial - - - - A partial method may not explicitly implement an interface method + + A partial member may not explicitly implement an interface member Um método parcial não pode implementar explicitamente um método de interface @@ -8162,21 +8232,11 @@ Se tal classe for usada como uma classe base e se a classe derivada definir um d Um método parcial não pode ter várias declarações de implementação - - Both partial method declarations must use a params parameter or neither may use a params parameter - As duas declarações do método parcial devem usar um parâmetro params ou nenhuma delas pode usar um parâmetro params - - No defining declaration found for implementing declaration of partial method '{0}' Nenhuma declaração de definição encontrada para implementar a declaração de método parcial "{0}" - - Both partial method declarations, '{0}' and '{1}', must use the same tuple element names. - Ambas as declarações de método parciais, '{0}' e '{1}', devem usar os mesmos elementos de nome de tupla. - - Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}' Declarações de método parciais de '{0}' têm restrições inconsistentes para o parâmetro de tipo '{1}' @@ -8187,16 +8247,11 @@ Se tal classe for usada como uma classe base e se a classe derivada definir um d Não é possível criar representante do método "{0}" porque ele é um método parcial sem declaração de implementação - - Both partial method declarations must be static or neither may be static + + Both partial member declarations must be static or neither may be static As duas declarações de métodos parciais devem ser estáticas ou nenhuma delas deve ser desse tipo - - Both partial method declarations must be unsafe or neither may be unsafe - As duas declarações de métodos parciais devem ser inseguras ou nenhuma delas deve ser desse tipo - - Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees Os métodos parciais com apenas uma declaração de definição ou métodos condicionais removidos não podem ser usados em árvores de expressão diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 7bc750767181d..6d5a28f4c0302 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1642,26 +1642,51 @@ Параметр params должен иметь допустимый тип коллекции. - - Both partial method declarations must have identical accessibility modifiers. - Оба объявления разделяемого метода должны иметь одинаковые модификаторы доступа. + + Both partial member declarations must have identical accessibility modifiers. + Both partial member declarations must have identical accessibility modifiers. - - Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. - Оба объявления разделяемого метода должны иметь одинаковые сочетания модификаторов "virtual", "override", "sealed" и "new". + + A partial member cannot have the 'abstract' modifier + A partial member cannot have the 'abstract' modifier + + + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. - - Both partial method declarations must be readonly or neither may be readonly - Либо оба объявления разделяемого метода должны иметь модификатор readonly, либо ни одно из них не должно иметь модификатор readonly. + + A partial member must be declared within a partial type + A partial member must be declared within a partial type - - Partial method declarations must have matching ref return values. + + Both partial member declarations must use a params parameter or neither may use a params parameter + Both partial member declarations must use a params parameter or neither may use a params parameter + + + + Both partial member declarations must be readonly or neither may be readonly + Both partial member declarations must be readonly or neither may be readonly + + + + Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + Оба объявления разделяемого метода должны иметь одинаковые сочетания модификаторов "virtual", "override", "sealed" и "new". + + + + Partial member declarations must have matching ref return values. Объявления разделяемого метода должны иметь одинаковые типы возвращаемого значения ref. + + Both partial member declarations must be unsafe or neither may be unsafe + Both partial member declarations must be unsafe or neither may be unsafe + + Both partial method declarations must have the same return type. Оба объявления разделяемого метода должны иметь одинаковый тип возвращаемого значения. @@ -1687,6 +1712,51 @@ Разделяемый метод "{0}" должен иметь модификаторы доступа, так как он содержит параметры "out". + + A partial property may not have multiple defining declarations, and cannot be an auto-property. + A partial property may not have multiple defining declarations, and cannot be an auto-property. + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be '{1}' to match the definition part + Property accessor '{0}' must be '{1}' to match the definition part + + + + 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 a definition part. + Partial property '{0}' must have a definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Both partial property declarations must be required or neither may be required + Both partial property declarations must be required or neither may be required + + + + Both partial property declarations must have the same type. + Both partial property declarations must have the same type. + + + + 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}". Используйте вместо этого пустую строку. @@ -1953,8 +2023,8 @@ - The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration. - Модификатор "scoped" параметра "{0}" не соответствует объявлению разделяемого метода. + The 'scoped' modifier of parameter '{0}' doesn't match partial definition. + Модификатор "scoped" параметра "{0}" не соответствует объявлению разделяемого метода. @@ -3047,6 +3117,16 @@ У параметра есть модификатор params в лямбде, но не в типе целевого делегата. + + Partial property declarations '{0}' and '{1}' have signature differences. + Partial property declarations '{0}' and '{1}' have signature differences. + + + + Partial property declarations have signature differences. + Partial property declarations have signature differences. + + Primary constructor parameter '{0}' is shadowed by a member from base. Параметр "{0}" первичной конструкции затемнен элементом из базы. @@ -4766,12 +4846,12 @@ Nullability of reference types in type of parameter '{0}' doesn't match partial method declaration. - Допустимость значения NULL для ссылочных типов в типе параметра "{0}" не совпадает с частичным объявлением метода. + Допустимость значения NULL для ссылочных типов в типе параметра "{0}" не совпадает с частичным объявлением метода. Nullability of reference types in type of parameter doesn't match partial method declaration. - Допустимость значения NULL для ссылочных типов в типе параметра не совпадает с частичным объявлением метода. + Допустимость значения NULL для ссылочных типов в типе параметра не совпадает с частичным объявлением метода. @@ -4816,12 +4896,12 @@ Nullability of reference types in return type doesn't match partial method declaration. - Допустимость значения NULL для ссылочных типов в типе возвращаемого значения не совпадает с объявлением разделяемого метода. + Допустимость значения NULL для ссылочных типов в типе возвращаемого значения не совпадает с объявлением разделяемого метода. Nullability of reference types in return type doesn't match partial method declaration. - Допустимость значения NULL для ссылочных типов в типе возвращаемого значения не совпадает с объявлением разделяемого метода. + Допустимость значения NULL для ссылочных типов в типе возвращаемого значения не совпадает с объявлением разделяемого метода. @@ -6525,8 +6605,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" и перед возвращаемым типом метода. @@ -8133,18 +8213,8 @@ If such a class is used as a base class and if the deriving class defines a dest Несовместимое использование лямбда-параметра; типы параметров должны быть либо все явными, либо все неявными. - - A partial method cannot have the 'abstract' modifier - Разделяемый метод не может иметь модификатор "abstract". - - - - A partial method must be declared within a partial type - Разделяемый метод должен быть объявлен в разделяемом типе. - - - - A partial method may not explicitly implement an interface method + + A partial member may not explicitly implement an interface member Разделяемый метод не может явно реализовывать метод интерфейса. @@ -8163,21 +8233,11 @@ If such a class is used as a base class and if the deriving class defines a dest Разделяемый метод не может иметь несколько реализующих объявлений. - - Both partial method declarations must use a params parameter or neither may use a params parameter - Параметр params должен использоваться в обоих объявлениях разделяемого метода или не должен использоваться ни в одном из них. - - No defining declaration found for implementing declaration of partial method '{0}' Отсутствует определяющее объявление для реализующего объявления разделяемого метода "{0}". - - Both partial method declarations, '{0}' and '{1}', must use the same tuple element names. - Оба объявления частичного метода, "{0}" и "{1}", должны использовать одинаковые имена элементов кортежа. - - Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}' Несогласованные ограничения для параметра типа "{1}" в частичных объявлениях метода "{0}". @@ -8188,16 +8248,11 @@ If such a class is used as a base class and if the deriving class defines a dest Невозможно создать делегат на основе метода "{0}, так как он является разделяемым методом без реализующего объявления. - - Both partial method declarations must be static or neither may be static + + Both partial member declarations must be static or neither may be static Объявления разделяемого метода либо оба должны иметь модификаторы static, либо ни одно из объявлений не должно иметь модификатора static. - - Both partial method declarations must be unsafe or neither may be unsafe - Либо оба объявления разделяемого метода должны иметь модификаторы unsafe, либо ни одно из объявлений не должно иметь модификатора unsafe. - - Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees В деревьях выражений не могут использоваться разделяемые методы, имеющие только определяющее объявление или только удаленные условные методы. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 61a78dded475d..830360d27c15b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1642,26 +1642,51 @@ Params parametresi geçerli bir koleksiyon türüne sahip olmalıdır - - Both partial method declarations must have identical accessibility modifiers. - Her iki kısmi metot bildirimi aynı erişilebilirlik değiştiricilerine sahip olmalıdır. + + Both partial member declarations must have identical accessibility modifiers. + Both partial member declarations must have identical accessibility modifiers. - - Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. - Her iki kısmi metot bildirimi 'virtual', 'override', 'sealed' ve 'new' değiştiricilerinde oluşan aynı bileşimlere sahip olmalıdır. + + A partial member cannot have the 'abstract' modifier + A partial member cannot have the 'abstract' modifier + + + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. - - Both partial method declarations must be readonly or neither may be readonly - İki kısmi yöntem bildiriminin de saltokunur olması ya da hiçbirinin saltokunur olmaması gerekir + + A partial member must be declared within a partial type + A partial member must be declared within a partial type - - Partial method declarations must have matching ref return values. + + Both partial member declarations must use a params parameter or neither may use a params parameter + Both partial member declarations must use a params parameter or neither may use a params parameter + + + + Both partial member declarations must be readonly or neither may be readonly + Both partial member declarations must be readonly or neither may be readonly + + + + Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + Her iki kısmi metot bildirimi 'virtual', 'override', 'sealed' ve 'new' değiştiricilerinde oluşan aynı bileşimlere sahip olmalıdır. + + + + Partial member declarations must have matching ref return values. Kısmi yöntem bildirimlerinin eşleşen başvuru dönüş değerleri olmalıdır. + + Both partial member declarations must be unsafe or neither may be unsafe + Both partial member declarations must be unsafe or neither may be unsafe + + Both partial method declarations must have the same return type. Her iki kısmi yöntem bildirimi aynı dönüş türüne sahip olmalıdır. @@ -1687,6 +1712,51 @@ '{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, and cannot be an auto-property. + A partial property may not have multiple defining declarations, and cannot be an auto-property. + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be '{1}' to match the definition part + Property accessor '{0}' must be '{1}' to match the definition part + + + + 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 a definition part. + Partial property '{0}' must have a definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Both partial property declarations must be required or neither may be required + Both partial property declarations must be required or neither may be required + + + + Both partial property declarations must have the same type. + Both partial property declarations must have the same type. + + + + 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. @@ -1953,8 +2023,8 @@ - The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration. - '{0}' parametresinin 'scoped' değiştiricisi, kısmi yöntem bildirimiyle eşleşmiyor. + The 'scoped' modifier of parameter '{0}' doesn't match partial definition. + '{0}' parametresinin 'scoped' değiştiricisi, kısmi yöntem bildirimiyle eşleşmiyor. @@ -3047,6 +3117,16 @@ Parametre, lambda içinde parametre değiştiricisi içeriyor ancak hedef temsilci türünde içermiyor. + + Partial property declarations '{0}' and '{1}' have signature differences. + Partial property declarations '{0}' and '{1}' have signature differences. + + + + Partial property declarations have signature differences. + Partial property declarations have signature differences. + + Primary constructor parameter '{0}' is shadowed by a member from base. '{0}' birincil oluşturucu parametresi, temel üye tarafından gölgelendi. @@ -4765,12 +4845,12 @@ Nullability of reference types in type of parameter '{0}' doesn't match partial method declaration. - '{0}' parametre türündeki başvuru türlerinin boş değer atanabilirliği kısmi metot bildirimi ile eşleşmiyor. + '{0}' parametre türündeki başvuru türlerinin boş değer atanabilirliği kısmi metot bildirimi ile eşleşmiyor. Nullability of reference types in type of parameter doesn't match partial method declaration. - Parametre türündeki başvuru türlerinin boş değer atanabilirliği kısmi metot bildirimi ile eşleşmiyor. + Parametre türündeki başvuru türlerinin boş değer atanabilirliği kısmi metot bildirimi ile eşleşmiyor. @@ -4815,12 +4895,12 @@ Nullability of reference types in return type doesn't match partial method declaration. - Dönüş türündeki başvuru türlerinin null değer atanabilirliği kısmi metot bildirimiyle eşleşmiyor. + Dönüş türündeki başvuru türlerinin null değer atanabilirliği kısmi metot bildirimiyle eşleşmiyor. Nullability of reference types in return type doesn't match partial method declaration. - Dönüş türündeki başvuru türlerinin null değer atanabilirliği kısmi metot bildirimiyle eşleşmiyor. + Dönüş türündeki başvuru türlerinin null değer atanabilirliği kısmi metot bildirimiyle eşleşmiyor. @@ -6524,8 +6604,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. @@ -8132,18 +8212,8 @@ Bu sınıf temel sınıf olarak kullanılırsa ve türetilen sınıf bir yıkıc Tutarsız lambda parametresi kullanımı; parametre türlerinin tümü explicit veya tümü implicit olmalıdır - - A partial method cannot have the 'abstract' modifier - Kısmi metot 'abstract' değiştiricisine sahip olamaz - - - - A partial method must be declared within a partial type - Parçalı bir metodun parçalı tür içinde bildirilmesi gerekir - - - - A partial method may not explicitly implement an interface method + + A partial member may not explicitly implement an interface member Kısmi bir yöntem bir arabirim yöntemini açık olarak uygulayamaz @@ -8162,21 +8232,11 @@ Bu sınıf temel sınıf olarak kullanılırsa ve türetilen sınıf bir yıkıc Kısmi yöntemin birden fazla uygulama bildirimi olamaz - - Both partial method declarations must use a params parameter or neither may use a params parameter - Her iki kısmi yöntem bildirimi de bir params parametresi kullanmalı ya da hiçbiri kullanmamalıdır - - No defining declaration found for implementing declaration of partial method '{0}' '{0}' kısmi yönteminin bildirimini uygulamak için tanımlayıcı bildirim bulunamadı - - Both partial method declarations, '{0}' and '{1}', must use the same tuple element names. - Kısmi metot bildirimlerinin ikisi de ('{0}' ve '{1}') aynı demet öğesi adını kullanmalıdır. - - Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}' '{0}' öğesinin kısmi metot bildirimleri, '{1}' tür parametresi için tutarsız kısıtlamalara sahip @@ -8187,16 +8247,11 @@ Bu sınıf temel sınıf olarak kullanılırsa ve türetilen sınıf bir yıkıc Uygulama bildirimi olmayan bir kısmi yöntem olduğundan '{0}' yönteminden temsilci oluşturulamıyor - - Both partial method declarations must be static or neither may be static + + Both partial member declarations must be static or neither may be static İki kısmi yöntem bildiriminin de statik olması ya da hiçbirinin statik olmaması gerekir - - Both partial method declarations must be unsafe or neither may be unsafe - İki kısmi yöntem bildiriminin de güvensiz olması ya da hiçbirinin güvensiz olmaması gerekir - - Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees Yalnızca bir tanımlama bildirimi olan kısmi yöntemler veya kaldırılmış koşullu yöntemler ifade ağaçlarında kullanılamaz diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index b0175426fa302..10f5f4a9cf404 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1642,26 +1642,51 @@ Params 参数必须具有有效的集合类型 - - Both partial method declarations must have identical accessibility modifiers. - 两个分部方法声明必须具有相同的可访问性修饰符。 + + Both partial member declarations must have identical accessibility modifiers. + Both partial member declarations must have identical accessibility modifiers. - - Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. - 两个分部方法声明必须具有 "virtual"、"override"、"sealed" 和 "new" 修饰符的相同组合。 + + A partial member cannot have the 'abstract' modifier + A partial member cannot have the 'abstract' modifier + + + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. - - Both partial method declarations must be readonly or neither may be readonly - 两个分部方法声明必须都是只读声明,或者两者都不能是只读声明 + + A partial member must be declared within a partial type + A partial member must be declared within a partial type - - Partial method declarations must have matching ref return values. + + Both partial member declarations must use a params parameter or neither may use a params parameter + Both partial member declarations must use a params parameter or neither may use a params parameter + + + + Both partial member declarations must be readonly or neither may be readonly + Both partial member declarations must be readonly or neither may be readonly + + + + Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + 两个分部方法声明必须具有 "virtual"、"override"、"sealed" 和 "new" 修饰符的相同组合。 + + + + Partial member declarations must have matching ref return values. 分部方法声明必须具有匹配的引用返回值。 + + Both partial member declarations must be unsafe or neither may be unsafe + Both partial member declarations must be unsafe or neither may be unsafe + + Both partial method declarations must have the same return type. 两个分部方法声明必须具有相同的返回类型。 @@ -1687,6 +1712,51 @@ 分部方法“{0}”必须具有可访问性修饰符,因为它具有 "out" 参数。 + + A partial property may not have multiple defining declarations, and cannot be an auto-property. + A partial property may not have multiple defining declarations, and cannot be an auto-property. + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be '{1}' to match the definition part + Property accessor '{0}' must be '{1}' to match the definition part + + + + 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 a definition part. + Partial property '{0}' must have a definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Both partial property declarations must be required or neither may be required + Both partial property declarations must be required or neither may be required + + + + Both partial property declarations must have the same type. + Both partial property declarations must have the same type. + + + + 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}' 的模式。请改用空字符串。 @@ -1953,8 +2023,8 @@ - The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration. - 参数 "{0}" 的 "scoped" 修饰符与部分方法声明不匹配。 + The 'scoped' modifier of parameter '{0}' doesn't match partial definition. + 参数 "{0}" 的 "scoped" 修饰符与部分方法声明不匹配。 @@ -3047,6 +3117,16 @@ 参数在 lambda 中具有参数修饰符,但在目标委托类型中没有参数修饰符。 + + Partial property declarations '{0}' and '{1}' have signature differences. + Partial property declarations '{0}' and '{1}' have signature differences. + + + + Partial property declarations have signature differences. + Partial property declarations have signature differences. + + Primary constructor parameter '{0}' is shadowed by a member from base. 主要构造函数参数“{0}”由基成员隐藏。 @@ -4765,12 +4845,12 @@ Nullability of reference types in type of parameter '{0}' doesn't match partial method declaration. - 参数“{0}”类型中引用类型的为 Null 性与分部方法声明不匹配。 + 参数“{0}”类型中引用类型的为 Null 性与分部方法声明不匹配。 Nullability of reference types in type of parameter doesn't match partial method declaration. - 参数类型中引用类型的为 Null 性与分部方法声明不匹配。 + 参数类型中引用类型的为 Null 性与分部方法声明不匹配。 @@ -4815,12 +4895,12 @@ Nullability of reference types in return type doesn't match partial method declaration. - 返回类型中引用类型的为 Null 性与分部方法声明不匹配。 + 返回类型中引用类型的为 Null 性与分部方法声明不匹配。 Nullability of reference types in return type doesn't match partial method declaration. - 返回类型中引用类型的为 Null 性与分部方法声明不匹配。 + 返回类型中引用类型的为 Null 性与分部方法声明不匹配。 @@ -6524,8 +6604,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" 或方法返回类型。 @@ -8132,18 +8212,8 @@ If such a class is used as a base class and if the deriving class defines a dest lambda 参数的用法不一致;参数类型必须全部为显式或全部为隐式 - - A partial method cannot have the 'abstract' modifier - 分部方法不能具有 "abstract" 修饰符 - - - - A partial method must be declared within a partial type - 分部方法必须在分部类型内声明 - - - - A partial method may not explicitly implement an interface method + + A partial member may not explicitly implement an interface member 分部方法不能显式实现接口方法 @@ -8162,21 +8232,11 @@ If such a class is used as a base class and if the deriving class defines a dest 分部方法不能有多个实现声明 - - Both partial method declarations must use a params parameter or neither may use a params parameter - 两种分部方法声明必须要么都使用 params 参数,要么都不使用 params 参数 - - No defining declaration found for implementing declaration of partial method '{0}' 没有为分部方法“{0}”的实现声明找到定义声明 - - Both partial method declarations, '{0}' and '{1}', must use the same tuple element names. - 两种分部方法声明(“{0}”和“{1}”)都必须使用相同的元组元素名称。 - - Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}' “{0}”的分部方法声明对类型参数“{1}”的约束不一致 @@ -8187,16 +8247,11 @@ If such a class is used as a base class and if the deriving class defines a dest 无法通过方法“{0}”创建委托,因为该方法是没有实现声明的分部方法 - - Both partial method declarations must be static or neither may be static + + Both partial member declarations must be static or neither may be static 两个分部方法声明必须都是静态声明,或者两者都不能是静态声明 - - Both partial method declarations must be unsafe or neither may be unsafe - 两个分部方法声明必须都是不安全声明,或者两者都不能是不安全声明 - - Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees 不能在表达式树中使用只有定义声明的分部方法或已移除的条件方法 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 59932f5945760..17836b6cf64a1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1642,26 +1642,51 @@ params 參數必須具有有效的集合型別 - - Both partial method declarations must have identical accessibility modifiers. - 兩個部分方法宣告都必須有完全相同的存取範圍修飾詞。 + + Both partial member declarations must have identical accessibility modifiers. + Both partial member declarations must have identical accessibility modifiers. - - Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. - 兩個部分方法宣告都必須有完全相同的 'virtual'、'override'、'sealed' 及 'new' 修飾元組合。 + + A partial member cannot have the 'abstract' modifier + A partial member cannot have the 'abstract' modifier + + + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. - - Both partial method declarations must be readonly or neither may be readonly - 兩個部份方法宣告必須都為唯讀,或者都不為唯讀 + + A partial member must be declared within a partial type + A partial member must be declared within a partial type - - Partial method declarations must have matching ref return values. + + Both partial member declarations must use a params parameter or neither may use a params parameter + Both partial member declarations must use a params parameter or neither may use a params parameter + + + + Both partial member declarations must be readonly or neither may be readonly + Both partial member declarations must be readonly or neither may be readonly + + + + Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + 兩個部分方法宣告都必須有完全相同的 'virtual'、'override'、'sealed' 及 'new' 修飾元組合。 + + + + Partial member declarations must have matching ref return values. 部分方法宣告必須有相符的參考傳回值。 + + Both partial member declarations must be unsafe or neither may be unsafe + Both partial member declarations must be unsafe or neither may be unsafe + + Both partial method declarations must have the same return type. 兩個部分方法宣告都必須有相同的傳回型別。 @@ -1687,6 +1712,51 @@ 因為部分方法 '{0}' 有 'out' 參數,所以其必須有存取範圍修飾詞。 + + A partial property may not have multiple defining declarations, and cannot be an auto-property. + A partial property may not have multiple defining declarations, and cannot be an auto-property. + + + + A partial property may not have multiple implementing declarations + A partial property may not have multiple implementing declarations + + + + Property accessor '{0}' must be '{1}' to match the definition part + Property accessor '{0}' must be '{1}' to match the definition part + + + + 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 a definition part. + Partial property '{0}' must have a definition part. + + + + Partial property '{0}' must have an implementation part. + Partial property '{0}' must have an implementation part. + + + + Both partial property declarations must be required or neither may be required + Both partial property declarations must be required or neither may be required + + + + Both partial property declarations must have the same type. + Both partial property declarations must have the same type. + + + + 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}' 的模式。請改為使用空字串。 @@ -1953,8 +2023,8 @@ - The 'scoped' modifier of parameter '{0}' doesn't match partial method declaration. - 參數 '{0}' 的 'scoped' 修飾元不符合部分方法宣告。 + The 'scoped' modifier of parameter '{0}' doesn't match partial definition. + 參數 '{0}' 的 'scoped' 修飾元不符合部分方法宣告。 @@ -3047,6 +3117,16 @@ 參數在 Lambda 中具有參數修飾元,但不在目標委派類型中。 + + Partial property declarations '{0}' and '{1}' have signature differences. + Partial property declarations '{0}' and '{1}' have signature differences. + + + + Partial property declarations have signature differences. + Partial property declarations have signature differences. + + Primary constructor parameter '{0}' is shadowed by a member from base. 主要建構函式參數 '{0}' 會伴隨著來自基底的成員。 @@ -4765,12 +4845,12 @@ strument:TestCoverage 產生檢測要收集 Nullability of reference types in type of parameter '{0}' doesn't match partial method declaration. - 參數 '{0}' 型別中參考型別的可 Null 性與部分方法宣告不符合。 + 參數 '{0}' 型別中參考型別的可 Null 性與部分方法宣告不符合。 Nullability of reference types in type of parameter doesn't match partial method declaration. - 參數型別中參考型別的可 Null 性與部分方法宣告不符合。 + 參數型別中參考型別的可 Null 性與部分方法宣告不符合。 @@ -4815,12 +4895,12 @@ strument:TestCoverage 產生檢測要收集 Nullability of reference types in return type doesn't match partial method declaration. - 傳回型別中參考型別的可 Null 性與部分方法宣告不符。 + 傳回型別中參考型別的可 Null 性與部分方法宣告不符。 Nullability of reference types in return type doesn't match partial method declaration. - 傳回型別中參考型別的可 Null 性與部分方法宣告不符。 + 傳回型別中參考型別的可 Null 性與部分方法宣告不符。 @@ -6524,8 +6604,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' 或方法傳回型別之前。 @@ -8132,18 +8212,8 @@ If such a class is used as a base class and if the deriving class defines a dest Lambda 參數用法不一致; 參數類型必須全部為明確類型或全部為隱含類型 - - A partial method cannot have the 'abstract' modifier - 部分方法不能有 'abstract' 修飾元 - - - - A partial method must be declared within a partial type - 在部分型別中必須宣告部分方法 - - - - A partial method may not explicitly implement an interface method + + A partial member may not explicitly implement an interface member 部分方法不可明確地實作介面方法 @@ -8162,21 +8232,11 @@ If such a class is used as a base class and if the deriving class defines a dest 部分方法不能有多重實作的宣告 - - Both partial method declarations must use a params parameter or neither may use a params parameter - 兩個部分方法宣告都必須使用 params 參數,或兩者都不使用 params 參數 - - No defining declaration found for implementing declaration of partial method '{0}' 找不到用以實作部分方法 '{0}' 宣告的定義宣告 - - Both partial method declarations, '{0}' and '{1}', must use the same tuple element names. - 部份方法宣告 '{0}' 與 '{1}' 必須使用相同的元組元素名稱。 - - Partial method declarations of '{0}' have inconsistent constraints for type parameter '{1}' '{0}' 的部分方法宣告對型別參數 '{1}' 有不一致的條件約束 @@ -8187,16 +8247,11 @@ If such a class is used as a base class and if the deriving class defines a dest 無法從方法 '{0}' 建立委派,因為它是無實作宣告的部分方法 - - Both partial method declarations must be static or neither may be static + + Both partial member declarations must be static or neither may be static 兩個部分方法宣告必須都是靜態,或者都不是靜態 - - Both partial method declarations must be unsafe or neither may be unsafe - 兩個部分方法宣告必須都是 unsafe,或者都不是 unsafe - - Partial methods with only a defining declaration or removed conditional methods cannot be used in expression trees 在運算式樹狀結構中,不可使用只具有定義宣告或已移除條件式方法的部分方法 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs index 7f3182aab7847..936f8a364de18 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs @@ -21007,15 +21007,15 @@ partial void M3((int a, int b) y) { } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (10,18): error CS8142: Both partial method declarations, 'C.M1((int a, int b))' and 'C.M1((int notA, int notB))', must use the same tuple element names. + // (10,18): error CS8142: Both partial member declarations, 'C.M1((int a, int b))' and 'C.M1((int notA, int notB))', must use the same tuple element names. // partial void M1((int notA, int notB) y) { } - Diagnostic(ErrorCode.ERR_PartialMethodInconsistentTupleNames, "M1").WithArguments("C.M1((int a, int b))", "C.M1((int notA, int notB))").WithLocation(10, 18), + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "M1").WithArguments("C.M1((int a, int b))", "C.M1((int notA, int notB))").WithLocation(10, 18), // (10,18): warning CS8826: Partial method declarations 'void C.M1((int a, int b) x)' and 'void C.M1((int notA, int notB) y)' have signature differences. // partial void M1((int notA, int notB) y) { } Diagnostic(ErrorCode.WRN_PartialMethodTypeDifference, "M1").WithArguments("void C.M1((int a, int b) x)", "void C.M1((int notA, int notB) y)").WithLocation(10, 18), - // (11,18): error CS8142: Both partial method declarations, 'C.M2((int a, int b))' and 'C.M2((int, int))', must use the same tuple element names. + // (11,18): error CS8142: Both partial member declarations, 'C.M2((int a, int b))' and 'C.M2((int, int))', must use the same tuple element names. // partial void M2((int, int) y) { } - Diagnostic(ErrorCode.ERR_PartialMethodInconsistentTupleNames, "M2").WithArguments("C.M2((int a, int b))", "C.M2((int, int))").WithLocation(11, 18), + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "M2").WithArguments("C.M2((int a, int b))", "C.M2((int, int))").WithLocation(11, 18), // (11,18): warning CS8826: Partial method declarations 'void C.M2((int a, int b) x)' and 'void C.M2((int, int) y)' have signature differences. // partial void M2((int, int) y) { } Diagnostic(ErrorCode.WRN_PartialMethodTypeDifference, "M2").WithArguments("void C.M2((int a, int b) x)", "void C.M2((int, int) y)").WithLocation(11, 18), diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs index f3e87f6d8dee1..bb837a94bb3fc 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs @@ -5262,20 +5262,20 @@ internal enum ErrorCode ERR_InvalidAnonymousTypeMemberDeclarator = 746, ERR_InvalidInitializerElementInitializer = 747, ERR_InconsistentLambdaParameterUsage = 748, - ERR_PartialMethodInvalidModifier = 750, - ERR_PartialMethodOnlyInPartialClass = 751, + ERR_PartialMemberCannotBeAbstract = 750, + ERR_PartialMemberOnlyInPartialClass = 751, ERR_PartialMethodCannotHaveOutParameters = 752, ERR_PartialMethodOnlyMethods = 753, - ERR_PartialMethodNotExplicit = 754, + ERR_PartialMemberNotExplicit = 754, ERR_PartialMethodExtensionDifference = 755, ERR_PartialMethodOnlyOneLatent = 756, ERR_PartialMethodOnlyOneActual = 757, - ERR_PartialMethodParamsDifference = 758, + ERR_PartialMemberParamsDifference = 758, ERR_PartialMethodMustHaveLatent = 759, ERR_PartialMethodInconsistentConstraints = 761, ERR_PartialMethodToDelegate = 762, - ERR_PartialMethodStaticDifference = 763, - ERR_PartialMethodUnsafeDifference = 764, + ERR_PartialMemberStaticDifference = 763, + ERR_PartialMemberUnsafeDifference = 764, ERR_PartialMethodInExpressionTree = 765, ERR_PartialMethodMustReturnVoid = 766, ERR_ExplicitImplCollisionOnRefOut = 767, diff --git a/src/Compilers/CSharp/Test/Emit/PrivateProtected.cs b/src/Compilers/CSharp/Test/Emit/PrivateProtected.cs index 059a107ff10ba..9f1745a691031 100644 --- a/src/Compilers/CSharp/Test/Emit/PrivateProtected.cs +++ b/src/Compilers/CSharp/Test/Emit/PrivateProtected.cs @@ -523,10 +523,7 @@ private public interface I {} // 8 Diagnostic(ErrorCode.ERR_BadMemberProtection, "V").WithLocation(10, 40), // (12,24): error CS0107: More than one protection modifier // private public int this[int index] => 1; // 9 - Diagnostic(ErrorCode.ERR_BadMemberProtection, "this").WithLocation(12, 24), - // (12,43): error CS0107: More than one protection modifier - // private public int this[int index] => 1; // 9 - Diagnostic(ErrorCode.ERR_BadMemberProtection, "1").WithLocation(12, 43) + Diagnostic(ErrorCode.ERR_BadMemberProtection, "this").WithLocation(12, 24) ); } diff --git a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_CallerInfoAttributes.cs b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_CallerInfoAttributes.cs index dd9319f97b459..bf4053c6fd9f2 100644 --- a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_CallerInfoAttributes.cs +++ b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_CallerInfoAttributes.cs @@ -3231,6 +3231,19 @@ public static void Main() // (14,10): warning CS4025: The CallerFilePathAttribute applied to parameter 'path' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments // [CallerFilePath] string path) { } Diagnostic(ErrorCode.WRN_CallerFilePathParamForUnconsumedLocation, "CallerFilePath").WithArguments("path")); + + CompileAndVerify(source, options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: verify); + + void verify(ModuleSymbol module) + { + // https://github.com/dotnet/roslyn/issues/73482 + // These are ignored in source but they still get written out to metadata. + // This means if the method is accessible from another compilation, then the attribute will be respected there, but not in the declaring compilation. + var goo = module.GlobalNamespace.GetMember("D.Goo"); + AssertEx.Equal(["System.Runtime.CompilerServices.CallerLineNumberAttribute"], goo.Parameters[0].GetAttributes().SelectAsArray(attr => attr.ToString())); + AssertEx.Equal(["System.Runtime.CompilerServices.CallerMemberNameAttribute"], goo.Parameters[1].GetAttributes().SelectAsArray(attr => attr.ToString())); + AssertEx.Equal(["System.Runtime.CompilerServices.CallerFilePathAttribute"], goo.Parameters[2].GetAttributes().SelectAsArray(attr => attr.ToString())); + } } [Fact] @@ -5836,5 +5849,106 @@ public CallerArgumentExpressionAttribute([CallerArgumentExpression(nameof(parame // public CallerArgumentExpressionAttribute([CallerArgumentExpression(nameof(parameterName))] string parameterName) Diagnostic(ErrorCode.ERR_BadCallerArgumentExpressionParamWithoutDefaultValue, "CallerArgumentExpression").WithLocation(5, 51)); } + + [Fact] + public void CallerMemberName_SetterValueParam() + { + // There is no way in C# to call a setter without passing an argument for the value, so the CallerMemberName effectively does nothing. + var source = """ + using System; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + public partial class C + { + public static void Main() + { + var c = new C(); + c[1] = "1"; + } + + public string this[int x] + { + [param: Optional, DefaultParameterValue("0")] + [param: CallerMemberName] + set + { + Console.Write(value); + } + } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "1"); + verifier.VerifyDiagnostics(); + + var source1 = """ + class D + { + void M() + { + var c = new C(); + c.set_Item(1); + } + } + """; + var comp1 = CreateCompilation(source1, references: [verifier.Compilation.EmitToImageReference()]); + comp1.VerifyEmitDiagnostics( + // (6,11): error CS0571: 'C.this[int].set': cannot explicitly call operator or accessor + // c.set_Item(1); + Diagnostic(ErrorCode.ERR_CantCallSpecialMethod, "set_Item").WithArguments("C.this[int].set").WithLocation(6, 11)); + } + + [Fact] + public void CallerArgumentExpression_SetterValueParam() + { + var source = """ + using System; + using System.Runtime.CompilerServices; + + namespace System.Runtime.CompilerServices + { + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = false)] + public sealed class CallerArgumentExpressionAttribute : Attribute + { + public CallerArgumentExpressionAttribute(string parameterName) + { + ParameterName = parameterName; + } + public string ParameterName { get; } + } + } + + partial class C + { + public static void Main() + { + var c = new C(); + c[1] = GetNumber(); + } + + public static int GetNumber() => 1; + + public int this[int x, [CallerArgumentExpression("value")] string argumentExpression = "0"] + { + set + { + Console.Write(argumentExpression); + } + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "0", symbolValidator: verify); + verifier.VerifyDiagnostics( + // (27,29): warning CS8963: The CallerArgumentExpressionAttribute applied to parameter 'argumentExpression' will have no effect. It is applied with an invalid parameter name. + // public int this[int x, [CallerArgumentExpression("value")] string argumentExpression = "0"] + Diagnostic(ErrorCode.WRN_CallerArgumentExpressionAttributeHasInvalidParameterName, "CallerArgumentExpression").WithArguments("argumentExpression").WithLocation(27, 29)); + + void verify(ModuleSymbol module) + { + var indexer = (PropertySymbol)module.GlobalNamespace.GetMember("C").Indexers.Single(); + AssertEx.Equal(["""System.Runtime.CompilerServices.CallerArgumentExpressionAttribute("value")"""], indexer.Parameters[1].GetAttributes().SelectAsArray(attr => attr.ToString())); + } + } } } diff --git a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_WellKnownAttributes.cs b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_WellKnownAttributes.cs index ca2be38186095..5ce634fdea9c3 100644 --- a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_WellKnownAttributes.cs +++ b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_WellKnownAttributes.cs @@ -11220,6 +11220,45 @@ void local1() Assert.True(verifier.HasLocalsInit("C.g__local1|3_0")); } + [Theory] + [InlineData("[SkipLocalsInit]", "")] + [InlineData("", "[SkipLocalsInit]")] + public void SkipLocalsInit_PartialPropertyAccessor_ContainsLocalFunction(string defAttrs, string implAttrs) + { + // SkipLocalsInit applied to either part affects the property and nested functions + var source = $$""" +using System.Runtime.CompilerServices; + +public partial class C +{ + {{defAttrs}} + partial int PropWithAttribute { get; } + + {{implAttrs}} + partial int PropWithAttribute + { + get + { + int w = 1; + w = w + w + w + w; + + void local1() + { + int x = 1; + x = x + x + x + x; + } + + return 0; + } + } +} +"""; + + var verifier = CompileAndVerifyWithSkipLocalsInit(source); + Assert.False(verifier.HasLocalsInit("C.PropWithAttribute.get")); + Assert.False(verifier.HasLocalsInit("C.g__local1|1_0")); + } + [Fact] public void SkipLocalsInit_EventAccessor_ContainsLocalFunction() { diff --git a/src/Compilers/CSharp/Test/Emit2/Diagnostics/GetDiagnosticsTests.cs b/src/Compilers/CSharp/Test/Emit2/Diagnostics/GetDiagnosticsTests.cs index 0d264412df26c..92cfc8eb21ea7 100644 --- a/src/Compilers/CSharp/Test/Emit2/Diagnostics/GetDiagnosticsTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Diagnostics/GetDiagnosticsTests.cs @@ -238,6 +238,69 @@ partial void PartialMethod() { } Assert.True(completedCompilationUnits.Contains(tree1.FilePath)); } + [Fact] + public void TestCompilationEventsForPartialProperty() + { + var source1 = @" +namespace N1 +{ + partial class Class + { + int NonPartialProp1 { get; set; } + partial int DefOnlyPartialProp { get; set; } + partial int ImplOnlyPartialProp { get => 1; set { } } + partial int PartialProp { get; set; } + } +} +"; + var source2 = @" +namespace N1 +{ + partial class Class + { + int NonPartialProp2 { get; set; } + partial int PartialProp { get => 1; set { } } + } +} +"; + + var tree1 = CSharpSyntaxTree.ParseText(source1, path: "file1"); + var tree2 = CSharpSyntaxTree.ParseText(source2, path: "file2"); + var eventQueue = new AsyncQueue(); + var compilation = CreateCompilationWithMscorlib45(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 completedCompilationUnits)); + + // Verify symbol declared events fired for all symbols declared in the first source file. + Assert.True(compilationStartedFired); + + // NB: NonPartialProp2 is missing here because we only asked for diagnostics in tree1 + AssertEx.Equal([ + "", + "Class", + "DefOnlyPartialProp", + "get_ImplOnlyPartialProp", + "get_NonPartialProp1", + "get_PartialProp", + "ImplOnlyPartialProp", + "N1", + "NonPartialProp1", + "PartialProp", + "set_ImplOnlyPartialProp", + "set_NonPartialProp1", + "set_PartialProp" + ], declaredSymbolNames.OrderBy(name => name)); + + AssertEx.Equal(["file1"], completedCompilationUnits.OrderBy(name => name)); + } + [Fact, WorkItem(8178, "https://github.com/dotnet/roslyn/issues/8178")] public void TestEarlyCancellation() { @@ -286,12 +349,7 @@ private static bool DequeueCompilationEvents(AsyncQueue eventQ var added = declaredSymbolNames.Add(symbol.Name); if (!added) { - var method = symbol.GetSymbol() as Symbols.MethodSymbol; - Assert.NotNull(method); - - var isPartialMethod = method.PartialDefinitionPart != null || - method.PartialImplementationPart != null; - Assert.True(isPartialMethod, "Unexpected multiple symbol declared events for symbol " + symbol); + Assert.True(symbol.GetSymbol().IsPartialMember(), "Unexpected multiple symbol declared events for symbol " + symbol); } } else diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs index f36059c8f8684..e8aa55df720ae 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs @@ -9835,19 +9835,19 @@ partial void Test3(scoped Span a) Assert.NotEqual(comp.GetMember("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember("C1.Test3").PartialImplementationPart.Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( - // (13,18): error CS0758: Both partial method declarations must use a params parameter or neither may use a params parameter + // (13,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test1(params Span a) - Diagnostic(ErrorCode.ERR_PartialMethodParamsDifference, "Test1").WithLocation(13, 18), - // (13,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial method declaration. + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test1").WithLocation(13, 18), + // (13,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial definition. // partial void Test1(params Span a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test1").WithArguments("a").WithLocation(13, 18), - // (17,18): error CS0758: Both partial method declarations must use a params parameter or neither may use a params parameter + // (17,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test2(params scoped Span a) - Diagnostic(ErrorCode.ERR_PartialMethodParamsDifference, "Test2").WithLocation(17, 18), - // (17,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial method declaration. + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test2").WithLocation(17, 18), + // (17,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial definition. // partial void Test2(params scoped Span a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test2").WithArguments("a").WithLocation(17, 18), - // (21,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial method declaration. + // (21,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial definition. // partial void Test3(scoped Span a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test3").WithArguments("a").WithLocation(21, 18) ); @@ -9888,19 +9888,19 @@ partial void Test3(Span a) Assert.NotEqual(comp.GetMember("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember("C1.Test3").PartialImplementationPart.Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( - // (13,18): error CS0758: Both partial method declarations must use a params parameter or neither may use a params parameter + // (13,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test1(Span a) - Diagnostic(ErrorCode.ERR_PartialMethodParamsDifference, "Test1").WithLocation(13, 18), - // (13,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial method declaration. + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test1").WithLocation(13, 18), + // (13,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial definition. // partial void Test1(Span a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test1").WithArguments("a").WithLocation(13, 18), - // (17,18): error CS0758: Both partial method declarations must use a params parameter or neither may use a params parameter + // (17,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test2(Span a) - Diagnostic(ErrorCode.ERR_PartialMethodParamsDifference, "Test2").WithLocation(17, 18), - // (17,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial method declaration. + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test2").WithLocation(17, 18), + // (17,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial definition. // partial void Test2(Span a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test2").WithArguments("a").WithLocation(17, 18), - // (21,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial method declaration. + // (21,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial definition. // partial void Test3(Span a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test3").WithArguments("a").WithLocation(21, 18) ); @@ -9947,15 +9947,15 @@ partial void Test3([UnscopedRef] Span a) Assert.Equal(comp.GetMember("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember("C1.Test3").PartialImplementationPart.Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( - // (14,18): error CS0758: Both partial method declarations must use a params parameter or neither may use a params parameter + // (14,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test1([UnscopedRef] Span a) - Diagnostic(ErrorCode.ERR_PartialMethodParamsDifference, "Test1").WithLocation(14, 18), + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test1").WithLocation(14, 18), // (14,25): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // partial void Test1([UnscopedRef] Span a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(14, 25), - // (18,18): error CS0758: Both partial method declarations must use a params parameter or neither may use a params parameter + // (18,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test2([UnscopedRef] Span a) - Diagnostic(ErrorCode.ERR_PartialMethodParamsDifference, "Test2").WithLocation(18, 18), + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test2").WithLocation(18, 18), // (18,25): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // partial void Test2([UnscopedRef] Span a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(18, 25), @@ -10006,9 +10006,9 @@ public partial Span Test3([UnscopedRef] params Span a) Assert.Equal(comp.GetMember("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember("C1.Test3").PartialImplementationPart.Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( - // (20,31): error CS0758: Both partial method declarations must use a params parameter or neither may use a params parameter + // (20,31): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // public partial Span Test3([UnscopedRef] params Span a) - Diagnostic(ErrorCode.ERR_PartialMethodParamsDifference, "Test3").WithLocation(20, 31) + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test3").WithLocation(20, 31) ); } @@ -14435,18 +14435,18 @@ partial void Test3(int a) // (5,24): error CS0225: The params parameter must have a valid collection type // partial void Test1(params int a) Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(5, 24), - // (11,18): error CS0758: Both partial method declarations must use a params parameter or neither may use a params parameter + // (11,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test2(params int a) - Diagnostic(ErrorCode.ERR_PartialMethodParamsDifference, "Test2").WithLocation(11, 18), + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test2").WithLocation(11, 18), // (11,24): error CS0225: The params parameter must have a valid collection type // partial void Test2(params int a) Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(11, 24), // (15,24): error CS0225: The params parameter must have a valid collection type // partial void Test3(params int a); Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(15, 24), - // (17,18): error CS0758: Both partial method declarations must use a params parameter or neither may use a params parameter + // (17,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test3(int a) - Diagnostic(ErrorCode.ERR_PartialMethodParamsDifference, "Test3").WithLocation(17, 18) + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test3").WithLocation(17, 18) ); } diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/RecordTests.cs index 12b140332a903..fc5a188c754b5 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/RecordTests.cs @@ -19236,9 +19236,6 @@ protected virtual System.Type EqualityContract // (10,27): error CS8879: Record member 'B.EqualityContract' must be private. // protected System.Type EqualityContract Diagnostic(ErrorCode.ERR_NonPrivateAPIInRecord, "EqualityContract").WithArguments("B.EqualityContract").WithLocation(10, 27), - // (11,12): warning CS0628: 'B.EqualityContract.get': new protected member declared in sealed type - // => throw null; - Diagnostic(ErrorCode.WRN_ProtectedInSealed, "throw null").WithArguments("B.EqualityContract.get").WithLocation(11, 12), // (16,35): warning CS0628: 'C.EqualityContract': new protected member declared in sealed type // protected virtual System.Type EqualityContract Diagnostic(ErrorCode.WRN_ProtectedInSealed, "EqualityContract").WithArguments("C.EqualityContract").WithLocation(16, 35), @@ -19321,9 +19318,6 @@ protected sealed override System.Type EqualityContract // (13,27): warning CS0114: 'C.EqualityContract' hides inherited member 'A.EqualityContract'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword. // protected System.Type EqualityContract Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "EqualityContract").WithArguments("C.EqualityContract", "A.EqualityContract").WithLocation(13, 27), - // (14,12): warning CS0628: 'C.EqualityContract.get': new protected member declared in sealed type - // => throw null; - Diagnostic(ErrorCode.WRN_ProtectedInSealed, "throw null").WithArguments("C.EqualityContract.get").WithLocation(14, 12), // (19,35): warning CS0628: 'D.EqualityContract': new protected member declared in sealed type // protected virtual System.Type EqualityContract Diagnostic(ErrorCode.WRN_ProtectedInSealed, "EqualityContract").WithArguments("D.EqualityContract").WithLocation(19, 35), @@ -19997,10 +19991,7 @@ public void EqualityContract_19() Diagnostic(ErrorCode.ERR_NonPrivateAPIInRecord, "EqualityContract").WithArguments("A.EqualityContract").WithLocation(3, 34), // (3,34): error CS8877: Record member 'A.EqualityContract' may not be static. // protected static System.Type EqualityContract => throw null; - Diagnostic(ErrorCode.ERR_StaticAPIInRecord, "EqualityContract").WithArguments("A.EqualityContract").WithLocation(3, 34), - // (3,54): warning CS0628: 'A.EqualityContract.get': new protected member declared in sealed type - // protected static System.Type EqualityContract => throw null; - Diagnostic(ErrorCode.WRN_ProtectedInSealed, "throw null").WithArguments("A.EqualityContract.get").WithLocation(3, 54) + Diagnostic(ErrorCode.ERR_StaticAPIInRecord, "EqualityContract").WithArguments("A.EqualityContract").WithLocation(3, 34) ); } @@ -26049,9 +26040,9 @@ public void RecordWithPartialMethodExplicitImplementation() partial void M(); }"; CreateCompilation(source).VerifyDiagnostics( - // (3,18): error CS0751: A partial method must be declared within a partial type + // (3,18): error CS0751: A partial member must be declared within a partial type // partial void M(); - Diagnostic(ErrorCode.ERR_PartialMethodOnlyInPartialClass, "M").WithLocation(3, 18) + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "M").WithLocation(3, 18) ); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs index ce4baa3ec6c98..141b23ed071f5 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs @@ -2210,12 +2210,12 @@ public void InterfaceWithPartialMethodExplicitImplementation() partial void I.M(); }"; CreateCompilation(source, parseOptions: TestOptions.Regular7, targetFramework: TargetFramework.NetCoreApp).VerifyDiagnostics( - // (3,20): error CS0754: A partial method may not explicitly implement an interface method + // (3,20): error CS0754: A partial member may not explicitly implement an interface member // partial void I.M(); - Diagnostic(ErrorCode.ERR_PartialMethodNotExplicit, "M").WithLocation(3, 20), - // (3,20): error CS0751: A partial method must be declared within a partial type + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "M").WithLocation(3, 20), + // (3,20): error CS0751: A partial member must be declared within a partial type // partial void I.M(); - Diagnostic(ErrorCode.ERR_PartialMethodOnlyInPartialClass, "M").WithLocation(3, 20), + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "M").WithLocation(3, 20), // (3,20): error CS8652: The feature 'default interface implementation' is not available in C# 7.0. Please use language version 8.0 or greater. // partial void I.M(); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "M").WithArguments("default interface implementation", "8.0").WithLocation(3, 20), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs index 148527fd407a4..bcd93d2fedcf3 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs @@ -108,9 +108,9 @@ private protected void IGoo.Method14() { } // (37,22): error CS0539: 'AbstractGoo.Method12()' in explicit interface declaration is not found among members of the interface that can be implemented // static void IGoo.Method12() { } Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "Method12").WithArguments("AbstractGoo.Method12()").WithLocation(37, 22), - // (38,23): error CS0754: A partial method may not explicitly implement an interface method + // (38,23): error CS0754: A partial member may not explicitly implement an interface member // partial void IGoo.Method13(); - Diagnostic(ErrorCode.ERR_PartialMethodNotExplicit, "Method13").WithLocation(38, 23), + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "Method13").WithLocation(38, 23), // (20,38): error CS0535: 'AbstractGoo' does not implement interface member 'IGoo.Method12()' // abstract partial class AbstractGoo : IGoo Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "IGoo").WithArguments("AbstractGoo", "IGoo.Method12()").WithLocation(20, 38), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ReadOnlyStructsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ReadOnlyStructsTests.cs index 3c90e3dc7bb35..bf3e005bd40d0 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ReadOnlyStructsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ReadOnlyStructsTests.cs @@ -410,9 +410,9 @@ readonly partial void M() "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( - // (10,27): error CS8662: Both partial method declarations must be readonly or neither may be readonly + // (10,27): error CS8662: Both partial member declarations must be readonly or neither may be readonly // readonly partial void M() - Diagnostic(ErrorCode.ERR_PartialMethodReadOnlyDifference, "M").WithLocation(10, 27), + Diagnostic(ErrorCode.ERR_PartialMemberReadOnlyDifference, "M").WithLocation(10, 27), // (12,9): error CS1604: Cannot assign to 'i' because it is read-only // i++; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(12, 9)); @@ -443,9 +443,9 @@ partial void M() "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( - // (10,18): error CS8662: Both partial method declarations must be readonly or neither may be readonly + // (10,18): error CS8662: Both partial member declarations must be readonly or neither may be readonly // partial void M() - Diagnostic(ErrorCode.ERR_PartialMethodReadOnlyDifference, "M").WithLocation(10, 18)); + Diagnostic(ErrorCode.ERR_PartialMemberReadOnlyDifference, "M").WithLocation(10, 18)); var method = comp.GetMember("S").GetMethod("M"); // Symbol APIs always return the declaration part of the partial method. diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs index fd22894c7dc8c..2dbf82572b301 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs @@ -3119,9 +3119,9 @@ public void RecordWithPartialMethodExplicitImplementation() partial void M(); }"; CreateCompilation(source).VerifyDiagnostics( - // (3,18): error CS0751: A partial method must be declared within a partial type + // (3,18): error CS0751: A partial member must be declared within a partial type // partial void M(); - Diagnostic(ErrorCode.ERR_PartialMethodOnlyInPartialClass, "M").WithLocation(3, 18) + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "M").WithLocation(3, 18) ); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 392842983ba7d..f037d2a226ec2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -16653,22 +16653,22 @@ static partial void M6(in R r) { } // 6 var expectedDiagnostics = new[] { - // (3,25): error CS8988: The 'scoped' modifier of parameter 'r' doesn't match partial method declaration. + // (3,25): error CS8988: The 'scoped' modifier of parameter 'r' doesn't match partial definition. // static partial void M1(scoped R r) { } // 1 Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "M1").WithArguments("r").WithLocation(3, 25), - // (4,25): error CS8988: The 'scoped' modifier of parameter 'r' doesn't match partial method declaration. + // (4,25): error CS8988: The 'scoped' modifier of parameter 'r' doesn't match partial definition. // static partial void M2(R r) { } // 2 Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "M2").WithArguments("r").WithLocation(4, 25), - // (5,25): error CS8988: The 'scoped' modifier of parameter 'r' doesn't match partial method declaration. + // (5,25): error CS8988: The 'scoped' modifier of parameter 'r' doesn't match partial definition. // static partial void M3(scoped ref R r) { } // 3 Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "M3").WithArguments("r").WithLocation(5, 25), - // (6,25): error CS8988: The 'scoped' modifier of parameter 'r' doesn't match partial method declaration. + // (6,25): error CS8988: The 'scoped' modifier of parameter 'r' doesn't match partial definition. // static partial void M4(ref R r) { } // 4 Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "M4").WithArguments("r").WithLocation(6, 25), - // (7,25): error CS8988: The 'scoped' modifier of parameter 'r' doesn't match partial method declaration. + // (7,25): error CS8988: The 'scoped' modifier of parameter 'r' doesn't match partial definition. // static partial void M5(scoped in R r) { } // 5 Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "M5").WithArguments("r").WithLocation(7, 25), - // (8,25): error CS8988: The 'scoped' modifier of parameter 'r' doesn't match partial method declaration. + // (8,25): error CS8988: The 'scoped' modifier of parameter 'r' doesn't match partial definition. // static partial void M6(in R r) { } // 6 Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "M6").WithArguments("r").WithLocation(8, 25) }; @@ -16718,16 +16718,16 @@ private partial void F4(ref int i) { } // 4 var expectedDiagnostics = new[] { - // (3,26): error CS8988: The 'scoped' modifier of parameter 'i' doesn't match partial method declaration. + // (3,26): error CS8988: The 'scoped' modifier of parameter 'i' doesn't match partial definition. // private partial void F1(scoped ref int i) { } // 1 Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "F1").WithArguments("i").WithLocation(3, 26), - // (4,26): error CS8988: The 'scoped' modifier of parameter 'i' doesn't match partial method declaration. + // (4,26): error CS8988: The 'scoped' modifier of parameter 'i' doesn't match partial definition. // private partial void F2(ref int i) { } // 2 Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "F2").WithArguments("i").WithLocation(4, 26), - // (5,26): error CS8988: The 'scoped' modifier of parameter 'i' doesn't match partial method declaration. + // (5,26): error CS8988: The 'scoped' modifier of parameter 'i' doesn't match partial definition. // private partial void F3(scoped in int i) { } // 3 Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "F3").WithArguments("i").WithLocation(5, 26), - // (6,26): error CS8988: The 'scoped' modifier of parameter 'i' doesn't match partial method declaration. + // (6,26): error CS8988: The 'scoped' modifier of parameter 'i' doesn't match partial definition. // private partial void F4(ref int i) { } // 4 Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "F4").WithArguments("i").WithLocation(6, 26) }; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs index a8723ce4cb72b..44615f646a7e6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs @@ -4422,9 +4422,9 @@ public void LocalFunctionStatement_04() // (14,14): error CS0759: No defining declaration found for implementing declaration of partial method '.localG()' // partial void localG() => System.Console.WriteLine(); Diagnostic(ErrorCode.ERR_PartialMethodMustHaveLatent, "localG").WithArguments(".localG()").WithLocation(14, 14), - // (14,14): error CS0751: A partial method must be declared within a partial type + // (14,14): error CS0751: A partial member must be declared within a partial type // partial void localG() => System.Console.WriteLine(); - Diagnostic(ErrorCode.ERR_PartialMethodOnlyInPartialClass, "localG").WithLocation(14, 14), + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "localG").WithLocation(14, 14), // (15,1): error CS0103: The name 'localG' does not exist in the current context // localG(); Diagnostic(ErrorCode.ERR_NameNotInContext, "localG").WithArguments("localG").WithLocation(15, 1), diff --git a/src/Compilers/CSharp/Test/Symbol/DocumentationComments/DocumentationCommentCompilerTests.cs b/src/Compilers/CSharp/Test/Symbol/DocumentationComments/DocumentationCommentCompilerTests.cs index c2b267aa23460..a795619ee010d 100644 --- a/src/Compilers/CSharp/Test/Symbol/DocumentationComments/DocumentationCommentCompilerTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/DocumentationComments/DocumentationCommentCompilerTests.cs @@ -914,6 +914,7 @@ partial class C [Fact] public void PartialMethod_NoImplementation() { + // Whole document XML does not include the member, but single symbol XML does include it var source = @" partial class C { @@ -923,20 +924,25 @@ partial class C "; var tree = SyntaxFactory.ParseSyntaxTree(source, options: TestOptions.RegularWithDocumentationComments); - var comp = CreateCompilation(tree, assemblyName: "Test"); - var actual = GetDocumentationCommentText(comp); - var expected = @" - - - - Test - - - - -".Trim(); - Assert.Equal(expected, actual); + var method = comp.GlobalNamespace.GetMember("C.M"); + + AssertEx.AssertLinesEqual(expected: """ + + + + Test + + + + + """, actual: GetDocumentationCommentText(comp)); + + AssertEx.AssertLinesEqual(""" + + Summary 2 + + """, DocumentationCommentCompiler.GetDocumentationCommentXml(method, processIncludes: true, cancellationToken: default)); } [Fact] @@ -1490,6 +1496,690 @@ void verify(CSharpTestSource source) } } + /// Counterpart to . + [Fact] + public void PartialProperty_NoImplementation() + { + // Whole document XML does not include the member, but single symbol XML does include it + var source = @" +partial class C +{ + /** Summary 2*/ + public partial int P { get; set; } +} +"; + + var tree = SyntaxFactory.ParseSyntaxTree(source, options: TestOptions.RegularWithDocumentationComments); + var comp = CreateCompilation(tree, assemblyName: "Test"); + var property = comp.GlobalNamespace.GetMember("C.P"); + + AssertEx.AssertLinesEqual(expected: """ + + + + Test + + + + + """, actual: GetDocumentationCommentText(comp)); + + AssertEx.AssertLinesEqual(""" + + Summary 2 + + """, DocumentationCommentCompiler.GetDocumentationCommentXml(property, processIncludes: true, cancellationToken: default)); + } + + /// Counterpart to . + [Fact] + public void PartialProperties_MultipleFiles() + { + var source1 = @" +/// Summary 0 +public partial class C +{ + /** Summary 1*/ + public partial int P => 42; +} +"; + + var source2 = @" +public partial class C +{ + /** Summary 2*/ + public partial int P { get; } +} +"; + + var tree1 = SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.RegularPreviewWithDocumentationComments); + var tree2 = SyntaxFactory.ParseSyntaxTree(source2, options: TestOptions.RegularPreviewWithDocumentationComments); + + // Files passed in order. + var compA = CreateCompilation(new[] { tree1, tree2 }, assemblyName: "Test"); + var actualA = GetDocumentationCommentText(compA); + var expected = @" + + + + Test + + + + Summary 0 + + + Summary 1 + + + +".Trim(); + AssertEx.Equal(expected, actualA); + + // Files passed in reverse order. + var compB = CreateCompilation(new[] { tree2, tree1 }, assemblyName: "Test"); + var actualB = GetDocumentationCommentText(compB); + Assert.Equal(expected, actualB); + } + + /// Counterpart to . + [Fact] + public void PartialIndexers_MultipleFiles() + { + var source1 = @" +/// Summary 0 +public partial class C +{ + /** Summary 1*/ + public partial int this[int p] => 42; +} +"; + + var source2 = @" +public partial class C +{ + /** Summary 2*/ + public partial int this[int p] { get; } +} +"; + + var tree1 = SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.RegularPreviewWithDocumentationComments); + var tree2 = SyntaxFactory.ParseSyntaxTree(source2, options: TestOptions.RegularPreviewWithDocumentationComments); + + // Files passed in order. + var compA = CreateCompilation(new[] { tree1, tree2 }, assemblyName: "Test"); + var actualA = GetDocumentationCommentText(compA); + var expected = @" + + + + Test + + + + Summary 0 + + + Summary 1 + + + +".Trim(); + AssertEx.Equal(expected, actualA); + + // Files passed in reverse order. + var compB = CreateCompilation(new[] { tree2, tree1 }, assemblyName: "Test"); + var actualB = GetDocumentationCommentText(compB); + Assert.Equal(expected, actualB); + } + + /// Counterpart to . + [Fact] + public void PartialIndexer_NoImplementation() + { + // Whole document XML does not include the member, but single symbol XML does include it + var source = """ + partial class C + { + /// Summary 2 + /// My param + public partial int this[int p] { get; set; } + } + """; + + var tree = SyntaxFactory.ParseSyntaxTree(source, options: TestOptions.RegularWithDocumentationComments); + var comp = CreateCompilation(tree, assemblyName: "Test"); + var property = comp.GlobalNamespace.GetMember("C").Indexers.Single(); + + AssertEx.AssertLinesEqual(expected: """ + + + + Test + + + + + """, actual: GetDocumentationCommentText(comp)); + + AssertEx.AssertLinesEqual(""" + + Summary 2 + My param + + """, DocumentationCommentCompiler.GetDocumentationCommentXml(property, processIncludes: true, cancellationToken: default)); + } + + /// Counterpart to . + [Fact] + public void PartialProperties_MultipleFiles_DefinitionComment() + { + var source1 = @" +/// Summary 0 +public partial class C +{ + public partial int P => 42; +} +"; + + var source2 = @" +public partial class C +{ + /** Summary 2*/ + public partial int P { get; } +} +"; + + var tree1 = SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.RegularPreviewWithDocumentationComments); + var tree2 = SyntaxFactory.ParseSyntaxTree(source2, options: TestOptions.RegularPreviewWithDocumentationComments); + + // Files passed in order. + var compA = CreateCompilation(new[] { tree1, tree2 }, assemblyName: "Test"); + compA.VerifyDiagnostics(); + var actualA = GetDocumentationCommentText(compA); + var expected = @" + + + + Test + + + + Summary 0 + + + Summary 2 + + + +".Trim(); + AssertEx.Equal(expected, actualA); + + // Files passed in reverse order. + var compB = CreateCompilation(new[] { tree2, tree1 }, assemblyName: "Test"); + compB.VerifyDiagnostics(); + var actualB = GetDocumentationCommentText(compB); + Assert.Equal(expected, actualB); + } + + /// Counterpart to . + [Fact] + public void PartialProperties_MultipleFiles_ImplementationComment() + { + var source1 = @" +/// Summary 0 +public partial class C +{ + /** Summary 1*/ + public partial int P => 42; +} +"; + + var source2 = @" +public partial class C +{ + public partial int P { get; } +} +"; + + var tree1 = SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.RegularPreviewWithDocumentationComments); + var tree2 = SyntaxFactory.ParseSyntaxTree(source2, options: TestOptions.RegularPreviewWithDocumentationComments); + + // Files passed in order. + var compA = CreateCompilation(new[] { tree1, tree2 }, assemblyName: "Test"); + compA.VerifyDiagnostics(); + var actualA = GetDocumentationCommentText(compA); + var expected = @" + + + + Test + + + + Summary 0 + + + Summary 1 + + + +".Trim(); + AssertEx.Equal(expected, actualA); + + // Files passed in reverse order. + var compB = CreateCompilation(new[] { tree2, tree1 }, assemblyName: "Test"); + compB.VerifyDiagnostics(); + var actualB = GetDocumentationCommentText(compB); + Assert.Equal(expected, actualB); + } + + /// Counterpart to . + [Fact] + public void PartialProperties_MultipleFiles_NoComment() + { + var source1 = @" +/// Summary 0 +public partial class C +{ + public partial int P => 42; +} +"; + + var source2 = @" +public partial class C +{ + public partial int P { get; } +} +"; + + var tree1 = SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.RegularPreviewWithDocumentationComments); + var tree2 = SyntaxFactory.ParseSyntaxTree(source2, options: TestOptions.RegularPreviewWithDocumentationComments); + + var expectedDiagnostics = new[] + { + // (4,24): warning CS1591: Missing XML comment for publicly visible type or member 'C.P' + // public partial int P { get; } + Diagnostic(ErrorCode.WRN_MissingXMLComment, "P").WithArguments("C.P").WithLocation(4, 24) + }; + + // Files passed in order. + var compA = CreateCompilation(new[] { tree1, tree2 }, assemblyName: "Test"); + compA.VerifyDiagnostics(expectedDiagnostics); + var actualA = GetDocumentationCommentText(compA, expectedDiagnostics); + var expected = @" + + + + Test + + + + Summary 0 + + + +".Trim(); + AssertEx.Equal(expected, actualA); + + // Files passed in reverse order. + var compB = CreateCompilation(new[] { tree2, tree1 }, assemblyName: "Test"); + compB.VerifyDiagnostics(expectedDiagnostics); + var actualB = GetDocumentationCommentText(compB, expectedDiagnostics); + Assert.Equal(expected, actualB); + } + + /// Counterpart to . + [Fact] + public void PartialProperties_MultipleFiles_Overlap() + { + var source1 = @" +partial class C +{ + /** Remarks 1 */ + public partial int P => 42; +} +"; + + var source2 = @" +partial class C +{ + /** Summary 2*/ + public partial int P { get; } +} +"; + + var tree1 = SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.RegularPreviewWithDocumentationComments); + var tree2 = SyntaxFactory.ParseSyntaxTree(source2, options: TestOptions.RegularPreviewWithDocumentationComments); + + // Files passed in order. + var compA = CreateCompilation(new[] { tree1, tree2 }, assemblyName: "Test"); + compA.VerifyDiagnostics(); + var actualA = GetDocumentationCommentText(compA); + var expected = @" + + + + Test + + + + Remarks 1 + + + +".Trim(); + AssertEx.Equal(expected, actualA); + + // Files passed in reverse order. + var compB = CreateCompilation(new[] { tree2, tree1 }, assemblyName: "Test"); + compB.VerifyDiagnostics(); + var actualB = GetDocumentationCommentText(compB); + Assert.Equal(expected, actualB); + } + + /// Counterpart to . + [Fact] + public void PartialProperties_MultipleFiles_ImplComment_Invalid() + { + var source1 = @" +partial class C +{ + /// + public partial int P => 42; +} +"; + + var source2 = @" +partial class C +{ + /** Summary 2*/ + public partial int P { get; } +} +"; + + var tree1 = SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.RegularPreviewWithDocumentationComments); + var tree2 = SyntaxFactory.ParseSyntaxTree(source2, options: TestOptions.RegularPreviewWithDocumentationComments); + + var expectedDiagnostics = new[] + { + // (4,20): warning CS1570: XML comment has badly formed XML -- 'End tag 'a' does not match the start tag 'summary'.' + // /// + Diagnostic(ErrorCode.WRN_XMLParseError, "a").WithArguments("a", "summary").WithLocation(4, 20), + // (4,22): warning CS1570: XML comment has badly formed XML -- 'End tag was not expected at this location.' + // /// + Diagnostic(ErrorCode.WRN_XMLParseError, "<").WithLocation(4, 22) + }; + + // Files passed in order. + var compA = CreateCompilation(new[] { tree1, tree2 }, assemblyName: "Test"); + compA.VerifyDiagnostics(expectedDiagnostics); + var actualA = GetDocumentationCommentText(compA); + var expected = @" + + + + Test + + + + + +".Trim(); + AssertEx.Equal(expected, actualA); + + // Files passed in reverse order. + var compB = CreateCompilation(new[] { tree2, tree1 }, assemblyName: "Test"); + compB.VerifyDiagnostics(expectedDiagnostics); + var actualB = GetDocumentationCommentText(compB); + Assert.Equal(expected, actualB); + } + + /// Counterpart to . + [Fact] + public void PartialIndexer_Paramref_01() + { + var source1 = @" +partial class C +{ + /** Accepts . */ + public partial int this[int p1] => 42; +} +"; + + var source2 = @" +partial class C +{ + public partial int this[int p2] { get; } +} +"; + + var tree1 = SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.RegularPreviewWithDocumentationComments); + var tree2 = SyntaxFactory.ParseSyntaxTree(source2, options: TestOptions.RegularPreviewWithDocumentationComments); + + // Files passed in order. + verify(new[] { tree1, tree2 }); + + // Files passed in reverse order. + verify(new[] { tree2, tree1 }); + + void verify(CSharpTestSource source) + { + var compilation = CreateCompilation(source, assemblyName: "Test"); + var verifier = CompileAndVerify(compilation, symbolValidator: module => + { + var indexer = module.GlobalNamespace.GetMember("C").Indexers.Single(); + Assert.Equal("p2", indexer.Parameters.Single().Name); + }); + verifier.VerifyDiagnostics( + // (5,24): warning CS9256: Partial property declarations 'int C.this[int p2]' and 'int C.this[int p1]' have signature differences. + // public partial int this[int p1] => 42; + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[int p2]", "int C.this[int p1]").WithLocation(5, 24)); + + var actual = GetDocumentationCommentText(compilation); + var expected = """ + + + + Test + + + + Accepts . + + + + """.Trim(); + AssertEx.Equal(expected, actual); + } + } + + /// Counterpart to . + [Fact] + public void PartialIndexer_Paramref_02() + { + var source1 = @" +partial class C +{ + /** Accepts . */ + public partial int this[int p1] => 42; +} +"; + + var source2 = @" +partial class C +{ + public partial int this[int p2] { get; } +} +"; + var tree1 = SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.RegularPreviewWithDocumentationComments); + var tree2 = SyntaxFactory.ParseSyntaxTree(source2, options: TestOptions.RegularPreviewWithDocumentationComments); + + // Files passed in order. + verify(new[] { tree1, tree2 }); + + // Files passed in reverse order. + verify(new[] { tree2, tree1 }); + + void verify(CSharpTestSource source) + { + var compilation = CreateCompilation(source, assemblyName: "Test"); + var verifier = CompileAndVerify(compilation, symbolValidator: module => + { + var indexer = module.GlobalNamespace.GetMember("C").Indexers.Single(); + Assert.Equal("p2", indexer.Parameters.Single().Name); + }); + verifier.VerifyDiagnostics( + // (4,42): warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'p2', but there is no parameter by that name + // /** Accepts . */ + Diagnostic(ErrorCode.WRN_UnmatchedParamRefTag, "p2").WithArguments("p2", "C.this[int]").WithLocation(4, 42), + // (5,24): warning CS9256: Partial property declarations 'int C.this[int p2]' and 'int C.this[int p1]' have signature differences. + // public partial int this[int p1] => 42; + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[int p2]", "int C.this[int p1]").WithLocation(5, 24)); + + var actual = GetDocumentationCommentText(compilation, + // (4,42): warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'p2', but there is no parameter by that name + // /** Accepts . */ + Diagnostic(ErrorCode.WRN_UnmatchedParamRefTag, "p2").WithArguments("p2", "C.this[int]").WithLocation(4, 42)); + var expected = @" + + + + Test + + + + Accepts . + + + + ".Trim(); + AssertEx.Equal(expected, actual); + } + } + + /// Counterpart to . + [Fact] + public void PartialIndexer_Paramref_03() + { + var source1 = @" +partial class C +{ + public partial int this[int p1] => 42; +} +"; + + var source2 = @" +partial class C +{ + /** Accepts . */ + public partial int this[int p2] { get; } +} +"; + var tree1 = SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.RegularPreviewWithDocumentationComments); + var tree2 = SyntaxFactory.ParseSyntaxTree(source2, options: TestOptions.RegularPreviewWithDocumentationComments); + + // Files passed in order. + verify(new[] { tree1, tree2 }); + + // Files passed in reverse order. + verify(new[] { tree2, tree1 }); + + void verify(CSharpTestSource source) + { + var compilation = CreateCompilation(source, assemblyName: "Test"); + var verifier = CompileAndVerify(compilation, symbolValidator: module => + { + var indexer = module.GlobalNamespace.GetMember("C").Indexers.Single(); + Assert.Equal("p2", indexer.Parameters.Single().Name); + }); + verifier.VerifyDiagnostics( + // (4,24): warning CS9256: Partial property declarations 'int C.this[int p2]' and 'int C.this[int p1]' have signature differences. + // public partial int this[int p1] => 42; + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[int p2]", "int C.this[int p1]").WithLocation(4, 24), + // (4,42): warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'p1', but there is no parameter by that name + // /** Accepts . */ + Diagnostic(ErrorCode.WRN_UnmatchedParamRefTag, "p1").WithArguments("p1", "C.this[int]").WithLocation(4, 42)); + + var actual = GetDocumentationCommentText(compilation, + // (4,42): warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'p1', but there is no parameter by that name + // /** Accepts . */ + Diagnostic(ErrorCode.WRN_UnmatchedParamRefTag, "p1").WithArguments("p1", "C.this[int]").WithLocation(4, 42)); + var expected = @" + + + + Test + + + + Accepts . + + + +".Trim(); + AssertEx.Equal(expected, actual); + } + } + + /// Counterpart to . + [Fact] + public void PartialIndexer_Paramref_04() + { + var source1 = @" +partial class C +{ + public partial int this[int p1] => 42; +} +"; + + var source2 = @" +partial class C +{ + /** Accepts . */ + public partial int this[int p2] { get; } +} +"; + var tree1 = SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.RegularPreviewWithDocumentationComments); + var tree2 = SyntaxFactory.ParseSyntaxTree(source2, options: TestOptions.RegularPreviewWithDocumentationComments); + + // Files passed in order. + verify(new[] { tree1, tree2 }); + + // Files passed in reverse order. + verify(new[] { tree2, tree1 }); + + void verify(CSharpTestSource source) + { + var compilation = CreateCompilation(source, assemblyName: "Test"); + var verifier = CompileAndVerify(compilation, symbolValidator: module => + { + var indexer = module.GlobalNamespace.GetMember("C").Indexers.Single(); + Assert.Equal("p2", indexer.Parameters.Single().Name); + }); + verifier.VerifyDiagnostics( + // (4,24): warning CS9256: Partial property declarations 'int C.this[int p2]' and 'int C.this[int p1]' have signature differences. + // public partial int this[int p1] => 42; + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[int p2]", "int C.this[int p1]").WithLocation(4, 24)); + + var actual = GetDocumentationCommentText(compilation); + var expected = """ + + + + Test + + + + Accepts . + + + + """.Trim(); + AssertEx.Equal(expected, actual); + } + } + #endregion Partial methods #region Crefs diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs index f40628bbb009d..6466391693427 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); @@ -11275,24 +11275,24 @@ interface I2 // (4,25): error CS8796: Partial method 'I1.M1()' must have accessibility modifiers because it has a 'virtual', 'override', 'sealed', 'new', or 'extern' modifier. // sealed partial void M1(); Diagnostic(ErrorCode.ERR_PartialMethodWithExtendedModMustHaveAccessMods, "M1").WithArguments("I1.M1()").WithLocation(4, 25), - // (5,27): error CS0750: A partial method cannot have the 'abstract' modifier + // (5,27): error CS0750: A partial member cannot have the 'abstract' modifier // abstract partial void M2(); - Diagnostic(ErrorCode.ERR_PartialMethodInvalidModifier, "M2").WithLocation(5, 27), + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "M2").WithLocation(5, 27), // (6,26): error CS8796: Partial method 'I1.M3()' must have accessibility modifiers because it has a 'virtual', 'override', 'sealed', 'new', or 'extern' modifier. // virtual partial void M3(); Diagnostic(ErrorCode.ERR_PartialMethodWithExtendedModMustHaveAccessMods, "M3").WithArguments("I1.M3()").WithLocation(6, 26), - // (10,21): error CS0754: A partial method may not explicitly implement an interface method + // (10,21): error CS0754: A partial member may not explicitly implement an interface member // partial void I2.M7(); - Diagnostic(ErrorCode.ERR_PartialMethodNotExplicit, "M7").WithLocation(10, 21), + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "M7").WithLocation(10, 21), // (15,25): error CS8796: Partial method 'I1.M4()' must have accessibility modifiers because it has a 'virtual', 'override', 'sealed', 'new', or 'extern' modifier. // sealed partial void M4() {} Diagnostic(ErrorCode.ERR_PartialMethodWithExtendedModMustHaveAccessMods, "M4").WithArguments("I1.M4()").WithLocation(15, 25), - // (15,25): error CS8798: Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // (15,25): error CS8798: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. // sealed partial void M4() {} - Diagnostic(ErrorCode.ERR_PartialMethodExtendedModDifference, "M4").WithLocation(15, 25), - // (16,27): error CS0750: A partial method cannot have the 'abstract' modifier + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "M4").WithLocation(15, 25), + // (16,27): error CS0750: A partial member cannot have the 'abstract' modifier // abstract partial void M5(); - Diagnostic(ErrorCode.ERR_PartialMethodInvalidModifier, "M5").WithLocation(16, 27), + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "M5").WithLocation(16, 27), // (16,27): error CS0756: A partial method may not have multiple defining declarations // abstract partial void M5(); Diagnostic(ErrorCode.ERR_PartialMethodOnlyOneLatent, "M5").WithLocation(16, 27), @@ -11302,15 +11302,15 @@ interface I2 // (17,26): error CS8796: Partial method 'I1.M6()' must have accessibility modifiers because it has a 'virtual', 'override', 'sealed', 'new', or 'extern' modifier. // virtual partial void M6() {} Diagnostic(ErrorCode.ERR_PartialMethodWithExtendedModMustHaveAccessMods, "M6").WithArguments("I1.M6()").WithLocation(17, 26), - // (17,26): error CS8798: Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // (17,26): error CS8798: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. // virtual partial void M6() {} - Diagnostic(ErrorCode.ERR_PartialMethodExtendedModDifference, "M6").WithLocation(17, 26), - // (19,21): error CS0754: A partial method may not explicitly implement an interface method + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "M6").WithLocation(17, 26), + // (19,21): error CS0754: A partial member may not explicitly implement an interface member // partial void I2.M7() {} - Diagnostic(ErrorCode.ERR_PartialMethodNotExplicit, "M7").WithLocation(19, 21), - // (25,18): error CS0751: A partial method must be declared within a partial type + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "M7").WithLocation(19, 21), + // (25,18): error CS0751: A partial member must be declared within a partial type // partial void M8(); - Diagnostic(ErrorCode.ERR_PartialMethodOnlyInPartialClass, "M8").WithLocation(25, 18) + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "M8").WithLocation(25, 18) ); } @@ -11426,12 +11426,12 @@ partial void M3() {} // (5,25): error CS0761: Partial method declarations of 'I1.M1()' have inconsistent constraints for type parameter 'T' // static partial void M1() {} Diagnostic(ErrorCode.ERR_PartialMethodInconsistentConstraints, "M1").WithArguments("I1.M1()", "T").WithLocation(5, 25), - // (8,25): error CS0758: Both partial method declarations must use a params parameter or neither may use a params parameter + // (8,25): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // static partial void M2(int[] x) {} - Diagnostic(ErrorCode.ERR_PartialMethodParamsDifference, "M2").WithLocation(8, 25), - // (11,18): error CS0763: Both partial method declarations must be static or neither may be static + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "M2").WithLocation(8, 25), + // (11,18): error CS0763: Both partial member declarations must be static or neither may be static // partial void M3() {} - Diagnostic(ErrorCode.ERR_PartialMethodStaticDifference, "M3").WithLocation(11, 18) + Diagnostic(ErrorCode.ERR_PartialMemberStaticDifference, "M3").WithLocation(11, 18) ); } @@ -51897,12 +51897,12 @@ class Test1 : I2 validate(compilation1.SourceModule); compilation1.VerifyDiagnostics( - // (9,30): error CS0754: A partial method may not explicitly implement an interface method + // (9,30): error CS0754: A partial member may not explicitly implement an interface member // abstract partial void I1.M1(); - Diagnostic(ErrorCode.ERR_PartialMethodNotExplicit, "M1").WithLocation(9, 30 + implModifiers.Length), - // (9,30): error CS0750: A partial method cannot have the 'abstract' modifier + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "M1").WithLocation(9, 30 + implModifiers.Length), + // (9,30): error CS0750: A partial member cannot have the 'abstract' modifier // abstract partial void I1.M1(); - Diagnostic(ErrorCode.ERR_PartialMethodInvalidModifier, "M1").WithLocation(9, 30 + implModifiers.Length), + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "M1").WithLocation(9, 30 + implModifiers.Length), // (12,15): error CS0535: 'Test1' does not implement interface member 'I1.M1()' // class Test1 : I2 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("Test1", "I1.M1()").WithLocation(12, 15) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtendedPartialMethodsTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtendedPartialMethodsTests.cs index 661d83856bae3..a92361f6fa327 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtendedPartialMethodsTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtendedPartialMethodsTests.cs @@ -289,7 +289,7 @@ internal partial void M1() { } comp.VerifyDiagnostics( // (5,28): error CS8797: Both partial method declarations must have equivalent accessibility modifiers. // internal partial void M1() { } - Diagnostic(ErrorCode.ERR_PartialMethodAccessibilityDifference, "M1").WithLocation(5, 28) + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "M1").WithLocation(5, 28) ); } @@ -304,9 +304,9 @@ partial void M1() { } }"; var comp = CreateCompilation(text, parseOptions: TestOptions.RegularWithExtendedPartialMethods); comp.VerifyDiagnostics( - // (5,19): error CS8797: Both partial method declarations must have identical accessibility modifiers. + // (5,19): error CS8797: Both partial member declarations must have identical accessibility modifiers. // partial void M1() { } - Diagnostic(ErrorCode.ERR_PartialMethodAccessibilityDifference, "M1").WithLocation(5, 19) + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "M1").WithLocation(5, 19) ); } @@ -321,9 +321,9 @@ private partial void M1() { } }"; var comp = CreateCompilation(text, parseOptions: TestOptions.RegularWithExtendedPartialMethods); comp.VerifyDiagnostics( - // (5,27): error CS8797: Both partial method declarations must have identical accessibility modifiers. + // (5,27): error CS8797: Both partial member declarations must have identical accessibility modifiers. // private partial void M1() { } - Diagnostic(ErrorCode.ERR_PartialMethodAccessibilityDifference, "M1").WithLocation(5, 27) + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "M1").WithLocation(5, 27) ); } @@ -440,7 +440,7 @@ internal partial void M1() { } comp.VerifyDiagnostics( // (5,27): error CS8798: Both partial method declarations must have equal combinations of 'virtual', 'override', 'sealed', or 'new' modifiers. // internal partial void M1() { } - Diagnostic(ErrorCode.ERR_PartialMethodExtendedModDifference, "M1").WithLocation(5, 27) + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "M1").WithLocation(5, 27) ); } @@ -457,7 +457,7 @@ internal virtual partial void M1() { } comp.VerifyDiagnostics( // (5,35): error CS8798: Both partial method declarations must have equal combinations of 'virtual', 'override', 'sealed', or 'new' modifiers. // internal virtual partial void M1() { } - Diagnostic(ErrorCode.ERR_PartialMethodExtendedModDifference, "M1").WithLocation(5, 35) + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "M1").WithLocation(5, 35) ); } @@ -792,7 +792,7 @@ internal partial void M1() { } comp.VerifyDiagnostics( // (9,27): error CS8798: Both partial method declarations must have equal combinations of 'virtual', 'override', 'sealed', or 'new' modifiers. // internal partial void M1() { } - Diagnostic(ErrorCode.ERR_PartialMethodExtendedModDifference, "M1").WithLocation(9, 27) + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "M1").WithLocation(9, 27) ); } @@ -816,7 +816,7 @@ internal override partial void M1() { } Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "M1").WithArguments("D.M1()", "C.M1()").WithLocation(8, 27), // (9,36): error CS8798: Both partial method declarations must have equal combinations of 'virtual', 'override', 'sealed', or 'new' modifiers. // internal override partial void M1() { } - Diagnostic(ErrorCode.ERR_PartialMethodExtendedModDifference, "M1").WithLocation(9, 36) + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "M1").WithLocation(9, 36) ); } @@ -965,7 +965,7 @@ internal override partial void M1() { } comp.VerifyDiagnostics( // (9,36): error CS8798: Both partial method declarations must have equal combinations of 'virtual', 'override', 'sealed', or 'new' modifiers. // internal override partial void M1() { } - Diagnostic(ErrorCode.ERR_PartialMethodExtendedModDifference, "M1").WithLocation(9, 36) + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "M1").WithLocation(9, 36) ); } @@ -986,7 +986,7 @@ internal sealed override partial void M1() { } comp.VerifyDiagnostics( // (9,43): error CS8798: Both partial method declarations must have equal combinations of 'virtual', 'override', 'sealed', or 'new' modifiers. // internal sealed override partial void M1() { } - Diagnostic(ErrorCode.ERR_PartialMethodExtendedModDifference, "M1").WithLocation(9, 43) + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "M1").WithLocation(9, 43) ); } @@ -1177,6 +1177,74 @@ static void validator(ModuleSymbol module) } } + [Fact] + public void Extern_Symbols_NoDllImport() + { + const string text1 = @" +public partial class C +{ + public static partial void M1(); + + public static extern partial void M1(); + + public static void M2() { M1(); } +}"; + + const string text2 = @" +public partial class C +{ + public static partial void M1(); + + public static extern partial void M1(); + + public static void M2() { M1(); } +}"; + const string expectedIL = @" +{ + // Code size 6 (0x6) + .maxstack 0 + IL_0000: call ""void C.M1()"" + IL_0005: ret +}"; + + var verifier = CompileAndVerify( + text1, + parseOptions: TestOptions.RegularWithExtendedPartialMethods, + sourceSymbolValidator: module => validator(module, isSource: true), + symbolValidator: module => validator(module, isSource: false), + // PEVerify fails when extern methods lack an implementation + verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M2", expectedIL); + + verifier = CompileAndVerify( + text2, + parseOptions: TestOptions.RegularWithExtendedPartialMethods, + sourceSymbolValidator: module => validator(module, isSource: true), + symbolValidator: module => validator(module, isSource: false), + // PEVerify fails when extern methods lack an implementation + verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M2", expectedIL); + + static void validator(ModuleSymbol module, bool isSource) + { + var type = module.ContainingAssembly.GetTypeByMetadataName("C"); + var method = type.GetMember("M1"); + + // 'IsExtern' is not round tripped when DllImport is missing + Assert.Equal(isSource, method.IsExtern); + if (method.PartialImplementationPart is MethodSymbol implementation) + { + Assert.True(method.IsPartialDefinition()); + Assert.Equal(isSource, implementation.IsExtern); + } + + var importData = method.GetDllImportData(); + Assert.Null(importData); + } + } + [Fact] public void Async_01() { @@ -1446,7 +1514,7 @@ internal partial void M1() { } comp.VerifyDiagnostics( // (10,27): error CS8798: Both partial method declarations must have equal combinations of 'virtual', 'override', 'sealed', or 'new' modifiers. // internal partial void M1() { } - Diagnostic(ErrorCode.ERR_PartialMethodExtendedModDifference, "M1").WithLocation(10, 27) + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "M1").WithLocation(10, 27) ); } @@ -1471,7 +1539,7 @@ partial class D : C Diagnostic(ErrorCode.WRN_NewRequired, "M1").WithArguments("D.M1()", "C.M1()").WithLocation(9, 27), // (10,31): error CS8798: Both partial method declarations must have equal combinations of 'virtual', 'override', 'sealed', or 'new' modifiers. // internal new partial void M1() { } - Diagnostic(ErrorCode.ERR_PartialMethodExtendedModDifference, "M1").WithLocation(10, 31) + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "M1").WithLocation(10, 31) ); } @@ -1819,12 +1887,12 @@ partial void I.M() { } // 2 "; var comp = CreateCompilation(text); comp.VerifyDiagnostics( - // (9,20): error CS0754: A partial method may not explicitly implement an interface method + // (9,20): error CS0754: A partial member may not explicitly implement an interface member // partial void I.M(); // 1 - Diagnostic(ErrorCode.ERR_PartialMethodNotExplicit, "M").WithLocation(9, 20), - // (10,20): error CS0754: A partial method may not explicitly implement an interface method + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "M").WithLocation(9, 20), + // (10,20): error CS0754: A partial member may not explicitly implement an interface member // partial void I.M() { } // 2 - Diagnostic(ErrorCode.ERR_PartialMethodNotExplicit, "M").WithLocation(10, 20)); + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "M").WithLocation(10, 20)); } [Fact] @@ -1844,15 +1912,15 @@ partial class C : I "; var comp = CreateCompilation(text, parseOptions: TestOptions.RegularWithExtendedPartialMethods); comp.VerifyDiagnostics( - // (9,19): error CS0754: A partial method may not explicitly implement an interface method + // (9,19): error CS0754: A partial member may not explicitly implement an interface member // partial int I.M(); - Diagnostic(ErrorCode.ERR_PartialMethodNotExplicit, "M").WithLocation(9, 19), + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "M").WithLocation(9, 19), // (9,19): error CS8794: Partial method 'C.I.M()' must have accessibility modifiers because it has a non-void return type. // partial int I.M(); Diagnostic(ErrorCode.ERR_PartialMethodWithNonVoidReturnMustHaveAccessMods, "M").WithArguments("C.I.M()").WithLocation(9, 19), - // (10,19): error CS0754: A partial method may not explicitly implement an interface method + // (10,19): error CS0754: A partial member may not explicitly implement an interface member // partial int I.M() => 42; - Diagnostic(ErrorCode.ERR_PartialMethodNotExplicit, "M").WithLocation(10, 19), + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "M").WithLocation(10, 19), // (10,19): error CS8794: Partial method 'C.I.M()' must have accessibility modifiers because it has a non-void return type. // partial int I.M() => 42; Diagnostic(ErrorCode.ERR_PartialMethodWithNonVoidReturnMustHaveAccessMods, "M").WithArguments("C.I.M()").WithLocation(10, 19)); @@ -2932,15 +3000,15 @@ partial class C }"; var comp = CreateCompilation(source, parseOptions: TestOptions.RegularWithExtendedPartialMethods); comp.VerifyDiagnostics( - // (5,24): error CS8818: Partial method declarations must have matching ref return values. + // (5,24): error CS8818: Partial member declarations must have matching ref return values. // public partial int M1() => throw null!; // 1 - Diagnostic(ErrorCode.ERR_PartialMethodRefReturnDifference, "M1").WithLocation(5, 24), + Diagnostic(ErrorCode.ERR_PartialMemberRefReturnDifference, "M1").WithLocation(5, 24), // (5,24): warning CS8826: Partial method declarations 'ref int C.M1()' and 'int C.M1()' have signature differences. // public partial int M1() => throw null!; // 1 Diagnostic(ErrorCode.WRN_PartialMethodTypeDifference, "M1").WithArguments("ref int C.M1()", "int C.M1()").WithLocation(5, 24), - // (8,28): error CS8818: Partial method declarations must have matching ref return values. + // (8,28): error CS8818: Partial member declarations must have matching ref return values. // public partial ref int M2() => throw null!; // 2 - Diagnostic(ErrorCode.ERR_PartialMethodRefReturnDifference, "M2").WithLocation(8, 28), + Diagnostic(ErrorCode.ERR_PartialMemberRefReturnDifference, "M2").WithLocation(8, 28), // (8,28): warning CS8826: Partial method declarations 'int C.M2()' and 'ref int C.M2()' have signature differences. // public partial ref int M2() => throw null!; // 2 Diagnostic(ErrorCode.WRN_PartialMethodTypeDifference, "M2").WithArguments("int C.M2()", "ref int C.M2()").WithLocation(8, 28)); @@ -2998,9 +3066,9 @@ partial class C }"; var comp = CreateCompilation(source, parseOptions: TestOptions.RegularWithExtendedPartialMethods); comp.VerifyDiagnostics( - // (5,37): error CS8142: Both partial method declarations, 'C.M1()' and 'C.M1()', must use the same tuple element names. + // (5,37): error CS8142: Both partial member declarations, 'C.M1()' and 'C.M1()', must use the same tuple element names. // public partial (int x1, int y1) M1() => default; // 1 - Diagnostic(ErrorCode.ERR_PartialMethodInconsistentTupleNames, "M1").WithArguments("C.M1()", "C.M1()").WithLocation(5, 37)); + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "M1").WithArguments("C.M1()", "C.M1()").WithLocation(5, 37)); } [Fact, WorkItem(44930, "https://github.com/dotnet/roslyn/issues/44930")] @@ -3085,15 +3153,15 @@ partial class C }"; var comp = CreateCompilation(source, parseOptions: TestOptions.RegularWithExtendedPartialMethods); comp.VerifyDiagnostics( - // (5,37): error CS8818: Partial method declarations must have matching ref return values. + // (5,37): error CS8818: Partial member declarations must have matching ref return values. // public partial ref readonly int M1() => throw null!; // 1 - Diagnostic(ErrorCode.ERR_PartialMethodRefReturnDifference, "M1").WithLocation(5, 37), + Diagnostic(ErrorCode.ERR_PartialMemberRefReturnDifference, "M1").WithLocation(5, 37), // (5,37): warning CS8826: Partial method declarations 'ref int C.M1()' and 'ref readonly int C.M1()' have signature differences. // public partial ref readonly int M1() => throw null!; // 1 Diagnostic(ErrorCode.WRN_PartialMethodTypeDifference, "M1").WithArguments("ref int C.M1()", "ref readonly int C.M1()").WithLocation(5, 37), - // (8,28): error CS8818: Partial method declarations must have matching ref return values. + // (8,28): error CS8818: Partial member declarations must have matching ref return values. // public partial ref int M2() => throw null!; // 2 - Diagnostic(ErrorCode.ERR_PartialMethodRefReturnDifference, "M2").WithLocation(8, 28), + Diagnostic(ErrorCode.ERR_PartialMemberRefReturnDifference, "M2").WithLocation(8, 28), // (8,28): warning CS8826: Partial method declarations 'ref readonly int C.M2()' and 'ref int C.M2()' have signature differences. // public partial ref int M2() => throw null!; // 2 Diagnostic(ErrorCode.WRN_PartialMethodTypeDifference, "M2").WithArguments("ref readonly int C.M2()", "ref int C.M2()").WithLocation(8, 28)); @@ -3208,9 +3276,9 @@ public void DifferentReturnTypes_18() // (4,27): error CS8817: Both partial method declarations must have the same return type. // public partial string F1() => null; Diagnostic(ErrorCode.ERR_PartialMethodReturnTypeDifference, "F1").WithLocation(4, 27), - // (4,27): error CS8818: Partial method declarations must have matching ref return values. + // (4,27): error CS8818: Partial member declarations must have matching ref return values. // public partial string F1() => null; - Diagnostic(ErrorCode.ERR_PartialMethodRefReturnDifference, "F1").WithLocation(4, 27)); + Diagnostic(ErrorCode.ERR_PartialMemberRefReturnDifference, "F1").WithLocation(4, 27)); } [Fact] @@ -3224,9 +3292,9 @@ public void DifferentReturnTypes_19() }"; var comp = CreateCompilation(source, parseOptions: TestOptions.RegularWithExtendedPartialMethods); comp.VerifyDiagnostics( - // (4,31): error CS8818: Partial method declarations must have matching ref return values. + // (4,31): error CS8818: Partial member declarations must have matching ref return values. // public partial (int, int) F1() => default; - Diagnostic(ErrorCode.ERR_PartialMethodRefReturnDifference, "F1").WithLocation(4, 31), + Diagnostic(ErrorCode.ERR_PartialMemberRefReturnDifference, "F1").WithLocation(4, 31), // (4,31): warning CS8826: Partial method declarations 'ref (int x, int y) C.F1()' and '(int, int) C.F1()' have signature differences. // public partial (int, int) F1() => default; Diagnostic(ErrorCode.WRN_PartialMethodTypeDifference, "F1").WithArguments("ref (int x, int y) C.F1()", "(int, int) C.F1()").WithLocation(4, 31)); @@ -3354,18 +3422,18 @@ internal partial void F4((T, U) t) { } // 3 static void verifyDiagnostics(CSharpCompilation comp) { comp.VerifyDiagnostics( - // (5,18): error CS8142: Both partial method declarations, 'C.F2((T, U))' and 'C.F2((T x, U y))', must use the same tuple element names. + // (5,18): error CS8142: Both partial member declarations, 'C.F2((T, U))' and 'C.F2((T x, U y))', must use the same tuple element names. // partial void F2((T x, U y) t) { } // 1 - Diagnostic(ErrorCode.ERR_PartialMethodInconsistentTupleNames, "F2").WithArguments("C.F2((T, U))", "C.F2((T x, U y))").WithLocation(5, 18), - // (8,18): error CS8142: Both partial method declarations, 'C.F3((dynamic, object))' and 'C.F3((object x, dynamic y))', must use the same tuple element names. + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "F2").WithArguments("C.F2((T, U))", "C.F2((T x, U y))").WithLocation(5, 18), + // (8,18): error CS8142: Both partial member declarations, 'C.F3((dynamic, object))' and 'C.F3((object x, dynamic y))', must use the same tuple element names. // partial void F3((object x, dynamic y) t) { } // 2 - Diagnostic(ErrorCode.ERR_PartialMethodInconsistentTupleNames, "F3").WithArguments("C.F3((dynamic, object))", "C.F3((object x, dynamic y))").WithLocation(8, 18), - // (10,27): error CS8142: Both partial method declarations, 'C.F4((T x, U y))' and 'C.F4((T, U))', must use the same tuple element names. + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "F3").WithArguments("C.F3((dynamic, object))", "C.F3((object x, dynamic y))").WithLocation(8, 18), + // (10,27): error CS8142: Both partial member declarations, 'C.F4((T x, U y))' and 'C.F4((T, U))', must use the same tuple element names. // internal partial void F4((T, U) t) { } // 3 - Diagnostic(ErrorCode.ERR_PartialMethodInconsistentTupleNames, "F4").WithArguments("C.F4((T x, U y))", "C.F4((T, U))").WithLocation(10, 27), - // (13,29): error CS8142: Both partial method declarations, 'C.F6()' and 'C.F6()', must use the same tuple element names. + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "F4").WithArguments("C.F4((T x, U y))", "C.F4((T, U))").WithLocation(10, 27), + // (13,29): error CS8142: Both partial member declarations, 'C.F6()' and 'C.F6()', must use the same tuple element names. // internal partial (T, U) F6() => default; // 4 - Diagnostic(ErrorCode.ERR_PartialMethodInconsistentTupleNames, "F6").WithArguments("C.F6()", "C.F6()").WithLocation(13, 29)); + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "F6").WithArguments("C.F6()", "C.F6()").WithLocation(13, 29)); } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs index 0d5d2a56ac694..204624bfe5c6c 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs @@ -156,9 +156,9 @@ static partial void local() { } // (5,29): error CS0759: No defining declaration found for implementing declaration of partial method 'C.local()' // static partial void local() { } Diagnostic(ErrorCode.ERR_PartialMethodMustHaveLatent, "local").WithArguments("C.local()").WithLocation(5, 29), - // (5,29): error CS0751: A partial method must be declared within a partial type + // (5,29): error CS0751: A partial member must be declared within a partial type // static partial void local() { } - Diagnostic(ErrorCode.ERR_PartialMethodOnlyInPartialClass, "local").WithLocation(5, 29), + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "local").WithLocation(5, 29), // (7,1): error CS1022: Type or namespace definition, or end-of-file expected // } Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(7, 1)); @@ -182,9 +182,9 @@ partial void local() { } // (5,22): error CS0759: No defining declaration found for implementing declaration of partial method 'C.local()' // partial void local() { } Diagnostic(ErrorCode.ERR_PartialMethodMustHaveLatent, "local").WithArguments("C.local()").WithLocation(5, 22), - // (5,22): error CS0751: A partial method must be declared within a partial type + // (5,22): error CS0751: A partial member must be declared within a partial type // partial void local() { } - Diagnostic(ErrorCode.ERR_PartialMethodOnlyInPartialClass, "local").WithLocation(5, 22), + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "local").WithLocation(5, 22), // (7,1): error CS1022: Type or namespace definition, or end-of-file expected // } Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(7, 1)); 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..e44733027dd10 --- /dev/null +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs @@ -0,0 +1,4977 @@ +// 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.Reflection; +using System.Reflection.Metadata.Ecma335; +using System.Runtime.InteropServices; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; +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.Symbols +{ + public class PartialPropertiesTests : CSharpTestBase + { + [Theory] + [InlineData("partial int P { get; set; }")] + [InlineData("partial int P { get; }")] + [InlineData("partial int P { set; }")] + [InlineData("partial int P { get; init; }")] + [InlineData("partial int P { init; }")] + public void MissingDeclaration_01(string definitionPart) + { + // definition without implementation + var source = $$""" + partial class C + { + {{definitionPart}} + } + """; + var comp = CreateCompilation([source, IsExternalInitTypeDefinition]); + comp.VerifyEmitDiagnostics( + // (3,17): error CS9248: 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"); + var prop = cClass.GetMember("P"); + Assert.True(prop.IsPartialDefinition); + Assert.Null(prop.PartialImplementationPart); + + var members = cClass.GetMembers().SelectAsArray(m => m.ToTestDisplayString()); + switch (definitionPart) + { + case "partial int P { get; set; }": + AssertEx.Equal([ + "System.Int32 C.P { get; set; }", + "System.Int32 C.P.get", + "void C.P.set", + "C..ctor()" + ], members); + break; + case "partial int P { get; }": + AssertEx.Equal([ + "System.Int32 C.P { get; }", + "System.Int32 C.P.get", + "C..ctor()" + ], members); + break; + case "partial int P { set; }": + AssertEx.Equal([ + "System.Int32 C.P { set; }", + "void C.P.set", + "C..ctor()" + ], members); + break; + case "partial int P { get; init; }": + AssertEx.Equal([ + "System.Int32 C.P { get; init; }", + "System.Int32 C.P.get", + "void modreq(System.Runtime.CompilerServices.IsExternalInit) C.P.init", + "C..ctor()" + ], members); + break; + case "partial int P { init; }": + AssertEx.Equal([ + "System.Int32 C.P { init; }", + "void modreq(System.Runtime.CompilerServices.IsExternalInit) C.P.init", + "C..ctor()" + ], members); + break; + default: + throw ExceptionUtilities.Unreachable(); + } + } + + [Theory] + [InlineData("partial int P { get => throw null!; set { } }")] + [InlineData("partial int P { get => throw null!; }")] + [InlineData("partial int P { set { } }")] + [InlineData("partial int P { get => throw null!; init { } }")] + [InlineData("partial int P { init { } }")] + public void MissingDeclaration_02(string implementationPart) + { + // implementation without definition + var source = $$""" + partial class C + { + {{implementationPart}} + } + """; + var comp = CreateCompilation([source, IsExternalInitTypeDefinition]); + comp.VerifyEmitDiagnostics( + // (3,17): error CS9249: Partial property 'C.P' must have a definition part. + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "P").WithArguments("C.P").WithLocation(3, 17) + ); + + var cClass = comp.GetMember("C"); + var prop = cClass.GetMember("P"); + Assert.True(prop.IsPartialImplementation); + Assert.Null(prop.PartialDefinitionPart); + + var members = cClass.GetMembers().SelectAsArray(m => m.ToTestDisplayString()); + switch (implementationPart) + { + case "partial int P { get => throw null!; set { } }": + AssertEx.Equal([ + "System.Int32 C.P { get; set; }", + "System.Int32 C.P.get", + "void C.P.set", + "C..ctor()" + ], members); + break; + case "partial int P { get => throw null!; }": + AssertEx.Equal([ + "System.Int32 C.P { get; }", + "System.Int32 C.P.get", + "C..ctor()" + ], members); + break; + case "partial int P { set { } }": + AssertEx.Equal([ + "System.Int32 C.P { set; }", + "void C.P.set", + "C..ctor()" + ], members); + break; + case "partial int P { get => throw null!; init { } }": + AssertEx.Equal([ + "System.Int32 C.P { get; init; }", + "System.Int32 C.P.get", + "void modreq(System.Runtime.CompilerServices.IsExternalInit) C.P.init", + "C..ctor()" + ], members); + break; + case "partial int P { init { } }": + AssertEx.Equal([ + "System.Int32 C.P { init; }", + "void modreq(System.Runtime.CompilerServices.IsExternalInit) C.P.init", + "C..ctor()" + ], members); + break; + default: + throw ExceptionUtilities.Unreachable(); + } + } + + [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 CS9248: 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 CS9250: A partial property may not have multiple defining declarations, and cannot be an auto-property. + // partial int P { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyDuplicateDefinition, "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 CS9250: A partial property may not have multiple defining declarations, and cannot be an auto-property. + // partial int P { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyDuplicateDefinition, "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 CS9249: Partial property 'C.P' must have a definition part. + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "P").WithArguments("C.P").WithLocation(3, 17), + // (4,17): error CS9251: A partial property may not have multiple implementing declarations + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyDuplicateImplementation, "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 CS9251: A partial property may not have multiple implementing declarations + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyDuplicateImplementation, "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 CS9250: A partial property may not have multiple defining declarations, and cannot be an auto-property. + // partial int P { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyDuplicateDefinition, "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 CS9251: A partial property may not have multiple implementing declarations + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyDuplicateImplementation, "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 DuplicateDeclaration_06() + { + // partial method and partial property have the same name + var source = """ + partial class C + { + public partial int P { get; set; } + public partial int P() => 1; + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,24): error CS9248: Partial property 'C.P' must have an implementation part. + // public partial int P { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "P").WithArguments("C.P").WithLocation(3, 24), + // (4,24): error CS0759: No defining declaration found for implementing declaration of partial method 'C.P()' + // public partial int P() => 1; + Diagnostic(ErrorCode.ERR_PartialMethodMustHaveLatent, "P").WithArguments("C.P()").WithLocation(4, 24), + // (4,24): error CS0102: The type 'C' already contains a definition for 'P' + // public partial int P() => 1; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "P").WithLocation(4, 24) + ); + } + + [Fact] + public void DuplicateDeclaration_07() + { + // partial method and partial property accessor have the same metadata name + var source = """ + partial class C + { + public partial int P { get; } + public partial int get_P() => 1; + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,24): error CS9248: Partial property 'C.P' must have an implementation part. + // public partial int P { get; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "P").WithArguments("C.P").WithLocation(3, 24), + // (3,28): error CS0082: Type 'C' already reserves a member called 'get_P' with the same parameter types + // public partial int P { get; } + Diagnostic(ErrorCode.ERR_MemberReserved, "get").WithArguments("get_P", "C").WithLocation(3, 28) + ); + } + + [Fact] + public void DuplicateDeclaration_08() + { + // multiple implementing declarations where accessors are "split" across declarations + var source = """ + partial class C + { + public partial int P { get; set; } + public partial int P { get => 1; } + public partial int P { set { } } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,24): error CS9252: Property accessor 'C.P.set' must be implemented because it is declared on the definition part + // public partial int P { get => 1; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingAccessor, "P").WithArguments("C.P.set").WithLocation(4, 24), + // (5,24): error CS9251: A partial property may not have multiple implementing declarations + // public partial int P { set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyDuplicateImplementation, "P").WithLocation(5, 24), + // (5,24): error CS0102: The type 'C' already contains a definition for 'P' + // public partial int P { set { } } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "P").WithLocation(5, 24) + ); + + if (comp.GetMembers("C.P") is not [SourcePropertySymbol prop, SourcePropertySymbol duplicateProp]) + throw ExceptionUtilities.UnexpectedValue(comp.GetMembers("C.P")); + + Assert.True(prop.IsPartialDefinition); + Assert.Equal("System.Int32 C.P { get; set; }", prop.ToTestDisplayString()); + Assert.Equal("System.Int32 C.P { get; }", prop.PartialImplementationPart.ToTestDisplayString()); + + Assert.True(duplicateProp.IsPartialImplementation); + Assert.Null(duplicateProp.PartialDefinitionPart); + Assert.Equal("System.Int32 C.P { set; }", duplicateProp.ToTestDisplayString()); + } + + [Fact] + public void DuplicateDeclaration_08_Definition() + { + // multiple defining declarations where accessors are "split" across declarations + var source = """ + partial class C + { + public partial int P { get; } + public partial int P { set; } + public partial int P { get => 1; set { } } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,24): error CS9250: A partial property may not have multiple defining declarations, and cannot be an auto-property. + // public partial int P { set; } + Diagnostic(ErrorCode.ERR_PartialPropertyDuplicateDefinition, "P").WithLocation(4, 24), + // (4,24): error CS0102: The type 'C' already contains a definition for 'P' + // public partial int P { set; } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "P").WithLocation(4, 24), + // (5,24): error CS9253: Property accessor 'C.P.set' does not implement any accessor declared on the definition part + // public partial int P { get => 1; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyUnexpectedAccessor, "P").WithArguments("C.P.set").WithLocation(5, 24) + ); + + if (comp.GetMembers("C.P") is not [SourcePropertySymbol prop, SourcePropertySymbol duplicateProp]) + throw ExceptionUtilities.UnexpectedValue(comp.GetMembers("C.P")); + + Assert.True(prop.IsPartialDefinition); + Assert.Equal("System.Int32 C.P { get; }", prop.ToTestDisplayString()); + Assert.Equal("System.Int32 C.P { get; set; }", prop.PartialImplementationPart.ToTestDisplayString()); + + Assert.True(duplicateProp.IsPartialDefinition); + Assert.Null(duplicateProp.PartialImplementationPart); + Assert.Equal("System.Int32 C.P { set; }", duplicateProp.ToTestDisplayString()); + } + + [Fact] + public void DuplicateDeclaration_09() + { + // partial indexer and partial property Item + var source = """ + partial class C + { + public partial int this[int i] { get; } + public partial int Item => 1; + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,24): error CS9248: Partial property 'C.this[int]' must have an implementation part. + // public partial int this[int i] { get; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "this").WithArguments("C.this[int]").WithLocation(3, 24), + // (3,24): error CS0102: The type 'C' already contains a definition for 'Item' + // public partial int this[int i] { get; } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "this").WithArguments("C", "Item").WithLocation(3, 24), + // (4,24): error CS9249: Partial property 'C.Item' must have a definition part. + // public partial int Item => 1; + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "Item").WithArguments("C.Item").WithLocation(4, 24) + ); + } + + [Fact] + public void DuplicateDeclaration_10() + { + // partial parameterless (error) indexer and partial property Item + var source = """ + partial class C + { + public partial int this[] { get; } + public partial int Item => 1; + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,24): error CS9248: Partial property 'C.this' must have an implementation part. + // public partial int this[] { get; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "this").WithArguments("C.this").WithLocation(3, 24), + // (3,24): error CS0102: The type 'C' already contains a definition for 'Item' + // public partial int this[] { get; } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "this").WithArguments("C", "Item").WithLocation(3, 24), + // (3,29): error CS1551: Indexers must have at least one parameter + // public partial int this[] { get; } + Diagnostic(ErrorCode.ERR_IndexerNeedsParam, "]").WithLocation(3, 29), + // (4,24): error CS9249: Partial property 'C.Item' must have a definition part. + // public partial int Item => 1; + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "Item").WithArguments("C.Item").WithLocation(4, 24) + ); + } + + [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 CS9252: 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 { set { } } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,17): error CS9252: Property accessor 'C.P.get' must be implemented because it is declared on the definition part + // partial int P { set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingAccessor, "P").WithArguments("C.P.get").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 CS9252: 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) + ); + } + + [Theory] + [InlineData("get")] + [InlineData("set")] + [InlineData("init")] + public void MissingAccessor_04(string accessorKind) + { + // duplicate property definitions, one with a single accessor, one empty + var source = $$""" + partial class C + { + partial int P { {{accessorKind}}; } + partial int P { } + } + """; + var comp = CreateCompilation([source, IsExternalInitTypeDefinition]); + comp.VerifyEmitDiagnostics( + // (3,17): error CS9248: Partial property 'C.P' must have an implementation part. + // partial int P { {{accessorKind}}; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "P").WithArguments("C.P").WithLocation(3, 17), + // (4,17): error CS9250: A partial property may not have multiple defining declarations, and cannot be an auto-property. + // partial int P { } + Diagnostic(ErrorCode.ERR_PartialPropertyDuplicateDefinition, "P").WithLocation(4, 17), + // (4,17): error CS0102: The type 'C' already contains a definition for 'P' + // partial int P { } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "P").WithLocation(4, 17), + // (4,17): error CS0548: 'C.P': property or indexer must have at least one accessor + // partial int P { } + Diagnostic(ErrorCode.ERR_PropertyWithNoAccessors, "P").WithArguments("C.P").WithLocation(4, 17) + ); + } + + [Theory] + [InlineData("get")] + [InlineData("set")] + [InlineData("init")] + public void MissingAccessor_05(string accessorKind) + { + // implementation single accessor, definition empty + var source = $$""" + partial class C + { + partial int P { {{accessorKind}} => throw null!; } + partial int P { } + } + """; + var comp = CreateCompilation([source, IsExternalInitTypeDefinition]); + comp.VerifyEmitDiagnostics( + // (3,17): error CS9253: Property accessor 'C.P.{accessorKind}' does not implement any accessor declared on the definition part + // partial int P { {{accessorKind}} => throw null!; } + Diagnostic(ErrorCode.ERR_PartialPropertyUnexpectedAccessor, "P").WithArguments($"C.P.{accessorKind}").WithLocation(3, 17), + // (4,17): error CS0548: 'C.P': property or indexer must have at least one accessor + // partial int P { } + Diagnostic(ErrorCode.ERR_PropertyWithNoAccessors, "P").WithArguments("C.P").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 CS9253: 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 CS9253: 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 CS9253: 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 AccessorKind_01() + { + // definition has set but implementation has init + var source = """ + partial class C + { + partial int P { get; set; } + partial int P { get => throw null!; init { } } + } + """; + + var comp = CreateCompilation([source, IsExternalInitTypeDefinition]); + comp.VerifyEmitDiagnostics( + // (4,41): error CS9254: Property accessor 'C.P.init' must be 'set' to match the definition part + // partial int P { get => throw null!; init { } } + Diagnostic(ErrorCode.ERR_PartialPropertyInitMismatch, "init").WithArguments("C.P.init", "set").WithLocation(4, 41) + ); + } + + [Fact] + public void AccessorKind_02() + { + // definition has init but implementation has set + var source = """ + partial class C + { + partial int P { get; init; } + partial int P { get => throw null!; set { } } + } + """; + var comp = CreateCompilation([source, IsExternalInitTypeDefinition]); + comp.VerifyEmitDiagnostics( + // (4,41): error CS9254: Property accessor 'C.P.set' must be 'init' to match the definition part + // partial int P { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyInitMismatch, "set").WithArguments("C.P.set", "init").WithLocation(4, 41) + ); + } + + [Fact] + public void Extern_01() + { + var source = """ + public partial class C + { + public partial int P { get; set; } + public extern partial int P { get; set; } + } + """; + + var verifier = CompileAndVerify( + [source, IsExternalInitTypeDefinition], + sourceSymbolValidator: verifySource, + symbolValidator: verifyMetadata, + // PEVerify fails when extern methods lack an implementation + verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + + void verifySource(ModuleSymbol module) + { + var prop = module.GlobalNamespace.GetMember("C.P"); + Assert.True(prop.GetPublicSymbol().IsExtern); + Assert.True(prop.GetMethod!.GetPublicSymbol().IsExtern); + Assert.True(prop.SetMethod!.GetPublicSymbol().IsExtern); + Assert.True(prop.PartialImplementationPart!.GetPublicSymbol().IsExtern); + Assert.True(prop.PartialImplementationPart!.GetMethod!.GetPublicSymbol().IsExtern); + Assert.True(prop.PartialImplementationPart!.SetMethod!.GetPublicSymbol().IsExtern); + } + + void verifyMetadata(ModuleSymbol module) + { + var prop = module.GlobalNamespace.GetMember("C.P"); + // IsExtern doesn't round trip from metadata when DllImportAttribute is missing + // This is consistent with the behavior for methods + Assert.False(prop.GetPublicSymbol().IsExtern); + Assert.False(prop.GetMethod!.GetPublicSymbol().IsExtern); + Assert.False(prop.SetMethod!.GetPublicSymbol().IsExtern); + } + } + + [Fact] + public void Extern_02() + { + var source = """ + partial class C + { + extern partial int P { get; set; } + extern partial int P { get; set; } + } + """; + var comp = CreateCompilation([source, IsExternalInitTypeDefinition]); + comp.VerifyEmitDiagnostics( + // (3,24): error CS9249: Partial property 'C.P' must have a definition part. + // extern partial int P { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "P").WithArguments("C.P").WithLocation(3, 24), + // (4,24): error CS9251: A partial property may not have multiple implementing declarations + // extern partial int P { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyDuplicateImplementation, "P").WithLocation(4, 24), + // (4,24): error CS0102: The type 'C' already contains a definition for 'P' + // extern partial int P { get; set; } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "P").WithLocation(4, 24) + ); + + var members = comp.GetMembers("C.P").SelectAsArray(m => (SourcePropertySymbol)m); + Assert.Equal(2, members.Length); + Assert.True(members[0].IsExtern); + Assert.True(members[0].IsPartialImplementation); + Assert.True(members[1].IsExtern); + Assert.True(members[1].IsPartialImplementation); + } + + /// Based on as a starting point. + [Fact] + public void Extern_03() + { + var source = """ + using System.Runtime.InteropServices; + + public partial class C + { + public static partial int P { get; set; } + public static extern partial int P + { + [DllImport("something.dll")] + get; + + [DllImport("something.dll")] + set; + } + } + """; + var verifier = CompileAndVerify( + [source, IsExternalInitTypeDefinition], + symbolValidator: module => verify(module, isSource: false), + sourceSymbolValidator: module => verify(module, isSource: true)); + verifier.VerifyDiagnostics(); + + void verify(ModuleSymbol module, bool isSource) + { + var prop = module.GlobalNamespace.GetMember("C.P"); + Assert.True(prop.GetPublicSymbol().IsExtern); + verifyAccessor(prop.GetMethod!); + verifyAccessor(prop.SetMethod!); + + if (isSource) + { + var implPart = ((SourcePropertySymbol)prop).PartialImplementationPart!; + Assert.True(implPart.GetPublicSymbol().IsExtern); + verifyAccessor(implPart.GetMethod!); + verifyAccessor(implPart.SetMethod!); + } + } + + void verifyAccessor(MethodSymbol accessor) + { + Assert.True(accessor.GetPublicSymbol().IsExtern); + + var importData = accessor.GetDllImportData()!; + Assert.NotNull(importData); + Assert.Equal("something.dll", importData.ModuleName); + Assert.Equal(accessor.MetadataName, importData.EntryPointName); // e.g. 'get_P' + 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); + } + } + + /// Based on 'AttributeTests_WellKnownAttributes.TestPseudoAttributes_DllImport_OperatorsAndAccessors'. + [Theory] + [CombinatorialData] + public void TestPseudoAttributes_DllImport(bool attributesOnDefinition) + { + var source = $$""" +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +public partial class C +{ + public static partial int F + { + {{(attributesOnDefinition ? @"[DllImport(""a"")]" : "")}} get; + {{(attributesOnDefinition ? @"[DllImport(""b"")]" : "")}} set; + } + + public extern static partial int F + { + {{(attributesOnDefinition ? "" : @"[DllImport(""a"")]")}} get; + {{(attributesOnDefinition ? "" : @"[DllImport(""b"")]")}} set; + } +} +"""; + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview.WithNoRefSafetyRulesAttribute(), assemblyValidator: (assembly) => + { + var metadataReader = assembly.GetMetadataReader(); + + // no backing fields should be generated -- all members are "extern" members: + Assert.Equal(0, metadataReader.FieldDefinitions.AsEnumerable().Count()); + + Assert.Equal(2, metadataReader.GetTableRowCount(TableIndex.ModuleRef)); + Assert.Equal(2, metadataReader.GetTableRowCount(TableIndex.ImplMap)); + var visitedEntryPoints = new Dictionary(); + + foreach (var method in metadataReader.GetImportedMethods()) + { + string moduleName = metadataReader.GetString(metadataReader.GetModuleReference(method.GetImport().Module).Name); + string entryPointName = metadataReader.GetString(method.Name); + switch (entryPointName) + { + case "get_F": + Assert.Equal("a", moduleName); + break; + + case "set_F": + Assert.Equal("b", moduleName); + break; + + default: + throw TestExceptionUtilities.UnexpectedValue(entryPointName); + } + + // This throws if we visit one entry point name twice. + visitedEntryPoints.Add(entryPointName, true); + } + + Assert.Equal(2, visitedEntryPoints.Count); + }); + } + + [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); + + var propDefinition = comp.GetMember("C.P"); + Assert.True(propDefinition.IsPartialDefinition); + + var propImplementation = propDefinition.PartialImplementationPart!; + Assert.True(propImplementation.IsPartialImplementation); + + Assert.Same(propDefinition, propImplementation.PartialDefinitionPart); + Assert.Null(propImplementation.PartialImplementationPart); + Assert.Same(propImplementation, propDefinition.PartialImplementationPart); + Assert.Null(propDefinition.PartialDefinitionPart); + + Assert.Same(propDefinition.GetMethod, comp.GetMember("C.get_P")); + Assert.Same(propDefinition.SetMethod, comp.GetMember("C.set_P")); + + verifyAccessor(propDefinition.GetMethod!, propImplementation.GetMethod!); + verifyAccessor(propDefinition.SetMethod!, propImplementation.SetMethod!); + + void verifyAccessor(MethodSymbol definitionAccessor, MethodSymbol implementationAccessor) + { + Assert.True(definitionAccessor.IsPartialDefinition()); + Assert.True(implementationAccessor.IsPartialImplementation()); + + Assert.Same(implementationAccessor, definitionAccessor.PartialImplementationPart); + Assert.Null(definitionAccessor.PartialDefinitionPart); + Assert.Same(definitionAccessor, implementationAccessor.PartialDefinitionPart); + Assert.Null(implementationAccessor.PartialImplementationPart); + } + } + + [Theory] + [InlineData("public partial int P { get => _p; }")] + [InlineData("public partial int P => _p;")] + public void Semantics_02(string implementationPart) + { + // get-only + var source = $$""" + using System; + + var c = new C(); + Console.Write(c.P); + + partial class C + { + public partial int P { get; } + } + + partial class C + { + private int _p = 1; + {{implementationPart}} + } + """; + 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 + } + """); + + 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; }", + "System.Int32 C.P.get", + "System.Int32 C._p", + "C..ctor()" + ], members); + + var propDefinition = comp.GetMember("C.P"); + Assert.True(propDefinition.IsPartialDefinition); + + var propImplementation = propDefinition.PartialImplementationPart!; + Assert.True(propImplementation.IsPartialImplementation); + + Assert.Same(propDefinition, propImplementation.PartialDefinitionPart); + Assert.Null(propImplementation.PartialImplementationPart); + Assert.Same(propImplementation, propDefinition.PartialImplementationPart); + Assert.Null(propDefinition.PartialDefinitionPart); + + Assert.Null(propDefinition.SetMethod); + Assert.Null(propImplementation.SetMethod); + + var definitionAccessor = propDefinition.GetMethod!; + var implementationAccessor = propImplementation.GetMethod!; + Assert.True(definitionAccessor.IsPartialDefinition()); + Assert.True(implementationAccessor.IsPartialImplementation()); + + Assert.Same(implementationAccessor, definitionAccessor.PartialImplementationPart); + Assert.Null(definitionAccessor.PartialDefinitionPart); + Assert.Same(definitionAccessor, implementationAccessor.PartialDefinitionPart); + Assert.Null(implementationAccessor.PartialImplementationPart); + } + + [Theory] + [InlineData("set")] + [InlineData("init")] + public void Semantics_03(string accessorKind) + { + // set/init-only + var source = $$""" + using System; + + var c = new C() { P = 1 }; + + partial class C + { + public partial int P { {{accessorKind}}; } + } + + partial class C + { + public partial int P + { + {{accessorKind}} + { + Console.Write(value); + } + } + } + """; + var verifier = CompileAndVerify([source, IsExternalInitTypeDefinition], expectedOutput: "1"); + verifier.VerifyDiagnostics(); + verifier.VerifyIL($"C.P.{accessorKind}", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.1 + IL_0001: call "void System.Console.Write(int)" + IL_0006: ret + } + """); + + var comp = (CSharpCompilation)verifier.Compilation; + var cClass = comp.GetMember("C"); + var members = cClass.GetMembers().SelectAsArray(m => m.ToTestDisplayString()); + + if (accessorKind == "set") + { + AssertEx.Equal([ + "System.Int32 C.P { set; }", + "void C.P.set", + "C..ctor()" + ], members); + } + else + { + AssertEx.Equal([ + "System.Int32 C.P { init; }", + "void modreq(System.Runtime.CompilerServices.IsExternalInit) C.P.init", + "C..ctor()" + ], + members); + } + + var propDefinition = comp.GetMember("C.P"); + Assert.True(propDefinition.IsPartialDefinition); + + var propImplementation = propDefinition.PartialImplementationPart!; + Assert.True(propImplementation.IsPartialImplementation); + + Assert.Same(propDefinition, propImplementation.PartialDefinitionPart); + Assert.Null(propImplementation.PartialImplementationPart); + Assert.Same(propImplementation, propDefinition.PartialImplementationPart); + Assert.Null(propDefinition.PartialDefinitionPart); + + Assert.Null(propDefinition.GetMethod); + Assert.Null(propImplementation.GetMethod); + + var definitionAccessor = propDefinition.SetMethod!; + var implementationAccessor = propImplementation.SetMethod!; + Assert.True(definitionAccessor.IsPartialDefinition()); + Assert.True(implementationAccessor.IsPartialImplementation()); + + Assert.Same(implementationAccessor, definitionAccessor.PartialImplementationPart); + Assert.Null(definitionAccessor.PartialDefinitionPart); + Assert.Same(definitionAccessor, implementationAccessor.PartialDefinitionPart); + Assert.Null(implementationAccessor.PartialImplementationPart); + } + + [Theory] + [InlineData("public partial int P { get => _p; set => _p = value; }")] + [InlineData("public partial int P { set => _p = value; get => _p; }")] + public void Semantics_04(string implementationPart) + { + // ordering difference between def and impl + var source = $$""" + using System; + + var c = new C() { P = 1 }; + Console.Write(c.P); + + partial class C + { + public partial int P { get; set; } + + private int _p; + {{implementationPart}} + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "1"); + verifier.VerifyDiagnostics(); + var comp = (CSharpCompilation)verifier.Compilation; + + var members = comp.GetMember("C").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); + + var reference = comp.EmitToImageReference(); + var comp1 = CreateCompilation([], options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), references: [reference]); + var members1 = comp1.GetMember("C").GetMembers().SelectAsArray(m => m.ToTestDisplayString()); + AssertEx.Equal([ + "System.Int32 C._p", + "System.Int32 C.P.get", + "void C.P.set", + "C..ctor()", + "System.Int32 C.P { get; set; }" + ], members1); + } + + [Theory] + [InlineData("public partial int P { get => _p; set => _p = value; }")] + [InlineData("public partial int P { set => _p = value; get => _p; }")] + public void Semantics_05(string implementationPart) + { + // ordering difference between def and impl (def set before get) + var source = $$""" + using System; + + var c = new C() { P = 1 }; + Console.Write(c.P); + + partial class C + { + public partial int P { set; get; } + + private int _p; + {{implementationPart}} + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "1"); + verifier.VerifyDiagnostics(); + var comp = (CSharpCompilation)verifier.Compilation; + + var members = comp.GetMember("C").GetMembers().SelectAsArray(m => m.ToTestDisplayString()); + // set accessor appears before get accessor, otherwise member order and symbol display is the same as Semantics_04 + AssertEx.Equal([ + "System.Int32 C.P { get; set; }", + "void C.P.set", + "System.Int32 C.P.get", + "System.Int32 C._p", + "C..ctor()" + ], members); + + var reference = comp.EmitToImageReference(); + var comp1 = CreateCompilation([], options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), references: [reference]); + var members1 = comp1.GetMember("C").GetMembers().SelectAsArray(m => m.ToTestDisplayString()); + AssertEx.Equal([ + "System.Int32 C._p", + "void C.P.set", + "System.Int32 C.P.get", + "C..ctor()", + "System.Int32 C.P { get; set; }" + ], members1); + } + + [Fact] + public void ModifierDifference_Accessibility_Property() + { + var source = """ + partial class C + { + public partial int P1 { get; set; } + partial int P2 { get; set; } + } + + partial class C + { + partial int P1 { get => throw null!; set { } } + protected partial int P2 { get => throw null!; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (9,17): error CS8799: Both partial member declarations must have identical accessibility modifiers. + // partial int P1 { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "P1").WithLocation(9, 17), + // (10,27): error CS8799: Both partial member declarations must have identical accessibility modifiers. + // protected partial int P2 { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "P2").WithLocation(10, 27)); + } + + // Property accessor modifiers can only include: + // - accessibility modifiers + // - readonly modifier + // The test burden for accessors is further reduced by the fact that explicit accessibility + // is only permitted when it is more restrictive than the containing property. + + [Fact] + public void ModifierDifference_Accessibility_Accessors() + { + // access modifier mismatch on accessors + var source = """ + partial class C + { + public partial int P { get; private set; } + } + + partial class C + { + public partial int P { internal get => throw null!; set { } } + } + """; + + var comp = CreateCompilation(source); + + comp.VerifyEmitDiagnostics( + // (8,37): error CS8799: Both partial member declarations must have identical accessibility modifiers. + // public partial int P { internal get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "get").WithLocation(8, 37), + // (8,57): error CS8799: Both partial member declarations must have identical accessibility modifiers. + // public partial int P { internal get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "set").WithLocation(8, 57)); + } + + [Fact] + public void Semantics_Readonly_01() + { + var source = """ + using System; + + class Program + { + static void Main() + { + M(new S()); + } + + static void M(in S s) + { + Console.Write(s.P1); + Console.Write(s.P2); + // We can't exercise S.P2.set here because non-readonly setters will error instead of implicitly copying the receiver. + s.P3 = 3; + Console.Write(s.P3); + Console.Write(s.P4); + } + } + + partial struct S + { + public readonly partial int P1 { get; } + public readonly partial int P1 { get => 1; } + + public partial int P2 { readonly get; set; } + public partial int P2 { readonly get => 2; set { } } + + public partial int P3 { get; readonly set; } + public partial int P3 { get => 3; readonly set { } } + + public partial int P4 { get; } + public partial int P4 { get => 4; } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "1234"); + verifier.VerifyDiagnostics(); + + // non-readonly accessors need to copy the value to temp before invoking. + verifier.VerifyIL("Program.M", """ + { + // Code size 68 (0x44) + .maxstack 2 + .locals init (S V_0) + IL_0000: ldarg.0 + IL_0001: call "readonly int S.P1.get" + IL_0006: call "void System.Console.Write(int)" + IL_000b: ldarg.0 + IL_000c: call "readonly int S.P2.get" + IL_0011: call "void System.Console.Write(int)" + IL_0016: ldarg.0 + IL_0017: ldc.i4.3 + IL_0018: call "readonly void S.P3.set" + IL_001d: ldarg.0 + IL_001e: ldobj "S" + IL_0023: stloc.0 + IL_0024: ldloca.s V_0 + IL_0026: call "int S.P3.get" + IL_002b: call "void System.Console.Write(int)" + IL_0030: ldarg.0 + IL_0031: ldobj "S" + IL_0036: stloc.0 + IL_0037: ldloca.s V_0 + IL_0039: call "int S.P4.get" + IL_003e: call "void System.Console.Write(int)" + IL_0043: ret + } + """); + + var comp = (CSharpCompilation)verifier.Compilation; + } + + [Fact] + public void ModifierDifference_Readonly_Property() + { + // readonly modifier mismatch on property + var source = """ + partial struct S + { + readonly partial int P1 { get; set; } + partial int P2 { get; set; } + readonly partial int P3 { get; set; } + } + + partial struct S + { + partial int P1 { get => throw null!; set { } } + readonly partial int P2 { get => throw null!; set { } } + readonly partial int P3 { get => throw null!; set { } } + } + """; + + var comp = CreateCompilation(source); + + comp.VerifyEmitDiagnostics( + // (10,17): error CS8663: Both partial member declarations must be readonly or neither may be readonly + // partial int P1 { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberReadOnlyDifference, "P1").WithLocation(10, 17), + // (11,26): error CS8663: Both partial member declarations must be readonly or neither may be readonly + // readonly partial int P2 { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberReadOnlyDifference, "P2").WithLocation(11, 26)); + } + + [Fact] + public void ModifierDifference_Readonly_Accessors() + { + // readonly modifier mismatch on accessors + var source = """ + partial struct S + { + public partial int P { readonly get; set; } + } + + partial struct S + { + public partial int P { get => throw null!; readonly set { } } + } + """; + + var comp = CreateCompilation(source); + + comp.VerifyEmitDiagnostics( + // (8,28): error CS8663: Both partial member declarations must be readonly or neither may be readonly + // public partial int P { get => throw null!; readonly set { } } + Diagnostic(ErrorCode.ERR_PartialMemberReadOnlyDifference, "get").WithLocation(8, 28), + // (8,57): error CS8663: Both partial member declarations must be readonly or neither may be readonly + // public partial int P { get => throw null!; readonly set { } } + Diagnostic(ErrorCode.ERR_PartialMemberReadOnlyDifference, "set").WithLocation(8, 57)); + } + + [Fact] + public void Accessibility_ExplicitDefault() + { + // properties can explicitly specify the default accessibility + var source = """ + using System; + + partial class C + { + private partial int P1 { get; } + + static void Main() + { + Console.Write(new C().P1); + } + } + + partial class C + { + private partial int P1 { get => 1; } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "1"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void Accessibility_ExplicitDefault_IsRespected() + { + var source = """ + using System; + + class Program + { + static void Main() + { + Console.Write(new C().P1); // 1 + } + + } + + partial class C + { + private partial int P1 { get; } + } + + partial class C + { + private partial int P1 { get => 1; } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,31): error CS0122: 'C.P1' is inaccessible due to its protection level + // Console.Write(new C().P1); // 1 + Diagnostic(ErrorCode.ERR_BadAccess, "P1").WithArguments("C.P1").WithLocation(7, 31)); + + var p1Def = comp.GetMember("C.P1"); + Assert.True(p1Def.IsPartialDefinition); + Assert.Equal(Accessibility.Private, p1Def.DeclaredAccessibility); + + var p1DefPublic = p1Def.GetPublicSymbol(); + Assert.Equal(Accessibility.Private, p1DefPublic.DeclaredAccessibility); + } + + [Fact] + public void ModifierDifference_Accessibility_ExplicitDefault_01() + { + // only one part explicitly specifies the default accessibility + var source = """ + partial class C + { + private partial int P1 { get; set; } + partial int P2 { get; set; } + } + + partial class C + { + partial int P1 { get => throw null!; set { } } + private partial int P2 { get => throw null!; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (9,17): error CS8799: Both partial member declarations must have identical accessibility modifiers. + // partial int P1 { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "P1").WithLocation(9, 17), + // (10,25): error CS8799: Both partial member declarations must have identical accessibility modifiers. + // private partial int P2 { get => throw null!; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "P2").WithLocation(10, 25)); + } + + [Fact] + public void ModifierDifference_Accessibility_ExplicitDefault_02() + { + var source = """ + using System; + + partial class C + { + private partial int P1 { get; set; } + partial int P2 { private get; private set; } + } + + partial class C + { + partial int P1 { private get => 1; private set; } + partial int P2 { private get => 1; private set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,17): error CS0274: Cannot specify accessibility modifiers for both accessors of the property or indexer 'C.P2' + // partial int P2 { private get; private set; } + Diagnostic(ErrorCode.ERR_DuplicatePropertyAccessMods, "P2").WithArguments("C.P2").WithLocation(6, 17), + // (6,30): error CS0273: The accessibility modifier of the 'C.P2.get' accessor must be more restrictive than the property or indexer 'C.P2' + // partial int P2 { private get; private set; } + Diagnostic(ErrorCode.ERR_InvalidPropertyAccessMod, "get").WithArguments("C.P2.get", "C.P2").WithLocation(6, 30), + // (6,43): error CS0273: The accessibility modifier of the 'C.P2.set' accessor must be more restrictive than the property or indexer 'C.P2' + // partial int P2 { private get; private set; } + Diagnostic(ErrorCode.ERR_InvalidPropertyAccessMod, "set").WithArguments("C.P2.set", "C.P2").WithLocation(6, 43), + // (11,17): error CS8799: Both partial member declarations must have identical accessibility modifiers. + // partial int P1 { private get => 1; private set; } + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "P1").WithLocation(11, 17), + // (11,30): error CS8799: Both partial member declarations must have identical accessibility modifiers. + // partial int P1 { private get => 1; private set; } + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "get").WithLocation(11, 30), + // (11,48): error CS8799: Both partial member declarations must have identical accessibility modifiers. + // partial int P1 { private get => 1; private set; } + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "set").WithLocation(11, 48)); + } + + [Fact] + public void TypeDifference_01() + { + var source = """ + using System.Collections.Generic; + + partial class C + { + partial int P1 { get; set; } + partial string P1 { get => ""; set { } } + + partial List P2 { get; set; } + partial List P2 { get => []; set { } } + + partial IEnumerable P3 { get; set; } + partial IEnumerable P3 { get => []; set { } } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,20): error CS9255: Both partial property declarations must have the same type. + // partial string P1 { get => ""; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyTypeDifference, "P1").WithLocation(6, 20), + // (9,26): error CS9255: Both partial property declarations must have the same type. + // partial List P2 { get => []; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyTypeDifference, "P2").WithLocation(9, 26), + // (12,33): error CS9255: Both partial property declarations must have the same type. + // partial IEnumerable P3 { get => []; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyTypeDifference, "P3").WithLocation(12, 33)); + } + + [Fact] + public void TypeDifference_02() + { + var source = """ + #nullable enable + partial class C + { + partial string? P1 { get; set; } + partial string P1 { get => ""; set { } } + + partial string P2 { get; set; } + partial string? P2 { get => ""; set { } } + + partial string?[] P3 { get; set; } + partial string[] P3 { get => []; set { } } + + partial string[] P4 { get; set; } + partial string?[] P4 { get => []; set { } } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,20): error CS9256: Partial property declarations 'string? C.P1' and 'string C.P1' have signature differences. + // partial string P1 { get => ""; set { } } + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P1").WithArguments("string? C.P1", "string C.P1").WithLocation(5, 20), + // (8,21): error CS9256: Partial property declarations 'string C.P2' and 'string? C.P2' have signature differences. + // partial string? P2 { get => ""; set { } } + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P2").WithArguments("string C.P2", "string? C.P2").WithLocation(8, 21), + // (11,22): error CS9256: Partial property declarations 'string?[] C.P3' and 'string[] C.P3' have signature differences. + // partial string[] P3 { get => []; set { } } + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P3").WithArguments("string?[] C.P3", "string[] C.P3").WithLocation(11, 22), + // (14,23): error CS9256: Partial property declarations 'string[] C.P4' and 'string?[] C.P4' have signature differences. + // partial string?[] P4 { get => []; set { } } + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P4").WithArguments("string[] C.P4", "string?[] C.P4").WithLocation(14, 23)); + } + + [Fact] + public void NullableDifference_NullableWarningsDisabled() + { + // 'safe' nullable differences for partial methods are still reported even when nullable warnings are disabled. + // For simplicity we replicate this behavior for partial properties. + var source = """ + partial class C + { + public partial string? P1 { get; set; } + public partial string P1 { get => ""; set { } } + + public partial string P2 { get; set; } + public partial string? P2 { get => ""; set { } } + + public partial string? M1(); + public partial string M1() => ""; + + public partial string M2(); + public partial string? M2() => ""; + } + """; + + var comp = CreateCompilation(source, options: TestOptions.DebugDll.WithNullableContextOptions(NullableContextOptions.Enable)); + comp.VerifyEmitDiagnostics( + // (4,27): warning CS9256: Partial property declarations 'string? C.P1' and 'string C.P1' have signature differences. + // public partial string P1 { get => ""; set { } } + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P1").WithArguments("string? C.P1", "string C.P1").WithLocation(4, 27), + // (7,28): warning CS9256: Partial property declarations 'string C.P2' and 'string? C.P2' have signature differences. + // public partial string? P2 { get => ""; set { } } + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P2").WithArguments("string C.P2", "string? C.P2").WithLocation(7, 28), + // (10,27): warning CS8826: Partial method declarations 'string? C.M1()' and 'string C.M1()' have signature differences. + // public partial string M1() => ""; + Diagnostic(ErrorCode.WRN_PartialMethodTypeDifference, "M1").WithArguments("string? C.M1()", "string C.M1()").WithLocation(10, 27), + // (13,28): warning CS8819: Nullability of reference types in return type doesn't match partial definition. + // public partial string? M2() => ""; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnPartial, "M2").WithLocation(13, 28)); + + comp = CreateCompilation(source, options: TestOptions.DebugDll.WithNullableContextOptions(NullableContextOptions.Annotations)); + comp.VerifyEmitDiagnostics( + // (4,27): warning CS9256: Partial property declarations 'string? C.P1' and 'string C.P1' have signature differences. + // public partial string P1 { get => ""; set { } } + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P1").WithArguments("string? C.P1", "string C.P1").WithLocation(4, 27), + // (7,28): warning CS9256: Partial property declarations 'string C.P2' and 'string? C.P2' have signature differences. + // public partial string? P2 { get => ""; set { } } + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P2").WithArguments("string C.P2", "string? C.P2").WithLocation(7, 28), + // (10,27): warning CS8826: Partial method declarations 'string? C.M1()' and 'string C.M1()' have signature differences. + // public partial string M1() => ""; + Diagnostic(ErrorCode.WRN_PartialMethodTypeDifference, "M1").WithArguments("string? C.M1()", "string C.M1()").WithLocation(10, 27)); + } + + [Fact] + public void NullableDifference_Oblivious() + { + var source = """ + #nullable enable + partial class C + { + public partial T P1 { get; set; } + public partial T? P2 { get; set; } + #nullable disable + public partial T P3 { get; set; } + } + + #nullable disable + partial class C + { + public partial T P1 { get => default!; set { } } + public partial T P2 { get => default!; set { } } + #nullable enable + public partial T P3 { get => default!; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var p1 = comp.GetMember("C.P1"); + Assert.True(p1.IsPartialDefinition); + Assert.Equal(NullableAnnotation.NotAnnotated, p1.TypeWithAnnotations.NullableAnnotation); + + var p2 = comp.GetMember("C.P2"); + Assert.True(p2.IsPartialDefinition); + Assert.Equal(NullableAnnotation.Annotated, p2.TypeWithAnnotations.NullableAnnotation); + + var p3 = comp.GetMember("C.P3"); + Assert.True(p3.IsPartialDefinition); + Assert.Equal(NullableAnnotation.Oblivious, p3.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void NullableDifference_Analysis() + { + // The implementation part signature is used to analyze the implementation part bodies. + // The definition part signature is used to analyze use sites. + // This is consistent with methods. + var source = """ + #nullable enable + partial class C + { + public partial string this[string? x] { get; set; } + public partial string? this[string x] // 1 + { + get => x.ToString(); + set => x.ToString(); + } + + public partial string? this[string x, bool ignored] { get; set; } + public partial string this[string? x, bool ignored] // 2 + { + get => x.ToString(); // 3 + set => x.ToString(); // 4 + } + + public partial string this[bool ignored] { get; } + public partial string? this[bool ignored] // 5 + { + get => null; + } + + public partial string? this[int ignored] { get; } + public partial string this[int ignored] // 6 + { + get => null; // 7 + } + + void Usage() + { + this[x: null].ToString(); + this[x: null, ignored: false].ToString(); // 8, 9 + } + } + """; + + var verifier = CompileAndVerify(source, symbolValidator: verify, sourceSymbolValidator: verify); + verifier.VerifyDiagnostics( + // (5,28): warning CS9256: Partial property declarations 'string C.this[string? x]' and 'string? C.this[string x]' have signature differences. + // public partial string? this[string x] // 1 + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("string C.this[string? x]", "string? C.this[string x]").WithLocation(5, 28), + // (12,27): warning CS9256: Partial property declarations 'string? C.this[string x, bool ignored]' and 'string C.this[string? x, bool ignored]' have signature differences. + // public partial string this[string? x, bool ignored] // 2 + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("string? C.this[string x, bool ignored]", "string C.this[string? x, bool ignored]").WithLocation(12, 27), + // (14,16): warning CS8602: Dereference of a possibly null reference. + // get => x.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(14, 16), + // (15,16): warning CS8602: Dereference of a possibly null reference. + // set => x.ToString(); // 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(15, 16), + // (19,28): warning CS9256: Partial property declarations 'string C.this[bool ignored]' and 'string? C.this[bool ignored]' have signature differences. + // public partial string? this[bool ignored] // 5 + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("string C.this[bool ignored]", "string? C.this[bool ignored]").WithLocation(19, 28), + // (25,27): warning CS9256: Partial property declarations 'string? C.this[int ignored]' and 'string C.this[int ignored]' have signature differences. + // public partial string this[int ignored] // 6 + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("string? C.this[int ignored]", "string C.this[int ignored]").WithLocation(25, 27), + // (27,16): warning CS8603: Possible null reference return. + // get => null; // 7 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(27, 16), + // (33,9): warning CS8602: Dereference of a possibly null reference. + // this[x: null, ignored: false].ToString(); // 8, 9 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "this[x: null, ignored: false]").WithLocation(33, 9), + // (33,17): warning CS8625: Cannot convert null literal to non-nullable reference type. + // this[x: null, ignored: false].ToString(); // 8, 9 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(33, 17)); + + void verify(ModuleSymbol module) + { + var indexers = module.GlobalNamespace.GetMember("C").Indexers; + Assert.Equal(4, indexers.Length); + AssertEx.Equal("System.String C.this[System.String? x] { get; set; }", indexers[0].ToTestDisplayString()); + AssertEx.Equal("System.String? C.this[System.String x, System.Boolean ignored] { get; set; }", indexers[1].ToTestDisplayString()); + AssertEx.Equal("System.String C.this[System.Boolean ignored] { get; }", indexers[2].ToTestDisplayString()); + AssertEx.Equal("System.String? C.this[System.Int32 ignored] { get; }", indexers[3].ToTestDisplayString()); + } + } + + [Fact] + public void TypeDifference_03() + { + // tuple element name difference + // this is an error for consistency with methods + var source = """ + using System.Collections.Generic; + + partial class C + { + partial (int a, int b) P1 { get; set; } + partial (int a, int x) P1 { get => default; set { } } + + partial (int a, int b) P2 { get; set; } + partial (int x, int y) P2 { get => default; set { } } + + partial List<(int a, int b)> P3 { get; set; } + partial List<(int x, int y)> P3 { get => null!; set { } } + + partial List<(int, int)> P4 { get; set; } + partial List<(int x, int y)> P4 { get => null!; set { } } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,28): error CS8142: Both partial member declarations, 'C.P1' and 'C.P1', must use the same tuple element names. + // partial (int a, int x) P1 { get => default; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "P1").WithArguments("C.P1", "C.P1").WithLocation(6, 28), + // (9,28): error CS8142: Both partial member declarations, 'C.P2' and 'C.P2', must use the same tuple element names. + // partial (int x, int y) P2 { get => default; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "P2").WithArguments("C.P2", "C.P2").WithLocation(9, 28), + // (12,34): error CS8142: Both partial member declarations, 'C.P3' and 'C.P3', must use the same tuple element names. + // partial List<(int x, int y)> P3 { get => null!; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "P3").WithArguments("C.P3", "C.P3").WithLocation(12, 34), + // (15,34): error CS8142: Both partial member declarations, 'C.P4' and 'C.P4', must use the same tuple element names. + // partial List<(int x, int y)> P4 { get => null!; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "P4").WithArguments("C.P4", "C.P4").WithLocation(15, 34)); + } + + [Fact] + public void Semantics_RefKind() + { + var source = """ + using System; + + var c = new C(); + ref int i = ref c.P1; + c.P1++; + Console.Write(i); + Console.Write(c.P2); + + partial class C + { + public partial ref int P1 { get; } + public partial ref readonly int P2 { get; } + + private int _p; + public partial ref int P1 => ref _p; + public partial ref readonly int P2 { get => ref _p; } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "11"); + verifier.VerifyDiagnostics(); + var comp = (CSharpCompilation)verifier.Compilation; + + var members = comp.GetMember("C").GetMembers().SelectAsArray(m => m.ToTestDisplayString()); + AssertEx.Equal([ + "ref System.Int32 C.P1 { get; }", + "ref System.Int32 C.P1.get", + "ref readonly modreq(System.Runtime.InteropServices.InAttribute) System.Int32 C.P2 { get; }", + "ref readonly modreq(System.Runtime.InteropServices.InAttribute) System.Int32 C.P2.get", + "System.Int32 C._p", + "C..ctor()" + ], members); + } + + [Fact] + public void RefKindDifference_01() + { + var source = """ + partial class C + { + partial int P1 { get; } + partial ref int P1 { get => throw null!; } + + partial int P2 { get; } + partial ref readonly int P2 { get => throw null!; } + + partial ref int P3 { get; } + partial int P3 { get => throw null!; } + + partial ref readonly int P4 { get; } + partial int P4 { get => throw null!; } + + partial ref readonly int P5 { get; } + partial ref int P5 { get => throw null!; } + + partial ref int P6 { get; } + partial ref readonly int P6 { get => throw null!; } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,21): error CS8818: Partial member declarations must have matching ref return values. + // partial ref int P1 { get => throw null!; } + Diagnostic(ErrorCode.ERR_PartialMemberRefReturnDifference, "P1").WithLocation(4, 21), + // (7,30): error CS8818: Partial member declarations must have matching ref return values. + // partial ref readonly int P2 { get => throw null!; } + Diagnostic(ErrorCode.ERR_PartialMemberRefReturnDifference, "P2").WithLocation(7, 30), + // (10,17): error CS8818: Partial member declarations must have matching ref return values. + // partial int P3 { get => throw null!; } + Diagnostic(ErrorCode.ERR_PartialMemberRefReturnDifference, "P3").WithLocation(10, 17), + // (13,17): error CS8818: Partial member declarations must have matching ref return values. + // partial int P4 { get => throw null!; } + Diagnostic(ErrorCode.ERR_PartialMemberRefReturnDifference, "P4").WithLocation(13, 17), + // (16,21): error CS8818: Partial member declarations must have matching ref return values. + // partial ref int P5 { get => throw null!; } + Diagnostic(ErrorCode.ERR_PartialMemberRefReturnDifference, "P5").WithLocation(16, 21), + // (19,30): error CS8818: Partial member declarations must have matching ref return values. + // partial ref readonly int P6 { get => throw null!; } + Diagnostic(ErrorCode.ERR_PartialMemberRefReturnDifference, "P6").WithLocation(19, 30)); + } + + [Fact] + public void AllTypeDifferences() + { + // Verify which diagnostics are reported when multiple kinds of type differences are present + var source = """ + #nullable enable + + partial class C + { + public partial ref (int x, string? y) Prop { get; } + public partial ref readonly (long x, string y) Prop => throw null!; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,52): error CS9255: Both partial property declarations must have the same type. + // public partial ref readonly (long x, string y) Prop => throw null!; + Diagnostic(ErrorCode.ERR_PartialPropertyTypeDifference, "Prop").WithLocation(6, 52), + // (6,52): error CS8818: Partial member declarations must have matching ref return values. + // public partial ref readonly (long x, string y) Prop => throw null!; + Diagnostic(ErrorCode.ERR_PartialMemberRefReturnDifference, "Prop").WithLocation(6, 52)); + } + + [Fact] + public void Semantics_Static() + { + var source = """ + using System; + + C.P++; + Console.Write(C.P); + + partial class C + { + public static partial int P { get; set; } + + public static partial int P { get => _p; set => _p = value; } + private static int _p; + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "1"); + verifier.VerifyDiagnostics(); + var comp = (CSharpCompilation)verifier.Compilation; + + var members = comp.GetMember("C").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 StaticDifference() + { + var source = """ + partial class C + { + public static partial int P1 { get; set; } + public partial int P1 { get => 1; set { } } + + public partial int P2 { get; set; } + public static partial int P2 { get => 1; set { } } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,24): error CS0763: Both partial member declarations must be static or neither may be static + // public partial int P1 { get => 1; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberStaticDifference, "P1").WithLocation(4, 24), + // (7,31): error CS0763: Both partial member declarations must be static or neither may be static + // public static partial int P2 { get => 1; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberStaticDifference, "P2").WithLocation(7, 31)); + } + + [Fact] + public void Semantics_Unsafe() + { + var source = """ + using System; + + class Program + { + static unsafe void Main() + { + int i = 1; + S s = new S() { P = &i }; + Console.Write(*s.P); + } + } + + partial struct S + { + public unsafe partial int* P { get; set; } + + public unsafe partial int* P { get => _p; set => _p = value; } + private unsafe int* _p; + } + """; + + var verifier = CompileAndVerify(source, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: "1"); + verifier.VerifyDiagnostics(); + var comp = (CSharpCompilation)verifier.Compilation; + + var members = comp.GetMember("S").GetMembers().SelectAsArray(m => m.ToTestDisplayString()); + AssertEx.Equal([ + "System.Int32* S.P { get; set; }", + "System.Int32* S.P.get", + "void S.P.set", + "System.Int32* S._p", + "S..ctor()" + ], members); + } + + [Fact] + public void UnsafeDifference_01() + { + // 'unsafe' modifiers are required to match across property declarations. + // Therefore an error is reported on implementation of 'P2' even though both parts are "effectively unsafe". + var source = """ + partial class C + { + public partial int P1 { get; set; } + public unsafe partial int P2 { get; set; } + } + + unsafe partial class C + { + public unsafe partial int P1 { get => 1; set { } } + public partial int P2 { get => 1; set { } } + } + """; + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll); + comp.VerifyEmitDiagnostics( + // (9,31): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe + // public unsafe partial int P1 { get => 1; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "P1").WithLocation(9, 31), + // (10,24): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe + // public partial int P2 { get => 1; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "P2").WithLocation(10, 24)); + + comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,15): error CS0227: Unsafe code may only appear if compiling with /unsafe + // partial class C + Diagnostic(ErrorCode.ERR_IllegalUnsafe, "C").WithLocation(1, 15), + // (4,31): error CS0227: Unsafe code may only appear if compiling with /unsafe + // public unsafe partial int P2 { get; set; } + Diagnostic(ErrorCode.ERR_IllegalUnsafe, "P2").WithLocation(4, 31), + // (9,31): error CS0227: Unsafe code may only appear if compiling with /unsafe + // public unsafe partial int P1 { get => 1; set { } } + Diagnostic(ErrorCode.ERR_IllegalUnsafe, "P1").WithLocation(9, 31)); + } + + [Fact] + public void UnsafeDifference_02() + { + // A difference in unsafe context only matters when unsafe types are used in the signature + var source = """ + unsafe partial class C + { + public partial int* P1 { get; set; } + public partial int P2 { get; set; } + } + + partial class C + { + public partial int* P1 { get => null; set { } } + public partial int P2 { get => 1; set { } } + } + """; + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll); + comp.VerifyEmitDiagnostics( + // (9,20): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // public partial int* P1 { get => null; set { } } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(9, 20)); + } + + [Fact] + public void Semantics_ExtendedModifier() + { + var source = """ + using System; + + C c = new C(); + Console.Write(c.P); + + c = new D1(); + Console.Write(c.P); + + c = new D2(); + Console.Write(c.P); + + c = new D3(); + Console.Write(c.P); + Console.Write(new D3().P); + + partial class C + { + public virtual partial int P { get; } + public virtual partial int P => 0; + } + + partial class D1 : C + { + public override partial int P { get; } + public override partial int P => 1; + } + + partial class D2 : C + { + public sealed override partial int P { get; } + public sealed override partial int P => 2; + } + + partial class D3 : C + { + public new partial int P { get; } + public new partial int P => 3; + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "01203"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void ExtendedDifference_01() + { + var source = """ + partial class C + { + public virtual partial int P1 { get; } + public partial int P1 => 1; // 1 + + public partial int P2 { get; } + public virtual partial int P2 => 1; // 2 + } + + partial class D1 : C + { + public partial int P1 { get; } // 3 + public override partial int P1 => 1; // 4 + + public override partial int P2 { get; } // 5 + public partial int P2 => 1; // 6 + } + + partial class D2 : C + { + public partial int P1 { get; } // 7 + public sealed override partial int P1 => 1; // 8 + + public sealed override partial int P2 { get; } // 9 + public partial int P2 => 1; // 10 + } + + partial class D3 : C + { + public sealed partial int P1 { get; } // 11, 12 + public override partial int P1 => 1; // 13 + + public override partial int P2 { get; } // 14 + public sealed partial int P2 => 1; // 15 + } + + partial class D4 : C + { + public partial int P1 { get; } // 16 + public new partial int P1 => 1; // 17 + + public new partial int P2 { get; } + public partial int P2 => 1; // 18 + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,24): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // public partial int P1 => 1; // 1 + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "P1").WithLocation(4, 24), + // (7,32): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // public virtual partial int P2 => 1; // 2 + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "P2").WithLocation(7, 32), + // (12,24): warning CS0114: 'D1.P1' hides inherited member 'C.P1'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword. + // public partial int P1 { get; } // 3 + Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "P1").WithArguments("D1.P1", "C.P1").WithLocation(12, 24), + // (13,33): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // public override partial int P1 => 1; // 4 + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "P1").WithLocation(13, 33), + // (15,33): error CS0506: 'D1.P2': cannot override inherited member 'C.P2' because it is not marked virtual, abstract, or override + // public override partial int P2 { get; } // 5 + Diagnostic(ErrorCode.ERR_CantOverrideNonVirtual, "P2").WithArguments("D1.P2", "C.P2").WithLocation(15, 33), + // (16,24): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // public partial int P2 => 1; // 6 + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "P2").WithLocation(16, 24), + // (21,24): warning CS0114: 'D2.P1' hides inherited member 'C.P1'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword. + // public partial int P1 { get; } // 7 + Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "P1").WithArguments("D2.P1", "C.P1").WithLocation(21, 24), + // (22,40): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // public sealed override partial int P1 => 1; // 8 + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "P1").WithLocation(22, 40), + // (24,40): error CS0506: 'D2.P2': cannot override inherited member 'C.P2' because it is not marked virtual, abstract, or override + // public sealed override partial int P2 { get; } // 9 + Diagnostic(ErrorCode.ERR_CantOverrideNonVirtual, "P2").WithArguments("D2.P2", "C.P2").WithLocation(24, 40), + // (25,24): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // public partial int P2 => 1; // 10 + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "P2").WithLocation(25, 24), + // (30,31): warning CS0114: 'D3.P1' hides inherited member 'C.P1'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword. + // public sealed partial int P1 { get; } // 11, 12 + Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "P1").WithArguments("D3.P1", "C.P1").WithLocation(30, 31), + // (30,31): error CS0238: 'D3.P1' cannot be sealed because it is not an override + // public sealed partial int P1 { get; } // 11, 12 + Diagnostic(ErrorCode.ERR_SealedNonOverride, "P1").WithArguments("D3.P1").WithLocation(30, 31), + // (31,33): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // public override partial int P1 => 1; // 13 + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "P1").WithLocation(31, 33), + // (33,33): error CS0506: 'D3.P2': cannot override inherited member 'C.P2' because it is not marked virtual, abstract, or override + // public override partial int P2 { get; } // 14 + Diagnostic(ErrorCode.ERR_CantOverrideNonVirtual, "P2").WithArguments("D3.P2", "C.P2").WithLocation(33, 33), + // (34,31): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // public sealed partial int P2 => 1; // 15 + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "P2").WithLocation(34, 31), + // (39,24): warning CS0114: 'D4.P1' hides inherited member 'C.P1'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword. + // public partial int P1 { get; } // 16 + Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "P1").WithArguments("D4.P1", "C.P1").WithLocation(39, 24), + // (40,28): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // public new partial int P1 => 1; // 17 + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "P1").WithLocation(40, 28), + // (43,24): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // public partial int P2 => 1; // 18 + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "P2").WithLocation(43, 24)); + } + + [Fact] + public void Abstract() + { + // 'abstract' is not permitted on partial declarations + var source = """ + abstract partial class C + { + public abstract partial int P1 { get; set; } + + public abstract partial int P2 { get => ""; set { } } + + public abstract partial int P3 { get; set; } + public abstract partial int P3 { get => ""; set { } } + + public abstract partial int P4 { get; set; } + public partial int P4 { get => ""; set { } } + + public partial int P5 { get; set; } + public abstract partial int P5 { get => ""; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,33): error CS9248: Partial property 'C.P1' must have an implementation part. + // public abstract partial int P1 { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "P1").WithArguments("C.P1").WithLocation(3, 33), + // (3,33): error CS0750: A partial member cannot have the 'abstract' modifier + // public abstract partial int P1 { get; set; } + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "P1").WithLocation(3, 33), + // (5,33): error CS9249: Partial property 'C.P2' must have a definition part. + // public abstract partial int P2 { get => ""; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "P2").WithArguments("C.P2").WithLocation(5, 33), + // (5,33): error CS0750: A partial member cannot have the 'abstract' modifier + // public abstract partial int P2 { get => ""; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "P2").WithLocation(5, 33), + // (5,38): error CS0500: 'C.P2.get' cannot declare a body because it is marked abstract + // public abstract partial int P2 { get => ""; set { } } + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("C.P2.get").WithLocation(5, 38), + // (5,49): error CS0500: 'C.P2.set' cannot declare a body because it is marked abstract + // public abstract partial int P2 { get => ""; set { } } + Diagnostic(ErrorCode.ERR_AbstractHasBody, "set").WithArguments("C.P2.set").WithLocation(5, 49), + // (7,33): error CS0750: A partial member cannot have the 'abstract' modifier + // public abstract partial int P3 { get; set; } + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "P3").WithLocation(7, 33), + // (8,38): error CS0500: 'C.P3.get' cannot declare a body because it is marked abstract + // public abstract partial int P3 { get => ""; set { } } + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("C.P3.get").WithLocation(8, 38), + // (8,49): error CS0500: 'C.P3.set' cannot declare a body because it is marked abstract + // public abstract partial int P3 { get => ""; set { } } + Diagnostic(ErrorCode.ERR_AbstractHasBody, "set").WithArguments("C.P3.set").WithLocation(8, 49), + // (10,33): error CS0750: A partial member cannot have the 'abstract' modifier + // public abstract partial int P4 { get; set; } + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "P4").WithLocation(10, 33), + // (11,36): error CS0029: Cannot implicitly convert type 'string' to 'int' + // public partial int P4 { get => ""; set { } } + Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""""").WithArguments("string", "int").WithLocation(11, 36), + // (14,38): error CS0500: 'C.P5.get' cannot declare a body because it is marked abstract + // public abstract partial int P5 { get => ""; set { } } + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("C.P5.get").WithLocation(14, 38), + // (14,49): error CS0500: 'C.P5.set' cannot declare a body because it is marked abstract + // public abstract partial int P5 { get => ""; set { } } + Diagnostic(ErrorCode.ERR_AbstractHasBody, "set").WithArguments("C.P5.set").WithLocation(14, 49)); + } + + [Fact] + public void Semantics_Required() + { + var source = """ + using System; + + partial class C + { + public required partial string P1 { get; set; } + public required partial string P1 { get => ""; set { Console.Write(value); } } + + static void Main() + { + _ = new C() { P1 = "A" }; + } + } + """; + + var verifier = CompileAndVerify([source, RequiredMemberAttribute, SetsRequiredMembersAttribute, CompilerFeatureRequiredAttribute], expectedOutput: "A"); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.Main", """ + { + // Code size 16 (0x10) + .maxstack 2 + IL_0000: newobj "C..ctor()" + IL_0005: ldstr "A" + IL_000a: callvirt "void C.P1.set" + IL_000f: ret + } + """); + } + + [Fact] + public void Required_CreationSiteError() + { + var source = """ + partial class C + { + public required partial string P1 { get; set; } + public required partial string P1 { get => ""; set { } } + + static void Main() + { + _ = new C(); + } + } + """; + + var comp = CreateCompilation([source, RequiredMemberAttribute, SetsRequiredMembersAttribute, CompilerFeatureRequiredAttribute]); + comp.VerifyEmitDiagnostics( + // (8,17): error CS9035: Required member 'C.P1' must be set in the object initializer or attribute constructor. + // _ = new C(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.P1").WithLocation(8, 17)); + } + + [Fact] + public void Required_Difference() + { + var source = """ + partial class C + { + public required partial string P1 { get; set; } + public partial string P1 { get => ""; set { } } + + public partial string P2 { get; set; } + public required partial string P2 { get => ""; set { } } + + static void Main() + { + _ = new C(); + } + } + """; + + var comp = CreateCompilation([source, RequiredMemberAttribute, SetsRequiredMembersAttribute, CompilerFeatureRequiredAttribute]); + comp.VerifyEmitDiagnostics( + // (4,27): error CS9257: Both partial property declarations must be required or neither may be required + // public partial string P1 { get => ""; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyRequiredDifference, "P1").WithLocation(4, 27), + // (7,36): error CS9257: Both partial property declarations must be required or neither may be required + // public required partial string P2 { get => ""; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyRequiredDifference, "P2").WithLocation(7, 36), + // (11,17): error CS9035: Required member 'C.P1' must be set in the object initializer or attribute constructor. + // _ = new C(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.P1").WithLocation(11, 17)); + } + + [Fact] + public void AliasDifference() + { + var source = """ + namespace NS; + + using MyInt = System.Int32; + using MyInt2 = System.Int32; + + partial class C + { + public partial int P1 { get; set; } + public partial MyInt P1 { get => 1; set { } } + + public partial MyInt P2 { get; set; } + public partial MyInt2 P2 { get => 2; set { } } + + public partial string P3 { get; set; } + public partial MyInt P3 { get => 3; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (15,26): error CS9255: Both partial property declarations must have the same type. + // public partial MyInt P3 { get => 3; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyTypeDifference, "P3").WithLocation(15, 26)); + } + + [Fact] + public void ExplicitImplementation() + { + var source = """ + interface I + { + public int P { get; set; } + } + + partial class C1 : I + { + partial int I.P { get; set; } + partial int I.P { get => 1; set { } } + } + + partial class C2 : I + { + partial int I.P { get; set; } + } + + partial class C3 : I + { + partial int I.P { get => 1; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,19): error CS0754: A partial member may not explicitly implement an interface member + // partial int I.P { get; set; } + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "P").WithLocation(8, 19), + // (14,19): error CS9248: Partial property 'C2.I.P' must have an implementation part. + // partial int I.P { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "P").WithArguments("C2.I.P").WithLocation(14, 19), + // (14,19): error CS0754: A partial member may not explicitly implement an interface member + // partial int I.P { get; set; } + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "P").WithLocation(14, 19), + // (19,19): error CS9249: Partial property 'C3.I.P' must have a definition part. + // partial int I.P { get => 1; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "P").WithArguments("C3.I.P").WithLocation(19, 19), + // (19,19): error CS0754: A partial member may not explicitly implement an interface member + // partial int I.P { get => 1; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "P").WithLocation(19, 19)); + } + + [Fact] + public void NotInPartialType() + { + var source = """ + class C + { + partial int P1 { get; set; } + partial int P1 { get => 1; set { } } + + partial int P2 { get; set; } + + partial int P3 { get => 1; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,17): error CS0751: A partial member must be declared within a partial type + // partial int P1 { get; set; } + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "P1").WithLocation(3, 17), + // (6,17): error CS9248: Partial property 'C.P2' must have an implementation part. + // partial int P2 { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "P2").WithArguments("C.P2").WithLocation(6, 17), + // (6,17): error CS0751: A partial member must be declared within a partial type + // partial int P2 { get; set; } + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "P2").WithLocation(6, 17), + // (8,17): error CS9249: Partial property 'C.P3' must have a definition part. + // partial int P3 { get => 1; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "P3").WithArguments("C.P3").WithLocation(8, 17), + // (8,17): error CS0751: A partial member must be declared within a partial type + // partial int P3 { get => 1; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "P3").WithLocation(8, 17)); + } + + [Fact] + public void Semantics_Indexers_01() + { + var source = """ + using System; + + partial class C + { + public partial int this[int i] { get; set; } + public partial int this[int i] + { + get => i; + set + { + Console.Write(i); + Console.Write(value); + } + } + + static void Main() + { + var c = new C(); + Console.Write(c[1]); + c[2] = 3; + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "123"); + verifier.VerifyDiagnostics(); + } + + [Theory] + [InlineData("ref")] + [InlineData("out")] + public void RefKindDifference_IndexerParameter_01(string refKind) + { + // byvalue is distinct from byreference for signature matching + var source = $$""" + partial class C1 + { + public partial int this[int i] { get; set; } + public partial int this[{{refKind}} int i] { get => i = 0; set => i = 0; } + } + + partial class C2 + { + public partial int this[{{refKind}} int i] { get; set; } + public partial int this[int i] { get => i; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,24): error CS9248: Partial property 'C1.this[int]' must have an implementation part. + // public partial int this[int i] { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "this").WithArguments("C1.this[int]").WithLocation(3, 24), + // (4,24): error CS9249: Partial property 'C1.this[ref int]' must have a definition part. + // public partial int this[ref int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "this").WithArguments($"C1.this[{refKind} int]").WithLocation(4, 24), + // (4,29): error CS0631: ref and out are not valid in this context + // public partial int this[ref int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_IllegalRefParam, refKind).WithLocation(4, 29), + // (9,24): error CS9248: Partial property 'C2.this[ref int]' must have an implementation part. + // public partial int this[ref int i] { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "this").WithArguments($"C2.this[{refKind} int]").WithLocation(9, 24), + // (9,29): error CS0631: ref and out are not valid in this context + // public partial int this[ref int i] { get; set; } + Diagnostic(ErrorCode.ERR_IllegalRefParam, refKind).WithLocation(9, 29), + // (10,24): error CS9249: Partial property 'C2.this[int]' must have a definition part. + // public partial int this[int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "this").WithArguments("C2.this[int]").WithLocation(10, 24)); + } + + [Theory] + [InlineData("in")] + [InlineData("ref readonly")] + public void RefKindDifference_IndexerParameter_02(string refKind) + { + var source = $$""" + partial class C1 + { + public partial int this[int i] { get; set; } + public partial int this[{{refKind}} int i] { get => i; set { } } + } + + partial class C2 + { + public partial int this[{{refKind}} int i] { get; set; } + public partial int this[int i] { get => i; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,24): error CS9248: Partial property 'C1.this[int]' must have an implementation part. + // public partial int this[int i] { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "this").WithArguments("C1.this[int]").WithLocation(3, 24), + // (4,24): error CS9249: Partial property 'C1.this[in int]' must have a definition part. + // public partial int this[in int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "this").WithArguments($"C1.this[{refKind} int]").WithLocation(4, 24), + // (9,24): error CS9248: Partial property 'C2.this[in int]' must have an implementation part. + // public partial int this[in int i] { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "this").WithArguments($"C2.this[{refKind} int]").WithLocation(9, 24), + // (10,24): error CS9249: Partial property 'C2.this[int]' must have a definition part. + // public partial int this[int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "this").WithArguments("C2.this[int]").WithLocation(10, 24)); + } + + [Fact] + public void RefKindDifference_IndexerParameter_03() + { + var source = """ + partial class C1 + { + public partial int this[ref int i] { get; set; } + public partial int this[out int i] { get => i = 0; set => i = 0; } + } + + partial class C2 + { + public partial int this[out int i] { get; set; } + public partial int this[ref int i] { get => i; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,24): error CS9248: Partial property 'C1.this[ref int]' must have an implementation part. + // public partial int this[ref int i] { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "this").WithArguments("C1.this[ref int]").WithLocation(3, 24), + // (3,29): error CS0631: ref and out are not valid in this context + // public partial int this[ref int i] { get; set; } + Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(3, 29), + // (4,24): error CS9249: Partial property 'C1.this[out int]' must have a definition part. + // public partial int this[out int i] { get => i = 0; set => i = 0; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "this").WithArguments("C1.this[out int]").WithLocation(4, 24), + // (4,24): error CS0111: Type 'C1' already defines a member called 'this' with the same parameter types + // public partial int this[out int i] { get => i = 0; set => i = 0; } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "this").WithArguments("this", "C1").WithLocation(4, 24), + // (4,29): error CS0631: ref and out are not valid in this context + // public partial int this[out int i] { get => i = 0; set => i = 0; } + Diagnostic(ErrorCode.ERR_IllegalRefParam, "out").WithLocation(4, 29), + // (9,24): error CS9248: Partial property 'C2.this[out int]' must have an implementation part. + // public partial int this[out int i] { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "this").WithArguments("C2.this[out int]").WithLocation(9, 24), + // (9,29): error CS0631: ref and out are not valid in this context + // public partial int this[out int i] { get; set; } + Diagnostic(ErrorCode.ERR_IllegalRefParam, "out").WithLocation(9, 29), + // (10,24): error CS9249: Partial property 'C2.this[ref int]' must have a definition part. + // public partial int this[ref int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "this").WithArguments("C2.this[ref int]").WithLocation(10, 24), + // (10,24): error CS0111: Type 'C2' already defines a member called 'this' with the same parameter types + // public partial int this[ref int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "this").WithArguments("this", "C2").WithLocation(10, 24), + // (10,29): error CS0631: ref and out are not valid in this context + // public partial int this[ref int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(10, 29)); + } + + [Fact] + public void RefKindDifference_IndexerParameter_04() + { + // note: this non-merging behavior in presence of ref kind differences is consistent with partial methods + var source = """ + partial class C1 + { + public partial int this[in int i] { get; set; } + public partial int this[ref readonly int i] { get => i; set { } } + } + + partial class C2 + { + public partial int this[ref readonly int i] { get; set; } + public partial int this[in int i] { get => i; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,24): error CS9248: Partial property 'C1.this[in int]' must have an implementation part. + // public partial int this[in int i] { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "this").WithArguments("C1.this[in int]").WithLocation(3, 24), + // (4,24): error CS9249: Partial property 'C1.this[ref readonly int]' must have a definition part. + // public partial int this[ref readonly int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "this").WithArguments("C1.this[ref readonly int]").WithLocation(4, 24), + // (4,24): error CS0111: Type 'C1' already defines a member called 'this' with the same parameter types + // public partial int this[ref readonly int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "this").WithArguments("this", "C1").WithLocation(4, 24), + // (9,24): error CS9248: Partial property 'C2.this[ref readonly int]' must have an implementation part. + // public partial int this[ref readonly int i] { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "this").WithArguments("C2.this[ref readonly int]").WithLocation(9, 24), + // (10,24): error CS9249: Partial property 'C2.this[in int]' must have a definition part. + // public partial int this[in int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "this").WithArguments("C2.this[in int]").WithLocation(10, 24), + // (10,24): error CS0111: Type 'C2' already defines a member called 'this' with the same parameter types + // public partial int this[in int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "this").WithArguments("this", "C2").WithLocation(10, 24)); + } + + [Theory] + [InlineData("in", "ref")] + [InlineData("in", "out")] + [InlineData("ref readonly", "ref")] + [InlineData("ref readonly", "out")] + public void RefKindDifference_IndexerParameter_05(string goodRefKind, string badRefKind) + { + // Show that errors occur when declarations differ between allowed vs. disallowed parameter ref kinds. + var source = $$""" + partial class C1 + { + public partial int this[{{goodRefKind}} int i] { get; set; } + public partial int this[{{badRefKind}} int i] { get => i = 0; set => i = 0; } + } + + partial class C2 + { + public partial int this[{{badRefKind}} int i] { get; set; } + public partial int this[{{goodRefKind}} int i] { get => i; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,24): error CS9248: Partial property 'C1.this[in int]' must have an implementation part. + // public partial int this[in int i] { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "this").WithArguments($"C1.this[{goodRefKind} int]").WithLocation(3, 24), + // (4,24): error CS9249: Partial property 'C1.this[ref int]' must have a definition part. + // public partial int this[ref int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "this").WithArguments($"C1.this[{badRefKind} int]").WithLocation(4, 24), + // (4,24): error CS0111: Type 'C1' already defines a member called 'this' with the same parameter types + // public partial int this[ref int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "this").WithArguments("this", "C1").WithLocation(4, 24), + // (4,29): error CS0631: ref and out are not valid in this context + // public partial int this[ref int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_IllegalRefParam, badRefKind).WithLocation(4, 29), + // (9,24): error CS9248: Partial property 'C2.this[ref int]' must have an implementation part. + // public partial int this[ref int i] { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "this").WithArguments($"C2.this[{badRefKind} int]").WithLocation(9, 24), + // (9,29): error CS0631: ref and out are not valid in this context + // public partial int this[ref int i] { get; set; } + Diagnostic(ErrorCode.ERR_IllegalRefParam, badRefKind).WithLocation(9, 29), + // (10,24): error CS9249: Partial property 'C2.this[in int]' must have a definition part. + // public partial int this[in int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "this").WithArguments($"C2.this[{goodRefKind} int]").WithLocation(10, 24), + // (10,24): error CS0111: Type 'C2' already defines a member called 'this' with the same parameter types + // public partial int this[in int i] { get => i; set { } } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "this").WithArguments("this", "C2").WithLocation(10, 24)); + } + + [Fact] + public void TypeDifference_IndexerParameter() + { + var source = """ + partial class C + { + public partial int this[int i] { get; set; } + public partial int this[string s] { get => 1; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,24): error CS9248: Partial property 'C.this[int]' must have an implementation part. + // public partial int this[int i] { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "this").WithArguments("C.this[int]").WithLocation(3, 24), + // (4,24): error CS9249: Partial property 'C.this[string]' must have a definition part. + // public partial int this[string s] { get => 1; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "this").WithArguments("C.this[string]").WithLocation(4, 24)); + } + + [Fact] + public void NullabilityDifference_IndexerParameter() + { + var source = $$""" + #nullable enable + partial class C + { + public partial int this[string s] { get; set; } + public partial int this[string? s] { get => 1; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,24): warning CS9256: Partial property declarations 'int C.this[string s]' and 'int C.this[string? s]' have signature differences. + // public partial int this[string? s] { get => 1; set { } } + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[string s]", "int C.this[string? s]").WithLocation(5, 24)); + } + + [Fact] + public void DynamicDifference_IndexerParameter() + { + var source = """ + #nullable enable + partial class C + { + public partial int this[dynamic[] s] { get; set; } + public partial int this[object[] s] { get => 1; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,24): warning CS9256: Partial property declarations 'int C.this[dynamic[] s]' and 'int C.this[object[] s]' have signature differences. + // public partial int this[object[] s] { get => 1; set { } } + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[dynamic[] s]", "int C.this[object[] s]").WithLocation(5, 24)); + } + + [Fact] + public void Semantics_Params() + { + var source = """ + using System; + using System.Collections.Generic; + + partial class C + { + public static void Main() + { + var c = new C(); + _ = c[1, 2, 3]; + _ = c["a", "b", "c"]; + } + + public partial int this[params int[] arr] { get; } + public partial int this[params int[] arr] + { + get + { + foreach (var i in arr) + Console.Write(i); + + return 0; + } + } + + public partial int this[params IEnumerable enumerable] { get; } + public partial int this[params IEnumerable enumerable] + { + get + { + foreach (var i in enumerable) + Console.Write(i); + + return 0; + } + } + + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "123abc"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void ParamsDifference_IndexerParameter() + { + var source = """ + #nullable enable + using System.Collections.Generic; + + partial class C + { + public partial int this[params object[] arr] { get; set; } + public partial int this[object[] arr] { get => 1; set { } } + + public partial int this[IEnumerable enumerable] { get; set; } + public partial int this[params IEnumerable enumerable] { get => 1; set { } } + + public partial int this[object[] enumerable, int _] { get; set; } + public partial int this[params object[] enumerable, int _] { get => 1; set { } } + } + + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,24): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter + // public partial int this[object[] arr] { get => 1; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "this").WithLocation(7, 24), + // (10,24): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter + // public partial int this[params IEnumerable enumerable] { get => 1; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "this").WithLocation(10, 24), + // (13,29): error CS0231: A params parameter must be the last parameter in a parameter list + // public partial int this[params object[] enumerable, int _] { get => 1; set { } } + Diagnostic(ErrorCode.ERR_ParamsLast, "params object[] enumerable").WithLocation(13, 29)); + } + + [Fact] + public void Semantics_Scoped() + { + var source = """ + using System; + + Console.Write(new C().M()._i); + + ref struct RS(ref int i) + { + public ref int _i = ref i; + } + + partial class C + { + static int s_i = 1; + + public partial RS this[scoped RS rs] { get; } + public partial RS this[scoped RS rs] { get => new RS(ref s_i); } + + public RS M() + { + int i = 0; + RS rs = new RS(ref i); + return this[rs]; // ok + } + } + """; + + var verifier = CompileAndVerify( + source, + targetFramework: TargetFramework.Net70, + verify: Verification.Fails, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "1" : null); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void Scoped_Errors() + { + var source = """ + ref struct RS(ref int i) { } + + partial class C1 + { + public partial RS this[scoped RS rs] { get; } + public partial RS this[scoped RS rs] { get => rs; } // 1 + } + + partial class C2 + { + public partial RS this[RS rs] { get; } + public partial RS this[RS rs] { get => rs; } // ok + + public RS M() + { + int i = 0; + RS rs = new RS(ref i); + return this[rs]; // error + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,23): warning CS9113: Parameter 'i' is unread. + // ref struct RS(ref int i) { } + Diagnostic(ErrorCode.WRN_UnreadPrimaryConstructorParameter, "i").WithArguments("i").WithLocation(1, 23), + // (6,51): error CS8352: Cannot use variable 'scoped RS rs' in this context because it may expose referenced variables outside of their declaration scope + // public partial RS this[scoped RS rs] { get => rs; } // 1 + Diagnostic(ErrorCode.ERR_EscapeVariable, "rs").WithArguments("scoped RS rs").WithLocation(6, 51), + // (18,16): error CS8347: Cannot use a result of 'C2.this[RS]' in this context because it may expose variables referenced by parameter 'rs' outside of their declaration scope + // return this[rs]; // error + Diagnostic(ErrorCode.ERR_EscapeCall, "this[rs]").WithArguments("C2.this[RS]", "rs").WithLocation(18, 16), + // (18,21): error CS8352: Cannot use variable 'rs' in this context because it may expose referenced variables outside of their declaration scope + // return this[rs]; // error + Diagnostic(ErrorCode.ERR_EscapeVariable, "rs").WithArguments("rs").WithLocation(18, 21)); + } + + [Fact] + public void ScopedDifference_IndexerParameter() + { + var source = """ + #nullable enable + + ref struct RS { } + + partial class C + { + public partial RS this[scoped RS rs] { get; set; } + public partial RS this[RS rs] { get => default; set { } } + + public partial RS this[RS rs, int _] { get; set; } + public partial RS this[scoped RS rs, int _] { get => default; set { } } + } + + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,23): error CS8988: The 'scoped' modifier of parameter 'rs' doesn't match partial definition. + // public partial RS this[RS rs] { get => default; set { } } + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "this").WithArguments("rs").WithLocation(8, 23), + // (11,23): error CS8988: The 'scoped' modifier of parameter 'rs' doesn't match partial definition. + // public partial RS this[scoped RS rs, int _] { get => default; set { } } + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "this").WithArguments("rs").WithLocation(11, 23)); + } + + [Theory] + [InlineData("in")] + [InlineData("ref readonly")] + public void ScopedDifference_IndexerParameter_SupportedRefKind(string refKind) + { + var source = $$""" + #nullable enable + + ref struct RS { } + + partial class C + { + public partial RS this[scoped {{refKind}} int i] { get; set; } + public partial RS this[{{refKind}} int i] { get => default; set { } } + + public partial RS this[{{refKind}} int i, int _] { get; set; } + public partial RS this[scoped {{refKind}} int i, int _] { get => default; set { } } + } + + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,23): error CS8988: The 'scoped' modifier of parameter 'i' doesn't match partial definition. + // public partial RS this[in int i] { get => default; set { } } + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "this").WithArguments("i").WithLocation(8, 23), + // (11,23): error CS8988: The 'scoped' modifier of parameter 'i' doesn't match partial definition. + // public partial RS this[scoped in int i, int _] { get => default; set { } } + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "this").WithArguments("i").WithLocation(11, 23)); + } + + [Fact] + public void ScopedDifference_IndexerParameter_UnsupportedRefKind() + { + var source = $$""" + #nullable enable + + ref struct RS { } + + partial class C + { + public partial RS this[scoped ref int i] { get; set; } + public partial RS this[ref int i] { get => default; set { } } + + public partial RS this[ref int i, int _] { get; set; } + public partial RS this[scoped ref int i, int _] { get => default; set { } } + } + + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,35): error CS0631: ref and out are not valid in this context + // public partial RS this[scoped ref int i] { get; set; } + Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(7, 35), + // (8,23): error CS8988: The 'scoped' modifier of parameter 'i' doesn't match partial definition. + // public partial RS this[ref int i] { get => default; set { } } + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "this").WithArguments("i").WithLocation(8, 23), + // (8,28): error CS0631: ref and out are not valid in this context + // public partial RS this[ref int i] { get => default; set { } } + Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(8, 28), + // (10,28): error CS0631: ref and out are not valid in this context + // public partial RS this[ref int i, int _] { get; set; } + Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(10, 28), + // (11,23): error CS8988: The 'scoped' modifier of parameter 'i' doesn't match partial definition. + // public partial RS this[scoped ref int i, int _] { get => default; set { } } + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "this").WithArguments("i").WithLocation(11, 23), + // (11,35): error CS0631: ref and out are not valid in this context + // public partial RS this[scoped ref int i, int _] { get => default; set { } } + Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(11, 35)); + } + + [Fact] + public void Semantics_OptionalParameters() + { + var source = """ + using System; + + var c = new C(); + Console.Write(c[1]); + Console.Write(c[1, 2]); + + partial class C + { + public partial int this[int x, int y = 1] { get; } + public partial int this[int x, int y] { get => y; } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "12"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void OptionalParameters_OnImplementationPart_ResultsInAWarning() + { + // A warning is reported for optional parameters on implementation part, even if it matches the definition part. + var source = """ + partial class C + { + public partial int this[int x, int y = 1] { get; set; } + public partial int this[int x, int y = 1] { get => y; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,40): warning CS1066: The default value specified for parameter 'y' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // public partial int this[int x, int y = 1] { get => y; set { } } + Diagnostic(ErrorCode.WRN_DefaultValueForUnconsumedLocation, "y").WithArguments("y").WithLocation(4, 40)); + } + + [Fact] + public void OptionalParameters_OnImplementationPart_NotRespectedAtCallSite_Semantics() + { + var source = """ + using System; + + partial class C + { + static void Main() + { + var c = new C(); + Console.Write(c[0, 0]); + Console.Write(c["a", 0]); + Console.Write(c["a"]); + } + + public partial int this[int x, int y] { get; set; } + public partial int this[int x, int y = 1] { get => y; set { } } + + public partial int this[string x, int y = 1] { get; set; } + public partial int this[string x, int y = 2] { get => y; set { } } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "001"); + verifier.VerifyDiagnostics( + // (14,40): warning CS1066: The default value specified for parameter 'y' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // public partial int this[int x, int y = 1] { get => y; set { } } + Diagnostic(ErrorCode.WRN_DefaultValueForUnconsumedLocation, "y").WithArguments("y").WithLocation(14, 40), + // (17,43): warning CS1066: The default value specified for parameter 'y' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // public partial int this[string x, int y = 2] { get => y; set { } } + Diagnostic(ErrorCode.WRN_DefaultValueForUnconsumedLocation, "y").WithArguments("y").WithLocation(17, 43)); + } + + [Fact] + public void OptionalParameters_OnImplementationPart_NotRespectedAtCallSite() + { + var source = """ + using System; + + partial class C + { + static void Main() + { + var c = new C(); + Console.Write(c[0]); + } + + public partial int this[int x, int y] { get; set; } + public partial int this[int x, int y = 1] { get => y; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,23): error CS7036: There is no argument given that corresponds to the required parameter 'y' of 'C.this[int, int]' + // Console.Write(c[0]); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "c[0]").WithArguments("y", "C.this[int, int]").WithLocation(8, 23), + // (12,40): warning CS1066: The default value specified for parameter 'y' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // public partial int this[int x, int y = 1] { get => y; set { } } + Diagnostic(ErrorCode.WRN_DefaultValueForUnconsumedLocation, "y").WithArguments("y").WithLocation(12, 40)); + } + + [Fact] + public void OptionalParameters_AllParametersAreOptional() + { + // An indexer access needs at least one argument in order to be valid + var source = """ + partial class C + { + void M() + { + _ = this[]; + _ = this[1]; + } + + public partial int this[int x = 1, int y = 2] { get; set; } + public partial int this[int x = 1, int y = 2] { get => y; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,18): error CS0443: Syntax error; value expected + // _ = this[]; + Diagnostic(ErrorCode.ERR_ValueExpected, "]").WithLocation(5, 18), + // (10,33): warning CS1066: The default value specified for parameter 'x' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // public partial int this[int x = 1, int y = 2] { get => y; set { } } + Diagnostic(ErrorCode.WRN_DefaultValueForUnconsumedLocation, "x").WithArguments("x").WithLocation(10, 33), + // (10,44): warning CS1066: The default value specified for parameter 'y' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // public partial int this[int x = 1, int y = 2] { get => y; set { } } + Diagnostic(ErrorCode.WRN_DefaultValueForUnconsumedLocation, "y").WithArguments("y").WithLocation(10, 44)); + } + + [Fact] + public void Indexers_MissingOrUnexpectedDeclarations() + { + var source = """ + partial class C + { + public partial int this[int x] { get; set; } // missing impl + + public partial int this[int x, int y] { get => 1; set { } } // missing decl + + public partial int this[int x, int y, int z] { get; set; } // duplicate decl + public partial int this[int x, int y, int z] { get; set; } + public partial int this[int x, int y, int z] { get => 1; set { } } + + public partial int this[int x, int y, int z, int a] { get; set; } // duplicate impl + public partial int this[int x, int y, int z, int a] { get => 1; set { } } + public partial int this[int x, int y, int z, int a] { get => 1; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,24): error CS9248: Partial property 'C.this[int]' must have an implementation part. + // public partial int this[int x] { get; set; } // missing impl + Diagnostic(ErrorCode.ERR_PartialPropertyMissingImplementation, "this").WithArguments("C.this[int]").WithLocation(3, 24), + // (5,24): error CS9249: Partial property 'C.this[int, int]' must have a definition part. + // public partial int this[int x, int y] { get => 1; set { } } // missing decl + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "this").WithArguments("C.this[int, int]").WithLocation(5, 24), + // (8,24): error CS9250: A partial property may not have multiple defining declarations, and cannot be an auto-property. + // public partial int this[int x, int y, int z] { get; set; } + Diagnostic(ErrorCode.ERR_PartialPropertyDuplicateDefinition, "this").WithLocation(8, 24), + // (8,24): error CS0111: Type 'C' already defines a member called 'this' with the same parameter types + // public partial int this[int x, int y, int z] { get; set; } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "this").WithArguments("this", "C").WithLocation(8, 24), + // (13,24): error CS9251: A partial property may not have multiple implementing declarations + // public partial int this[int x, int y, int z, int a] { get => 1; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyDuplicateImplementation, "this").WithLocation(13, 24), + // (13,24): error CS0111: Type 'C' already defines a member called 'this' with the same parameter types + // public partial int this[int x, int y, int z, int a] { get => 1; set { } } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "this").WithArguments("this", "C").WithLocation(13, 24)); + } + + [Fact] + public void Indexers_ReturnTypeDifference() + { + var source = """ + partial class C + { + public partial int[] this[int x] { get; set; } + public partial string[] this[int x] { get => []; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,29): error CS9255: Both partial property declarations must have the same type. + // public partial string[] this[int x] { get => []; set { } } + Diagnostic(ErrorCode.ERR_PartialPropertyTypeDifference, "this").WithLocation(4, 29)); + } + + [Fact] + public void Indexers_TupleElementNameDifference() + { + var source = """ + partial class C + { + // in return type + public partial (int x, int y)[] this[int x] { get; set; } + public partial (int a, int b)[] this[int x] { get => []; set { } } + + // in parameter type + public partial int this[(int x, int y) pair] { get; set; } + public partial int this[(int a, int b) pair] { get => 1; set { } } + + // in both return and parameter type + public partial (int x, int y)[] this[(int x, int y, int z) pair] { get; set; } + public partial (int a, int b)[] this[(int a, int b, int c) pair] { get => []; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,37): error CS8142: Both partial member declarations, 'C.this[int]' and 'C.this[int]', must use the same tuple element names. + // public partial (int a, int b)[] this[int x] { get => []; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "this").WithArguments("C.this[int]", "C.this[int]").WithLocation(5, 37), + // (9,24): error CS8142: Both partial member declarations, 'C.this[(int x, int y)]' and 'C.this[(int a, int b)]', must use the same tuple element names. + // public partial int this[(int a, int b) pair] { get => 1; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "this").WithArguments("C.this[(int x, int y)]", "C.this[(int a, int b)]").WithLocation(9, 24), + // (13,37): error CS8142: Both partial member declarations, 'C.this[(int x, int y, int z)]' and 'C.this[(int a, int b, int c)]', must use the same tuple element names. + // public partial (int a, int b)[] this[(int a, int b, int c) pair] { get => []; set { } } + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "this").WithArguments("C.this[(int x, int y, int z)]", "C.this[(int a, int b, int c)]").WithLocation(13, 37)); + } + + [Theory] + [InlineData("A(1)", "B(2)")] + [InlineData("B(2)", "A(1)")] + public void Attributes_Property_01(string declAttribute, string implAttribute) + { + // Name or arguments to attributes doesn't affect order of emit. + // Attributes on the declaration part precede attributes on the implementation part. + var source = $$""" + #pragma warning disable CS9113 // Primary constructor parameter is unread + + using System; + + class A(int i) : Attribute { } + class B(int i) : Attribute { } + + partial class C + { + [{{declAttribute}}] + public partial int P { get; set; } + + [{{implAttribute}}] + public partial int P { get => 1; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var property = comp.GetMember("C.P"); + AssertEx.Equal([declAttribute, implAttribute], property.GetAttributes().ToStrings()); + AssertEx.Equal([declAttribute, implAttribute], property.PartialImplementationPart!.GetAttributes().ToStrings()); + } + + [Theory] + [InlineData("A(1)", "B(2)")] + [InlineData("B(2)", "A(1)")] + public void Attributes_Property_02(string declAttribute, string implAttribute) + { + // Lexical order of the partial declarations themselves doesn't affect order that attributes are emitted. + var source = $$""" + #pragma warning disable CS9113 // Primary constructor parameter is unread + + using System; + + class A(int i) : Attribute { } + class B(int i) : Attribute { } + + partial class C + { + [{{implAttribute}}] + public partial int P { get => 1; set { } } + + [{{declAttribute}}] + public partial int P { get; set; } + } + """; + + var verifier = CompileAndVerify(source, symbolValidator: module => verify(module, isSource: false), sourceSymbolValidator: module => verify(module, isSource: true)); + verifier.VerifyDiagnostics(); + + void verify(ModuleSymbol module, bool isSource) + { + var property = module.GlobalNamespace.GetMember("C.P"); + AssertEx.Equal([declAttribute, implAttribute], property.GetAttributes().ToStrings()); + if (isSource) + { + AssertEx.Equal([declAttribute, implAttribute], ((SourcePropertySymbol)property).PartialImplementationPart!.GetAttributes().ToStrings()); + } + } + } + + [Fact] + public void Attributes_Property_03() + { + // Order of attributes within a part is preserved. + var source = """ + #pragma warning disable CS9113 // Primary constructor parameter is unread + + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class A(int i) : Attribute { } + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class B(int i) : Attribute { } + + partial class C + { + [A(2), B(2)] + public partial int P { get => 1; set { } } + + [A(1), B(1)] + public partial int P { get; set; } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var property = comp.GetMember("C.P"); + AssertEx.Equal(["A(1)", "B(1)", "A(2)", "B(2)"], property.GetAttributes().ToStrings()); + AssertEx.Equal(["A(1)", "B(1)", "A(2)", "B(2)"], property.PartialImplementationPart!.GetAttributes().ToStrings()); + } + + [Fact] + public void Attributes_GetAccessor() + { + // Order of attributes within a part is preserved. + var source = """ + #pragma warning disable CS9113 // Primary constructor parameter is unread + + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class A(int i) : Attribute { } + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class B(int i) : Attribute { } + + partial class C + { + public partial int P + { + [A(2), B(2)] + get => 1; + set { } + } + + public partial int P + { + [A(1), B(1)] + get; + set; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var accessor = comp.GetMember("C.get_P"); + AssertEx.Equal(["A(1)", "B(1)", "A(2)", "B(2)"], accessor.GetAttributes().ToStrings()); + AssertEx.Equal(["A(1)", "B(1)", "A(2)", "B(2)"], accessor.PartialImplementationPart!.GetAttributes().ToStrings()); + } + + [Theory] + [CombinatorialData] + public void Attributes_SetAccessor(bool definitionFirst) + { + var definitionPart = """ + public partial int P + { + get; + [A(1), B(1)] + set; + } + """; + + var implementationPart = """ + public partial int P + { + get => 1; + [A(2), B(2)] + set { } + } + """; + + var source = $$""" + #pragma warning disable CS9113 // Primary constructor parameter is unread + + using System; + + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class A(int i) : Attribute { } + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class B(int i) : Attribute { } + + partial class C + { + {{(definitionFirst ? definitionPart : implementationPart)}} + + {{(definitionFirst ? implementationPart : definitionPart)}} + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var accessor = comp.GetMember("C.set_P"); + AssertEx.Equal(["A(1)", "B(1)", "A(2)", "B(2)"], accessor.GetAttributes().ToStrings()); + AssertEx.Equal(["A(1)", "B(1)", "A(2)", "B(2)"], accessor.PartialImplementationPart!.GetAttributes().ToStrings()); + } + + [Fact] + public void Attributes_SetValueParam() + { + // Just as with parameter attributes on partial methods, + // the implementation part attributes are emitted before the definition part attributes. + var source = """ + #pragma warning disable CS9113 // Primary constructor parameter is unread + + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class A(int i) : Attribute { } + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class B(int i) : Attribute { } + + partial class C + { + public partial int P + { + get => 1; + [param: A(2), B(2)] + set { } + } + + public partial int P + { + get; + [param: A(1), B(1)] + set; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var accessor = comp.GetMember("C.set_P"); + AssertEx.Equal([], accessor.GetAttributes().ToStrings()); + AssertEx.Equal([], accessor.PartialImplementationPart.GetAttributes().ToStrings()); + AssertEx.Equal(["A(2)", "B(2)", "A(1)", "B(1)"], accessor.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal(["A(2)", "B(2)", "A(1)", "B(1)"], accessor.PartialImplementationPart!.Parameters.Single().GetAttributes().ToStrings()); + } + + [Fact] + public void Attributes_Indexer() + { + var source = """ + #pragma warning disable CS9113 // Primary constructor parameter is unread + + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class A(int i) : Attribute { } + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class B(int i) : Attribute { } + + partial class C + { + [A(2), B(2)] + public partial int this[int i] { get => 1; set { } } + + [A(1), B(1)] + public partial int this[int i] { get; set; } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var indexer = (SourcePropertySymbol)comp.GetMember("C").Indexers.Single(); + AssertEx.Equal(["A(1)", "B(1)", "A(2)", "B(2)"], indexer.GetAttributes().ToStrings()); + AssertEx.Equal(["A(1)", "B(1)", "A(2)", "B(2)"], indexer.PartialImplementationPart!.GetAttributes().ToStrings()); + } + + [Fact] + public void Attributes_IndexerParameter() + { + // Unlike other symbol kinds, for parameters, the implementation part attributes are emitted first, then the definition part attributes. + // This is consistent with partial methods. + var source = """ + #pragma warning disable CS9113 // Primary constructor parameter is unread + + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class A(int i) : Attribute { } + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class B(int i) : Attribute { } + + partial class C + { + public partial int this[[A(2), B(2)] int i] { get => 1; set { } } + + public partial int this[[A(1), B(1)] int i] { get; set; } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var indexer = (SourcePropertySymbol)comp.GetMember("C").Indexers.Single(); + + verify(indexer.Parameters.Single()); + verify(indexer.GetMethod!.Parameters.Single()); + verify(indexer.SetMethod!.Parameters[0]); + + verify(indexer.PartialImplementationPart!.Parameters.Single()); + verify(indexer.PartialImplementationPart!.GetMethod!.Parameters.Single()); + verify(indexer.PartialImplementationPart!.SetMethod!.Parameters[0]); + + void verify(ParameterSymbol param) + { + AssertEx.Equal(["A(2)", "B(2)", "A(1)", "B(1)"], param.GetAttributes().ToStrings()); + } + } + + [Fact] + public void Attributes_Property_DisallowedDuplicates() + { + var source = """ + using System; + + class Attr : Attribute { } + + partial class C + { + [Attr] + public partial int P + { + [Attr] + get; + + [Attr] + [param: Attr] set; // 1 + } + + [Attr] // 2 + public partial int P + { + [Attr] // 3 + get => 1; + + [Attr] // 4 + [param: Attr] set { } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (14,17): error CS0579: Duplicate 'Attr' attribute + // [param: Attr] set; // 1 + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "Attr").WithArguments("Attr").WithLocation(14, 17), + // (17,6): error CS0579: Duplicate 'Attr' attribute + // [Attr] // 2 + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "Attr").WithArguments("Attr").WithLocation(17, 6), + // (20,10): error CS0579: Duplicate 'Attr' attribute + // [Attr] // 3 + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "Attr").WithArguments("Attr").WithLocation(20, 10), + // (23,10): error CS0579: Duplicate 'Attr' attribute + // [Attr] // 4 + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "Attr").WithArguments("Attr").WithLocation(23, 10)); + + var property = comp.GetMember("C.P"); + AssertEx.Equal(["Attr", "Attr"], property.GetAttributes().ToStrings()); + AssertEx.Equal(["Attr", "Attr"], property.GetMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["Attr", "Attr"], property.SetMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["Attr", "Attr"], property.SetMethod!.Parameters.Single().GetAttributes().ToStrings()); + } + + [Fact] + public void Attributes_Indexer_DisallowedDuplicates() + { + var source = """ + using System; + + class Attr : Attribute { } + + partial class C + { + [Attr] + public partial int this[ + [Attr] int x, // 1 + [Attr] int y] // 2 + { + [Attr] get; + [Attr] + [param: Attr] set; // 3 + } + + [Attr] // 4 + public partial int this[ + [Attr] int x, + [Attr] int y] + { + [Attr] // 5 + get => 1; + + [Attr] // 6 + [param: Attr] set { } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (9,10): error CS0579: Duplicate 'Attr' attribute + // [Attr] int x, // 1 + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "Attr").WithArguments("Attr").WithLocation(9, 10), + // (10,10): error CS0579: Duplicate 'Attr' attribute + // [Attr] int y] // 2 + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "Attr").WithArguments("Attr").WithLocation(10, 10), + // (14,17): error CS0579: Duplicate 'Attr' attribute + // [param: Attr] set; // 3 + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "Attr").WithArguments("Attr").WithLocation(14, 17), + // (17,6): error CS0579: Duplicate 'Attr' attribute + // [Attr] // 4 + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "Attr").WithArguments("Attr").WithLocation(17, 6), + // (22,10): error CS0579: Duplicate 'Attr' attribute + // [Attr] // 5 + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "Attr").WithArguments("Attr").WithLocation(22, 10), + // (25,10): error CS0579: Duplicate 'Attr' attribute + // [Attr] // 6 + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "Attr").WithArguments("Attr").WithLocation(25, 10)); + + var property = (SourcePropertySymbol)comp.GetMember("C").Indexers.Single(); + AssertEx.Equal(["Attr", "Attr"], property.GetAttributes().ToStrings()); + AssertEx.Equal(["Attr", "Attr"], property.GetMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["Attr", "Attr"], property.GetMethod!.Parameters[0].GetAttributes().ToStrings()); + AssertEx.Equal(["Attr", "Attr"], property.GetMethod!.Parameters[1].GetAttributes().ToStrings()); + AssertEx.Equal(["Attr", "Attr"], property.SetMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["Attr", "Attr"], property.SetMethod!.Parameters[0].GetAttributes().ToStrings()); + AssertEx.Equal(["Attr", "Attr"], property.SetMethod!.Parameters[1].GetAttributes().ToStrings()); + AssertEx.Equal(["Attr", "Attr"], property.SetMethod!.Parameters[2].GetAttributes().ToStrings()); + } + + [Fact] + public void Attributes_Property_BackingField() + { + var source = """ + using System; + + class Attr : Attribute { } + + partial class C + { + [field: Attr] + public partial int P { get; set; } + + [field: Attr] + public partial int P { get => 1; set { } } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,6): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'property'. All attributes in this block will be ignored. + // [field: Attr] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "property").WithLocation(7, 6), + // (10,6): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'property'. All attributes in this block will be ignored. + // [field: Attr] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "property").WithLocation(10, 6)); + + // https://github.com/dotnet/roslyn/issues/57012 + // 'field' keyword in properties feature should test partial properties where the implementation uses 'field' and one or both parts have 'field:' targeted attribute lists. + var property = comp.GetMember("C.P"); + AssertEx.Equal([], property.GetAttributes().ToStrings()); + } + + [Fact] + public void PropertyInitializer() + { + var source = """ + partial class C + { + public partial string P1 { get; set; } = "a"; + public partial string P1 { get => ""; set { } } + + public partial string P2 { get; set; } + public partial string P2 { get => ""; set { } } = "b"; + + public partial string P3 { get; set; } = "c"; + public partial string P3 { get => ""; set { } } = "d"; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,27): error CS8050: Only auto-implemented properties can have initializers. + // public partial string P1 { get; set; } = "a"; + Diagnostic(ErrorCode.ERR_InitializerOnNonAutoProperty, "P1").WithLocation(3, 27), + // (7,27): error CS8050: Only auto-implemented properties can have initializers. + // public partial string P2 { get => ""; set { } } = "b"; + Diagnostic(ErrorCode.ERR_InitializerOnNonAutoProperty, "P2").WithLocation(7, 27), + // (9,27): error CS8050: Only auto-implemented properties can have initializers. + // public partial string P3 { get; set; } = "c"; + Diagnostic(ErrorCode.ERR_InitializerOnNonAutoProperty, "P3").WithLocation(9, 27), + // (10,27): error CS8050: Only auto-implemented properties can have initializers. + // public partial string P3 { get => ""; set { } } = "d"; + Diagnostic(ErrorCode.ERR_InitializerOnNonAutoProperty, "P3").WithLocation(10, 27)); + } + + [Fact] + public void PropertyInitializer_ContainsErrors() + { + var source = """ + partial class C + { + public partial string P1 { get; set; } = ERROR; + public partial string P1 { get => ""; set { } } + + public partial string P2 { get; set; } + public partial string P2 { get => ""; set { } } = ERROR; + + public partial string P3 { get; set; } = ERROR; + public partial string P3 { get => ""; set { } } = ERROR; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,27): error CS8050: Only auto-implemented properties can have initializers. + // public partial string P1 { get; set; } = ERROR; + Diagnostic(ErrorCode.ERR_InitializerOnNonAutoProperty, "P1").WithLocation(3, 27), + // (3,46): error CS0103: The name 'ERROR' does not exist in the current context + // public partial string P1 { get; set; } = ERROR; + Diagnostic(ErrorCode.ERR_NameNotInContext, "ERROR").WithArguments("ERROR").WithLocation(3, 46), + // (7,27): error CS8050: Only auto-implemented properties can have initializers. + // public partial string P2 { get => ""; set { } } = ERROR; + Diagnostic(ErrorCode.ERR_InitializerOnNonAutoProperty, "P2").WithLocation(7, 27), + // (7,55): error CS0103: The name 'ERROR' does not exist in the current context + // public partial string P2 { get => ""; set { } } = ERROR; + Diagnostic(ErrorCode.ERR_NameNotInContext, "ERROR").WithArguments("ERROR").WithLocation(7, 55), + // (9,27): error CS8050: Only auto-implemented properties can have initializers. + // public partial string P3 { get; set; } = ERROR; + Diagnostic(ErrorCode.ERR_InitializerOnNonAutoProperty, "P3").WithLocation(9, 27), + // (9,46): error CS0103: The name 'ERROR' does not exist in the current context + // public partial string P3 { get; set; } = ERROR; + Diagnostic(ErrorCode.ERR_NameNotInContext, "ERROR").WithArguments("ERROR").WithLocation(9, 46), + // (10,27): error CS8050: Only auto-implemented properties can have initializers. + // public partial string P3 { get => ""; set { } } = ERROR; + Diagnostic(ErrorCode.ERR_InitializerOnNonAutoProperty, "P3").WithLocation(10, 27), + // (10,55): error CS0103: The name 'ERROR' does not exist in the current context + // public partial string P3 { get => ""; set { } } = ERROR; + Diagnostic(ErrorCode.ERR_NameNotInContext, "ERROR").WithArguments("ERROR").WithLocation(10, 55)); + } + + [Fact] + public void PropertyInitializer_AndFieldTargetedAttribute() + { + // A synthesized field symbol is created when property has an initializer, even if the property is not an auto-property. + var source = """ + public class Attr1 : System.Attribute { } + public class Attr2 : System.Attribute { } + + partial class C + { + [field: Attr1] + public partial string P1 { get; set; } = "a"; + [field: Attr2] + public partial string P1 { get => ""; set { } } + + [field: Attr1] + public partial string P2 { get; set; } + [field: Attr2] + public partial string P2 { get => ""; set { } } = "b"; + + [field: Attr1] + public partial string P3 { get; set; } = "c"; + [field: Attr2] + public partial string P3 { get => ""; set { } } = "d"; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,6): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'property'. All attributes in this block will be ignored. + // [field: Attr1] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "property").WithLocation(6, 6), + // (7,27): error CS8050: Only auto-implemented properties can have initializers. + // public partial string P1 { get; set; } = "a"; + Diagnostic(ErrorCode.ERR_InitializerOnNonAutoProperty, "P1").WithLocation(7, 27), + // (8,6): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'property'. All attributes in this block will be ignored. + // [field: Attr2] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "property").WithLocation(8, 6), + // (11,6): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'property'. All attributes in this block will be ignored. + // [field: Attr1] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "property").WithLocation(11, 6), + // (13,6): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'property'. All attributes in this block will be ignored. + // [field: Attr2] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "property").WithLocation(13, 6), + // (14,27): error CS8050: Only auto-implemented properties can have initializers. + // public partial string P2 { get => ""; set { } } = "b"; + Diagnostic(ErrorCode.ERR_InitializerOnNonAutoProperty, "P2").WithLocation(14, 27), + // (16,6): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'property'. All attributes in this block will be ignored. + // [field: Attr1] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "property").WithLocation(16, 6), + // (17,27): error CS8050: Only auto-implemented properties can have initializers. + // public partial string P3 { get; set; } = "c"; + Diagnostic(ErrorCode.ERR_InitializerOnNonAutoProperty, "P3").WithLocation(17, 27), + // (18,6): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'property'. All attributes in this block will be ignored. + // [field: Attr2] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "property").WithLocation(18, 6), + // (19,27): error CS8050: Only auto-implemented properties can have initializers. + // public partial string P3 { get => ""; set { } } = "d"; + Diagnostic(ErrorCode.ERR_InitializerOnNonAutoProperty, "P3").WithLocation(19, 27)); + + AssertEx.Equal([ + "System.String C.k__BackingField", + "System.String C.P1 { get; set; }", + "System.String C.P1.get", + "void C.P1.set", + "System.String C.P2 { get; set; }", + "System.String C.P2.get", + "void C.P2.set", + "System.String C.k__BackingField", + "System.String C.k__BackingField", + "System.String C.P3 { get; set; }", + "System.String C.P3.get", + "void C.P3.set", + "System.String C.k__BackingField", + "C..ctor()"], + comp.GetMember("C").GetMembers().SelectAsArray(m => m.ToTestDisplayString())); + + Assert.Empty(comp.GetMember("C.k__BackingField").GetAttributes()); + Assert.Empty(comp.GetMember("C.k__BackingField").GetAttributes()); + + var p3Fields = comp.GetMembers("C.k__BackingField"); + Assert.Equal(2, p3Fields.Length); + Assert.Empty(p3Fields[0].GetAttributes()); + Assert.Empty(p3Fields[1].GetAttributes()); + } + + [Theory] + [InlineData("", "[UnscopedRef] ")] + [InlineData("[UnscopedRef] ", "")] + public void Attributes_UnscopedRef(string defAttrs, string implAttrs) + { + // There aren't many interesting scenarios to test with UnscopedRef because: + // - no out parameters (so no removing the implicit 'scoped' from them) + // - no ref parameters either (so no interesting differences in ref safety analysis for ref readonlys marked with `[UnscopedRef]`) + var source = $$""" + #pragma warning disable CS9113 // Primary constructor parameter is unread + + using System.Diagnostics.CodeAnalysis; + + public ref struct RS([UnscopedRef] ref readonly int ri) { } + + partial class C + { + public partial RS this[{{defAttrs}}ref readonly int i] { get; } + public partial RS this[{{implAttrs}}ref readonly int i] + { + get => new RS(in i); + } + } + """; + + var comp = CreateCompilation([source, UnscopedRefAttributeDefinition]); + comp.VerifyEmitDiagnostics(); + + var indexer = (SourcePropertySymbol)comp.GetMember("C").Indexers.Single(); + Assert.True(indexer.Parameters[0].HasUnscopedRefAttribute); + Assert.True(indexer.PartialImplementationPart!.Parameters[0].HasUnscopedRefAttribute); + Assert.True(indexer.GetMethod!.Parameters[0].HasUnscopedRefAttribute); + Assert.True(indexer.GetMethod!.PartialImplementationPart!.Parameters[0].HasUnscopedRefAttribute); + } + + [Fact] + public void Attributes_CallerLineNumber_OnDefinition() + { + var source = $$""" + using System; + using System.Runtime.CompilerServices; + + partial class C + { + public static void Main() + { + var c = new C(); + #line 1 + Console.Write(c[2]); + } + + public partial int this[int x, [CallerLineNumber] int lineNumber = 0] { get; } + public partial int this[int x, int lineNumber] + { + get + { + Console.Write(lineNumber); + return x; + } + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "12", symbolValidator: verify); + verifier.VerifyDiagnostics(); + + void verify(ModuleSymbol module) + { + var indexer = (PropertySymbol)module.GlobalNamespace.GetMember("C").Indexers.Single(); + AssertEx.Equal(["System.Runtime.CompilerServices.CallerLineNumberAttribute"], indexer.Parameters[1].GetAttributes().ToStrings()); + } + } + + [Fact] + public void Attributes_CallerLineNumber_OnImplementation() + { + var source = $$""" + using System; + using System.Runtime.CompilerServices; + + partial class C + { + public static void Main() + { + var c = new C(); + #line 1 + Console.Write(c[2]); + } + + public partial int this[int x, int lineNumber = 0] { get; } + public partial int this[int x, [CallerLineNumber] int lineNumber] + { + get + { + Console.Write(lineNumber); + return x; + } + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "02", symbolValidator: verify); + verifier.VerifyDiagnostics( + // (5,37): warning CS4024: The CallerLineNumberAttribute applied to parameter 'lineNumber' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // public partial int this[int x, [CallerLineNumber] int lineNumber] + Diagnostic(ErrorCode.WRN_CallerLineNumberParamForUnconsumedLocation, "CallerLineNumber").WithArguments("lineNumber").WithLocation(5, 37)); + + void verify(ModuleSymbol module) + { + // https://github.com/dotnet/roslyn/issues/73482 + // The attribute is still written out to metadata even though it is ignored in source. + // We could consider changing this for both properties and methods. + var indexer = (PropertySymbol)module.GlobalNamespace.GetMember("C").Indexers.Single(); + AssertEx.Equal(["System.Runtime.CompilerServices.CallerLineNumberAttribute"], indexer.Parameters[1].GetAttributes().ToStrings()); + } + } + + [Fact] + public void Attributes_CallerFilePath_OnDefinition() + { + var source = ($$""" + using System; + using System.Runtime.CompilerServices; + + partial class C + { + public static void Main() + { + var c = new C(); + Console.Write(c[2]); + } + + public partial int this[int x, [CallerFilePath] string filePath = "0"] { get; } + public partial int this[int x, string filePath] + { + get + { + Console.Write(filePath); + return x; + } + } + } + """, filePath: "Program.cs"); + var verifier = CompileAndVerify(source, expectedOutput: "Program.cs2", symbolValidator: verify); + verifier.VerifyDiagnostics(); + + void verify(ModuleSymbol module) + { + var indexer = (PropertySymbol)module.GlobalNamespace.GetMember("C").Indexers.Single(); + AssertEx.Equal(["System.Runtime.CompilerServices.CallerFilePathAttribute"], indexer.Parameters[1].GetAttributes().ToStrings()); + } + } + + [Fact] + public void Attributes_CallerFilePath_OnImplementation() + { + var source = ($$""" + using System; + using System.Runtime.CompilerServices; + + partial class C + { + public static void Main() + { + var c = new C(); + Console.Write(c[2]); + } + + public partial int this[int x, string filePath = "0"] { get; } + public partial int this[int x, [CallerFilePath] string filePath] + { + get + { + Console.Write(filePath); + return x; + } + } + } + """, filePath: "Program.cs"); + var verifier = CompileAndVerify(source, expectedOutput: "02", symbolValidator: verify); + verifier.VerifyDiagnostics( + // Program.cs(13,37): warning CS4025: The CallerFilePathAttribute applied to parameter 'filePath' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // public partial int this[int x, [CallerFilePath] string filePath] + Diagnostic(ErrorCode.WRN_CallerFilePathParamForUnconsumedLocation, "CallerFilePath").WithArguments("filePath").WithLocation(13, 37)); + + void verify(ModuleSymbol module) + { + // https://github.com/dotnet/roslyn/issues/73482 + // The attribute is still written out to metadata even though it is ignored in source. + // We could consider changing this for both properties and methods. + var indexer = (PropertySymbol)module.GlobalNamespace.GetMember("C").Indexers.Single(); + AssertEx.Equal(["System.Runtime.CompilerServices.CallerFilePathAttribute"], indexer.Parameters[1].GetAttributes().ToStrings()); + } + } + + [Fact] + public void Attributes_CallerMemberName_OnDefinition() + { + var source = $$""" + using System; + using System.Runtime.CompilerServices; + + partial class C + { + public static void Main() + { + var c = new C(); + Console.Write(c[2]); + } + + public partial int this[int x, [CallerMemberName] string filePath = "0"] { get; } + public partial int this[int x, string filePath] + { + get + { + Console.Write(filePath); + return x; + } + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "Main2", symbolValidator: verify); + verifier.VerifyDiagnostics(); + + void verify(ModuleSymbol module) + { + var indexer = (PropertySymbol)module.GlobalNamespace.GetMember("C").Indexers.Single(); + AssertEx.Equal(["System.Runtime.CompilerServices.CallerMemberNameAttribute"], indexer.Parameters[1].GetAttributes().ToStrings()); + } + } + + [Fact] + public void Attributes_CallerMemberName_OnImplementation() + { + var source = $$""" + using System; + using System.Runtime.CompilerServices; + + partial class C + { + public static void Main() + { + var c = new C(); + Console.Write(c[2]); + } + + public partial int this[int x, string filePath = "0"] { get; } + public partial int this[int x, [CallerMemberName] string filePath] + { + get + { + Console.Write(filePath); + return x; + } + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "02", symbolValidator: verify); + verifier.VerifyDiagnostics( + // (13,37): warning CS4026: The CallerMemberNameAttribute applied to parameter 'filePath' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // public partial int this[int x, [CallerMemberName] string filePath] + Diagnostic(ErrorCode.WRN_CallerMemberNameParamForUnconsumedLocation, "CallerMemberName").WithArguments("filePath").WithLocation(13, 37)); + + void verify(ModuleSymbol module) + { + // https://github.com/dotnet/roslyn/issues/73482 + // The attribute is still written out to metadata even though it is ignored in source. + // We could consider changing this for both properties and methods. + var indexer = (PropertySymbol)module.GlobalNamespace.GetMember("C").Indexers.Single(); + AssertEx.Equal(["System.Runtime.CompilerServices.CallerMemberNameAttribute"], indexer.Parameters[1].GetAttributes().ToStrings()); + } + } + + [Fact] + public void Attributes_CallerArgumentExpression_OnDefinition() + { + var source = $$""" + using System; + using System.Runtime.CompilerServices; + + namespace System.Runtime.CompilerServices + { + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = false)] + public sealed class CallerArgumentExpressionAttribute : Attribute + { + public CallerArgumentExpressionAttribute(string parameterName) + { + ParameterName = parameterName; + } + public string ParameterName { get; } + } + } + + partial class C + { + public static void Main() + { + var c = new C(); + Console.Write(c[GetNumber()]); + } + + public static int GetNumber() => 2; + + public partial int this[int x, [CallerArgumentExpression("x")] string argumentExpression = "0"] { get; } + public partial int this[int x, string argumentExpression] + { + get + { + Console.Write(argumentExpression); + return x; + } + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "GetNumber()2", symbolValidator: verify); + verifier.VerifyDiagnostics(); + + void verify(ModuleSymbol module) + { + var indexer = (PropertySymbol)module.GlobalNamespace.GetMember("C").Indexers.Single(); + AssertEx.Equal(["""System.Runtime.CompilerServices.CallerArgumentExpressionAttribute("x")"""], indexer.Parameters[1].GetAttributes().ToStrings()); + } + } + + [Fact] + public void Attributes_CallerArgumentExpression_OnImplementation() + { + var source = """ + using System; + using System.Runtime.CompilerServices; + + namespace System.Runtime.CompilerServices + { + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = false)] + public sealed class CallerArgumentExpressionAttribute : Attribute + { + public CallerArgumentExpressionAttribute(string parameterName) + { + ParameterName = parameterName; + } + public string ParameterName { get; } + } + } + + partial class C + { + public static void Main() + { + var c = new C(); + Console.Write(c[GetNumber()]); + } + + public static int GetNumber() => 2; + + public partial int this[int x, string argumentExpression = "0"] { get; } + public partial int this[int x, [CallerArgumentExpression("x")] string argumentExpression] + { + get + { + Console.Write(argumentExpression); + return x; + } + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "02", symbolValidator: verify); + verifier.VerifyDiagnostics( + // (28,37): warning CS8966: The CallerArgumentExpressionAttribute applied to parameter 'argumentExpression' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // public partial int this[int x, [CallerArgumentExpression("x")] string argumentExpression] + Diagnostic(ErrorCode.WRN_CallerArgumentExpressionParamForUnconsumedLocation, "CallerArgumentExpression").WithArguments("argumentExpression").WithLocation(28, 37)); + + void verify(ModuleSymbol module) + { + // https://github.com/dotnet/roslyn/issues/73482 + // The attribute is still written out to metadata even though it is ignored in source. + // We could consider changing this for both properties and methods. + var indexer = (PropertySymbol)module.GlobalNamespace.GetMember("C").Indexers.Single(); + AssertEx.Equal(["""System.Runtime.CompilerServices.CallerArgumentExpressionAttribute("x")"""], indexer.Parameters[1].GetAttributes().ToStrings()); + } + } + + [Fact] + public void CallerMemberName_SetterValueParam_ImplementationPart() + { + // Counterpart to test 'AttributeTests_CallerInfoAttributes.CallerMemberName_SetterValueParam' + // There is no way in C# to call a setter without passing an argument for the value, so the CallerMemberName effectively does nothing. + // It would be reasonable to also warn here about the CallerInfo attribute on implementation, + // but this is a corner case that clearly won't work regardless of which part the attribute is on, so it's not a big deal that the warning is missing. + // Verify that our checks for caller-info attributes on implementation part parameters behave gracefully here. + var source = """ + using System; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + partial class C + { + public static void Main() + { + var c = new C(); + c[1] = "1"; + } + + public partial string this[int x] { set; } + public partial string this[int x] + { + [param: Optional, DefaultParameterValue("0")] + [param: CallerMemberName] + set + { + Console.Write(value); + } + } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "1"); + verifier.VerifyDiagnostics(); + } + + [Theory] + [InlineData("[AllowNull] ", "")] + [InlineData("", "[AllowNull] ")] + public void AllowNull_Property(string defAttrs, string implAttrs) + { + var source = $$""" + #nullable enable + + using System; + using System.Diagnostics.CodeAnalysis; + + partial class C + { + public static void Main() + { + var c = new C(); + try + { + c.Prop = null; // no warning + } + catch + { + Console.Write(1); + } + } + + {{defAttrs}} + public partial string Prop { get; set; } + + {{implAttrs}} + public partial string Prop + { + get => ""; + set + { + value.ToString(); // warning + } + } + } + """; + + var verifier = CompileAndVerify([source, AllowNullAttributeDefinition], expectedOutput: "1"); + verifier.VerifyDiagnostics( + // (30,13): warning CS8602: Dereference of a possibly null reference. + // value.ToString(); // warning + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "value").WithLocation(30, 13)); + } + + [Theory] + [InlineData("[AllowNull] ", "")] + [InlineData("", "[AllowNull] ")] + public void AllowNull_IndexerParam(string defAttrs, string implAttrs) + { + var source = $$""" + #nullable enable + + using System; + using System.Diagnostics.CodeAnalysis; + + partial class C + { + public static void Main() + { + var c = new C(); + try + { + _ = c[null]; // no warning + } + catch + { + Console.Write(1); + } + } + + public partial string this[{{defAttrs}}string s] { get; } + public partial string this[{{implAttrs}}string s] + { + get + { + return s.ToString(); // https://github.com/dotnet/roslyn/issues/73484: missing a warning here + } + } + } + """; + + var verifier = CompileAndVerify([source, AllowNullAttributeDefinition], expectedOutput: "1"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void Obsolete_01() + { + var source = """ + using System; + + partial class C + { + [Obsolete] + public partial int Prop { get; } + + public partial int Prop { get => M(); } // no diagnostic for use of obsolete member + + [Obsolete] + int M() => 1; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void Obsolete_02() + { + var source = """ + using System; + + partial class C + { + public partial int this[int x, int y = VALUE] { get; } // no diagnostic for use of obsolete const + + [Obsolete] + public partial int this[int x, int y] => x; + + [Obsolete] + public const int VALUE = 1; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void Obsolete_03() + { + var source = """ + using System; + + partial class C + { + public partial int this[int x, int y = VALUE] { get; } // 1 + + public partial int this[int x, int y] { [Obsolete] get => x; } + + [Obsolete] + public const int VALUE = 1; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,44): warning CS0612: 'C.VALUE' is obsolete + // public partial int this[int x, int y = VALUE] { get; } // 1 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "VALUE").WithArguments("C.VALUE").WithLocation(5, 44)); + } + + [Theory] + [CombinatorialData] + public void Obsolete_04( + [CombinatorialValues("public partial int Prop { [Obsolete] get; }", "[Obsolete] public partial int Prop { get; }")] + string declPart, + [CombinatorialValues("public partial int Prop { get => M(); }", "public partial int Prop => M();")] + string implPart) + { + // note that one of the combinations here is redundant with Obsolete_01, but that seems fine, + // as a failure in Obsolete_01 may be easier to triage. + var source = $$""" + using System; + + partial class C + { + {{declPart}} + {{implPart}} // no diagnostic for use of obsolete member + + [Obsolete] + int M() => 1; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Theory] + [InlineData("[Obsolete]", "")] + [InlineData("", "[Obsolete]")] + public void Obsolete_05(string defAttrs, string implAttrs) + { + var source = $$""" + using System; + + partial class C + { + {{defAttrs}} + public partial int this[int x] { get; } + + {{implAttrs}} + public partial int this[int x] { get => x; } + } + + class D + { + void M() + { + var c = new C(); + _ = c[1]; // 1 + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (17,13): warning CS0612: 'C.this[int]' is obsolete + // _ = c[1]; // 1 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "c[1]").WithArguments("C.this[int]").WithLocation(17, 13)); + } + + [Theory] + [InlineData("[Obsolete]", "")] + [InlineData("", "[Obsolete]")] + public void Obsolete_06(string defAttrs, string implAttrs) + { + var source = $$""" + using System; + + partial class C + { + public partial int this[int x] { {{defAttrs}} get; } + + public partial int this[int x] { {{implAttrs}} get => x; } + } + + class D + { + void M() + { + var c = new C(); + _ = c[1]; // 1 + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (15,13): warning CS0612: 'C.this[int].get' is obsolete + // _ = c[1]; // 1 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "c[1]").WithArguments("C.this[int].get").WithLocation(15, 13)); + } + + [Fact] + public void UnmanagedCallersOnly() + { + var source = $$""" + using System.Runtime.InteropServices; + + partial class C + { + [UnmanagedCallersOnly] // 1 + public partial int P1 { get; } + public partial int P1 => 1; + + public partial int P2 { get; } + [UnmanagedCallersOnly] // 2 + public partial int P2 => 1; + + public partial int P3 { [UnmanagedCallersOnly] get; } // 3 + public partial int P3 => 1; + + public partial int P4 { get; } + public partial int P4 { [UnmanagedCallersOnly] get => 1; } // 4 + } + """; + + var comp = CreateCompilation([source, UnmanagedCallersOnlyAttributeDefinition]); + comp.VerifyEmitDiagnostics( + // (5,6): error CS0592: Attribute 'UnmanagedCallersOnly' is not valid on this declaration type. It is only valid on 'method' declarations. + // [UnmanagedCallersOnly] // 1 + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "UnmanagedCallersOnly").WithArguments("UnmanagedCallersOnly", "method").WithLocation(5, 6), + // (10,6): error CS0592: Attribute 'UnmanagedCallersOnly' is not valid on this declaration type. It is only valid on 'method' declarations. + // [UnmanagedCallersOnly] // 2 + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "UnmanagedCallersOnly").WithArguments("UnmanagedCallersOnly", "method").WithLocation(10, 6), + // (13,30): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract, non-virtual methods or static local functions. + // public partial int P3 { [UnmanagedCallersOnly] get; } // 3 + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(13, 30), + // (17,30): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract, non-virtual methods or static local functions. + // public partial int P4 { [UnmanagedCallersOnly] get => 1; } // 4 + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(17, 30)); + } + + /// See also + [Fact] + public void IndexerParameterNameDifference() + { + var source = """ + using System; + + partial class C + { + public partial int this[int p1] { get; set; } + public partial int this[int p2] { get => p2; set => p2.ToString(); } + + static void Main() + { + var c = new C(); + Console.Write(c[p1: 1]); + } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "1", symbolValidator: verify, sourceSymbolValidator: verify); + verifier.VerifyDiagnostics( + // (6,24): warning CS9256: Partial property declarations 'int C.this[int p1]' and 'int C.this[int p2]' have signature differences. + // public partial int this[int p2] { get => p2; set { } } + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[int p1]", "int C.this[int p2]").WithLocation(6, 24)); + + void verify(ModuleSymbol module) + { + var indexer = module.GlobalNamespace.GetMember("C").Indexers.Single(); + Assert.Equal("p1", indexer.Parameters.Single().Name); + } + } + + /// See also + [Fact] + public void IndexerParameterNameDifference_Attributes() + { + var source = """ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class Attr(string s) : Attribute { } // 1 (unread parameter) + + partial class C + { + [Attr(nameof(p1))] + [Attr(nameof(p2))] // 2 + public partial int this[int p1] + { + get; + + [param: Attr(nameof(p1))] // 3 + [param: Attr(nameof(p2))] + set; + } + + [Attr(nameof(p1))] + [Attr(nameof(p2))] // 4 + public partial int this[int p2] // 5 + { + get => p2; + + [param: Attr(nameof(p1))] // 6 + [param: Attr(nameof(p2))] + set => p2.ToString(); + } + + [Attr(nameof(p1))] + [Attr(nameof(p2))] // 7 + public partial void M( + [Attr(nameof(p1))] // 8 + [Attr(nameof(p2))] + int p1); + public partial void M( // 9 + [Attr(nameof(p1))] // 10 + [Attr(nameof(p2))] + int p2) { } + } + """; + + var verifier = CreateCompilation(source); + verifier.VerifyDiagnostics( + // (4,26): warning CS9113: Parameter 's' is unread. + // public class Attr(string s) : Attribute { } // 1 (unread parameter) + Diagnostic(ErrorCode.WRN_UnreadPrimaryConstructorParameter, "s").WithArguments("s").WithLocation(4, 26), + // (9,18): error CS0103: The name 'p2' does not exist in the current context + // [Attr(nameof(p2))] // 2 + Diagnostic(ErrorCode.ERR_NameNotInContext, "p2").WithArguments("p2").WithLocation(9, 18), + // (14,29): error CS0103: The name 'p1' does not exist in the current context + // [param: Attr(nameof(p1))] // 3 + Diagnostic(ErrorCode.ERR_NameNotInContext, "p1").WithArguments("p1").WithLocation(14, 29), + // (20,18): error CS0103: The name 'p2' does not exist in the current context + // [Attr(nameof(p2))] // 4 + Diagnostic(ErrorCode.ERR_NameNotInContext, "p2").WithArguments("p2").WithLocation(20, 18), + // (21,24): warning CS9256: Partial property declarations 'int C.this[int p1]' and 'int C.this[int p2]' have signature differences. + // public partial int this[int p2] // 5 + Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[int p1]", "int C.this[int p2]").WithLocation(21, 24), + // (25,29): error CS0103: The name 'p1' does not exist in the current context + // [param: Attr(nameof(p1))] // 6 + Diagnostic(ErrorCode.ERR_NameNotInContext, "p1").WithArguments("p1").WithLocation(25, 29), + // (31,18): error CS0103: The name 'p2' does not exist in the current context + // [Attr(nameof(p2))] // 7 + Diagnostic(ErrorCode.ERR_NameNotInContext, "p2").WithArguments("p2").WithLocation(31, 18), + // (33,22): error CS0103: The name 'p1' does not exist in the current context + // [Attr(nameof(p1))] // 8 + Diagnostic(ErrorCode.ERR_NameNotInContext, "p1").WithArguments("p1").WithLocation(33, 22), + // (36,25): warning CS8826: Partial method declarations 'void C.M(int p1)' and 'void C.M(int p2)' have signature differences. + // public partial void M( // 9 + Diagnostic(ErrorCode.WRN_PartialMethodTypeDifference, "M").WithArguments("void C.M(int p1)", "void C.M(int p2)").WithLocation(36, 25), + // (37,22): error CS0103: The name 'p1' does not exist in the current context + // [Attr(nameof(p1))] // 10 + Diagnostic(ErrorCode.ERR_NameNotInContext, "p1").WithArguments("p1").WithLocation(37, 22)); + } + + [Fact] + public void BindExpressionInPropertyWithoutAccessors() + { + // Exercise an assertion in 'BinderFactoryVisitor.VisitAccessorDeclaration'. + var source = """ + class C + { + int X = 1; + public int P + { + Console.Write(X); + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,9): warning CS0414: The field 'C.X' is assigned but its value is never used + // int X = 1; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "X").WithArguments("C.X").WithLocation(3, 9), + // (4,16): error CS0548: 'C.P': property or indexer must have at least one accessor + // public int P + Diagnostic(ErrorCode.ERR_PropertyWithNoAccessors, "P").WithArguments("C.P").WithLocation(4, 16), + // (6,9): error CS1014: A get or set accessor expected + // Console.Write(X); + Diagnostic(ErrorCode.ERR_GetOrSetExpected, "Console").WithLocation(6, 9), + // (6,16): error CS1014: A get or set accessor expected + // Console.Write(X); + Diagnostic(ErrorCode.ERR_GetOrSetExpected, ".").WithLocation(6, 16), + // (6,17): error CS1014: A get or set accessor expected + // Console.Write(X); + Diagnostic(ErrorCode.ERR_GetOrSetExpected, "Write").WithLocation(6, 17), + // (6,22): error CS1513: } expected + // Console.Write(X); + Diagnostic(ErrorCode.ERR_RbraceExpected, "(").WithLocation(6, 22), + // (6,24): error CS8124: Tuple must contain at least two elements. + // Console.Write(X); + Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(6, 24), + // (6,25): error CS1519: Invalid token ';' in class, record, struct, or interface member declaration + // Console.Write(X); + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(6, 25), + // (8,1): error CS1022: Type or namespace definition, or end-of-file expected + // } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(8, 1)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Where(name => name.ToString() == "X").Last(); + Assert.Equal(SyntaxKind.TupleElement, node.Parent!.Kind()); + var symbolInfo = model.GetSymbolInfo(node); + Assert.Null(symbolInfo.Symbol); + Assert.Empty(symbolInfo.CandidateSymbols); + } + + [Fact] + public void LangVersion_01() + { + var source = """ + partial class C + { + public partial int P { get; set; } + public partial int P { get => 1; set { } } + + public partial int this[int i] { get; } + public partial int this[int i] { get => i; } + } + """; + + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(source, parseOptions: TestOptions.Regular12); + comp.VerifyEmitDiagnostics( + // (3,24): error CS8703: The modifier 'partial' is not valid for this item in C# 12.0. Please use language version 'preview' or greater. + // public partial int P { get; set; } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "P").WithArguments("partial", "12.0", "preview").WithLocation(3, 24), + // (4,24): error CS8703: The modifier 'partial' is not valid for this item in C# 12.0. Please use language version 'preview' or greater. + // public partial int P { get => 1; set { } } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "P").WithArguments("partial", "12.0", "preview").WithLocation(4, 24), + // (6,24): error CS8703: The modifier 'partial' is not valid for this item in C# 12.0. Please use language version 'preview' or greater. + // public partial int this[int i] { get; } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "this").WithArguments("partial", "12.0", "preview").WithLocation(6, 24), + // (7,24): error CS8703: The modifier 'partial' is not valid for this item in C# 12.0. Please use language version 'preview' or greater. + // public partial int this[int i] { get => i; } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "this").WithArguments("partial", "12.0", "preview").WithLocation(7, 24)); + } + + [Fact] + public void GetDeclaredSymbol_01() + { + var source = (""" + partial class C + { + public partial int Prop { get; } + public partial int Prop { get => 1; } + } + """.NormalizeLineEndings(), "Program.cs"); + + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees[0]; + + var model = comp.GetSemanticModel(tree); + var properties = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(2, properties.Length); + + var defSymbol = model.GetDeclaredSymbol(properties[0])!; + Assert.Equal("System.Int32 C.Prop { get; }", defSymbol.ToTestDisplayString()); + + var implSymbol = model.GetDeclaredSymbol(properties[1])!; + Assert.Equal("System.Int32 C.Prop { get; }", implSymbol.ToTestDisplayString()); + + Assert.NotEqual(defSymbol, implSymbol); + Assert.Same(implSymbol, defSymbol.PartialImplementationPart); + Assert.Same(defSymbol, implSymbol.PartialDefinitionPart); + Assert.True(defSymbol.IsPartialDefinition); + Assert.False(implSymbol.IsPartialDefinition); + + // This is consistent with partial methods. + Assert.Equal("SourceFile(Program.cs[43..47))", defSymbol.Locations.Single().ToString()); + Assert.Equal("SourceFile(Program.cs[81..85))", implSymbol.Locations.Single().ToString()); + } + + [Fact] + public void GetDeclaredSymbol_02() + { + // Property contained in generic type. Check original definition and constructed symbols. + var source = (""" + partial class C + { + public partial int Prop { get; } + public partial int Prop { get => 1; } + } + """.NormalizeLineEndings(), "Program.cs"); + + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees[0]; + + var model = comp.GetSemanticModel(tree); + var properties = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(2, properties.Length); + + var defSymbol = model.GetDeclaredSymbol(properties[0])!; + Assert.Equal("System.Int32 C.Prop { get; }", defSymbol.ToTestDisplayString()); + + var implSymbol = model.GetDeclaredSymbol(properties[1])!; + Assert.Equal("System.Int32 C.Prop { get; }", implSymbol.ToTestDisplayString()); + + Assert.NotEqual(defSymbol, implSymbol); + Assert.Same(implSymbol, defSymbol.PartialImplementationPart); + Assert.Same(defSymbol, implSymbol.PartialDefinitionPart); + + Assert.True(defSymbol.IsPartialDefinition); + Assert.False(implSymbol.IsPartialDefinition); + + // This is consistent with partial methods. + Assert.Equal("SourceFile(Program.cs[46..50))", defSymbol.Locations.Single().ToString()); + Assert.Equal("SourceFile(Program.cs[84..88))", implSymbol.Locations.Single().ToString()); + + var intSymbol = comp.GetSpecialType(SpecialType.System_Int32); + var cOfTSymbol = defSymbol.ContainingType!; + var cOfIntSymbol = cOfTSymbol.Construct([intSymbol]); + + // Constructed symbols always return null/false from the partial-related public APIs + var defOfIntSymbol = (IPropertySymbol)cOfIntSymbol.GetMember("Prop"); + Assert.Equal("System.Int32 C.Prop { get; }", defOfIntSymbol.ToTestDisplayString()); + Assert.Null(defOfIntSymbol.PartialImplementationPart); + Assert.False(defOfIntSymbol.IsPartialDefinition); + } + + [Fact] + public void GetDeclaredSymbol_03() + { + // Indexer + var source = (""" + partial class C + { + public partial int this[int i] { get; } + public partial int this[int i] { get => 1; } + } + """.NormalizeLineEndings(), "Program.cs"); + + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees[0]; + + var model = comp.GetSemanticModel(tree); + var indexers = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(2, indexers.Length); + + var defSymbol = model.GetDeclaredSymbol(indexers[0])!; + Assert.Equal("System.Int32 C.this[System.Int32 i] { get; }", defSymbol.ToTestDisplayString()); + + var implSymbol = model.GetDeclaredSymbol(indexers[1])!; + Assert.Equal("System.Int32 C.this[System.Int32 i] { get; }", implSymbol.ToTestDisplayString()); + + Assert.NotEqual(defSymbol, implSymbol); + Assert.Same(implSymbol, defSymbol.PartialImplementationPart); + Assert.Same(defSymbol, implSymbol.PartialDefinitionPart); + + Assert.True(defSymbol.IsPartialDefinition); + Assert.False(implSymbol.IsPartialDefinition); + + // This is consistent with partial methods. + Assert.Equal("SourceFile(Program.cs[43..47))", defSymbol.Locations.Single().ToString()); + Assert.Equal("SourceFile(Program.cs[88..92))", implSymbol.Locations.Single().ToString()); + } + + [Fact] + public void GetDeclaredSymbol_04() + { + // Indexer parameter + var source = (""" + partial class C + { + public partial int this[int i] { get; } + public partial int this[int i] { get => 1; } + } + """.NormalizeLineEndings(), "Program.cs"); + + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees[0]; + + var model = comp.GetSemanticModel(tree); + var parameters = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(2, parameters.Length); + + var defSymbol = model.GetDeclaredSymbol(parameters[0])!; + Assert.Equal("System.Int32 i", defSymbol.ToTestDisplayString()); + + var implSymbol = model.GetDeclaredSymbol(parameters[1])!; + Assert.Equal("System.Int32 i", implSymbol.ToTestDisplayString()); + + Assert.NotEqual(defSymbol, implSymbol); + Assert.Same(implSymbol, ((IPropertySymbol)defSymbol.ContainingSymbol).PartialImplementationPart!.Parameters[0]); + Assert.Same(defSymbol, ((IPropertySymbol)implSymbol.ContainingSymbol).PartialDefinitionPart!.Parameters[0]); + + // This is consistent with partial methods. + Assert.Equal("SourceFile(Program.cs[52..53))", defSymbol.Locations.Single().ToString()); + Assert.Equal("SourceFile(Program.cs[97..98))", implSymbol.Locations.Single().ToString()); + } + } +} diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/CustomModifierCopyTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/CustomModifierCopyTests.cs index b1068ddbae44f..27b140fe6ef97 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/CustomModifierCopyTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/CustomModifierCopyTests.cs @@ -269,6 +269,71 @@ class Derived : MethodCustomModifierCombinations } } + /// + /// Test copying custom modifiers in/on parameters/return types. + /// + [Fact] + public void TestPartialMethodOverrideCombinations() + { + var text = @" +partial class Derived : MethodCustomModifierCombinations +{ + public override partial int[] Method1111(int[] a) { return a; } + public override partial int[] Method1110(int[] a) { return a; } + public override partial int[] Method1101(int[] a) { return a; } + public override partial int[] Method1100(int[] a) { return a; } + public override partial int[] Method1011(int[] a) { return a; } + public override partial int[] Method1010(int[] a) { return a; } + public override partial int[] Method1001(int[] a) { return a; } + public override partial int[] Method1000(int[] a) { return a; } + public override partial int[] Method0111(int[] a) { return a; } + public override partial int[] Method0110(int[] a) { return a; } + public override partial int[] Method0101(int[] a) { return a; } + public override partial int[] Method0100(int[] a) { return a; } + public override partial int[] Method0011(int[] a) { return a; } + public override partial int[] Method0010(int[] a) { return a; } + public override partial int[] Method0001(int[] a) { return a; } + public override partial int[] Method0000(int[] a) { return a; } + + public override partial int[] Method1111(int[] a); + public override partial int[] Method1110(int[] a); + public override partial int[] Method1101(int[] a); + public override partial int[] Method1100(int[] a); + public override partial int[] Method1011(int[] a); + public override partial int[] Method1010(int[] a); + public override partial int[] Method1001(int[] a); + public override partial int[] Method1000(int[] a); + public override partial int[] Method0111(int[] a); + public override partial int[] Method0110(int[] a); + public override partial int[] Method0101(int[] a); + public override partial int[] Method0100(int[] a); + public override partial int[] Method0011(int[] a); + public override partial int[] Method0010(int[] a); + public override partial int[] Method0001(int[] a); + public override partial int[] Method0000(int[] a); +} +"; + + var ilAssemblyReference = TestReferences.SymbolsTests.CustomModifiers.Modifiers.dll; + + var comp = CreateCompilation(text, new MetadataReference[] { ilAssemblyReference }); + var global = comp.GlobalNamespace; + + comp.VerifyEmitDiagnostics(); + + var @class = global.GetMember("Derived"); + + for (int i = 0; i < 0xf; i++) + { + CheckMethodCustomModifiers( + @class.GetMember("Method" + Convert.ToString(i, 2).PadLeft(4, '0')), + inReturnType: (i & 0x8) != 0, + onReturnType: (i & 0x4) != 0, + inParameterType: (i & 0x2) != 0, + onParameterType: (i & 0x1) != 0); + } + } + /// /// Test copying custom modifiers in/on property types. /// @@ -290,7 +355,61 @@ class Derived : PropertyCustomModifierCombinations var comp = CreateCompilation(text, new MetadataReference[] { ilAssemblyReference }); var global = comp.GlobalNamespace; - comp.VerifyDiagnostics(); + comp.VerifyEmitDiagnostics(); + + var @class = global.GetMember("Derived"); + + for (int i = 0; i < 0x4; i++) + { + PropertySymbol property = @class.GetMember("Property" + Convert.ToString(i, 2).PadLeft(2, '0')); + bool inType = (i & 0x2) != 0; + bool onType = (i & 0x1) != 0; + + CheckPropertyCustomModifiers(property, inType, onType); + CheckMethodCustomModifiers( + property.GetMethod, + inReturnType: inType, + onReturnType: onType, + inParameterType: false, + onParameterType: false); + CheckMethodCustomModifiers( + property.SetMethod, + inReturnType: false, + onReturnType: false, + inParameterType: inType, + onParameterType: onType); + } + } + + /// + /// Test copying custom modifiers in/on partial property types. + /// + [Fact] + public void TestPartialPropertyOverrideCombinations() + { + var text = @" +partial class Derived : PropertyCustomModifierCombinations +{ + public override partial int[] Property11 { get; set; } + public override partial int[] Property11 { get => new int[0]; set { } } + + public override partial int[] Property10 { get; set; } + public override partial int[] Property10 { get => new int[0]; set { } } + + public override partial int[] Property01 { get; set; } + public override partial int[] Property01 { get => new int[0]; set { } } + + public override partial int[] Property00 { get; set; } + public override partial int[] Property00 { get => new int[0]; set { } } +} +"; + + var ilAssemblyReference = TestReferences.SymbolsTests.CustomModifiers.Modifiers.dll; + + var comp = CreateCompilation(text, new MetadataReference[] { ilAssemblyReference }); + var global = comp.GlobalNamespace; + + comp.VerifyEmitDiagnostics(); var @class = global.GetMember("Derived"); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs index 71c3daabe6c32..aa88b97f8efa6 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs @@ -2458,12 +2458,12 @@ partial void M() {} var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (4,18): error CS0751: A partial method must be declared within a partial type + // (4,18): error CS0751: A partial member must be declared within a partial type // partial void M(); - Diagnostic(ErrorCode.ERR_PartialMethodOnlyInPartialClass, "M").WithLocation(4, 18), - // (5,18): error CS0751: A partial method must be declared within a partial type + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "M").WithLocation(4, 18), + // (5,18): error CS0751: A partial member must be declared within a partial type // partial void M() {} - Diagnostic(ErrorCode.ERR_PartialMethodOnlyInPartialClass, "M").WithLocation(5, 18) + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "M").WithLocation(5, 18) ); var m = comp.GetMember("C.M").GetPublicSymbol(); Assert.True(m.IsPartialDefinition); @@ -2551,5 +2551,38 @@ public partial class C Assert.Null(partialDef.PartialDefinitionPart); Assert.Null(partialImpl.PartialImplementationPart); } + + [Fact] + public void IsPartialDefinition_Constructed() + { + var source = @" +public partial class C +{ + public partial void M(); + public partial void M() { } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var syntax = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(syntax); + + var type = syntax.GetRoot().DescendantNodes().OfType().Single(); + var methods = syntax.GetRoot().DescendantNodes().OfType().ToArray(); + + var classC = model.GetDeclaredSymbol(type); + var partialDef = model.GetDeclaredSymbol(methods[0]); + var partialDefConstructed = partialDef.Construct(classC); // M() + + Assert.True(partialDef.IsPartialDefinition); + Assert.False(partialDefConstructed.IsPartialDefinition); + + var partialImpl = model.GetDeclaredSymbol(methods[1]); + var partialImplConstructed = partialImpl.Construct(classC); // M() + + Assert.False(partialImpl.IsPartialDefinition); + Assert.False(partialImplConstructed.IsPartialDefinition); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index 34d5a4d767262..0dde9e5f3fb67 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -1145,9 +1145,9 @@ partial interface I1 targetFramework: _supportingFramework); compilation1.VerifyDiagnostics( - // (4,34): error CS0750: A partial method cannot have the 'abstract' modifier + // (4,34): error CS0750: A partial member cannot have the 'abstract' modifier // abstract static partial void M01(); - Diagnostic(ErrorCode.ERR_PartialMethodInvalidModifier, "M01").WithLocation(4, 34) + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "M01").WithLocation(4, 34) ); var i1 = compilation1.GetTypeByMetadataName("I1"); @@ -1260,15 +1260,15 @@ abstract static partial void M01() {} targetFramework: _supportingFramework); compilation1.VerifyDiagnostics( - // (4,34): error CS0750: A partial method cannot have the 'abstract' modifier + // (4,34): error CS0750: A partial member cannot have the 'abstract' modifier // abstract static partial void M01(); - Diagnostic(ErrorCode.ERR_PartialMethodInvalidModifier, "M01").WithLocation(4, 34), + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "M01").WithLocation(4, 34), // (8,34): error CS0500: 'I1.M01()' cannot declare a body because it is marked abstract // abstract static partial void M01() {} Diagnostic(ErrorCode.ERR_AbstractHasBody, "M01").WithArguments("I1.M01()").WithLocation(8, 34), - // (8,34): error CS0750: A partial method cannot have the 'abstract' modifier + // (8,34): error CS0750: A partial member cannot have the 'abstract' modifier // abstract static partial void M01() {} - Diagnostic(ErrorCode.ERR_PartialMethodInvalidModifier, "M01").WithLocation(8, 34) + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "M01").WithLocation(8, 34) ); var i1 = compilation1.GetTypeByMetadataName("I1"); @@ -1372,9 +1372,9 @@ static partial void M01() {} targetFramework: _supportingFramework); compilation1.VerifyDiagnostics( - // (4,34): error CS0750: A partial method cannot have the 'abstract' modifier + // (4,34): error CS0750: A partial member cannot have the 'abstract' modifier // abstract static partial void M01(); - Diagnostic(ErrorCode.ERR_PartialMethodInvalidModifier, "M01").WithLocation(4, 34) + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "M01").WithLocation(4, 34) ); var i1 = compilation1.GetTypeByMetadataName("I1"); @@ -1427,12 +1427,12 @@ static partial void M01() {} targetFramework: _supportingFramework); compilation1.VerifyDiagnostics( - // (8,25): error CS8799: Both partial method declarations must have identical accessibility modifiers. + // (8,25): error CS8799: Both partial member declarations must have identical accessibility modifiers. // static partial void M01() {} - Diagnostic(ErrorCode.ERR_PartialMethodAccessibilityDifference, "M01").WithLocation(8, 25), - // (8,25): error CS8800: Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "M01").WithLocation(8, 25), + // (8,25): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. // static partial void M01() {} - Diagnostic(ErrorCode.ERR_PartialMethodExtendedModDifference, "M01").WithLocation(8, 25) + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "M01").WithLocation(8, 25) ); var i1 = compilation1.GetTypeByMetadataName("I1"); @@ -1485,9 +1485,9 @@ public static partial void M01() {} targetFramework: _supportingFramework); compilation1.VerifyDiagnostics( - // (8,32): error CS8800: Both partial method declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // (8,32): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. // public static partial void M01() {} - Diagnostic(ErrorCode.ERR_PartialMethodExtendedModDifference, "M01").WithLocation(8, 32) + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "M01").WithLocation(8, 32) ); var i1 = compilation1.GetTypeByMetadataName("I1"); @@ -1543,9 +1543,9 @@ abstract static partial void M01() {} // (8,34): error CS0500: 'I1.M01()' cannot declare a body because it is marked abstract // abstract static partial void M01() {} Diagnostic(ErrorCode.ERR_AbstractHasBody, "M01").WithArguments("I1.M01()").WithLocation(8, 34), - // (8,34): error CS0750: A partial method cannot have the 'abstract' modifier + // (8,34): error CS0750: A partial member cannot have the 'abstract' modifier // abstract static partial void M01() {} - Diagnostic(ErrorCode.ERR_PartialMethodInvalidModifier, "M01").WithLocation(8, 34) + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "M01").WithLocation(8, 34) ); var i1 = compilation1.GetTypeByMetadataName("I1"); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs index 99fb35186708f..97aca352abf83 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs @@ -13947,7 +13947,7 @@ public void CS0739ERR_DuplicateTypeForwarder_3() } [Fact] - public void CS0750ERR_PartialMethodInvalidModifier() + public void CS0750ERR_PartialMemberCannotBeAbstract() { var text = @" @@ -14004,9 +14004,9 @@ public static int Main() // (23,26): error CS8796: Partial method 'C.PartE()' must have accessibility modifiers because it has a 'virtual', 'override', 'sealed', 'new', or 'extern' modifier. // virtual partial void PartE(); Diagnostic(ErrorCode.ERR_PartialMethodWithExtendedModMustHaveAccessMods, "PartE").WithArguments("C.PartE()").WithLocation(23, 26), - // (24,27): error CS0750: A partial method cannot have the 'abstract' modifier + // (24,27): error CS0750: A partial member cannot have the 'abstract' modifier // abstract partial void PartF(); - Diagnostic(ErrorCode.ERR_PartialMethodInvalidModifier, "PartF").WithLocation(24, 27), + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "PartF").WithLocation(24, 27), // (25,27): error CS8796: Partial method 'C.PartG()' must have accessibility modifiers because it has a 'virtual', 'override', 'sealed', 'new', or 'extern' modifier. // override partial void PartG(); Diagnostic(ErrorCode.ERR_PartialMethodWithExtendedModMustHaveAccessMods, "PartG").WithArguments("C.PartG()").WithLocation(25, 27), @@ -14031,7 +14031,7 @@ public static int Main() } [Fact] - public void CS0751ERR_PartialMethodOnlyInPartialClass() + public void CS0751ERR_PartialMemberOnlyInPartialClass() { var text = @" @@ -14045,7 +14045,7 @@ public static int Main() } "; var comp = DiagnosticsUtils.VerifyErrorsAndGetCompilationWithMscorlib(text, - new ErrorDescription { Code = (int)ErrorCode.ERR_PartialMethodOnlyInPartialClass, Line = 5, Column = 18 }); + new ErrorDescription { Code = (int)ErrorCode.ERR_PartialMemberOnlyInPartialClass, Line = 5, Column = 18 }); } [Fact] @@ -14084,22 +14084,22 @@ partial int this[int index] "; var comp = CreateCompilation(text); comp.VerifyDiagnostics( - // (4,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (4,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. // partial int f; Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(4, 5), - // (5,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (5,20): error CS9249: Partial property 'C.P' must have a definition part. // partial object P { get { return null; } } - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(5, 5), - // (6,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "P").WithArguments("C.P").WithLocation(5, 20), + // (6,17): error CS9249: Partial property 'C.this[int]' must have a definition part. // partial int this[int index] - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(6, 5), + Diagnostic(ErrorCode.ERR_PartialPropertyMissingDefinition, "this").WithArguments("C.this[int]").WithLocation(6, 17), // (4,17): warning CS0169: The field 'C.f' is never used // partial int f; Diagnostic(ErrorCode.WRN_UnreferencedField, "f").WithArguments("C.f").WithLocation(4, 17)); } [Fact] - public void CS0754ERR_PartialMethodNotExplicit() + public void CS0754ERR_PartialMemberNotExplicit() { var text = @" public interface IF @@ -14116,7 +14116,7 @@ public static int Main() } "; var comp = DiagnosticsUtils.VerifyErrorsAndGetCompilationWithMscorlib(text, - new ErrorDescription { Code = (int)ErrorCode.ERR_PartialMethodNotExplicit, Line = 8, Column = 21 }); + new ErrorDescription { Code = (int)ErrorCode.ERR_PartialMemberNotExplicit, Line = 8, Column = 21 }); } [Fact] @@ -14184,7 +14184,7 @@ public static int Main() } [Fact] - public void CS0758ERR_PartialMethodParamsDifference() + public void CS0758ERR_PartialMemberParamsDifference() { var text = @"partial class C @@ -14196,9 +14196,9 @@ partial void M2(int n, params object[] args) { } }"; CreateCompilation(text).VerifyDiagnostics( // (4,18): error CS0758: Both partial method declarations must use a parameter array or neither may use a parameter array - Diagnostic(ErrorCode.ERR_PartialMethodParamsDifference, "M1").WithLocation(4, 18), + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "M1").WithLocation(4, 18), // (5,18): error CS0758: Both partial method declarations must use a parameter array or neither may use a parameter array - Diagnostic(ErrorCode.ERR_PartialMethodParamsDifference, "M2").WithLocation(5, 18)); + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "M2").WithLocation(5, 18)); } [Fact] @@ -14418,14 +14418,14 @@ static partial void M2() { } partial void M2(); }"; CreateCompilation(text).VerifyDiagnostics( - // (4,18): error CS07: Both partial method declarations must be static or neither may be static - Diagnostic(ErrorCode.ERR_PartialMethodStaticDifference, "M1").WithLocation(4, 18), - // (5,25): error CS07: Both partial method declarations must be static or neither may be static - Diagnostic(ErrorCode.ERR_PartialMethodStaticDifference, "M2").WithLocation(5, 25)); + // (4,18): error CS07: Both partial member declarations must be static or neither may be static + Diagnostic(ErrorCode.ERR_PartialMemberStaticDifference, "M1").WithLocation(4, 18), + // (5,25): error CS07: Both partial member declarations must be static or neither may be static + Diagnostic(ErrorCode.ERR_PartialMemberStaticDifference, "M2").WithLocation(5, 25)); } [Fact] - public void CS0764ERR_PartialMethodUnsafeDifference() + public void CS0764ERR_PartialMemberUnsafeDifference() { var text = @"partial class C @@ -14436,10 +14436,10 @@ unsafe partial void M2() { } partial void M2(); }"; CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (4,18): error CS0764: Both partial method declarations must be unsafe or neither may be unsafe - Diagnostic(ErrorCode.ERR_PartialMethodUnsafeDifference, "M1").WithLocation(4, 18), - // (5,25): error CS0764: Both partial method declarations must be unsafe or neither may be unsafe - Diagnostic(ErrorCode.ERR_PartialMethodUnsafeDifference, "M2").WithLocation(5, 25)); + // (4,18): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe + Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "M1").WithLocation(4, 18), + // (5,25): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe + Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "M2").WithLocation(5, 25)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index 9f5490d05adac..093d3b02dcc72 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -323,6 +323,7 @@ public void WarningLevel_2() case ErrorCode.WRN_RefReadonlyParameterDefaultValue: case ErrorCode.WRN_Experimental: case ErrorCode.WRN_ConvertingLock: + case ErrorCode.WRN_PartialPropertySignatureDifference: Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode)); break; case ErrorCode.WRN_MainIgnored: diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs index 51342e36cb491..0296a9fd5be7b 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs @@ -5629,9 +5629,9 @@ partial int Goo() { } // (4,17): error CS0759: No defining declaration found for implementing declaration of partial method 'C.Goo()' // partial int Goo() { } Diagnostic(ErrorCode.ERR_PartialMethodMustHaveLatent, "Goo").WithArguments("C.Goo()").WithLocation(4, 17), - // (4,17): error CS0751: A partial method must be declared within a partial type + // (4,17): error CS0751: A partial member must be declared within a partial type // partial int Goo() { } - Diagnostic(ErrorCode.ERR_PartialMethodOnlyInPartialClass, "Goo").WithLocation(4, 17), + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "Goo").WithLocation(4, 17), // (4,17): error CS8796: Partial method 'C.Goo()' must have accessibility modifiers because it has a non-void return type. // partial int Goo() { } Diagnostic(ErrorCode.ERR_PartialMethodWithNonVoidReturnMustHaveAccessMods, "Goo").WithArguments("C.Goo()").WithLocation(4, 17), @@ -5645,9 +5645,9 @@ partial int Goo() { } // (4,17): error CS0759: No defining declaration found for implementing declaration of partial method 'C.Goo()' // partial int Goo() { } Diagnostic(ErrorCode.ERR_PartialMethodMustHaveLatent, "Goo").WithArguments("C.Goo()").WithLocation(4, 17), - // (4,17): error CS0751: A partial method must be declared within a partial type + // (4,17): error CS0751: A partial member must be declared within a partial type // partial int Goo() { } - Diagnostic(ErrorCode.ERR_PartialMethodOnlyInPartialClass, "Goo").WithLocation(4, 17), + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "Goo").WithLocation(4, 17), // (4,17): error CS8796: Partial method 'C.Goo()' must have accessibility modifiers because it has a non-void return type. // partial int Goo() { } Diagnostic(ErrorCode.ERR_PartialMethodWithNonVoidReturnMustHaveAccessMods, "Goo").WithArguments("C.Goo()").WithLocation(4, 17), diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs index db54971ea9269..40acf22276ee5 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs @@ -204,6 +204,7 @@ void processMembers(IEnumerable members) memberSet.Add(member); // Ensure that we include symbols for both parts of partial methods. + // https://github.com/dotnet/roslyn/issues/73772: also cascade to partial property implementation part if (member is IMethodSymbol method && !(method.PartialImplementationPart is null)) { diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs index 2f8d1fa1fd9f7..a23c7ef140d3f 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs @@ -1118,6 +1118,10 @@ static bool shouldIncludeSymbol(ISymbolInternal symbol, SyntaxTree tree, Cancell } } + // https://github.com/dotnet/roslyn/issues/73772: should we also check IPropertySymbol? + // there is no interface IPropertySymbolInternal + // where are tests for this? + return false; } } diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolChanges.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolChanges.cs index 88c563a21e702..89c2cf1fd101a 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolChanges.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolChanges.cs @@ -414,6 +414,7 @@ private void CalculateChanges( // Partial methods are supplied as implementations but recorded // internally as definitions since definitions are used in emit. + // https://github.com/dotnet/roslyn/issues/73772: should we also make sure to use the definition for a partial property? if (newMember.Kind == SymbolKind.Method) { var newMethod = (IMethodSymbolInternal)newMember; diff --git a/src/Compilers/Core/Portable/Emit/SemanticEdit.cs b/src/Compilers/Core/Portable/Emit/SemanticEdit.cs index be2f00be20c11..14cec150ebc50 100644 --- a/src/Compilers/Core/Portable/Emit/SemanticEdit.cs +++ b/src/Compilers/Core/Portable/Emit/SemanticEdit.cs @@ -142,6 +142,7 @@ public SemanticEdit(SemanticEditKind kind, ISymbol? oldSymbol, ISymbol? newSymbo } } + // https://github.com/dotnet/roslyn/issues/73772: should we also do this check for partial properties? if (oldSymbol is IMethodSymbol { PartialImplementationPart: not null }) { throw new ArgumentException("Partial method implementation required", nameof(oldSymbol)); diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 2168ea24ba522..0f032dbf8c7a9 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -1,5 +1,8 @@ Microsoft.CodeAnalysis.IParameterSymbol.IsParamsCollection.get -> bool Microsoft.CodeAnalysis.IParameterSymbol.IsParamsArray.get -> bool +Microsoft.CodeAnalysis.IPropertySymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IPropertySymbol? +Microsoft.CodeAnalysis.IPropertySymbol.PartialImplementationPart.get -> Microsoft.CodeAnalysis.IPropertySymbol? +Microsoft.CodeAnalysis.IPropertySymbol.IsPartialDefinition.get -> bool Microsoft.CodeAnalysis.Operations.ArgumentKind.ParamCollection = 4 -> Microsoft.CodeAnalysis.Operations.ArgumentKind Microsoft.CodeAnalysis.Diagnostics.SuppressionInfo.ProgrammaticSuppressions.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Emit.InstrumentationKind.ModuleCancellation = 3 -> Microsoft.CodeAnalysis.Emit.InstrumentationKind diff --git a/src/Compilers/Core/Portable/Symbols/IPropertySymbol.cs b/src/Compilers/Core/Portable/Symbols/IPropertySymbol.cs index 177e264f5aaa6..12735dd442946 100644 --- a/src/Compilers/Core/Portable/Symbols/IPropertySymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/IPropertySymbol.cs @@ -110,5 +110,22 @@ public interface IPropertySymbol : ISymbol /// The list of custom modifiers, if any, associated with the type of the property. /// ImmutableArray TypeCustomModifiers { get; } + + /// + /// If this is a partial property implementation part, returns the corresponding + /// definition part. Otherwise null. + /// + IPropertySymbol? PartialDefinitionPart { get; } + + /// + /// If this is a partial property definition part, returns the corresponding + /// implementation part. Otherwise null. + /// + IPropertySymbol? PartialImplementationPart { get; } + + /// + /// Returns true if this is a partial definition part. Otherwise false. + /// + bool IsPartialDefinition { get; } } } diff --git a/src/Compilers/Test/Core/Assert/AssertEx.cs b/src/Compilers/Test/Core/Assert/AssertEx.cs index f254328b52e3a..244b92a4d6aa9 100644 --- a/src/Compilers/Test/Core/Assert/AssertEx.cs +++ b/src/Compilers/Test/Core/Assert/AssertEx.cs @@ -872,11 +872,19 @@ private sealed class LineComparer : IEqualityComparer public int GetHashCode(string str) => str.Trim().GetHashCode(); } - public static void AssertLinesEqual(string expected, string actual, string message, string expectedValueSourcePath, int expectedValueSourceLine, bool escapeQuotes) - { - IEnumerable GetLines(string str) => + private static IEnumerable GetLines(string str) => str.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + public static void AssertLinesEqual(string expected, string actual) + { + AssertEx.Equal( + GetLines(expected), + GetLines(actual), + comparer: LineComparer.Instance); + } + + public static void AssertLinesEqual(string expected, string actual, string message, string expectedValueSourcePath, int expectedValueSourceLine, bool escapeQuotes) + { AssertEx.Equal( GetLines(expected), GetLines(actual), diff --git a/src/Compilers/Test/Utilities/CSharp/Extensions.cs b/src/Compilers/Test/Utilities/CSharp/Extensions.cs index 76fa6c8d66365..6b4e838a76726 100644 --- a/src/Compilers/Test/Utilities/CSharp/Extensions.cs +++ b/src/Compilers/Test/Utilities/CSharp/Extensions.cs @@ -416,6 +416,9 @@ public static void VerifyNamedArgumentValue(this CSharpAttributeData attr, in Assert.True(IsEqual(arg, v)); } + public static ImmutableArray ToStrings(this ImmutableArray attributes) + => attributes.SelectAsArray(a => a.ToString()); + internal static bool IsEqual(TypedConstant arg, object expected) { switch (arg.Kind) diff --git a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs index fed48bba64878..5801478f74e88 100644 --- a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs +++ b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs @@ -42,6 +42,7 @@ public static class TestOptions public static readonly CSharpParseOptions Regular11 = Regular.WithLanguageVersion(LanguageVersion.CSharp11); public static readonly CSharpParseOptions Regular12 = Regular.WithLanguageVersion(LanguageVersion.CSharp12); public static readonly CSharpParseOptions RegularWithDocumentationComments = Regular.WithDocumentationMode(DocumentationMode.Diagnose); + public static readonly CSharpParseOptions RegularPreviewWithDocumentationComments = RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose); public static readonly CSharpParseOptions RegularWithLegacyStrongName = Regular.WithFeature("UseLegacyStrongNameProvider"); public static readonly CSharpParseOptions WithoutImprovedOverloadCandidates = Regular.WithLanguageVersion(MessageID.IDS_FeatureImprovedOverloadCandidates.RequiredVersion() - 1); public static readonly CSharpParseOptions WithCovariantReturns = Regular.WithLanguageVersion(MessageID.IDS_FeatureCovariantReturnsForOverrides.RequiredVersion()); diff --git a/src/Compilers/VisualBasic/Portable/Symbols/PropertySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/PropertySymbol.vb index 22cdc43eb0c50..c3e8138ec3749 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/PropertySymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/PropertySymbol.vb @@ -621,6 +621,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Private ReadOnly Property IPropertySymbol_PartialDefinitionPart As IPropertySymbol Implements IPropertySymbol.PartialDefinitionPart + Get + ' Feature not supported in VB + Return Nothing + End Get + End Property + + Private ReadOnly Property IPropertySymbol_PartialImplementationPart As IPropertySymbol Implements IPropertySymbol.PartialImplementationPart + Get + ' Feature not supported in VB + Return Nothing + End Get + End Property + + Private ReadOnly Property IPropertySymbol_IsPartialDefinition As Boolean Implements IPropertySymbol.IsPartialDefinition + Get + ' Feature not supported in VB + Return False + End Get + End Property + Public Overrides Sub Accept(visitor As SymbolVisitor) visitor.VisitProperty(Me) End Sub diff --git a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs index d3048b9827ae7..429d27899a731 100644 --- a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs +++ b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs @@ -606,6 +606,20 @@ public async Task FindPartialMethods(TestHost testHost, Composition composition) }); } + [Theory, CombinatorialData] + public async Task FindPartialProperties(TestHost testHost, Composition composition) + { + await TestAsync(testHost, composition, "partial class Goo { partial int Prop { get; set; } } partial class Goo { partial int Prop { get => 1; set { } } }", async w => + { + var expecteditem1 = new NavigateToItem("Prop", NavigateToItemKind.Property, "csharp", null, null, s_emptyExactPatternMatch, null); + var expecteditems = new List { expecteditem1, expecteditem1 }; + + var items = await _aggregator.GetItemsAsync("Prop"); + + VerifyNavigateToResultItems(expecteditems, items); + }); + } + [Theory, CombinatorialData] public async Task FindPartialMethodDefinitionOnly(TestHost testHost, Composition composition) { diff --git a/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyCompilationsTests.cs b/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyCompilationsTests.cs index 2864f54807b68..b9f1ec2e3ae6c 100644 --- a/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyCompilationsTests.cs +++ b/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyCompilationsTests.cs @@ -159,6 +159,35 @@ public partial void M() { } var definition = type.GetMembers("M").First() as IMethodSymbol; var implementation = definition.PartialImplementationPart; + // Assert that both the definition and implementation resolve back to themselves + // https://github.com/dotnet/roslyn/issues/73772: add a similar test for properties + Assert.Equal(definition, ResolveSymbol(definition, comp, SymbolKeyComparison.None)); + Assert.Equal(implementation, ResolveSymbol(implementation, comp, SymbolKeyComparison.None)); + } + + [Fact] + public void ExtendedPartialPropertyDefinitionAndImplementationResolveCorrectly() + { + var src = """ + using System; + namespace NS + { + public partial class C1 + { + private int x; + public partial int Prop { get; set; } + public partial int Prop { get => x; set => x = value; } + } + } + """; + + var comp = (Compilation)CreateCompilation(src, assemblyName: "Test"); + + var ns = comp.SourceModule.GlobalNamespace.GetMembers("NS").Single() as INamespaceSymbol; + var type = ns.GetTypeMembers("C1").FirstOrDefault(); + var definition = type.GetMembers("Prop").First() as IPropertySymbol; + var implementation = definition.PartialImplementationPart; + // Assert that both the definition and implementation resolve back to themselves Assert.Equal(definition, ResolveSymbol(definition, comp, SymbolKeyComparison.None)); Assert.Equal(implementation, ResolveSymbol(implementation, comp, SymbolKeyComparison.None)); diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb index 31e2400d4036f..0a9a3db21b813 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb @@ -1558,5 +1558,32 @@ class P Await TestAPIAndFeature(input, kind, host) End Function + + + + Public Async Function TestCSharp_PartialProperty1(kind As TestKind, host As TestHost) As Task + Dim input = + + + +using System; +namespace ConsoleApplication22 +{ + class Program + { + public static partial int {|Definition:Prop|} { get; } + public static partial int {|Definition:P$$rop|} => 1; + + static void Main(string[] args) + { + int temp = Program.[|Prop|]; + } + } +} + + + + Await TestAPIAndFeature(input, kind, host) + End Function End Class End Namespace diff --git a/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb b/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb index af34b5fed0fbe..0c890ed6a17b2 100644 --- a/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb +++ b/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb @@ -277,6 +277,39 @@ class Program Await TestAsync(workspace) End Function + + Public Async Function TestCSharpGotoDefinitionPartialProperty() As Task + Dim workspace = + + + + partial class Test + { + public partial int Prop { get; set; } + } + + + partial class Test + { + void Goo() + { + var t = new Test(); + int i = t.Prop$$; + } + + public partial void [|Prop|] + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + } + + + + + Await TestAsync(workspace) + End Function + Public Async Function TestCSharpGotoDefinitionOnMethodCall1() As Task Dim workspace = diff --git a/src/Features/CSharp/Portable/NavigationBar/CSharpNavigationBarItemService.cs b/src/Features/CSharp/Portable/NavigationBar/CSharpNavigationBarItemService.cs index df3ba6343c311..bd8d73f27781c 100644 --- a/src/Features/CSharp/Portable/NavigationBar/CSharpNavigationBarItemService.cs +++ b/src/Features/CSharp/Portable/NavigationBar/CSharpNavigationBarItemService.cs @@ -73,18 +73,27 @@ private static ImmutableArray GetMembersInTypes( continue; } - var method = member as IMethodSymbol; - if (method != null && method.PartialImplementationPart != null) + if (member is IMethodSymbol { PartialImplementationPart: { } } methodSymbol) { - memberItems.AddIfNotNull(CreateItemForMember(solution, method, tree, cancellationToken)); - memberItems.AddIfNotNull(CreateItemForMember(solution, method.PartialImplementationPart, tree, cancellationToken)); + memberItems.AddIfNotNull(CreateItemForMember(solution, methodSymbol, tree, cancellationToken)); + memberItems.AddIfNotNull(CreateItemForMember(solution, methodSymbol.PartialImplementationPart, tree, cancellationToken)); } - else + else if (member is IPropertySymbol { PartialImplementationPart: { } } propertySymbol) + { + memberItems.AddIfNotNull(CreateItemForMember(solution, propertySymbol, tree, cancellationToken)); + memberItems.AddIfNotNull(CreateItemForMember(solution, propertySymbol.PartialImplementationPart, tree, cancellationToken)); + } + else if (member is IMethodSymbol or IPropertySymbol) { - Debug.Assert(method == null || method.PartialDefinitionPart == null, "NavBar expected GetMembers to return partial method definition parts but the implementation part was returned."); + Debug.Assert(member is IMethodSymbol { PartialDefinitionPart: null } or IPropertySymbol { PartialDefinitionPart: null }, + $"NavBar expected GetMembers to return partial method/property definition parts but the implementation part was returned."); memberItems.AddIfNotNull(CreateItemForMember(solution, member, tree, cancellationToken)); } + else + { + memberItems.AddIfNotNull(CreateItemForMember(solution, member, tree, cancellationToken)); + } } memberItems.Sort((x, y) => diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractPartialMethodCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractPartialMethodCompletionProvider.cs index 11ef0079da66c..ddf3d0b4c1397 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractPartialMethodCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractPartialMethodCompletionProvider.cs @@ -90,6 +90,7 @@ protected override async Task GenerateMemberAsync(ISymbol member, IName if (enclosingSymbol.TypeKind is not (TypeKind.Struct or TypeKind.Class)) return null; + // https://github.com/dotnet/roslyn/issues/73772: should we also add an `AbstractPartialPropertiesProvider`? var symbols = semanticModel.LookupSymbols(position, container: enclosingSymbol) .OfType() .Where(m => IsPartial(m) && m.PartialImplementationPart == null); diff --git a/src/Features/Core/Portable/Debugging/AbstractBreakpointResolver.cs b/src/Features/Core/Portable/Debugging/AbstractBreakpointResolver.cs index 3d03ff39d94a1..75229c8f43e08 100644 --- a/src/Features/Core/Portable/Debugging/AbstractBreakpointResolver.cs +++ b/src/Features/Core/Portable/Debugging/AbstractBreakpointResolver.cs @@ -211,7 +211,7 @@ private IEnumerable FindMembers(IEnumerable con private IEnumerable FindMembers(IEnumerable types, NameAndArity nameAndArity) { // Get the matching members from all types (including constructors and explicit interface - // implementations). If there is a partial method, prefer returning the implementation over + // implementations). If there is a partial method/property, prefer returning the implementation over // the definition (since the definition will not be a candidate for setting a breakpoint). var members = types.SelectMany(t => GetMembers(t, nameAndArity.Name)) .Select(s => GetPartialImplementationPartOrNull(s) ?? s); @@ -227,8 +227,12 @@ private async Task> GetAllTypesAsync(CancellationT return namespaces.GetAllTypes(cancellationToken); } - private static IMethodSymbol GetPartialImplementationPartOrNull(ISymbol symbol) - => (symbol.Kind == SymbolKind.Method) ? ((IMethodSymbol)symbol).PartialImplementationPart : null; + private static ISymbol GetPartialImplementationPartOrNull(ISymbol symbol) => symbol.Kind switch + { + SymbolKind.Method => ((IMethodSymbol)symbol).PartialImplementationPart, + SymbolKind.Property => ((IPropertySymbol)symbol).PartialImplementationPart, + _ => null + }; /// /// Is this method or property a valid place to set a breakpoint and does it match the expected parameter count? diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index e8d2fd1d3de3c..769cf5bc507d0 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -3185,6 +3185,7 @@ IFieldSymbol or // The partial type needs to be specified in the following cases: // 1) partial method is updated (in case both implementation and definition are updated) // 2) partial type is updated + // https://github.com/dotnet/roslyn/issues/73772: do we also need to check IPropertySymbol.PartialDefinitionPart here? var partialType = editKind == SemanticEditKind.Update && symbol is IMethodSymbol { PartialDefinitionPart: not null } ? symbolCache.GetKey(symbol.ContainingType, cancellationToken) : IsPartialTypeEdit(oldSymbol, newSymbol, oldTree, newTree) @@ -3354,6 +3355,7 @@ bool PreprocessSymbolEdit(ref ISymbol? oldSymbol, ref ISymbol? newSymbol) var result = symbolKey.Resolve(compilation, ignoreAssemblyKey: true, cancellationToken).Symbol; // If we were looking for a definition and an implementation is returned the definition does not exist. + // https://github.com/dotnet/roslyn/issues/73772: Does PartialDefinitionPart also need to be checked here? return symbol is IMethodSymbol { PartialDefinitionPart: not null } && result is IMethodSymbol { IsPartialDefinition: true } ? null : result; } @@ -3666,6 +3668,7 @@ void AddDelete(ISymbol? symbol) if (symbol is null) return; + // https://github.com/dotnet/roslyn/issues/73772 Debug.Assert(symbol is not IMethodSymbol { IsPartialDefinition: true }); var partialType = symbol is IMethodSymbol { PartialDefinitionPart: not null } ? SymbolKey.Create(symbol.ContainingType, cancellationToken) : (SymbolKey?)null; diff --git a/src/Features/Core/Portable/EditAndContinue/Utilities/Extensions.cs b/src/Features/Core/Portable/EditAndContinue/Utilities/Extensions.cs index 3123b0a18407f..0d2226145bc2a 100644 --- a/src/Features/Core/Portable/EditAndContinue/Utilities/Extensions.cs +++ b/src/Features/Core/Portable/EditAndContinue/Utilities/Extensions.cs @@ -183,6 +183,7 @@ private static bool ParsePrimaryParameterBackingFieldName(string fieldName, [Not => (IMethodSymbol?)constructor.ContainingType.GetMembers(WellKnownMemberNames.DeconstructMethodName).FirstOrDefault( static (symbol, constructor) => symbol is IMethodSymbol method && HasDeconstructorSignature(method, constructor), constructor)?.PartialAsImplementation(); + // https://github.com/dotnet/roslyn/issues/73772: does this helper need to be updated to use IPropertySymbol.PartialImplementationPart? public static ISymbol PartialAsImplementation(this ISymbol symbol) => symbol is IMethodSymbol { PartialImplementationPart: { } impl } ? impl : symbol; diff --git a/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs b/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs index fe8ea325bc736..3dcdb191550a0 100644 --- a/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs +++ b/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs @@ -45,10 +45,9 @@ internal static class GoToDefinitionFeatureHelpers symbol = definition ?? symbol; - // If it is a partial method declaration with no body, choose to go to the implementation - // that has a method body. - if (symbol is IMethodSymbol method) - symbol = method.PartialImplementationPart ?? symbol; + // If symbol has a partial implementation part, prefer to go to it, since that is where the body is. + symbol = (symbol as IMethodSymbol)?.PartialImplementationPart ?? symbol; + symbol = (symbol as IPropertySymbol)?.PartialImplementationPart ?? symbol; return symbol; } diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedPropertySymbol.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedPropertySymbol.cs index b29838a199da9..e2cfb6aefba23 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedPropertySymbol.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedPropertySymbol.cs @@ -66,5 +66,11 @@ public ImmutableArray ExplicitInterfaceImplementations return this; } } + + public IPropertySymbol PartialDefinitionPart => _symbol.PartialDefinitionPart; + + public IPropertySymbol PartialImplementationPart => _symbol.PartialImplementationPart; + + public bool IsPartialDefinition => _symbol.IsPartialDefinition; } } diff --git a/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestVerifier.cs b/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestVerifier.cs index b4898d7604f13..cf39e98564710 100644 --- a/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestVerifier.cs +++ b/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestVerifier.cs @@ -322,6 +322,7 @@ SymbolKey CreateSymbolKey(SemanticEditDescription edit) // Symbol key will happily resolve to a definition part that has no implementation, so we validate that // differently + // https://github.com/dotnet/roslyn/issues/73772: what about deletion of partial property? if (expectedOldSymbol is IMethodSymbol { IsPartialDefinition: true } && symbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true).Symbol is IMethodSymbol resolvedMethod) { diff --git a/src/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs b/src/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs index 83808fd9d2e61..3b6aca89890ba 100644 --- a/src/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs @@ -180,6 +180,30 @@ public partial class C AssertLocationsEqual(testLspServer.GetLocations("definition"), results); } + [Theory, CombinatorialData] + [WorkItem("https://github.com/dotnet/vscode-csharp/issues/5740")] + public async Task TestGotoDefinitionPartialProperties(bool mutatingLspWorkspace) + { + var markup = + """ + using System; + + public partial class C + { + partial int {|caret:|}Prop { get; set; } + } + + public partial class C + { + partial int {|definition:Prop|} { get => 1; set { } } + } + """; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + + var results = await RunGotoDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); + AssertLocationsEqual(testLspServer.GetLocations("definition"), results); + } + [Theory] [InlineData("ValueTuple valueTuple1;")] [InlineData("ValueTuple valueTuple2;")] diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OrdinaryMethodReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OrdinaryMethodReferenceFinder.cs index a046043129790..6813b72dd61ba 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OrdinaryMethodReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OrdinaryMethodReferenceFinder.cs @@ -34,6 +34,7 @@ protected override ValueTask> DetermineCascadedSymbolsAs private static ImmutableArray GetOtherPartsOfPartial(IMethodSymbol symbol) { + // https://github.com/dotnet/roslyn/issues/73772: define/use a similar helper for PropertySymbolReferenceFinder+PropertyAccessorSymbolReferenceFinder? if (symbol.PartialDefinitionPart != null) return [symbol.PartialDefinitionPart]; diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ParameterSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ParameterSymbolReferenceFinder.cs index 5cffb6d4ca570..93978ac446a97 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ParameterSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ParameterSymbolReferenceFinder.cs @@ -241,6 +241,7 @@ private static void CascadeBetweenPartialMethodParameters( IParameterSymbol parameter, ArrayBuilder results) { + // https://github.com/dotnet/roslyn/issues/73772: also cascade partial indexer parameters if (parameter.ContainingSymbol is IMethodSymbol method) { var ordinal = parameter.Ordinal; diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs index 0012906694f2e..1132babf80056 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs @@ -35,6 +35,7 @@ protected override ValueTask> DetermineCascadedSymbolsAs { using var _ = ArrayBuilder.GetInstance(out var result); + CascadeToOtherPartOfPartial(symbol, result); CascadeToBackingFields(symbol, result); CascadeToAccessors(symbol, result); CascadeToPrimaryConstructorParameters(symbol, result, cancellationToken); @@ -42,6 +43,12 @@ protected override ValueTask> DetermineCascadedSymbolsAs return new(result.ToImmutable()); } + private static void CascadeToOtherPartOfPartial(IPropertySymbol symbol, ArrayBuilder result) + { + result.AddIfNotNull(symbol.PartialDefinitionPart); + result.AddIfNotNull(symbol.PartialImplementationPart); + } + private static void CascadeToBackingFields(IPropertySymbol symbol, ArrayBuilder result) { foreach (var member in symbol.ContainingType.GetMembers()) diff --git a/src/Workspaces/Core/Portable/Rename/ConflictEngine/DeclarationConflictHelpers.cs b/src/Workspaces/Core/Portable/Rename/ConflictEngine/DeclarationConflictHelpers.cs index f683b96b5fe64..7e17a0ca24a75 100644 --- a/src/Workspaces/Core/Portable/Rename/ConflictEngine/DeclarationConflictHelpers.cs +++ b/src/Workspaces/Core/Portable/Rename/ConflictEngine/DeclarationConflictHelpers.cs @@ -55,6 +55,7 @@ private static ImmutableArray GetConflictLocations(ISymbol renamedMemb { if (signatureToConflictingMember.TryGetValue(signature, out var conflictingSymbol)) { + // https://github.com/dotnet/roslyn/issues/73772: add other partial property part as conflicting symbol if (isMethod && conflictingSymbol is IMethodSymbol conflictingMethod && renamedMember is IMethodSymbol renamedMethod) { if (!(conflictingMethod.PartialDefinitionPart != null && Equals(conflictingMethod.PartialDefinitionPart, renamedMethod)) && diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.MethodSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.MethodSymbolKey.cs index 04727e0303788..785458b4296dc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.MethodSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.MethodSymbolKey.cs @@ -164,7 +164,7 @@ protected sealed override SymbolKeyResolution Resolve( var containingType = reader.ReadSymbolKey(contextualSymbol?.ContainingSymbol, out var containingTypeFailureReason); var arity = reader.ReadInteger(); - var isPartialMethodImplementationPart = reader.ReadBoolean(); + var isPartialImplementationPart = reader.ReadBoolean(); using var parameterRefKinds = reader.ReadRefKindArray(); // For each method that we look at, we'll have to resolve the parameter list and @@ -195,7 +195,7 @@ protected sealed override SymbolKeyResolution Resolve( // resolving this method. using (reader.PushMethod(candidate)) { - method = Resolve(reader, isPartialMethodImplementationPart, candidate); + method = Resolve(reader, isPartialImplementationPart, candidate); } // Note: after finding the first method that matches we stop. That's necessary as we cache results @@ -242,7 +242,7 @@ protected sealed override SymbolKeyResolution Resolve( } private static IMethodSymbol? Resolve( - SymbolKeyReader reader, bool isPartialMethodImplementationPart, IMethodSymbol method) + SymbolKeyReader reader, bool isPartialImplementationPart, IMethodSymbol method) { var returnType = (ITypeSymbol?)reader.ReadSymbolKey(contextualSymbol: method.ReturnType, out _).GetAnySymbol(); if (returnType != null && @@ -257,10 +257,8 @@ protected sealed override SymbolKeyResolution Resolve( getContextualType: static (method, i) => SafeGet(method.Parameters, i)?.Type, method.OriginalDefinition.Parameters)) { - if (isPartialMethodImplementationPart) - { + if (isPartialImplementationPart) method = method.PartialImplementationPart ?? method; - } Debug.Assert(method != null); return method; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PropertySymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PropertySymbolKey.cs index 151548b7951ff..49b92a73b2224 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PropertySymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PropertySymbolKey.cs @@ -2,6 +2,8 @@ // 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.Diagnostics; + namespace Microsoft.CodeAnalysis; internal partial struct SymbolKey @@ -15,6 +17,7 @@ public sealed override void Create(IPropertySymbol symbol, SymbolKeyWriter visit visitor.WriteString(symbol.MetadataName); visitor.WriteSymbolKey(symbol.ContainingSymbol); visitor.WriteBoolean(symbol.IsIndexer); + visitor.WriteBoolean(symbol.PartialDefinitionPart != null); visitor.WriteRefKindArray(symbol.Parameters); visitor.WriteParameterTypesArray(symbol.OriginalDefinition.Parameters); } @@ -27,6 +30,7 @@ protected sealed override SymbolKeyResolution Resolve( var containingTypeResolution = reader.ReadSymbolKey(contextualSymbol?.ContainingSymbol, out var containingTypeFailureReason); var isIndexer = reader.ReadBoolean(); + var isPartialImplementationPart = reader.ReadBoolean(); using var refKinds = reader.ReadRefKindArray(); using var properties = GetMembersOfNamedType(containingTypeResolution, metadataName: null); @@ -51,7 +55,7 @@ protected sealed override SymbolKeyResolution Resolve( continue; } - property = Resolve(reader, candidate); + property = Resolve(reader, isPartialImplementationPart, candidate); if (property != null) break; @@ -86,6 +90,7 @@ protected sealed override SymbolKeyResolution Resolve( private static IPropertySymbol? Resolve( SymbolKeyReader reader, + bool isPartialImplementationPart, IPropertySymbol property) { if (reader.ParameterTypesMatch( @@ -93,6 +98,10 @@ protected sealed override SymbolKeyResolution Resolve( getContextualType: static (property, i) => SafeGet(property.OriginalDefinition.Parameters, i)?.Type, property.OriginalDefinition.Parameters)) { + if (isPartialImplementationPart) + property = property.PartialImplementationPart ?? property; + + Debug.Assert(property != null); return property; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs index a4efca7badcac..64c2f3e0110d5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs @@ -582,6 +582,8 @@ private bool PropertiesAreEquivalent(IPropertySymbol x, IPropertySymbol y, Dicti x.IsIndexer == y.IsIndexer && x.MetadataName == y.MetadataName && x.Parameters.Length == y.Parameters.Length && + IsPartialMethodDefinitionPart(x) == IsPartialMethodDefinitionPart(y) && + IsPartialMethodImplementationPart(x) == IsPartialMethodImplementationPart(y) && ParametersAreEquivalent(x.Parameters, y.Parameters, equivalentTypesWithDifferingAssemblies) && AreEquivalent(x.ContainingSymbol, y.ContainingSymbol, equivalentTypesWithDifferingAssemblies); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.GetHashCodeVisitor.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.GetHashCodeVisitor.cs index c436b4b95d895..f54b8bc87d787 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.GetHashCodeVisitor.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.GetHashCodeVisitor.cs @@ -245,7 +245,9 @@ private int CombineHashCodes(IPropertySymbol x, int currentHash) Hash.Combine(x.IsIndexer, Hash.Combine(x.Name, Hash.Combine(x.Parameters.Length, - GetHashCode(x.ContainingSymbol, currentHash)))); + Hash.Combine(IsPartialMethodImplementationPart(x), + Hash.Combine(IsPartialMethodDefinitionPart(x), + GetHashCode(x.ContainingSymbol, currentHash)))))); return CombineHashCodes(x.Parameters, currentHash, _parameterAggregator); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs index a8bb3f5bc68ae..9a5192089d6a1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs @@ -217,6 +217,12 @@ private static bool IsPartialMethodDefinitionPart(IMethodSymbol symbol) private static bool IsPartialMethodImplementationPart(IMethodSymbol symbol) => symbol.PartialDefinitionPart != null; + private static bool IsPartialMethodDefinitionPart(IPropertySymbol symbol) + => symbol.PartialImplementationPart != null; + + private static bool IsPartialMethodImplementationPart(IPropertySymbol symbol) + => symbol.PartialDefinitionPart != null; + private static TypeKind GetTypeKind(INamedTypeSymbol x) => x.TypeKind switch { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationPropertySymbol.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationPropertySymbol.cs index 242f6901f374b..361647a0027d9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationPropertySymbol.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationPropertySymbol.cs @@ -85,4 +85,10 @@ public override TResult Accept(SymbolVisitor RefCustomModifiers => []; public ImmutableArray TypeCustomModifiers => []; + + public IPropertySymbol PartialImplementationPart => null; + + public IPropertySymbol PartialDefinitionPart => null; + + public bool IsPartialDefinition => false; }