From 8258db4000f770a813cf76ca63d23f6b91aa89c5 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 12 Oct 2022 13:56:29 -0700 Subject: [PATCH] Add tests for ref fields --- .../Test/Semantic/Semantics/RefFieldTests.cs | 261 +++++++++++++++++- .../Syntax/Parsing/RefFieldParsingTests.cs | 203 ++++++++++++++ .../SyntacticClassifierTests.cs | 50 ++++ 3 files changed, 507 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 6bd94cd0af17c..4141410d132ee 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -345,6 +345,148 @@ ref struct B VerifyFieldSymbol(field, "ref readonly System.Int32 A.F", RefKind.RefReadOnly, new string[0]); } + [WorkItem(64682, "https://github.com/dotnet/roslyn/issues/64682")] + [Fact] + public void RefFieldInNonRefStruct() + { + var sourceA = +@".class public A +{ + .field public !0& F1 +} +.class public sealed S extends [mscorlib]System.ValueType +{ + .field public int32 F2 +}"; + var refA = CompileIL(sourceA); + + var sourceB = +@"class Program +{ + static ref int F1(ref A a) => ref a.F1; // 1 + static ref int F2(ref S s) => ref s.F2; // 2 +}"; + // https://github.com/dotnet/roslyn/issues/64682: Should report use-site errors. + var comp = CreateCompilation(sourceB, new[] { refA }, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, verify: Verification.Skipped); + } + + [Fact] + public void RefAndReadonlyRefStruct_01() + { + var source = +@"#pragma warning disable 169 +ref struct A +{ + ref int A1; + ref readonly int A2; + readonly ref int A3; + readonly ref readonly int A4; +} +readonly ref struct B +{ + ref int B1; + ref readonly int B2; + readonly ref int B3; + readonly ref readonly int B4; +}"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (11,13): error CS8340: Instance fields of readonly structs must be readonly. + // ref int B1; + Diagnostic(ErrorCode.ERR_FieldsInRoStruct, "B1").WithLocation(11, 13), + // (12,22): error CS8340: Instance fields of readonly structs must be readonly. + // ref readonly int B2; + Diagnostic(ErrorCode.ERR_FieldsInRoStruct, "B2").WithLocation(12, 22)); + } + + /// + /// ref readonly fields emitted as initonly. + /// ref readonly fields emitted with System.Runtime.CompilerServices.IsReadOnlyAttribute. + /// + [Fact] + public void RefAndReadonlyRefStruct_02() + { + var source = +@"#pragma warning disable 169 +ref struct A +{ + ref int A1; + ref readonly int A2; + readonly ref int A3; + readonly ref readonly int A4; +} +readonly ref struct B +{ + readonly ref int B3; + readonly ref readonly int B4; +}"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics(); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped); + verifier.VerifyTypeIL("A", +@".class private sequential ansi sealed beforefieldinit A + extends [System.Runtime]System.ValueType +{ + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + .custom instance void [System.Runtime]System.ObsoleteAttribute::.ctor(string, bool) = ( + 01 00 52 54 79 70 65 73 20 77 69 74 68 20 65 6d + 62 65 64 64 65 64 20 72 65 66 65 72 65 6e 63 65 + 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f 72 + 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 73 + 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d 70 + 69 6c 65 72 2e 01 00 00 + ) + .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::.ctor(string) = ( + 01 00 0a 52 65 66 53 74 72 75 63 74 73 00 00 + ) + // Fields + .field private int32& A1 + .field private int32& A2 + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + .field private initonly int32& A3 + .field private initonly int32& A4 + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) +} // end of class A +"); + verifier.VerifyTypeIL("B", +@".class private sequential ansi sealed beforefieldinit B + extends [System.Runtime]System.ValueType +{ + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + .custom instance void [System.Runtime]System.ObsoleteAttribute::.ctor(string, bool) = ( + 01 00 52 54 79 70 65 73 20 77 69 74 68 20 65 6d + 62 65 64 64 65 64 20 72 65 66 65 72 65 6e 63 65 + 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f 72 + 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 73 + 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d 70 + 69 6c 65 72 2e 01 00 00 + ) + .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::.ctor(string) = ( + 01 00 0a 52 65 66 53 74 72 75 63 74 73 00 00 + ) + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field private initonly int32& B3 + .field private initonly int32& B4 + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) +} // end of class B +"); + } + [Fact] public void TupleField() { @@ -7422,19 +7564,21 @@ class Program static void Main() { int i = 1; - Try(() => ReadAndDiscard1(ref i)); - Try(() => ReadAndDiscardNoArg()); - Try(() => ReadAndDiscard2(new S(ref i))); + Try(1, () => ReadAndDiscard1(ref i)); + Try(2, () => ReadAndDiscardNoArg()); + Try(3, () => ReadAndDiscard2(new S(ref i))); + Try(4, () => ReadAndDiscard2(new S())); - void Try(Action a) + void Try(int i, Action a) { try { a(); + Console.WriteLine(i); } catch (NullReferenceException) { - System.Console.WriteLine(""NullReferenceException""); + Console.WriteLine(""NullReferenceException""); } } } @@ -7452,7 +7596,12 @@ static void ReadAndDiscard2(in S s) } }"; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); - var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("NullReferenceException")); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( +@"1 +NullReferenceException +3 +NullReferenceException +")); verifier.VerifyIL("Program.ReadAndDiscard1", """ { // Code size 18 (0x12) @@ -22320,7 +22469,7 @@ static ref int F2([UnscopedRef(F1())] out int i) } [Fact] - public void UnscopedRefAttribute_ScopeRefAttribute() + public void UnscopedRefAttribute_ScopeRefAttribute_Out() { var sourceA = @".assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } @@ -22417,6 +22566,104 @@ static ref int F4() VerifyParameterSymbol(typeA.GetMethod("ScopedRefAndUnscopedRef").Parameters[0], "out System.Int32 i", RefKind.Out, DeclarationScope.Unscoped); } + [WorkItem(64778, "https://github.com/dotnet/roslyn/issues/64778")] + [Fact] + public void UnscopedRefAttribute_ScopeRefAttribute_RefToRefStruct() + { + var sourceA = +@".assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } +.assembly '<>' { } +.module '<>.dll' +.custom instance void System.Runtime.CompilerServices.RefSafetyRulesAttribute::.ctor(int32) = { int32(11) } +.class private System.Runtime.CompilerServices.RefSafetyRulesAttribute extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname instance void .ctor(int32 version) cil managed { ret } + .field public int32 Version +} +.class private System.Diagnostics.CodeAnalysis.UnscopedRefAttribute extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } +} +.class private System.Runtime.CompilerServices.ScopedRefAttribute extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } +} +.class public sealed R extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = (01 00 00 00) + .field public int32& F +} +.class public A +{ + .method public static void NoAttributes(valuetype R& x, valuetype R& y) + { + ldnull + throw + } + .method public static void ScopedRefOnly(valuetype R& x, valuetype R& y) + { + .param [1] + .custom instance void System.Runtime.CompilerServices.ScopedRefAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + throw + } + .method public static void UnscopedRefOnly(valuetype R& x, valuetype R& y) + { + .param [1] + .custom instance void System.Diagnostics.CodeAnalysis.UnscopedRefAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + throw + } + .method public static void ScopedRefAndUnscopedRef(valuetype R& x, valuetype R& y) + { + .param [1] + .custom instance void System.Diagnostics.CodeAnalysis.UnscopedRefAttribute::.ctor() = ( 01 00 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.ScopedRefAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + throw + } +} +"; + var refA = CompileIL(sourceA, prependDefaultHeader: false); + + var sourceB = +@"class Program +{ + static void F1(ref R x, ref R y) + { + A.NoAttributes(ref x, ref y); + } + static void F2(ref R x, ref R y) + { + A.ScopedRefOnly(ref x, ref y); + } + static void F3(ref R x, ref R y) + { + A.UnscopedRefOnly(ref x, ref y); // 1 + } + static void F4(ref R x, ref R y) + { + A.ScopedRefAndUnscopedRef(ref x, ref y); // 2 + } +}"; + // https://github.com/dotnet/roslyn/issues/64778: No error reported for F3 because + // [UnscopedRef] ref R y is currently treated as ref R y from metadata. + var comp = CreateCompilation(sourceB, new[] { refA }); + comp.VerifyEmitDiagnostics( + // (17,11): error CS0570: 'A.ScopedRefAndUnscopedRef(ref R, ref R)' is not supported by the language + // A.ScopedRefAndUnscopedRef(ref x, ref y); // 2 + Diagnostic(ErrorCode.ERR_BindToBogus, "ScopedRefAndUnscopedRef").WithArguments("A.ScopedRefAndUnscopedRef(ref R, ref R)").WithLocation(17, 11)); + + // https://github.com/dotnet/roslyn/issues/64778: + // [UnscopedRef] ref R y is currently treated as ref R y from metadata. + var typeA = comp.GetMember("A"); + VerifyParameterSymbol(typeA.GetMethod("NoAttributes").Parameters[0], "ref R x", RefKind.Ref, DeclarationScope.Unscoped); + VerifyParameterSymbol(typeA.GetMethod("ScopedRefOnly").Parameters[0], "scoped ref R x", RefKind.Ref, DeclarationScope.RefScoped); + VerifyParameterSymbol(typeA.GetMethod("UnscopedRefOnly").Parameters[0], "ref R x", RefKind.Ref, DeclarationScope.Unscoped); + VerifyParameterSymbol(typeA.GetMethod("ScopedRefAndUnscopedRef").Parameters[0], "ref R x", RefKind.Ref, DeclarationScope.Unscoped); + } + [Fact] [WorkItem(63529, "https://github.com/dotnet/roslyn/issues/63529")] public void CallUnscopedRefMethodFromScopedOne() diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs index 2757f2136b62a..87edf31432db6 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs @@ -679,5 +679,208 @@ public void RefComplexElementInitializer(LanguageVersion languageVersion) } EOF(); } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void AnonymousType_01(LanguageVersion languageVersion) + { + string source = "new { ref x }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,7): error CS1525: Invalid expression term 'ref' + // new { ref x } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref x").WithArguments("ref").WithLocation(1, 7)); + + N(SyntaxKind.AnonymousObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void AnonymousType_02(LanguageVersion languageVersion) + { + string source = "new { ref x, y }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,7): error CS1525: Invalid expression term 'ref' + // new { ref x, y } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref x").WithArguments("ref").WithLocation(1, 7)); + + N(SyntaxKind.AnonymousObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void AnonymousType_03(LanguageVersion languageVersion) + { + string source = "new { x, ref y }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,10): error CS1525: Invalid expression term 'ref' + // new { x, ref y } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref y").WithArguments("ref").WithLocation(1, 10)); + + N(SyntaxKind.AnonymousObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void AnonymousType_04(LanguageVersion languageVersion) + { + string source = "new { P = ref x, y }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,11): error CS1525: Invalid expression term 'ref' + // new { P = ref x, y } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref x").WithArguments("ref").WithLocation(1, 11)); + + N(SyntaxKind.AnonymousObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.NameEquals); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "P"); + } + N(SyntaxKind.EqualsToken); + } + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void AnonymousType_05(LanguageVersion languageVersion) + { + string source = "new { x, Q = ref y }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,14): error CS1525: Invalid expression term 'ref' + // new { x, Q = ref y } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref y").WithArguments("ref").WithLocation(1, 14)); + + N(SyntaxKind.AnonymousObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.NameEquals); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Q"); + } + N(SyntaxKind.EqualsToken); + } + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } } } diff --git a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs index e9faf5a62e018..caf5cae8344d0 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs @@ -6591,5 +6591,55 @@ static void staticLocalFunction() { }|] Punctuation.OpenCurly, Punctuation.CloseCurly); } + + [Theory] + [CombinatorialData] + public async Task ScopedParameter(TestHost testHost) + { + await TestInMethodAsync(@"interface I { void F(scoped R r); }", + testHost, + Keyword("interface"), + Interface("I"), + Punctuation.OpenCurly, + Keyword("void"), + Method("F"), + Punctuation.OpenParen, + Keyword("scoped"), + Identifier("R"), + Parameter("r"), + Punctuation.CloseParen, + Punctuation.Semicolon, + Punctuation.CloseCurly); + } + + [Theory] + [CombinatorialData] + public async Task ScopedLocalDeclaration(TestHost testHost) + { + await TestInMethodAsync(@"scoped var v;", + testHost, + Keyword("scoped"), + Identifier("var"), + Local("v"), + Punctuation.Semicolon); + } + + [Theory] + [CombinatorialData] + public async Task ScopedOutDeclaration(TestHost testHost) + { + await TestInMethodAsync(@"F(x, out scoped R y);", + testHost, + Identifier("F"), + Punctuation.OpenParen, + Identifier("x"), + Punctuation.Comma, + Keyword("out"), + Keyword("scoped"), + Identifier("R"), + Local("y"), + Punctuation.CloseParen, + Punctuation.Semicolon); + } } }