diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 0dc34093a0e1c..8e03ebf86f977 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6212,7 +6212,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 264a6876ee59c..a40fba9b77710 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 43917bf2dafbc..7aceebcd28eb1 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 9bc27156d91a6..b11ba3d893ad0 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 250dac25b7894..f7bdf54251b38 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 991508a364e72..88739140a5f59 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 226489ab90f35..4b85334bc2c9b 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 7f54d5272385f..931b55830f995 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 f3a92910ee907..75d1cdc8a54c3 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 324f851bae83e..709acb7ea93e9 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 3ffdd5d944674..031628ceca3f3 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 9d89405eaecba..6d534b54aa4ef 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 2bc01e35e4bcf..2081ce715b081 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 04884e12b25e8..7ad3d8242de9e 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 c773704ed041d..f83cc1df04088 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,43 @@ 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); + CompileAndVerify(comp, expectedOutput: "(2, 2)", verify: Verification.Skipped /* init-only */).VerifyDiagnostics(); + } + + [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 +817,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 +1582,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() { @@ -9949,7 +10264,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)); @@ -18177,6 +18492,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() { @@ -19119,6 +19462,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..456a890d265d4 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', 'record', 'struct', 'interface', or 'void' + // partial public record C + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(2, 1) + ); + } + [Fact] public void DataClassAndStruct() {