From 56cfdeb7ecaaa38b8a1e6145a7bf6774f046fbcd Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 29 Jul 2020 13:15:54 -0700 Subject: [PATCH 1/3] Add tests from records test plan --- .../CSharp/Portable/CSharpResources.resx | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 4 +- .../Portable/xlf/CSharpResources.de.xlf | 4 +- .../Portable/xlf/CSharpResources.es.xlf | 4 +- .../Portable/xlf/CSharpResources.fr.xlf | 4 +- .../Portable/xlf/CSharpResources.it.xlf | 4 +- .../Portable/xlf/CSharpResources.ja.xlf | 4 +- .../Portable/xlf/CSharpResources.ko.xlf | 4 +- .../Portable/xlf/CSharpResources.pl.xlf | 4 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 4 +- .../Portable/xlf/CSharpResources.ru.xlf | 4 +- .../Portable/xlf/CSharpResources.tr.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 4 +- .../Test/Semantic/Semantics/RecordTests.cs | 374 +++++++++++++++++- .../Test/Symbol/Symbols/Source/RecordTests.cs | 15 + 16 files changed, 412 insertions(+), 31 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 8b69d94aeaf3d..d96087e0635f9 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6152,7 +6152,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ init-only setters - A positional record must have both a 'data' modifier and non-empty parameter list + A positional record must have a non-empty parameter list The receiver of a `with` expression must have a non-void type. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 2d72ca040048e..5786b6996ab0c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -138,8 +138,8 @@ - A positional record must have both a 'data' modifier and non-empty parameter list - Poziční záznam musí mít modifikátor data a neprázdný seznam parametrů. + A positional record must have a non-empty parameter list + Poziční záznam musí mít modifikátor data a neprázdný seznam parametrů. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 24b7bfd937b38..a62e8489df149 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -138,8 +138,8 @@ - A positional record must have both a 'data' modifier and non-empty parameter list - Ein positioneller Datensatz muss sowohl einen data-Modifizierer als auch eine nicht leere Parameterliste aufweisen. + A positional record must have a non-empty parameter list + Ein positioneller Datensatz muss sowohl einen data-Modifizierer als auch eine nicht leere Parameterliste aufweisen. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 468c9875f8442..b4a14acb6cb3f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -138,8 +138,8 @@ - A positional record must have both a 'data' modifier and non-empty parameter list - Un registro posicional debe tener un modificador "data" y una lista de parámetros no vacía + A positional record must have a non-empty parameter list + Un registro posicional debe tener un modificador "data" y una lista de parámetros no vacía diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 7fad19657ce32..64cc743e7351a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -138,8 +138,8 @@ - A positional record must have both a 'data' modifier and non-empty parameter list - Un enregistrement positionnel doit avoir à la fois un modificateur 'data' et une liste de paramètres non vide + A positional record must have a non-empty parameter list + Un enregistrement positionnel doit avoir à la fois un modificateur 'data' et une liste de paramètres non vide diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 28c3739cf1ac9..5eb832379f7ca 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -138,8 +138,8 @@ - A positional record must have both a 'data' modifier and non-empty parameter list - Un record posizionale deve include sia un modificatore 'data' che un elenco di parametri non vuoto + A positional record must have a non-empty parameter list + Un record posizionale deve include sia un modificatore 'data' che un elenco di parametri non vuoto diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index f6080b15f0976..c224e701ced35 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -138,8 +138,8 @@ - A positional record must have both a 'data' modifier and non-empty parameter list - 位置指定レコードには、'data' 修飾子と空でないパラメーター リストの両方が必要です + A positional record must have a non-empty parameter list + 位置指定レコードには、'data' 修飾子と空でないパラメーター リストの両方が必要です diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 427cbbdf0451c..1868939eb9661 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -138,8 +138,8 @@ - A positional record must have both a 'data' modifier and non-empty parameter list - 위치 레코드에는 'data' 한정자와 비어 있지 않은 매개 변수 목록이 둘 다 있어야 합니다. + A positional record must have a non-empty parameter list + 위치 레코드에는 'data' 한정자와 비어 있지 않은 매개 변수 목록이 둘 다 있어야 합니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 535fbddfc7158..950cb973141c5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -138,8 +138,8 @@ - A positional record must have both a 'data' modifier and non-empty parameter list - Rekord pozycyjny musi mieć zarówno modyfikator „data”, jak i niepustą listę parametrów + A positional record must have a non-empty parameter list + Rekord pozycyjny musi mieć zarówno modyfikator „data”, jak i niepustą listę parametrów diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 0340587a59fd5..ce2b6dc3f0e68 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -138,8 +138,8 @@ - A positional record must have both a 'data' modifier and non-empty parameter list - Um registro posicional precisa ter um modificador 'data' e uma lista de parâmetros não vazios + A positional record must have a non-empty parameter list + Um registro posicional precisa ter um modificador 'data' e uma lista de parâmetros não vazios diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 60f46d0f4b3cb..e02c3e370ee01 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -138,8 +138,8 @@ - A positional record must have both a 'data' modifier and non-empty parameter list - Позиционная запись должна иметь модификатор "data" и непустой список параметров. + A positional record must have a non-empty parameter list + Позиционная запись должна иметь модификатор "data" и непустой список параметров. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index d7535fdf13d7d..6639a9681a457 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -138,8 +138,8 @@ - A positional record must have both a 'data' modifier and non-empty parameter list - Konumsal bir kaydın hem 'data' değiştiricisi hem de boş olmayan bir parametre listesi olmalıdır + A positional record must have a non-empty parameter list + Konumsal bir kaydın hem 'data' değiştiricisi hem de boş olmayan bir parametre listesi olmalıdır diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 40db8f961e779..25852c072f33f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -138,8 +138,8 @@ - A positional record must have both a 'data' modifier and non-empty parameter list - 位置记录必须同时具有 "data" 修饰符和非空参数列表 + A positional record must have a non-empty parameter list + 位置记录必须同时具有 "data" 修饰符和非空参数列表 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index e617fc714b865..f585bcfba2896 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -138,8 +138,8 @@ - A positional record must have both a 'data' modifier and non-empty parameter list - 位置記錄必須同時具有 'data' 修飾元及非空白的參數清單 + A positional record must have a non-empty parameter list + 位置記錄必須同時具有 'data' 修飾元及非空白的參數清單 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index d4d053833a8fe..d91c08442d476 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -41,7 +41,7 @@ private CompilationVerifier CompileAndVerify( // init-only is unverifiable verify: Verification.Skipped); - [Fact] + [Fact, WorkItem(45900, "https://github.com/dotnet/roslyn/issues/45900")] public void RecordLanguageVersion() { var src1 = @" @@ -151,6 +151,86 @@ record Point(int x, int y); comp.VerifyDiagnostics(); } + [Fact, WorkItem(45900, "https://github.com/dotnet/roslyn/issues/45900")] + public void RecordLanguageVersion_Nested() + { + var src1 = @" +class C +{ + class Point(int x, int y); +} +"; + var src2 = @" +class D +{ + record Point { } +} +"; + var src3 = @" +class E +{ + record Point(int x, int y); +} +"; + var comp = CreateCompilation(src1, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (4,16): error CS1514: { expected + // class Point(int x, int y); + Diagnostic(ErrorCode.ERR_LbraceExpected, "(").WithLocation(4, 16), + // (4,16): error CS1513: } expected + // class Point(int x, int y); + Diagnostic(ErrorCode.ERR_RbraceExpected, "(").WithLocation(4, 16), + // (4,30): error CS1519: Invalid token ';' in class, struct, or interface member declaration + // class Point(int x, int y); + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(4, 30), + // (4,30): error CS1519: Invalid token ';' in class, struct, or interface member declaration + // class Point(int x, int y); + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(4, 30) + ); + + comp = CreateCompilation(src2, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (4,5): error CS0246: The type or namespace name 'record' could not be found (are you missing a using directive or an assembly reference?) + // record Point { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "record").WithArguments("record").WithLocation(4, 5), + // (4,12): error CS0548: 'D.Point': property or indexer must have at least one accessor + // record Point { } + Diagnostic(ErrorCode.ERR_PropertyWithNoAccessors, "Point").WithArguments("D.Point").WithLocation(4, 12) + ); + + comp = CreateCompilation(src3, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (4,5): error CS0246: The type or namespace name 'record' could not be found (are you missing a using directive or an assembly reference?) + // record Point(int x, int y); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "record").WithArguments("record").WithLocation(4, 5), + // (4,12): error CS0501: 'E.Point(int, int)' must declare a body because it is not marked abstract, extern, or partial + // record Point(int x, int y); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "Point").WithArguments("E.Point(int, int)").WithLocation(4, 12) + ); + + comp = CreateCompilation(src1); + comp.VerifyDiagnostics( + // (4,16): error CS1514: { expected + // class Point(int x, int y); + Diagnostic(ErrorCode.ERR_LbraceExpected, "(").WithLocation(4, 16), + // (4,16): error CS1513: } expected + // class Point(int x, int y); + Diagnostic(ErrorCode.ERR_RbraceExpected, "(").WithLocation(4, 16), + // (4,30): error CS1519: Invalid token ';' in class, struct, or interface member declaration + // class Point(int x, int y); + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(4, 30), + // (4,30): error CS1519: Invalid token ';' in class, struct, or interface member declaration + // class Point(int x, int y); + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(4, 30) + ); + + comp = CreateCompilation(src2); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(src3); + comp.VerifyDiagnostics(); + } + [Fact, WorkItem(46123, "https://github.com/dotnet/roslyn/issues/46123")] public void IncompletePositionalRecord() { @@ -410,7 +490,7 @@ public static void M() } [Fact] - public void PartialRecordMixedWithClass() + public void PartialRecord_MixedWithClass() { var src = @" partial record C(int X, int Y) @@ -428,6 +508,44 @@ partial class C ); } + [Fact] + public void PartialRecord_ParametersInScopeOfBothParts() + { + var src = @" +var c = new C(2); +System.Console.Write((c.P1, c.P2)); + +public partial record C(int X) +{ + public int P1 { get; set; } = X; +} +public partial record C +{ + public int P2 { get; set; } = X; +} +"; + var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(2, 2)", verify: Verification.Skipped /* init-only */); + } + + [Fact] + public void RecordInsideGenericType() + { + var src = @" +var c = new C.Nested(2); +System.Console.Write(c.T); + +public class C +{ + public record Nested(T T); +} +"; + var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "2", verify: Verification.Skipped /* init-only */); + } + [Fact] public void RecordProperties_01() { @@ -700,7 +818,7 @@ record C(); "; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (2,9): error CS8850: A positional record must have both a 'data' modifier and non-empty parameter list + // (2,9): error CS8850: A positional record must have a non-empty parameter list // record C(); Diagnostic(ErrorCode.ERR_BadRecordDeclaration, "()").WithLocation(2, 9) ); @@ -1465,6 +1583,204 @@ public static void Main() ); } + [Fact] + public void WithExpr24_Dynamic() + { + var src = @" +record C(int X) +{ + public static void Main() + { + dynamic c = new C(1); + var x = c with { X = 2 }; + } +}"; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (7,17): error CS8858: The receiver type 'dynamic' is not a valid record type. + // var x = c with { X = 2 }; + Diagnostic(ErrorCode.ERR_NoSingleCloneMethod, "c").WithArguments("dynamic").WithLocation(7, 17) + ); + } + + [Fact, WorkItem(46427, "https://github.com/dotnet/roslyn/issues/46427")] + public void WithExpr25_TypeParameterWithRecordConstraint() + { + var src = @" +record R(int X); + +class C +{ + public static void M(T t) where T : R + { + _ = t with { X = 2 }; + } +}"; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (8,13): error CS8858: The receiver type 'T' is not a valid record type. + // _ = t with { X = 2 }; + Diagnostic(ErrorCode.ERR_NoSingleCloneMethod, "t").WithArguments("T").WithLocation(8, 13) + ); + } + + [Fact, WorkItem(46427, "https://github.com/dotnet/roslyn/issues/46427")] + public void WithExpr26_TypeParameterWithRecordAndInterfaceConstraint() + { + var src = @" +record R(int X); +interface I { int Property { get; set; } } + +class C +{ + public static void M(T t) where T : R, I + { + _ = t with { X = 2, Property = 3 }; + } +}"; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (9,13): error CS8858: The receiver type 'T' is not a valid record type. + // _ = t with { X = 2, Property = 3 }; + Diagnostic(ErrorCode.ERR_NoSingleCloneMethod, "t").WithArguments("T").WithLocation(9, 13) + ); + } + + [Fact] + public void WithExpr27_InExceptionFilter() + { + var src = @" +var r = new R(1); + +try +{ + throw new System.Exception(); +} +catch (System.Exception) when ((r = r with { X = 2 }).X == 2) +{ + System.Console.Write(""RAN ""); + System.Console.Write(r.X); +} + +record R(int X); +"; + var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "RAN 2", verify: Verification.Skipped /* init-only */); + } + + [Fact] + public void WithExpr28_WithAwait() + { + var src = @" +var r = new R(1); +r = r with { X = await System.Threading.Tasks.Task.FromResult(42) }; +System.Console.Write(r.X); + +record R(int X); +"; + var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "42", verify: Verification.Skipped /* init-only */); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var x = tree.GetRoot().DescendantNodes().OfType().Last().Left; + Assert.Equal("X", x.ToString()); + var symbol = model.GetSymbolInfo(x).Symbol; + Assert.Equal("System.Int32 R.X { get; init; }", symbol.ToTestDisplayString()); + } + + [Fact, WorkItem(46465, "https://github.com/dotnet/roslyn/issues/46465")] + public void WithExpr29_DisallowedAsExpressionStatement() + { + var src = @" +record R(int X) +{ + void M() + { + var r = new R(1); + r with { X = 2 }; + } +} +"; + // Note: we didn't parse the `with` as a `with` expression, but as a broken local declaration + // Tracked by https://github.com/dotnet/roslyn/issues/46465 + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (7,9): error CS0118: 'r' is a variable but is used like a type + // r with { X = 2 }; + Diagnostic(ErrorCode.ERR_BadSKknown, "r").WithArguments("r", "variable", "type").WithLocation(7, 9), + // (7,11): warning CS0168: The variable 'with' is declared but never used + // r with { X = 2 }; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "with").WithArguments("with").WithLocation(7, 11), + // (7,16): error CS1002: ; expected + // r with { X = 2 }; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "{").WithLocation(7, 16), + // (7,18): error CS8852: Init-only property or indexer 'R.X' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor. + // r with { X = 2 }; + Diagnostic(ErrorCode.ERR_AssignmentInitOnly, "X").WithArguments("R.X").WithLocation(7, 18), + // (7,24): error CS1002: ; expected + // r with { X = 2 }; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "}").WithLocation(7, 24) + ); + } + + [Fact] + public void TypeNamedRecord() + { + var src = @" +class record { } + +class C +{ + record M(record r) => r; +}"; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (6,24): error CS1514: { expected + // record M(record r) => r; + Diagnostic(ErrorCode.ERR_LbraceExpected, "=>").WithLocation(6, 24), + // (6,24): error CS1513: } expected + // record M(record r) => r; + Diagnostic(ErrorCode.ERR_RbraceExpected, "=>").WithLocation(6, 24), + // (6,24): error CS1519: Invalid token '=>' in class, struct, or interface member declaration + // record M(record r) => r; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "=>").WithArguments("=>").WithLocation(6, 24), + // (6,28): error CS1519: Invalid token ';' in class, struct, or interface member declaration + // record M(record r) => r; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(6, 28), + // (6,28): error CS1519: Invalid token ';' in class, struct, or interface member declaration + // record M(record r) => r; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(6, 28) + ); + } + + [Fact] + public void TypeNamedRecord_EscapedReturnType() + { + var src = @" +class record { } + +class C +{ + @record M(record r) + { + System.Console.Write(""RAN""); + return r; + } + + public static void Main() + { + var c = new C(); + _ = c.M(new record()); + } +}"; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "RAN"); + } + [Fact, WorkItem(45591, "https://github.com/dotnet/roslyn/issues/45591")] public void Clone_DisallowedInSource() { @@ -9893,7 +10209,7 @@ static void Main() "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (2,9): error CS8850: A positional record must have both a 'data' modifier and non-empty parameter list + // (2,9): error CS8850: A positional record must have a non-empty parameter list // record C() Diagnostic(ErrorCode.ERR_BadRecordDeclaration, "()").WithLocation(2, 9)); @@ -18121,6 +18437,34 @@ record B : IEquatable> Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "B").WithArguments("A.B", "System.IEquatable.B>", "System.IEquatable.B>").WithLocation(4, 12)); } + [Fact] + public void InterfaceImplementation() + { + var source = @" +interface I +{ + int P1 { get; init; } + int P2 { get; init; } + int P3 { get; set; } +} +record R(int P1) : I +{ + public int P2 { get; init; } + int I.P3 { get; set; } + + public static void Main() + { + I r = new R(42) { P2 = 43 }; + r.P3 = 44; + System.Console.Write((r.P1, r.P2, r.P3)); + } +} +"; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(42, 43, 44)", verify: Verification.Skipped /* init-only */); + } + [Fact] public void Initializers_01() { @@ -19063,6 +19407,28 @@ protected C(C other) ); } + [Fact] + public void AttributesOnPrimaryConstructorParameters_09_CallerMemberName() + { + string source = @" +using System.Runtime.CompilerServices; +record R([CallerMemberName] string S = """"); + +class C +{ + public static void Main() + { + var r = new R(); + System.Console.Write(r.S); + } +} +"; + var comp = CompileAndVerify(new[] { source, IsExternalInitTypeDefinition }, expectedOutput: "Main", + parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe, verify: Verification.Skipped /* init-only */); + + comp.VerifyDiagnostics(); + } + [Fact] public void RecordWithConstraints_NullableWarning() { diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs index 49b26f6790879..1406809e6daa3 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs @@ -1176,6 +1176,21 @@ .maxstack 2 }"); } + [Fact] + public void PartialTypes_04_PartialBeforeModifiers() + { + var src = @" +partial public record C +{ +} +"; + CreateCompilation(src).VerifyDiagnostics( + // (2,1): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'struct', 'interface', or 'void' + // partial public record C + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(2, 1) + ); + } + [Fact] public void DataClassAndStruct() { From 5777b907e6fdb24dd61d2a9ddb55c2c3a70d77e4 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 3 Aug 2020 16:00:05 -0700 Subject: [PATCH 2/3] Address feedback --- src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs | 3 +-- .../CSharp/Test/Symbol/Symbols/Source/RecordTests.cs | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index d91c08442d476..a834bdbb9ab8d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -525,8 +525,7 @@ public partial record C } "; var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); - comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "(2, 2)", verify: Verification.Skipped /* init-only */); + CompileAndVerify(comp, expectedOutput: "(2, 2)", verify: Verification.Skipped /* init-only */).VerifyDiagnostics(); } [Fact] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs index 1406809e6daa3..a247a450222d7 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs @@ -1185,9 +1185,7 @@ partial public record C } "; CreateCompilation(src).VerifyDiagnostics( - // (2,1): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'struct', 'interface', or 'void' - // partial public record C - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(2, 1) + // TODO2 ); } From b60ab8fdd517b2bf056ab512dff169e2087de214 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 3 Aug 2020 16:18:19 -0700 Subject: [PATCH 3/3] Update test comment --- .../CSharp/Test/Symbol/Symbols/Source/RecordTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs index a247a450222d7..456a890d265d4 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs @@ -1185,7 +1185,9 @@ partial public record C } "; CreateCompilation(src).VerifyDiagnostics( - // TODO2 + // (2,1): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or 'void' + // partial public record C + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(2, 1) ); }