From 7c23472896b10ac56846bbae1b156039f261bb29 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 28 Feb 2023 10:25:53 -0800 Subject: [PATCH 1/6] In progress --- .../StringKeywordRecommenderTests.cs | 98 +++++++++++++++++++ .../StringKeywordRecommender.cs | 52 +++++++++- 2 files changed, 145 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs index 52b3cd0ee7328..48b9f76c5b62e 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs @@ -813,5 +813,103 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyAtTopLevel1() + { + await VerifyAbsenceAsync( +@"readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyKeywordAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyKeywordAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs index 397a8d2a64a0d..1c1111dbff987 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs @@ -8,16 +8,20 @@ using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.Shared.Extensions; +using System; +using System.Linq; namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders { - internal class StringKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender + internal sealed class StringKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender { public StringKeywordRecommender() : base(SyntaxKind.StringKeyword) { } + protected override SpecialType SpecialType => SpecialType.System_String; + protected override bool IsValidContextWorker(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -39,8 +43,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsCrefContext || syntaxTree.IsDefaultExpressionContext(position, context.LeftToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || context.IsPossibleTupleContext || @@ -48,9 +50,49 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c validModifiers: SyntaxKindSet.AllMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: false, - cancellationToken: cancellationToken); + cancellationToken) || + IsAfterRefOrReadonlyInTopLevelOrMemberDeclaration(context, position, cancellationToken); } - protected override SpecialType SpecialType => SpecialType.System_String; + private static bool IsAfterRefOrReadonlyInTopLevelOrMemberDeclaration(CSharpSyntaxContext context, int position, CancellationToken cancellationToken) + { + var syntaxTree = context.SyntaxTree; + if (!syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) && + !syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken)) + { + return false; + } + + var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken); + token = token.GetPreviousTokenIfTouchingWord(position); + + // if we have `readonly` move backwards to see if we have `ref readonly`. + if (token.Kind() is SyntaxKind.ReadOnlyKeyword) + token = syntaxTree.FindTokenOnLeftOfPosition(token.SpanStart, cancellationToken); + + // if we're not after `ref` or `ref readonly` then don't offer `string` here. + if (token.Kind() != SyntaxKind.RefKeyword) + return false; + + // check if the location prior to the 'ref/readonly' is itself a member start location. If so, + // then it's fine to show 'string'. For example, `class C { public ref $$ }` + if (syntaxTree.IsMemberDeclarationContext( + token.SpanStart, + contextOpt: null, + validModifiers: SyntaxKindSet.AllMemberModifiers, + validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, + canBePartial: false, + cancellationToken)) + { + return true; + } + + // Compiler error recovery sometimes treats 'ref' standing along as an incomplete member syntax. + if (token.Parent is RefTypeSyntax { Parent: IncompleteMemberSyntax { Parent: CompilationUnitSyntax } }) + return true; + + // Otherwise see if we're in a global statement. + return token.GetAncestors().Any(a => a is GlobalStatementSyntax); + } } } From c4424cb77847543e36aaa51bbf088a2749eb9e61 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 28 Feb 2023 10:38:59 -0800 Subject: [PATCH 2/6] Add tests --- .../StringKeywordRecommenderTests.cs | 26 ++++++++++++++----- .../StringKeywordRecommender.cs | 25 +++++++----------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs index 48b9f76c5b62e..ba824acacc206 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.VisualStudio.Shell.Interop; using Roslyn.Test.Utilities; using Xunit; @@ -822,11 +823,22 @@ await VerifyKeywordAsync( @"ref $$"); } - [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] - public async Task TestNotAfterReadonlyAtTopLevel1() - { - await VerifyAbsenceAsync( -@"readonly $$"); + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions); + } } [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] @@ -841,7 +853,7 @@ await VerifyKeywordAsync( public async Task TestNotAfterRefInNamespace() { // This is only legal for a struct declaration - await VerifyKeywordAsync( + await VerifyAbsenceAsync( @"namespace N { ref $$ @@ -863,7 +875,7 @@ await VerifyAbsenceAsync( public async Task TestNotAfterRefReadonlyInNamespace() { // This is only legal for a struct declaration - await VerifyKeywordAsync( + await VerifyAbsenceAsync( @"namespace N { ref readonly $$ diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs index 1c1111dbff987..e90d4471733ad 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs @@ -74,25 +74,18 @@ private static bool IsAfterRefOrReadonlyInTopLevelOrMemberDeclaration(CSharpSynt if (token.Kind() != SyntaxKind.RefKeyword) return false; - // check if the location prior to the 'ref/readonly' is itself a member start location. If so, - // then it's fine to show 'string'. For example, `class C { public ref $$ }` - if (syntaxTree.IsMemberDeclarationContext( - token.SpanStart, - contextOpt: null, - validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, - cancellationToken)) - { + // If we're inside a type, this is always to have a ref/readonly string. + var containingType = token.GetAncestor(); + if (containingType != null) return true; - } - // Compiler error recovery sometimes treats 'ref' standing along as an incomplete member syntax. - if (token.Parent is RefTypeSyntax { Parent: IncompleteMemberSyntax { Parent: CompilationUnitSyntax } }) - return true; + // If not in a type, but in a namespace, this is not ok to have a ref/readonly string. + var containingNamespace = token.GetAncestor(); + if (containingNamespace != null) + return false; - // Otherwise see if we're in a global statement. - return token.GetAncestors().Any(a => a is GlobalStatementSyntax); + // otherwise, we're at top level. Can have a ref/readonly top-level local/function. + return true; } } } From bc2f28ad21e83ddf692337b0ef71e0ea720ea337 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 28 Feb 2023 10:41:32 -0800 Subject: [PATCH 3/6] Add tests --- .../Recommendations/StringKeywordRecommenderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs index ba824acacc206..83fc51b2aca61 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs @@ -837,7 +837,7 @@ await VerifyKeywordAsync( { // no legal top level statement can start with `readonly string` await VerifyAbsenceAsync( -@"readonly $$", CSharp9ParseOptions); +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); } } From b2469592446a6e23a54fc95ff273738c882d092d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 28 Feb 2023 13:20:40 -0800 Subject: [PATCH 4/6] Move check up --- .../BoolKeywordRecommenderTests.cs | 109 ++++++++++++++++++ .../ByteKeywordRecommenderTests.cs | 109 ++++++++++++++++++ .../CharKeywordRecommenderTests.cs | 109 ++++++++++++++++++ .../DecimalKeywordRecommenderTests.cs | 109 ++++++++++++++++++ .../DoubleKeywordRecommenderTests.cs | 109 ++++++++++++++++++ .../FloatKeywordRecommenderTests.cs | 109 ++++++++++++++++++ .../IntKeywordRecommenderTests.cs | 109 ++++++++++++++++++ .../LongKeywordRecommenderTests.cs | 109 ++++++++++++++++++ .../ObjectKeywordRecommenderTests.cs | 109 ++++++++++++++++++ .../SByteKeywordRecommenderTests.cs | 109 ++++++++++++++++++ .../ShortKeywordRecommenderTests.cs | 109 ++++++++++++++++++ .../UIntKeywordRecommenderTests.cs | 109 ++++++++++++++++++ .../ULongKeywordRecommenderTests.cs | 109 ++++++++++++++++++ .../UShortKeywordRecommenderTests.cs | 109 ++++++++++++++++++ ...ecialTypePreselectingKeywordRecommender.cs | 41 ++++++- .../BoolKeywordRecommender.cs | 2 - .../ByteKeywordRecommender.cs | 2 - .../CharKeywordRecommender.cs | 2 - .../DecimalKeywordRecommender.cs | 2 - .../DoubleKeywordRecommender.cs | 2 - .../FloatKeywordRecommender.cs | 2 - .../IntKeywordRecommender.cs | 2 - .../LongKeywordRecommender.cs | 2 - .../ObjectKeywordRecommender.cs | 2 - .../SByteKeywordRecommender.cs | 2 - .../ShortKeywordRecommender.cs | 2 - .../StringKeywordRecommender.cs | 37 +----- .../UIntKeywordRecommender.cs | 2 - .../ULongKeywordRecommender.cs | 2 - .../UShortKeywordRecommender.cs | 2 - 30 files changed, 1566 insertions(+), 66 deletions(-) diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs index 2ceeaa92c1524..259d42f271016 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs @@ -799,5 +799,114 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); + } + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs index b8aff259dfea4..4a002d4f9f1f0 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs @@ -800,5 +800,114 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); + } + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs index 62dc9a5db6443..0dc0bfc76e6d3 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs @@ -839,5 +839,114 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); + } + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs index 780f27840012f..4bc8350b5c687 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs @@ -791,5 +791,114 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); + } + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs index 952c936337ff8..02db3a9370f34 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs @@ -777,5 +777,114 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); + } + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs index 4a0b5d88dbf26..051003f288993 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs @@ -750,5 +750,114 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); + } + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs index e37d084789a3f..de513f23de581 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs @@ -864,5 +864,114 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); + } + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs index 873ca30570cfc..ef4b5bb1c3a95 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs @@ -777,5 +777,114 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); + } + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs index af410741c1ca2..27fe77337e6e4 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs @@ -798,5 +798,114 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); + } + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs index 22874eddc4592..5e2496b49e405 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs @@ -777,5 +777,114 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); + } + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs index 233aa121bdc35..9f80bb57722eb 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs @@ -777,5 +777,114 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); + } + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs index 371721df65dde..38450fa55fa77 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs @@ -777,5 +777,114 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); + } + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs index d0030a3272468..0b6b7a7c2b97a 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs @@ -777,5 +777,114 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); + } + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs index 67430834633af..4df571da2706e 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs @@ -777,5 +777,114 @@ class C required $$ }"); } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref $$"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [CombinatorialData] + public async Task TestAfterReadonlyAtTopLevel1(bool script) + { + if (script) + { + // A legal top level script field. + await VerifyKeywordAsync( +@"readonly $$", Options.Script); + } + else + { + // no legal top level statement can start with `readonly string` + await VerifyAbsenceAsync( +@"readonly $$", CSharp9ParseOptions, CSharp9ParseOptions); + } + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestAfterRefReadonlyAtTopLevel1() + { + // Could be defining a ref-local in top-level-code + await VerifyKeywordAsync( +@"ref readonly $$"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + readonly $$ +}"); + } + + [Fact, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + public async Task TestNotAfterRefReadonlyInNamespace() + { + // This is only legal for a struct declaration + await VerifyAbsenceAsync( +@"namespace N +{ + ref readonly $$ +}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + readonly $$ +}}"); + } + + [Theory, WorkItem(67061, "https://github.com/dotnet/roslyn/issues/67061")] + [InlineData("class")] + [InlineData("interface")] + [InlineData("struct")] + [InlineData("record")] + public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) + { + await VerifyKeywordAsync( +$@"{type} N +{{ + ref readonly $$ +}}"); + } } } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractSpecialTypePreselectingKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractSpecialTypePreselectingKeywordRecommender.cs index 9d1768a0409a6..f2607db1936a7 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractSpecialTypePreselectingKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractSpecialTypePreselectingKeywordRecommender.cs @@ -2,10 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.Completion.Providers; +using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders { @@ -36,7 +38,42 @@ protected sealed override bool IsValidContext(int position, CSharpSyntaxContext if (context.IsTaskLikeTypeContext) return false; - return IsValidContextWorker(position, context, cancellationToken); + return IsValidContextWorker(position, context, cancellationToken) || + IsAfterRefOrReadonlyInTopLevelOrMemberDeclaration(context, position, cancellationToken); + } + + private static bool IsAfterRefOrReadonlyInTopLevelOrMemberDeclaration(CSharpSyntaxContext context, int position, CancellationToken cancellationToken) + { + var syntaxTree = context.SyntaxTree; + if (!syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) && + !syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken)) + { + return false; + } + + var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken); + token = token.GetPreviousTokenIfTouchingWord(position); + + // if we have `readonly` move backwards to see if we have `ref readonly`. + if (token.Kind() is SyntaxKind.ReadOnlyKeyword) + token = syntaxTree.FindTokenOnLeftOfPosition(token.SpanStart, cancellationToken); + + // if we're not after `ref` or `ref readonly` then don't offer `string` here. + if (token.Kind() != SyntaxKind.RefKeyword) + return false; + + // If we're inside a type, this is always to have a ref/readonly string. + var containingType = token.GetAncestor(); + if (containingType != null) + return true; + + // If not in a type, but in a namespace, this is not ok to have a ref/readonly string. + var containingNamespace = token.GetAncestor(); + if (containingNamespace != null) + return false; + + // otherwise, we're at top level. Can have a ref/readonly top-level local/function. + return true; } } } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BoolKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BoolKeywordRecommender.cs index e4fc2557c5601..f4be4ed8f8a85 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BoolKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BoolKeywordRecommender.cs @@ -39,8 +39,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs index f382666eefb1e..c7bdfd936eaf7 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs @@ -40,8 +40,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs index 3ea3e68a4dcf5..5e7ccc72f5504 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs @@ -39,8 +39,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs index 218cf5154f7e0..4c22773c4ece7 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs @@ -39,8 +39,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs index b094e83c3f8cd..a33ef2c8ba9e4 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs @@ -39,8 +39,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs index f639605b7c6f0..14f5c03191ca5 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs @@ -39,8 +39,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs index ef83b4fadc4e8..4d085f92914ca 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs @@ -40,8 +40,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs index 5e3a25f616cc0..c5cb4439bd56d 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs @@ -40,8 +40,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs index 723fa9629bd2f..939b7d9d65f87 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs @@ -39,8 +39,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsCrefContext || syntaxTree.IsDefaultExpressionContext(position, context.LeftToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || context.IsPossibleTupleContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs index cd264773d59be..7c7cb47598ad6 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs @@ -40,8 +40,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs index 7df11c939ecc8..2fe184b6bb97e 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs @@ -40,8 +40,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs index e90d4471733ad..66fc345198420 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs @@ -50,42 +50,7 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c validModifiers: SyntaxKindSet.AllMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: false, - cancellationToken) || - IsAfterRefOrReadonlyInTopLevelOrMemberDeclaration(context, position, cancellationToken); - } - - private static bool IsAfterRefOrReadonlyInTopLevelOrMemberDeclaration(CSharpSyntaxContext context, int position, CancellationToken cancellationToken) - { - var syntaxTree = context.SyntaxTree; - if (!syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) && - !syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken)) - { - return false; - } - - var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken); - token = token.GetPreviousTokenIfTouchingWord(position); - - // if we have `readonly` move backwards to see if we have `ref readonly`. - if (token.Kind() is SyntaxKind.ReadOnlyKeyword) - token = syntaxTree.FindTokenOnLeftOfPosition(token.SpanStart, cancellationToken); - - // if we're not after `ref` or `ref readonly` then don't offer `string` here. - if (token.Kind() != SyntaxKind.RefKeyword) - return false; - - // If we're inside a type, this is always to have a ref/readonly string. - var containingType = token.GetAncestor(); - if (containingType != null) - return true; - - // If not in a type, but in a namespace, this is not ok to have a ref/readonly string. - var containingNamespace = token.GetAncestor(); - if (containingNamespace != null) - return false; - - // otherwise, we're at top level. Can have a ref/readonly top-level local/function. - return true; + cancellationToken); } } } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs index d6a055c98c083..776039575e020 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs @@ -40,8 +40,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs index 2d4a8e8da6b83..03d9f05a6e7c3 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs @@ -40,8 +40,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs index 45fbe5bff6c58..fd871600eff1c 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs @@ -47,8 +47,6 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || - syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) || context.IsDelegateReturnTypeContext || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || From a233b73787a33b300b83b089bfd4d6088e074016 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 1 Mar 2023 08:25:15 -0800 Subject: [PATCH 5/6] Update src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractSpecialTypePreselectingKeywordRecommender.cs --- .../AbstractSpecialTypePreselectingKeywordRecommender.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractSpecialTypePreselectingKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractSpecialTypePreselectingKeywordRecommender.cs index f2607db1936a7..1771edab56e20 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractSpecialTypePreselectingKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractSpecialTypePreselectingKeywordRecommender.cs @@ -58,7 +58,7 @@ private static bool IsAfterRefOrReadonlyInTopLevelOrMemberDeclaration(CSharpSynt if (token.Kind() is SyntaxKind.ReadOnlyKeyword) token = syntaxTree.FindTokenOnLeftOfPosition(token.SpanStart, cancellationToken); - // if we're not after `ref` or `ref readonly` then don't offer `string` here. + // if we're not after `ref` or `ref readonly` then don't offer a type-keyword here. if (token.Kind() != SyntaxKind.RefKeyword) return false; From 14551b35f8c15e4fd30b514871b03979195f9a1b Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 1 Mar 2023 08:25:38 -0800 Subject: [PATCH 6/6] Apply suggestions from code review Co-authored-by: DoctorKrolic <70431552+DoctorKrolic@users.noreply.github.com> --- .../AbstractSpecialTypePreselectingKeywordRecommender.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractSpecialTypePreselectingKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractSpecialTypePreselectingKeywordRecommender.cs index 1771edab56e20..95a086856533c 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractSpecialTypePreselectingKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractSpecialTypePreselectingKeywordRecommender.cs @@ -62,12 +62,12 @@ private static bool IsAfterRefOrReadonlyInTopLevelOrMemberDeclaration(CSharpSynt if (token.Kind() != SyntaxKind.RefKeyword) return false; - // If we're inside a type, this is always to have a ref/readonly string. + // If we're inside a type, this is always to have a ref/readonly type name. var containingType = token.GetAncestor(); if (containingType != null) return true; - // If not in a type, but in a namespace, this is not ok to have a ref/readonly string. + // If not in a type, but in a namespace, this is not ok to have a ref/readonly type name. var containingNamespace = token.GetAncestor(); if (containingNamespace != null) return false;