diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 65cc4012fb82b..b2b2641a2fd89 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -4880,8 +4880,8 @@ private void ParseParameterModifiers(SyntaxListBuilder modifiers, bool isFunctio { modifiers.Add(scopedKeyword); - // Look if ref/out/in/readonly are next - while (this.CurrentToken.Kind is SyntaxKind.RefKeyword or SyntaxKind.OutKeyword or SyntaxKind.InKeyword or SyntaxKind.ReadOnlyKeyword) + // Look if ref/out/in/readonly/this are next + while (this.CurrentToken.Kind is SyntaxKind.RefKeyword or SyntaxKind.OutKeyword or SyntaxKind.InKeyword or SyntaxKind.ReadOnlyKeyword or SyntaxKind.ThisKeyword) { modifiers.Add(this.EatToken()); } diff --git a/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests_LifetimeAnnotation.cs b/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests_LifetimeAnnotation.cs index 38c30ad1dcda4..457880e546c26 100644 --- a/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests_LifetimeAnnotation.cs +++ b/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests_LifetimeAnnotation.cs @@ -560,6 +560,66 @@ [ScopedRef] scoped R r }); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78491")] + public void EmitAttribute_ExtensionMethodParameters_ScopedInThis() + { + var source = +@"static class Extensions +{ + public static void F(scoped in this int i) { } +}"; + var comp = CreateCompilation(source); + var expected = +@"void Extensions.F(this scoped in System.Int32 i) + [ScopedRef] scoped in System.Int32 i +"; + CompileAndVerify(comp, symbolValidator: module => + { + Assert.Equal("System.Runtime.CompilerServices.ScopedRefAttribute", GetScopedRefType(module).ToTestDisplayString()); + AssertScopedRefAttributes(module, expected); + }); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78491")] + public void EmitAttribute_ExtensionMethodParameters_ScopedRefThis() + { + var source = +@"static class Extensions +{ + public static void F(scoped ref this int i) { } +}"; + var comp = CreateCompilation(source); + var expected = +@"void Extensions.F(this scoped ref System.Int32 i) + [ScopedRef] scoped ref System.Int32 i +"; + CompileAndVerify(comp, symbolValidator: module => + { + Assert.Equal("System.Runtime.CompilerServices.ScopedRefAttribute", GetScopedRefType(module).ToTestDisplayString()); + AssertScopedRefAttributes(module, expected); + }); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78491")] + public void EmitAttribute_ExtensionMethodParameters_ScopedRefReadonlyThis() + { + var source = +@"static class Extensions +{ + public static void F(scoped ref readonly this int i) { } +}"; + var comp = CreateCompilation(source); + var expected = +@"void Extensions.F(this scoped ref readonly System.Int32 i) + [ScopedRef] scoped ref readonly System.Int32 i +"; + CompileAndVerify(comp, symbolValidator: module => + { + Assert.Equal("System.Runtime.CompilerServices.ScopedRefAttribute", GetScopedRefType(module).ToTestDisplayString()); + AssertScopedRefAttributes(module, expected); + }); + } + private static void AssertScopedRefAttributes(ModuleSymbol module, string expected) { var actual = ScopedRefAttributesVisitor.GetString((PEModuleSymbol)module); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationScopeParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationScopeParsingTests.cs index 6740c40c0c393..82b1632859b52 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationScopeParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationScopeParsingTests.cs @@ -14944,5 +14944,144 @@ public void Catch_03() } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78491")] + public void ScopedWithThis_01() + { + var source = "void M(scoped in this int p);"; + UsingDeclaration(source); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.InKeyword); + N(SyntaxKind.ThisKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "p"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78491")] + public void ScopedWithThis_02() + { + var source = "void M(scoped ref this int p);"; + UsingDeclaration(source); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ThisKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "p"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78491")] + public void ScopedWithThis_03() + { + // Test scoped ref readonly this + var source = "void M(scoped ref readonly this int p);"; + UsingDeclaration(source); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.ThisKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "p"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78491")] + public void ScopedWithThis_04() + { + // Test that this scoped in still works + var source = "void M(this scoped in int p);"; + UsingDeclaration(source); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ThisKeyword); + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.InKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "p"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } } }