From 4c0770ac6af2143192ac86eb2e93c6dfa0b77fc6 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 28 May 2020 10:41:31 -0700 Subject: [PATCH 1/7] Use inherited properties --- .../Source/SourceMemberContainerSymbol.cs | 40 +- .../Source/SourceOrRecordPropertySymbol.cs | 1 - .../Records/SynthesizedRecordGetHashCode.cs | 6 +- .../Records/SynthesizedRecordObjEquals.cs | 5 +- .../SynthesizedRecordPropertySymbol.cs | 3 - .../Test/Semantic/Semantics/RecordTests.cs | 624 ++++++++++++++++-- 6 files changed, 627 insertions(+), 52 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index d556fe26138e2..923f44193f791 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -3000,10 +3000,14 @@ void addCloneMethod() void addProperties(ImmutableArray recordParameters) { - foreach (ParameterSymbol param in ctor.Parameters) + var inheritedProperties = PooledDictionary.GetInstance(); + HashSet? useSiteDiagnostics = null; + addInheritedProperties(inheritedProperties, this, this, ref useSiteDiagnostics); + foreach (ParameterSymbol param in recordParameters) { var property = new SynthesizedRecordPropertySymbol(this, param, diagnostics); - if (!memberSignatures.ContainsKey(property)) + if (!memberSignatures.ContainsKey(property) && + !(inheritedProperties.TryGetValue(property.Name, out var inheritedProperty) && matchesRecordProperty(inheritedProperty, param.TypeWithAnnotations))) { members.Add(property); members.Add(property.GetMethod); @@ -3011,6 +3015,34 @@ void addProperties(ImmutableArray recordParameters) members.Add(property.BackingField); } } + inheritedProperties.Free(); + } + + static bool matchesRecordProperty(PropertySymbol property, TypeWithAnnotations requiredType) + { + Debug.Assert(property.ParameterCount == 0); + + return !property.IsStatic && + property.RefKind == RefKind.None && + requiredType.Equals(property.TypeWithAnnotations, TypeCompareKind.AllIgnoreOptions); + } + + static void addInheritedProperties(Dictionary properties, NamedTypeSymbol within, NamedTypeSymbol type, ref HashSet? useSiteDiagnostics) + { + type = type.BaseTypeNoUseSiteDiagnostics; + if (type is null) + { + return; + } + addInheritedProperties(properties, within, type, ref useSiteDiagnostics); + foreach (var member in type.GetMembers()) + { + if (member is PropertySymbol property && + AccessCheck.IsSymbolAccessible(property, within, ref useSiteDiagnostics)) + { + properties[property.Name] = property; + } + } } void addObjectEquals(MethodSymbol thisEquals) @@ -3018,7 +3050,7 @@ void addObjectEquals(MethodSymbol thisEquals) var objEquals = new SynthesizedRecordObjEquals(this, thisEquals); if (!memberSignatures.ContainsKey(objEquals)) { - // PROTOTYPE: Don't add if the overridden method is sealed + // https://github.com/dotnet/roslyn/issues/44617: Don't add if the overridden method is sealed members.Add(objEquals); } } @@ -3028,7 +3060,7 @@ void addHashCode() var hashCode = new SynthesizedRecordGetHashCode(this); if (!memberSignatures.ContainsKey(hashCode)) { - // PROTOTYPE: Don't add if the overridden method is sealed + // https://github.com/dotnet/roslyn/issues/44617: Don't add if the overridden method is sealed members.Add(hashCode); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrRecordPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrRecordPropertySymbol.cs index c80ab42c45f22..37f0054257ca3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrRecordPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrRecordPropertySymbol.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Symbols diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordGetHashCode.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordGetHashCode.cs index d3d2dfcb584df..bdb1eade521a3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordGetHashCode.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordGetHashCode.cs @@ -1,8 +1,7 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. - #nullable enable using System; @@ -10,7 +9,6 @@ using System.Collections.Immutable; using System.Reflection; using Microsoft.Cci; -using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp.Symbols { @@ -118,4 +116,4 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, F.CloseMethod(F.Return(F.Literal(0))); } } -} \ No newline at end of file +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjEquals.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjEquals.cs index 6b930b37d0126..08fc95051514d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjEquals.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjEquals.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -20,7 +20,6 @@ internal sealed class SynthesizedRecordObjEquals : SynthesizedInstanceMethodSymb public override ImmutableArray Parameters { get; } - public SynthesizedRecordObjEquals(NamedTypeSymbol containingType, MethodSymbol typedRecordEquals) { _typedRecordEquals = typedRecordEquals; @@ -152,4 +151,4 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, F.CloseMethod(F.Block(ImmutableArray.Create(F.Return(expression)))); } } -} \ No newline at end of file +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs index 2c0badf5b1107..2d241308c872d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs @@ -2,17 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - #nullable enable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Reflection; -using System.Text; using Microsoft.Cci; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index 1452f13a56343..cf6516ee62cd2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -217,16 +217,41 @@ data class C(int X, int X) public void RecordProperties_06() { var src = @" -data class C(int X) +data class C(int X, int Y) { - public void get_X() {} + public void get_X() { } + public void set_X() { } + int get_Y(int value) => value; + int set_Y(int value) => value; }"; var comp = CreateCompilation(src); comp.VerifyDiagnostics( // (2,18): error CS0082: Type 'C' already reserves a member called 'get_X' with the same parameter types - // data class C(int X) - Diagnostic(ErrorCode.ERR_MemberReserved, "X").WithArguments("get_X", "C").WithLocation(2, 18) - ); + // data class C(int X, int Y) + Diagnostic(ErrorCode.ERR_MemberReserved, "X").WithArguments("get_X", "C").WithLocation(2, 18), + // (2,25): error CS0082: Type 'C' already reserves a member called 'set_Y' with the same parameter types + // data class C(int X, int Y) + Diagnostic(ErrorCode.ERR_MemberReserved, "Y").WithArguments("set_Y", "C").WithLocation(2, 25)); + + var actualMembers = comp.GetMember("C").GetMembers().ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Int32 C.k__BackingField", + "System.Int32 C.X.get", + "System.Int32 C.X { get; init; }", + "System.Int32 C.k__BackingField", + "System.Int32 C.Y.get", + "System.Int32 C.Y { get; init; }", + "void C.get_X()", + "void C.set_X()", + "System.Int32 C.get_Y(System.Int32 value)", + "System.Int32 C.set_Y(System.Int32 value)", + "System.Boolean C.Equals(C? )", + "System.Boolean C.Equals(System.Object? )", + "System.Int32 C.GetHashCode()", + "C..ctor(System.Int32 X, System.Int32 Y)", + }; + AssertEx.Equal(expectedMembers, actualMembers); } [Fact] @@ -264,6 +289,34 @@ data class C1(object O1) Diagnostic(ErrorCode.ERR_FieldInitRefNonstatic, "O1").WithArguments("C1.O1").WithLocation(5, 33) ); } + + [Fact] + public void RecordProperties_09() + { + var src = +@"data class C(object P1, object P2, object P3, object P4) +{ + class P1 { } + object P2 = 2; + int P3() => 3; + const int P4 = 4; +}"; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (1,21): error CS0102: The type 'C' already contains a definition for 'P1' + // data class C(object P1, object P2, object P3, object P4) + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P1").WithArguments("C", "P1").WithLocation(1, 21), + // (1,32): error CS0102: The type 'C' already contains a definition for 'P2' + // data class C(object P1, object P2, object P3, object P4) + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P2").WithArguments("C", "P2").WithLocation(1, 32), + // (1,43): error CS0102: The type 'C' already contains a definition for 'P3' + // data class C(object P1, object P2, object P3, object P4) + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P3").WithArguments("C", "P3").WithLocation(1, 43), + // (1,54): error CS0102: The type 'C' already contains a definition for 'P4' + // data class C(object P1, object P2, object P3, object P4) + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P4").WithArguments("C", "P4").WithLocation(1, 54)); + } + [Fact] public void EmptyRecord() { @@ -1557,6 +1610,7 @@ public static void Main() ); } + [WorkItem(44616, "https://github.com/dotnet/roslyn/issues/44616")] [Fact] public void WithExprNotRecord() { @@ -1785,16 +1839,12 @@ data class B(object P1, object P2, object P3, object P4, object P5, object P6) : var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); var expectedMembers = new[] { - "System.Object B.P1 { get; init; }", - "System.Object B.P2 { get; init; }", - "System.Object B.P3 { get; init; }", - "System.Object B.P4 { get; init; }", - "System.Object B.P5 { get; init; }", "System.Object B.P6 { get; init; }", }; AssertEx.Equal(expectedMembers, actualMembers); } + [WorkItem(44616, "https://github.com/dotnet/roslyn/issues/44616")] [Fact] public void Inheritance_02() { @@ -1811,14 +1861,10 @@ private data class B(object P1, object P2) : A var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var actualMembers = GetProperties(comp, "A.B").ToTestDisplayStrings(); - var expectedMembers = new[] - { - "System.Object A.B.P1 { get; init; }", - "System.Object A.B.P2 { get; init; }", - }; - AssertEx.Equal(expectedMembers, actualMembers); + AssertEx.Equal(new string[0], actualMembers); } + [WorkItem(44616, "https://github.com/dotnet/roslyn/issues/44616")] [Theory] [InlineData(false)] [InlineData(true)] @@ -1830,22 +1876,27 @@ public void Inheritance_03(bool useCompilationReference) public A() { } internal object P { get; set; } } -data class B1(object P) : A +public data class B(object Q) : A +{ + public B() { } +} +data class C1(object P, object Q) : B { }"; var comp = CreateCompilation(sourceA); - AssertEx.Equal(new[] { "System.Object B1.P { get; init; }" }, GetProperties(comp, "B1").ToTestDisplayStrings()); + AssertEx.Equal(new string[0], GetProperties(comp, "C1").ToTestDisplayStrings()); var refA = useCompilationReference ? comp.ToMetadataReference() : comp.EmitToImageReference(); var sourceB = -@"data class B2(object P) : A +@"data class C2(object P, object Q) : B { }"; comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics(); - AssertEx.Equal(new[] { "System.Object B2.P { get; init; }" }, GetProperties(comp, "B2").ToTestDisplayStrings()); + AssertEx.Equal(new[] { "System.Object C2.P { get; init; }" }, GetProperties(comp, "C2").ToTestDisplayStrings()); } + [WorkItem(44616, "https://github.com/dotnet/roslyn/issues/44616")] [Fact] public void Inheritance_04() { @@ -1869,11 +1920,6 @@ data class B(object P1, object P2, object P3, object P4, object P5, object P6, o var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); var expectedMembers = new[] { - "System.Object B.P1 { get; init; }", - "System.Object B.P2 { get; init; }", - "System.Object B.P3 { get; init; }", - "System.Object B.P4 { get; init; }", - "System.Object B.P5 { get; init; }", "System.Object B.P6 { get; init; }", "System.Object B.P7 { get; init; }", }; @@ -1932,13 +1978,7 @@ static void Main() }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (15,9): error CS8852: Init-only property or indexer 'B.X' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor. - // b.X = 4; - Diagnostic(ErrorCode.ERR_AssignmentInitOnly, "b.X").WithArguments("B.X").WithLocation(15, 9), - // (16,9): error CS8852: Init-only property or indexer 'B.Y' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor. - // b.Y = 5; - Diagnostic(ErrorCode.ERR_AssignmentInitOnly, "b.Y").WithArguments("B.Y").WithLocation(16, 9), - // (17,9): error CS8852: Init-only property or indexer 'B.Z' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor. + // (17,9): error CS0200: Property or indexer 'B.Z' cannot be assigned to -- it is read only // b.Z = 6; Diagnostic(ErrorCode.ERR_AssignmentInitOnly, "b.Z").WithArguments("B.Z").WithLocation(17, 9)); } @@ -1952,20 +1992,69 @@ public void Inheritance_07() public abstract int X { get; } public virtual int Y { get; } } -data class B(int X, int Y) : A +abstract data class B1(int X, int Y) : A +{ +} +data class B2(int X, int Y) : A { }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (6,12): error CS0534: 'B' does not implement inherited abstract member 'A.X.get' - // data class B(int X, int Y) : A - Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "B").WithArguments("B", "A.X.get").WithLocation(6, 12)); + // (9,12): error CS0534: 'B2' does not implement inherited abstract member 'A.X.get' + // data class B2(int X, int Y) : A + Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "B2").WithArguments("B2", "A.X.get").WithLocation(9, 12)); + + AssertEx.Equal(new string[0], GetProperties(comp, "B1").ToTestDisplayStrings()); + AssertEx.Equal(new string[0], GetProperties(comp, "B2").ToTestDisplayStrings()); } [Fact] public void Inheritance_08() { var source = +@"abstract class A +{ + public abstract int X { get; } + public virtual int Y { get; } + public virtual int Z { get; } +} +abstract class B : A +{ + public override abstract int Y { get; } +} +data class C(int X, int Y, int Z) : B +{ +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (11,12): error CS0534: 'C' does not implement inherited abstract member 'A.X.get' + // data class C(int X, int Y, int Z) : B + Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "C").WithArguments("C", "A.X.get").WithLocation(11, 12), + // (11,12): error CS0534: 'C' does not implement inherited abstract member 'B.Y.get' + // data class C(int X, int Y, int Z) : B + Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "C").WithArguments("C", "B.Y.get").WithLocation(11, 12)); + + var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); + AssertEx.Equal(new string[0], actualMembers); + } + + [Fact] + public void Inheritance_09() + { + var source = +@"abstract data class C(int X, int Y) +{ + public abstract int X { get; } + public virtual int Y { get; } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void Inheritance_10() + { + var source = @"using System; interface IA { @@ -2001,7 +2090,7 @@ static void Main() } [Fact] - public void Inheritance_09() + public void Inheritance_11() { var source = @"interface IA @@ -2035,6 +2124,467 @@ data struct S(object X, object Y) : IA, IB ); } + [Fact] + public void Inheritance_12() + { + var source = +@"class A +{ + public object X { get; } + public object Y { get; } +} +data class B(object X, object Y) : A +{ + public object X { get; } + public object Y { get; } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,19): warning CS0108: 'B.X' hides inherited member 'A.X'. Use the new keyword if hiding was intended. + // public object X { get; } + Diagnostic(ErrorCode.WRN_NewRequired, "X").WithArguments("B.X", "A.X").WithLocation(8, 19), + // (9,19): warning CS0108: 'B.Y' hides inherited member 'A.Y'. Use the new keyword if hiding was intended. + // public object Y { get; } + Diagnostic(ErrorCode.WRN_NewRequired, "Y").WithArguments("B.Y", "A.Y").WithLocation(9, 19)); + var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object B.X { get; }", + "System.Object B.Y { get; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [Fact] + public void Inheritance_13() + { + var source = +@"data class A(object X, object Y) +{ + internal A() { } +} +data class B(object X, object Y) : A +{ + public object X { get; } + public object Y { get; } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,19): warning CS0108: 'B.X' hides inherited member 'A.X'. Use the new keyword if hiding was intended. + // public object X { get; } + Diagnostic(ErrorCode.WRN_NewRequired, "X").WithArguments("B.X", "A.X").WithLocation(7, 19), + // (8,19): warning CS0108: 'B.Y' hides inherited member 'A.Y'. Use the new keyword if hiding was intended. + // public object Y { get; } + Diagnostic(ErrorCode.WRN_NewRequired, "Y").WithArguments("B.Y", "A.Y").WithLocation(8, 19)); + var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object B.X { get; }", + "System.Object B.Y { get; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [Fact] + public void Inheritance_14() + { + var source = +@"class A +{ + public object P1 { get; } + public object P2 { get; } + public object P3 { get; } + public object P4 { get; } +} +class B : A +{ + public new int P1 { get; } + public new int P2 { get; } +} +data class C(object P1, int P2, object P3, int P4) : B +{ +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object C.P1 { get; init; }", + "System.Int32 C.P4 { get; init; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [WorkItem(44694, "https://github.com/dotnet/roslyn/issues/44694")] + [Fact] + public void Inheritance_15() + { + var source = +@"data class C(int P1, object P2) +{ + public object P1 { get; set; } + public int P2 { get; set; } +}"; + var comp = CreateCompilation(source); + // https://github.com/dotnet/roslyn/issues/44694: Report mismatch for `object P2` parameter and `int P2` property. + comp.VerifyDiagnostics(); + var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object C.P1 { get; set; }", + "System.Int32 C.P2 { get; set; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [Fact] + public void Inheritance_16() + { + var source = +@"class A +{ + public int P1 { get; } + public int P2 { get; } + public int P3 { get; } + public int P4 { get; } +} +data class B(object P1, int P2, object P3, int P4) : A +{ + public new object P1 { get; } + public new object P2 { get; } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object B.P3 { get; init; }", + "System.Object B.P1 { get; }", + "System.Object B.P2 { get; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [WorkItem(44694, "https://github.com/dotnet/roslyn/issues/44694")] + [Fact] + public void Inheritance_17() + { + var source = +@"class A +{ + public object P1 { get; } + public object P2 { get; } + public object P3 { get; } + public object P4 { get; } +} +data class B(object P1, int P2, object P3, int P4) : A +{ + public new int P1 { get; } + public new int P2 { get; } +}"; + var comp = CreateCompilation(source); + // https://github.com/dotnet/roslyn/issues/44694: Report mismatch for `object P1` parameter and `int P1` property. + comp.VerifyDiagnostics(); + + var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Int32 B.P4 { get; init; }", + "System.Int32 B.P1 { get; }", + "System.Int32 B.P2 { get; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [WorkItem(44694, "https://github.com/dotnet/roslyn/issues/44694")] + [Fact] + public void Inheritance_18() + { + var source = +@"data class C(object P1, object P2, object P3, object P4, object P5) +{ + public object P1 { get { return null; } set { } } + public object P2 { get; init; } + public object P3 { set { } } + public static object P4 { get; set; } + public ref object P5 => throw null; +}"; + var comp = CreateCompilation(source); + // https://github.com/dotnet/roslyn/issues/44694: Report mismatches between parameters and explicit properties. + comp.VerifyDiagnostics(); + + var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object C.P1 { get; set; }", + "System.Object C.P2 { get; init; }", + "System.Object C.P3 { set; }", + "System.Object C.P4 { get; set; }", + "ref System.Object C.P5 { get; init; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [Fact] + public void Inheritance_19() + { + var source = +@"#pragma warning disable 8618 +#nullable enable +class A +{ + internal A() { } + public object P1 { get; } + public dynamic[] P2 { get; } + public object? P3 { get; } + public object[] P4 { get; } + public (int X, int Y) P5 { get; } + public (int, int)[] P6 { get; } + public nint P7 { get; } + public System.UIntPtr[] P8 { get; } +} +data class B(dynamic P1, object[] P2, object P3, object?[] P4, (int, int) P5, (int X, int Y)[] P6, System.IntPtr P7, nuint[] P8) : A +{ +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); + AssertEx.Equal(new string[0], actualMembers); + } + + [Fact] + public void Inheritance_20() + { + var source = +@"#pragma warning disable 8618 +#nullable enable +data class C(dynamic P1, object[] P2, object P3, object?[] P4, (int, int) P5, (int X, int Y)[] P6, System.IntPtr P7, nuint[] P8) +{ + public object P1 { get; } + public dynamic[] P2 { get; } + public object? P3 { get; } + public object[] P4 { get; } + public (int X, int Y) P5 { get; } + public (int, int)[] P6 { get; } + public nint P7 { get; } + public System.UIntPtr[] P8 { get; } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object C.P1 { get; init; }", + "dynamic[] C.P2 { get; init; }", + "System.Object? C.P3 { get; init; }", + "System.Object[] C.P4 { get; init; }", + "(System.Int32 X, System.Int32 Y) C.P5 { get; init; }", + "(System.Int32, System.Int32)[] C.P6 { get; init; }", + "nint C.P7 { get; init; }", + "System.UIntPtr[] C.P8 { get; init; }" + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [Fact] + public void Inheritance_21() + { + var source = +@"class A +{ + internal object P1 { get; } + private object P2 { get; } +} +class B : A +{ + private new object P1 { get; } + internal object P2 { get; } +} +data class C(object P1, object P2) : B +{ +} +class Program +{ + static void Main() + { + var c = new C(1, 2); + System.Console.WriteLine(""({0}, {1})"", c.P1, c.P2); + } +}"; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics(); + + var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); + AssertEx.Equal(new string[0], actualMembers); + + // https://github.com/dotnet/roslyn/issues/44693: Record constructor should assign to explicit properties. + var verifier = CompileAndVerify(comp, expectedOutput: "(, )"); + verifier.VerifyIL("C..ctor", +@"{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""B..ctor()"" + IL_0006: ret +}"); + verifier.VerifyIL("Program.Main", +@"{ + // Code size 41 (0x29) + .maxstack 3 + .locals init (C V_0) //c + IL_0000: ldc.i4.1 + IL_0001: box ""int"" + IL_0006: ldc.i4.2 + IL_0007: box ""int"" + IL_000c: newobj ""C..ctor(object, object)"" + IL_0011: stloc.0 + IL_0012: ldstr ""({0}, {1})"" + IL_0017: ldloc.0 + IL_0018: callvirt ""object A.P1.get"" + IL_001d: ldloc.0 + IL_001e: callvirt ""object B.P2.get"" + IL_0023: call ""void System.Console.WriteLine(string, object, object)"" + IL_0028: ret +}"); + + // PROTOTYPE: Should be testing C from a different assembly when + // A has public property and B has internal property. + Assert.False(true); + } + + [Fact] + public void Inheritance_22() + { + var source = +@"class A +{ + public ref object P1 => throw null; + public object P2 => throw null; +} +class B : A +{ + public new object P1 => throw null; + public new ref object P2 => throw null; +} +data class C(object P1, object P2) : B +{ +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object C.P2 { get; init; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [Fact] + public void Inheritance_23() + { + var source = +@"class A +{ + public static object P1 { get; } + public object P2 { get; } +} +class B : A +{ + public new object P1 { get; } + public new static object P2 { get; } +} +data class C(object P1, object P2) : B +{ +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object C.P2 { get; init; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [Fact] + public void Inheritance_24() + { + var source = +@"class A +{ + public object get_P() => null; +} +data class B(object P) : A +{ +} +data class C(object P) +{ + public object get_P() => null; +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,21): error CS0082: Type 'C' already reserves a member called 'get_P' with the same parameter types + // data class C(object P) + Diagnostic(ErrorCode.ERR_MemberReserved, "P").WithArguments("get_P", "C").WithLocation(8, 21)); + + var expectedMembers = new[] + { + "System.Object B.

k__BackingField", + "System.Object B.P.get", + "System.Object B.P { get; init; }", + "System.Boolean B.Equals(B? )", + "System.Boolean B.Equals(System.Object? )", + "System.Int32 B.GetHashCode()", + "B..ctor(System.Object P)" + }; + AssertEx.Equal(expectedMembers, comp.GetMember("B").GetMembers().ToTestDisplayStrings()); + + expectedMembers = new[] + { + "System.Object C.

k__BackingField", + "System.Object C.P.get", + "System.Object C.P { get; init; }", + "System.Object C.get_P()", + "System.Boolean C.Equals(C? )", + "System.Boolean C.Equals(System.Object? )", + "System.Int32 C.GetHashCode()", + "C..ctor(System.Object P)" + }; + AssertEx.Equal(expectedMembers, comp.GetMember("C").GetMembers().ToTestDisplayStrings()); + } + + [Fact] + public void Inheritance_25() + { + var source = +@"interface I +{ + object P { get; } +} +class A : I +{ + object I.P => null; +} +data class B(object P) : A +{ +} +data class C(object P) : I +{ + object I.P => null; +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + AssertEx.Equal(new[] { "System.Object B.P { get; init; }" }, GetProperties(comp, "B").ToTestDisplayStrings()); + AssertEx.Equal(new[] { "System.Object C.P { get; init; }", "System.Object C.I.P { get; }" }, GetProperties(comp, "C").ToTestDisplayStrings()); + } + + [Fact] + public void Inheritance_26() + { + // PROTOTYPE: Test with base classes from VB with properties with parameters. + Assert.False(true); + } + [Fact] public void Overrides_01() { From 7aac024d66821f44f56c730b363d1cfe6e25c8d3 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 29 May 2020 22:02:48 -0700 Subject: [PATCH 2/7] Fix tests --- .../Source/SourceMemberContainerSymbol.cs | 1 + .../Test/Semantic/Semantics/RecordTests.cs | 268 +++++++++++++++--- 2 files changed, 229 insertions(+), 40 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 923f44193f791..33ad6a0512a18 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -3038,6 +3038,7 @@ static void addInheritedProperties(Dictionary properties foreach (var member in type.GetMembers()) { if (member is PropertySymbol property && + property.ParameterCount == 0 && AccessCheck.IsSymbolAccessible(property, within, ref useSiteDiagnostics)) { properties[property.Name] = property; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index cf6516ee62cd2..5d9578b8e798f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -236,11 +236,14 @@ public void set_X() { } var actualMembers = comp.GetMember("C").GetMembers().ToTestDisplayStrings(); var expectedMembers = new[] { + "C C.Clone()", "System.Int32 C.k__BackingField", "System.Int32 C.X.get", + "void modreq(System.Runtime.CompilerServices.IsExternalInit) C.X.init", "System.Int32 C.X { get; init; }", "System.Int32 C.k__BackingField", "System.Int32 C.Y.get", + "void modreq(System.Runtime.CompilerServices.IsExternalInit) C.Y.init", "System.Int32 C.Y { get; init; }", "void C.get_X()", "void C.set_X()", @@ -249,6 +252,7 @@ public void set_X() { } "System.Boolean C.Equals(C? )", "System.Boolean C.Equals(System.Object? )", "System.Int32 C.GetHashCode()", + "C..ctor(C )", "C..ctor(System.Int32 X, System.Int32 Y)", }; AssertEx.Equal(expectedMembers, actualMembers); @@ -294,27 +298,44 @@ data class C1(object O1) public void RecordProperties_09() { var src = -@"data class C(object P1, object P2, object P3, object P4) +@"data class C(object P1, object P2, object P3) { class P1 { } object P2 = 2; int P3() => 3; - const int P4 = 4; }"; var comp = CreateCompilation(src); comp.VerifyDiagnostics( // (1,21): error CS0102: The type 'C' already contains a definition for 'P1' - // data class C(object P1, object P2, object P3, object P4) + // data class C(object P1, object P2, object P3) Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P1").WithArguments("C", "P1").WithLocation(1, 21), // (1,32): error CS0102: The type 'C' already contains a definition for 'P2' - // data class C(object P1, object P2, object P3, object P4) + // data class C(object P1, object P2, object P3) Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P2").WithArguments("C", "P2").WithLocation(1, 32), // (1,43): error CS0102: The type 'C' already contains a definition for 'P3' - // data class C(object P1, object P2, object P3, object P4) - Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P3").WithArguments("C", "P3").WithLocation(1, 43), - // (1,54): error CS0102: The type 'C' already contains a definition for 'P4' - // data class C(object P1, object P2, object P3, object P4) - Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P4").WithArguments("C", "P4").WithLocation(1, 54)); + // data class C(object P1, object P2, object P3) + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P3").WithArguments("C", "P3").WithLocation(1, 43)); + } + + [Fact] + public void RecordProperties_10() + { + var src = +@"data class C(object P) +{ + const int P = 4; +}"; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (1,1): warning CS1717: Assignment made to same variable; did you mean to assign something else? + // data class C(object P) + Diagnostic(ErrorCode.WRN_AssignmentToSelf, @"data class C(object P) +{ + const int P = 4; +}").WithLocation(1, 1), + // (1,21): error CS0102: The type 'C' already contains a definition for 'P' + // data class C(object P) + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "P").WithLocation(1, 21)); } [Fact] @@ -1817,6 +1838,7 @@ public static void Main() ); } + [WorkItem(44616, "https://github.com/dotnet/roslyn/issues/44616")] [Fact] public void Inheritance_01() { @@ -2304,23 +2326,33 @@ public void Inheritance_18() @"data class C(object P1, object P2, object P3, object P4, object P5) { public object P1 { get { return null; } set { } } - public object P2 { get; init; } + public object P2 { get; } public object P3 { set { } } public static object P4 { get; set; } public ref object P5 => throw null; }"; var comp = CreateCompilation(source); // https://github.com/dotnet/roslyn/issues/44694: Report mismatches between parameters and explicit properties. - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (1,1): warning CS1717: Assignment made to same variable; did you mean to assign something else? + // data class C(object P1, object P2, object P3, object P4, object P5) + Diagnostic(ErrorCode.WRN_AssignmentToSelf, @"data class C(object P1, object P2, object P3, object P4, object P5) +{ + public object P1 { get { return null; } set { } } + public object P2 { get; } + public object P3 { set { } } + public static object P4 { get; set; } + public ref object P5 => throw null; +}").WithLocation(1, 1)); var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); var expectedMembers = new[] { "System.Object C.P1 { get; set; }", - "System.Object C.P2 { get; init; }", + "System.Object C.P2 { get; }", "System.Object C.P3 { set; }", "System.Object C.P4 { get; set; }", - "ref System.Object C.P5 { get; init; }", + "ref System.Object C.P5 { get; }", }; AssertEx.Equal(expectedMembers, actualMembers); } @@ -2374,33 +2406,39 @@ data class C(dynamic P1, object[] P2, object P3, object?[] P4, (int, int) P5, (i var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); var expectedMembers = new[] { - "System.Object C.P1 { get; init; }", - "dynamic[] C.P2 { get; init; }", - "System.Object? C.P3 { get; init; }", - "System.Object[] C.P4 { get; init; }", - "(System.Int32 X, System.Int32 Y) C.P5 { get; init; }", - "(System.Int32, System.Int32)[] C.P6 { get; init; }", - "nint C.P7 { get; init; }", - "System.UIntPtr[] C.P8 { get; init; }" + "System.Object C.P1 { get; }", + "dynamic[] C.P2 { get; }", + "System.Object? C.P3 { get; }", + "System.Object[] C.P4 { get; }", + "(System.Int32 X, System.Int32 Y) C.P5 { get; }", + "(System.Int32, System.Int32)[] C.P6 { get; }", + "nint C.P7 { get; }", + "System.UIntPtr[] C.P8 { get; }" }; AssertEx.Equal(expectedMembers, actualMembers); } - [Fact] - public void Inheritance_21() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Inheritance_21(bool useCompilationReference) { - var source = -@"class A -{ - internal object P1 { get; } - private object P2 { get; } -} -class B : A + var sourceA = +@"public class A { - private new object P1 { get; } + public object P1 { get; } internal object P2 { get; } } -data class C(object P1, object P2) : B +public class B : A +{ + internal new object P1 { get; } + public new object P2 { get; } +}"; + var comp = CreateCompilation(sourceA); + var refA = useCompilationReference ? comp.ToMetadataReference() : comp.EmitToImageReference(); + + var sourceB = +@"data class C(object P1, object P2) : B { } class Program @@ -2411,7 +2449,7 @@ static void Main() System.Console.WriteLine(""({0}, {1})"", c.P1, c.P2); } }"; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseExe); + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics(); var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); @@ -2419,7 +2457,7 @@ static void Main() // https://github.com/dotnet/roslyn/issues/44693: Record constructor should assign to explicit properties. var verifier = CompileAndVerify(comp, expectedOutput: "(, )"); - verifier.VerifyIL("C..ctor", + verifier.VerifyIL("C..ctor(object, object)", @"{ // Code size 7 (0x7) .maxstack 1 @@ -2446,10 +2484,6 @@ .locals init (C V_0) //c IL_0023: call ""void System.Console.WriteLine(string, object, object)"" IL_0028: ret }"); - - // PROTOTYPE: Should be testing C from a different assembly when - // A has public property and B has internal property. - Assert.False(true); } [Fact] @@ -2529,25 +2563,31 @@ data class C(object P) var expectedMembers = new[] { + "B B.Clone()", "System.Object B.

k__BackingField", "System.Object B.P.get", + "void modreq(System.Runtime.CompilerServices.IsExternalInit) B.P.init", "System.Object B.P { get; init; }", "System.Boolean B.Equals(B? )", "System.Boolean B.Equals(System.Object? )", "System.Int32 B.GetHashCode()", + "B..ctor(B )", "B..ctor(System.Object P)" }; AssertEx.Equal(expectedMembers, comp.GetMember("B").GetMembers().ToTestDisplayStrings()); expectedMembers = new[] { + "C C.Clone()", "System.Object C.

k__BackingField", "System.Object C.P.get", + "void modreq(System.Runtime.CompilerServices.IsExternalInit) C.P.init", "System.Object C.P { get; init; }", "System.Object C.get_P()", "System.Boolean C.Equals(C? )", "System.Boolean C.Equals(System.Object? )", "System.Int32 C.GetHashCode()", + "C..ctor(C )", "C..ctor(System.Object P)" }; AssertEx.Equal(expectedMembers, comp.GetMember("C").GetMembers().ToTestDisplayStrings()); @@ -2581,8 +2621,156 @@ data class C(object P) : I [Fact] public void Inheritance_26() { - // PROTOTYPE: Test with base classes from VB with properties with parameters. - Assert.False(true); + var sourceA = +@"Public Class A + Public Property P(o As Object) As Object + Get + Return Nothing + End Get + Set + End Set + End Property + Public Property Q(x As Object, y As Object) As Object + Get + Return Nothing + End Get + Set + End Set + End Property +End Class +"; + var compA = CreateVisualBasicCompilation(sourceA); + compA.VerifyDiagnostics(); + var refA = compA.EmitToImageReference(); + + var sourceB = +@"data class B(object P, object Q) : A +{ + object P { get; } +}"; + var compB = CreateCompilation(new[] { sourceB, IsExternalInitTypeDefinition }, references: new[] { refA }, parseOptions: TestOptions.RegularPreview); + compB.VerifyDiagnostics(); + + var actualMembers = GetProperties(compB, "B").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object B.Q { get; init; }", + "System.Object B.P { get; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [Fact] + public void Inheritance_27() + { + var sourceA = +@"Public Class A + Public ReadOnly Overloads Property P() As Object + Get + Return Nothing + End Get + End Property + Public ReadOnly Overloads Property P(o As Object) As Object + Get + Return Nothing + End Get + End Property + Public Overloads Property Q(o As Object) As Object + Get + Return Nothing + End Get + Set + End Set + End Property + Public Overloads Property Q() As Object + Get + Return Nothing + End Get + Set + End Set + End Property +End Class +"; + var compA = CreateVisualBasicCompilation(sourceA); + compA.VerifyDiagnostics(); + var refA = compA.EmitToImageReference(); + + var sourceB = +@"data class B(object P, object Q) : A +{ +}"; + var compB = CreateCompilation(new[] { sourceB, IsExternalInitTypeDefinition }, references: new[] { refA }, parseOptions: TestOptions.RegularPreview); + compB.VerifyDiagnostics(); + + var actualMembers = GetProperties(compB, "B").ToTestDisplayStrings(); + AssertEx.Equal(new string[0], actualMembers); + } + + [Fact] + public void Inheritance_28() + { + var sourceA = +@"Public Class A + Public ReadOnly Property P() As Object + Get + Return Nothing + End Get + End Property + Public Property Q(o As Object) As Object + Get + Return Nothing + End Get + Set + End Set + End Property + Public Property R(o As Object) As Object + Get + Return Nothing + End Get + Set + End Set + End Property +End Class +Public Class B + Inherits A + Public ReadOnly Overloads Property P(o As Object) As Object + Get + Return Nothing + End Get + End Property + Public Overloads Property Q() As Object + Get + Return Nothing + End Get + Set + End Set + End Property + Public Overloads Property R(x As Object, y As Object) As Object + Get + Return Nothing + End Get + Set + End Set + End Property +End Class +"; + var compA = CreateVisualBasicCompilation(sourceA); + compA.VerifyDiagnostics(); + var refA = compA.EmitToImageReference(); + + var sourceB = +@"data class C(object P, object Q, object R) : B +{ +}"; + var compB = CreateCompilation(new[] { sourceB, IsExternalInitTypeDefinition }, references: new[] { refA }, parseOptions: TestOptions.RegularPreview); + compB.VerifyDiagnostics(); + + var actualMembers = GetProperties(compB, "C").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object C.R { get; init; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); } [Fact] From 7a6ff04337385ff8587ab88e4751aabe1b41f095 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 29 May 2020 22:59:25 -0700 Subject: [PATCH 3/7] Misc. --- src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index 5d9578b8e798f..9188f890784e3 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -1631,7 +1631,6 @@ public static void Main() ); } - [WorkItem(44616, "https://github.com/dotnet/roslyn/issues/44616")] [Fact] public void WithExprNotRecord() { From f0921cf61524b75cfa3c30578e3b4d4ceecb0986 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 29 May 2020 23:32:22 -0700 Subject: [PATCH 4/7] Handle duplicate properties --- .../Symbols/MemberSymbolExtensions.cs | 1 + .../Source/SourceMemberContainerSymbol.cs | 6 +- .../Test/Semantic/Semantics/RecordTests.cs | 89 +++++++++++++++++++ 3 files changed, 94 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs index d2b1874573460..bb6148d6ef8cf 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs @@ -102,6 +102,7 @@ internal static int GetParameterCount(this Symbol member) case SymbolKind.Property: return ((PropertySymbol)member).ParameterCount; case SymbolKind.Event: + case SymbolKind.Field: return 0; default: throw ExceptionUtilities.UnexpectedValue(member.Kind); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 33ad6a0512a18..9806e7cfd8d23 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -2946,11 +2946,13 @@ private void AddSynthesizedRecordMembersIfNecessary(MembersAndInitializersBuilde BinderFactory binderFactory = this.DeclaringCompilation.GetBinderFactory(paramList.SyntaxTree); var binder = binderFactory.GetBinder(paramList); - // PROTOTYPE: need to check base members as well var memberSignatures = s_duplicateMemberSignatureDictionary.Allocate(); foreach (var member in members) { - memberSignatures.Add(member, member); + if (!memberSignatures.ContainsKey(member)) + { + memberSignatures.Add(member, member); + } } var ctor = addCtor(paramList); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index 9188f890784e3..4e02c8f811ef8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -2855,6 +2855,95 @@ data class B(int X, int Y) : A AssertEx.Equal(expectedMembers, actualMembers); } + [WorkItem(44692, "https://github.com/dotnet/roslyn/issues/44692")] + [Fact] + public void DuplicateProperty_01() + { + var src = +@"data class C(object Q) +{ + public object P { get; } + public object P { get; } +}"; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (4,19): error CS0102: The type 'C' already contains a definition for 'P' + // public object P { get; } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "P").WithLocation(4, 19)); + + var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object C.Q { get; init; }", + "System.Object C.P { get; }", + "System.Object C.P { get; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [WorkItem(44692, "https://github.com/dotnet/roslyn/issues/44692")] + [Fact] + public void DuplicateProperty_02() + { + var src = +@"data class C(object P, object Q) +{ + public object P { get; } + public int P { get; } + public int Q { get; } + public object Q { get; } +}"; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (4,16): error CS0102: The type 'C' already contains a definition for 'P' + // public int P { get; } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C", "P").WithLocation(4, 16), + // (6,19): error CS0102: The type 'C' already contains a definition for 'Q' + // public object Q { get; } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "Q").WithArguments("C", "Q").WithLocation(6, 19)); + + var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object C.P { get; }", + "System.Int32 C.P { get; }", + "System.Int32 C.Q { get; }", + "System.Object C.Q { get; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [Fact] + public void DuplicateProperty_03() + { + var src = +@"class A +{ + public object P { get; } + public object P { get; } + public object Q { get; } + public int Q { get; } +} +data class B(object Q) : A +{ +}"; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (4,19): error CS0102: The type 'A' already contains a definition for 'P' + // public object P { get; } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("A", "P").WithLocation(4, 19), + // (6,16): error CS0102: The type 'A' already contains a definition for 'Q' + // public int Q { get; } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "Q").WithArguments("A", "Q").WithLocation(6, 16)); + + var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object B.Q { get; init; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + private static ImmutableArray GetProperties(CSharpCompilation comp, string typeName) { return comp.GetMember(typeName).GetMembers().WhereAsArray(m => m.Kind == SymbolKind.Property); From b6f906e3855493cb37162eb1b5ce2d4d8a804c76 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Mon, 1 Jun 2020 12:29:50 -0700 Subject: [PATCH 5/7] Match updated spec --- .../OverriddenOrHiddenMembersHelpers.cs | 2 +- .../Source/SourceMemberContainerSymbol.cs | 51 +++-- .../Test/Semantic/Semantics/RecordTests.cs | 175 ++++++++++++------ 3 files changed, 139 insertions(+), 89 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersHelpers.cs index b38e316698d8c..dcf9801afce47 100644 --- a/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersHelpers.cs @@ -473,7 +473,7 @@ internal static OverriddenOrHiddenMembersResult MakeInterfaceOverriddenOrHiddenM /// /// In incorrect or imported code, it is possible that both currTypeBestMatch and hiddenBuilder will be populated. /// - private static void FindOverriddenOrHiddenMembersInType( + internal static void FindOverriddenOrHiddenMembersInType( Symbol member, bool memberIsFromSomeCompilation, NamedTypeSymbol memberContainingType, diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 9806e7cfd8d23..cdd6160a4eced 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -3002,14 +3002,11 @@ void addCloneMethod() void addProperties(ImmutableArray recordParameters) { - var inheritedProperties = PooledDictionary.GetInstance(); - HashSet? useSiteDiagnostics = null; - addInheritedProperties(inheritedProperties, this, this, ref useSiteDiagnostics); foreach (ParameterSymbol param in recordParameters) { var property = new SynthesizedRecordPropertySymbol(this, param, diagnostics); if (!memberSignatures.ContainsKey(property) && - !(inheritedProperties.TryGetValue(property.Name, out var inheritedProperty) && matchesRecordProperty(inheritedProperty, param.TypeWithAnnotations))) + !hidesInheritedMember(property, this)) { members.Add(property); members.Add(property.GetMethod); @@ -3017,35 +3014,31 @@ void addProperties(ImmutableArray recordParameters) members.Add(property.BackingField); } } - inheritedProperties.Free(); } - static bool matchesRecordProperty(PropertySymbol property, TypeWithAnnotations requiredType) + static bool hidesInheritedMember(Symbol symbol, NamedTypeSymbol type) { - Debug.Assert(property.ParameterCount == 0); - - return !property.IsStatic && - property.RefKind == RefKind.None && - requiredType.Equals(property.TypeWithAnnotations, TypeCompareKind.AllIgnoreOptions); - } - - static void addInheritedProperties(Dictionary properties, NamedTypeSymbol within, NamedTypeSymbol type, ref HashSet? useSiteDiagnostics) - { - type = type.BaseTypeNoUseSiteDiagnostics; - if (type is null) - { - return; - } - addInheritedProperties(properties, within, type, ref useSiteDiagnostics); - foreach (var member in type.GetMembers()) + while ((type = type.BaseTypeNoUseSiteDiagnostics) is object) { - if (member is PropertySymbol property && - property.ParameterCount == 0 && - AccessCheck.IsSymbolAccessible(property, within, ref useSiteDiagnostics)) + OverriddenOrHiddenMembersHelpers.FindOverriddenOrHiddenMembersInType( + symbol, + memberIsFromSomeCompilation: true, + memberContainingType: symbol.ContainingType, + currType: type, + out var bestMatch, + out bool hasSameKindNonMatch, + out var hiddenBuilder); + if (hiddenBuilder is object) { - properties[property.Name] = property; + hiddenBuilder.Free(); + return true; + } + if (bestMatch is object) + { + return true; } } + return false; } void addObjectEquals(MethodSymbol thisEquals) @@ -3597,9 +3590,9 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r } } - #endregion +#endregion - #region Extension Methods +#region Extension Methods internal bool ContainsExtensionMethods { @@ -3637,7 +3630,7 @@ public override bool MightContainExtensionMethods } } - #endregion +#endregion public sealed override NamedTypeSymbol ConstructedFrom { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index 4e02c8f811ef8..35ac6b2f2415f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -298,23 +298,27 @@ data class C1(object O1) public void RecordProperties_09() { var src = -@"data class C(object P1, object P2, object P3) +@"data class C(object P1, object P2, object P3, object P4) { class P1 { } object P2 = 2; - int P3() => 3; + int P3(object o) => 3; + int P4(T t) => 4; }"; var comp = CreateCompilation(src); comp.VerifyDiagnostics( // (1,21): error CS0102: The type 'C' already contains a definition for 'P1' - // data class C(object P1, object P2, object P3) + // data class C(object P1, object P2, object P3, object P4) Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P1").WithArguments("C", "P1").WithLocation(1, 21), // (1,32): error CS0102: The type 'C' already contains a definition for 'P2' - // data class C(object P1, object P2, object P3) + // data class C(object P1, object P2, object P3, object P4) Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P2").WithArguments("C", "P2").WithLocation(1, 32), // (1,43): error CS0102: The type 'C' already contains a definition for 'P3' - // data class C(object P1, object P2, object P3) - Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P3").WithArguments("C", "P3").WithLocation(1, 43)); + // data class C(object P1, object P2, object P3, object P4) + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P3").WithArguments("C", "P3").WithLocation(1, 43), + // (1,54): error CS0102: The type 'C' already contains a definition for 'P4' + // data class C(object P1, object P2, object P3, object P4) + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P4").WithArguments("C", "P4").WithLocation(1, 54)); } [Fact] @@ -1939,12 +1943,7 @@ data class B(object P1, object P2, object P3, object P4, object P5, object P6, o var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); - var expectedMembers = new[] - { - "System.Object B.P6 { get; init; }", - "System.Object B.P7 { get; init; }", - }; - AssertEx.Equal(expectedMembers, actualMembers); + AssertEx.Equal(new string[0], actualMembers); } [Fact] @@ -1963,12 +1962,7 @@ data class B(int P1, object P2) : A var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); - var expectedMembers = new[] - { - "System.Int32 B.P1 { get; init; }", - "System.Object B.P2 { get; init; }", - }; - AssertEx.Equal(expectedMembers, actualMembers); + AssertEx.Equal(new string[0], actualMembers); } [Fact] @@ -1998,10 +1992,9 @@ static void Main() } }"; var comp = CreateCompilation(source); - comp.VerifyDiagnostics( - // (17,9): error CS0200: Property or indexer 'B.Z' cannot be assigned to -- it is read only - // b.Z = 6; - Diagnostic(ErrorCode.ERR_AssignmentInitOnly, "b.Z").WithArguments("B.Z").WithLocation(17, 9)); + comp.VerifyDiagnostics(); + var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); + AssertEx.Equal(new string[0], actualMembers); } [Fact] @@ -2228,15 +2221,9 @@ data class C(object P1, int P2, object P3, int P4) : B var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); - var expectedMembers = new[] - { - "System.Object C.P1 { get; init; }", - "System.Int32 C.P4 { get; init; }", - }; - AssertEx.Equal(expectedMembers, actualMembers); + AssertEx.Equal(new string[0], actualMembers); } - [WorkItem(44694, "https://github.com/dotnet/roslyn/issues/44694")] [Fact] public void Inheritance_15() { @@ -2247,7 +2234,6 @@ public void Inheritance_15() public int P2 { get; set; } }"; var comp = CreateCompilation(source); - // https://github.com/dotnet/roslyn/issues/44694: Report mismatch for `object P2` parameter and `int P2` property. comp.VerifyDiagnostics(); var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); var expectedMembers = new[] @@ -2279,14 +2265,12 @@ data class B(object P1, int P2, object P3, int P4) : A var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); var expectedMembers = new[] { - "System.Object B.P3 { get; init; }", "System.Object B.P1 { get; }", "System.Object B.P2 { get; }", }; AssertEx.Equal(expectedMembers, actualMembers); } - [WorkItem(44694, "https://github.com/dotnet/roslyn/issues/44694")] [Fact] public void Inheritance_17() { @@ -2304,20 +2288,17 @@ data class B(object P1, int P2, object P3, int P4) : A public new int P2 { get; } }"; var comp = CreateCompilation(source); - // https://github.com/dotnet/roslyn/issues/44694: Report mismatch for `object P1` parameter and `int P1` property. comp.VerifyDiagnostics(); var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); var expectedMembers = new[] { - "System.Int32 B.P4 { get; init; }", "System.Int32 B.P1 { get; }", "System.Int32 B.P2 { get; }", }; AssertEx.Equal(expectedMembers, actualMembers); } - [WorkItem(44694, "https://github.com/dotnet/roslyn/issues/44694")] [Fact] public void Inheritance_18() { @@ -2331,7 +2312,6 @@ public object P3 { set { } } public ref object P5 => throw null; }"; var comp = CreateCompilation(source); - // https://github.com/dotnet/roslyn/issues/44694: Report mismatches between parameters and explicit properties. comp.VerifyDiagnostics( // (1,1): warning CS1717: Assignment made to same variable; did you mean to assign something else? // data class C(object P1, object P2, object P3, object P4, object P5) @@ -2454,7 +2434,6 @@ static void Main() var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); AssertEx.Equal(new string[0], actualMembers); - // https://github.com/dotnet/roslyn/issues/44693: Record constructor should assign to explicit properties. var verifier = CompileAndVerify(comp, expectedOutput: "(, )"); verifier.VerifyIL("C..ctor(object, object)", @"{ @@ -2505,11 +2484,7 @@ data class C(object P1, object P2) : B var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); - var expectedMembers = new[] - { - "System.Object C.P2 { get; init; }", - }; - AssertEx.Equal(expectedMembers, actualMembers); + AssertEx.Equal(new string[0], actualMembers); } [Fact] @@ -2532,11 +2507,7 @@ data class C(object P1, object P2) : B var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings(); - var expectedMembers = new[] - { - "System.Object C.P2 { get; init; }", - }; - AssertEx.Equal(expectedMembers, actualMembers); + AssertEx.Equal(new string[0], actualMembers); } [Fact] @@ -2546,19 +2517,21 @@ public void Inheritance_24() @"class A { public object get_P() => null; + public object set_Q() => null; } -data class B(object P) : A +data class B(object P, object Q) : A { } data class C(object P) { public object get_P() => null; + public object set_Q() => null; }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (8,21): error CS0082: Type 'C' already reserves a member called 'get_P' with the same parameter types + // (9,21): error CS0082: Type 'C' already reserves a member called 'get_P' with the same parameter types // data class C(object P) - Diagnostic(ErrorCode.ERR_MemberReserved, "P").WithArguments("get_P", "C").WithLocation(8, 21)); + Diagnostic(ErrorCode.ERR_MemberReserved, "P").WithArguments("get_P", "C").WithLocation(9, 21)); var expectedMembers = new[] { @@ -2567,11 +2540,15 @@ data class C(object P) "System.Object B.P.get", "void modreq(System.Runtime.CompilerServices.IsExternalInit) B.P.init", "System.Object B.P { get; init; }", + "System.Object B.k__BackingField", + "System.Object B.Q.get", + "void modreq(System.Runtime.CompilerServices.IsExternalInit) B.Q.init", + "System.Object B.Q { get; init; }", "System.Boolean B.Equals(B? )", "System.Boolean B.Equals(System.Object? )", "System.Int32 B.GetHashCode()", "B..ctor(B )", - "B..ctor(System.Object P)" + "B..ctor(System.Object P, System.Object Q)" }; AssertEx.Equal(expectedMembers, comp.GetMember("B").GetMembers().ToTestDisplayStrings()); @@ -2583,6 +2560,7 @@ data class C(object P) "void modreq(System.Runtime.CompilerServices.IsExternalInit) C.P.init", "System.Object C.P { get; init; }", "System.Object C.get_P()", + "System.Object C.set_Q()", "System.Boolean C.Equals(C? )", "System.Boolean C.Equals(System.Object? )", "System.Int32 C.GetHashCode()", @@ -2594,6 +2572,89 @@ data class C(object P) [Fact] public void Inheritance_25() + { + var sourceA = +@"public class A +{ + public class P1 { } + internal object P2 = 2; + public int P3(object o) => 3; + internal int P4(T t) => 4; +}"; + var sourceB = +@"data class B(object P1, object P2, object P3, object P4) : A +{ +}"; + var comp = CreateCompilation(new[] { sourceA, sourceB }); + comp.VerifyDiagnostics(); + var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object B.P4 { get; init; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + + comp = CreateCompilation(sourceA); + var refA = comp.EmitToImageReference(); + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); + expectedMembers = new[] + { + "System.Object B.P2 { get; init; }", + "System.Object B.P4 { get; init; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [Fact] + public void Inheritance_26() + { + var sourceA = +@"public class A +{ + internal const int P = 4; +}"; + var sourceB = +@"data class B(object P) : A +{ +}"; + var comp = CreateCompilation(new[] { sourceA, sourceB }); + comp.VerifyDiagnostics(); + AssertEx.Equal(new string[0], GetProperties(comp, "B").ToTestDisplayStrings()); + + comp = CreateCompilation(sourceA); + var refA = comp.EmitToImageReference(); + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + AssertEx.Equal(new[] { "System.Object B.P { get; init; }" }, GetProperties(comp, "B").ToTestDisplayStrings()); + } + + [Fact] + public void Inheritance_27() + { + var source = +@"class A +{ + public object P { get; } + public object Q { get; set; } +} +data class B(object get_P, object set_Q) : A +{ +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); + var expectedMembers = new[] + { + "System.Object B.get_P { get; init; }", + "System.Object B.set_Q { get; init; }", + }; + AssertEx.Equal(expectedMembers, actualMembers); + } + + [Fact] + public void Inheritance_28() { var source = @"interface I @@ -2618,7 +2679,7 @@ data class C(object P) : I } [Fact] - public void Inheritance_26() + public void Inheritance_29() { var sourceA = @"Public Class A @@ -2660,7 +2721,7 @@ End Class } [Fact] - public void Inheritance_27() + public void Inheritance_30() { var sourceA = @"Public Class A @@ -2706,7 +2767,7 @@ End Class } [Fact] - public void Inheritance_28() + public void Inheritance_31() { var sourceA = @"Public Class A @@ -2937,11 +2998,7 @@ data class B(object Q) : A Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "Q").WithArguments("A", "Q").WithLocation(6, 16)); var actualMembers = GetProperties(comp, "B").ToTestDisplayStrings(); - var expectedMembers = new[] - { - "System.Object B.Q { get; init; }", - }; - AssertEx.Equal(expectedMembers, actualMembers); + AssertEx.Equal(new string[0], actualMembers); } private static ImmutableArray GetProperties(CSharpCompilation comp, string typeName) From 454320bb8f0af2dd788fdf9ffab69c120d63b2b6 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Tue, 2 Jun 2020 12:11:32 -0700 Subject: [PATCH 6/7] Fix formatting --- .../Portable/Symbols/Source/SourceMemberContainerSymbol.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index cdd6160a4eced..802ef292f4242 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -3590,9 +3590,9 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r } } -#endregion + #endregion -#region Extension Methods + #region Extension Methods internal bool ContainsExtensionMethods { @@ -3630,7 +3630,7 @@ public override bool MightContainExtensionMethods } } -#endregion + #endregion public sealed override NamedTypeSymbol ConstructedFrom { From 46b87c4361f9030b9aab1cf45a887a1927db8fa8 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Tue, 2 Jun 2020 17:19:10 -0700 Subject: [PATCH 7/7] Misc. --- .../Test/Semantic/Semantics/RecordTests.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index e39565f4b897a..ff6f652f4c111 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -2555,6 +2555,7 @@ static void Main() AssertEx.Equal(new string[0], actualMembers); } + [WorkItem(44785, "https://github.com/dotnet/roslyn/issues/44785")] [Fact] public void Inheritance_07() { @@ -2580,6 +2581,7 @@ data class B2(int X, int Y) : A AssertEx.Equal(new string[0], GetProperties(comp, "B2").ToTestDisplayStrings()); } + [WorkItem(44785, "https://github.com/dotnet/roslyn/issues/44785")] [Fact] public void Inheritance_08() { @@ -2621,6 +2623,23 @@ public void Inheritance_09() }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); + + var actualMembers = comp.GetMember("C").GetMembers().ToTestDisplayStrings(); + var expectedMembers = new[] + { + "C C.Clone()", + "System.Int32 C.X { get; }", + "System.Int32 C.X.get", + "System.Int32 C.k__BackingField", + "System.Int32 C.Y { get; }", + "System.Int32 C.Y.get", + "System.Boolean C.Equals(C? )", + "System.Boolean C.Equals(System.Object? )", + "System.Int32 C.GetHashCode()", + "C..ctor(C )", + "C..ctor(System.Int32 X, System.Int32 Y)", + }; + AssertEx.Equal(expectedMembers, actualMembers); } [Fact]