Skip to content

Commit a2d6fb1

Browse files
authored
Merge pull request #72893 from DoctorKrolic/use-semantic-check
Use semantics to check for readonly struct
2 parents a1576d6 + 52196dc commit a2d6fb1

8 files changed

+113
-10
lines changed

src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/AbstractCSharpAutoPropertyCompletionProviderTests.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ struct MyStruct
8080
[WpfFact, Trait(Traits.Feature, Traits.Features.Completion)]
8181
public abstract Task InsertSnippetInReadonlyStruct();
8282

83+
[WpfFact, Trait(Traits.Feature, Traits.Features.Completion)]
84+
public abstract Task InsertSnippetInReadonlyStruct_ReadonlyModifierInOtherPartialDeclaration();
85+
86+
[WpfFact, Trait(Traits.Feature, Traits.Features.Completion)]
87+
public abstract Task InsertSnippetInReadonlyStruct_ReadonlyModifierInOtherPartialDeclaration_MissingPartialModifier();
88+
8389
// This case might produce non-default results for different snippets (e.g. no `set` accessor in 'propg' snippet),
8490
// so it is tested separately for all of them
8591
[WpfFact, Trait(Traits.Feature, Traits.Features.Completion)]

src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropSnippetCompletionProviderTests.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,39 @@ readonly struct MyStruct
2424
""", "public int MyProperty { get; }");
2525
}
2626

27+
public override async Task InsertSnippetInReadonlyStruct_ReadonlyModifierInOtherPartialDeclaration()
28+
{
29+
// Ensure we don't generate redundant `set` accessor when executed in readonly struct
30+
await VerifyPropertyAsync("""
31+
partial struct MyStruct
32+
{
33+
$$
34+
}
35+
36+
readonly partial struct MyStruct
37+
{
38+
}
39+
""", "public int MyProperty { get; }");
40+
}
41+
42+
public override async Task InsertSnippetInReadonlyStruct_ReadonlyModifierInOtherPartialDeclaration_MissingPartialModifier()
43+
{
44+
// Even though there is no `partial` modifier on the first declaration
45+
// compiler still treats the whole type as partial since it is more likely that
46+
// the user's intent was to have a partial type and they just forgot the modifier.
47+
// Thus we still recognize that as `readonly` context and don't generate a setter
48+
await VerifyPropertyAsync("""
49+
struct MyStruct
50+
{
51+
$$
52+
}
53+
54+
readonly partial struct MyStruct
55+
{
56+
}
57+
""", "public int MyProperty { get; }");
58+
}
59+
2760
public override async Task InsertSnippetInInterface()
2861
{
2962
await VerifyDefaultPropertyAsync("""

src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropgSnippetCompletionProviderTests.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,39 @@ readonly struct MyStruct
2424
""", "public int MyProperty { get; }");
2525
}
2626

27+
public override async Task InsertSnippetInReadonlyStruct_ReadonlyModifierInOtherPartialDeclaration()
28+
{
29+
// Ensure we don't generate redundant `set` accessor when executed in readonly struct
30+
await VerifyPropertyAsync("""
31+
partial struct MyStruct
32+
{
33+
$$
34+
}
35+
36+
readonly partial struct MyStruct
37+
{
38+
}
39+
""", "public int MyProperty { get; }");
40+
}
41+
42+
public override async Task InsertSnippetInReadonlyStruct_ReadonlyModifierInOtherPartialDeclaration_MissingPartialModifier()
43+
{
44+
// Even though there is no `partial` modifier on the first declaration
45+
// compiler still treats the whole type as partial since it is more likely that
46+
// the user's intent was to have a partial type and they just forgot the modifier.
47+
// Thus we still recognize that as `readonly` context and don't generate a setter
48+
await VerifyPropertyAsync("""
49+
struct MyStruct
50+
{
51+
$$
52+
}
53+
54+
readonly partial struct MyStruct
55+
{
56+
}
57+
""", "public int MyProperty { get; }");
58+
}
59+
2760
public override async Task InsertSnippetInInterface()
2861
{
2962
// Ensure we don't generate redundant `set` accessor when executed in interface

src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropiSnippetCompletionProviderTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,34 @@ readonly struct MyStruct
2323
""");
2424
}
2525

26+
public override async Task InsertSnippetInReadonlyStruct_ReadonlyModifierInOtherPartialDeclaration()
27+
{
28+
await VerifyDefaultPropertyAsync("""
29+
partial struct MyStruct
30+
{
31+
$$
32+
}
33+
34+
readonly partial struct MyStruct
35+
{
36+
}
37+
""");
38+
}
39+
40+
public override async Task InsertSnippetInReadonlyStruct_ReadonlyModifierInOtherPartialDeclaration_MissingPartialModifier()
41+
{
42+
await VerifyDefaultPropertyAsync("""
43+
struct MyStruct
44+
{
45+
$$
46+
}
47+
48+
readonly partial struct MyStruct
49+
{
50+
}
51+
""");
52+
}
53+
2654
public override async Task InsertSnippetInInterface()
2755
{
2856
await VerifyDefaultPropertyAsync("""

src/Features/CSharp/Portable/Snippets/AbstractCSharpAutoPropertySnippetProvider.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ namespace Microsoft.CodeAnalysis.CSharp.Snippets;
2424

2525
internal abstract class AbstractCSharpAutoPropertySnippetProvider : AbstractPropertySnippetProvider<PropertyDeclarationSyntax>
2626
{
27-
protected virtual AccessorDeclarationSyntax? GenerateGetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator)
27+
protected virtual AccessorDeclarationSyntax? GenerateGetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator, CancellationToken cancellationToken)
2828
=> (AccessorDeclarationSyntax)generator.GetAccessorDeclaration();
2929

30-
protected virtual AccessorDeclarationSyntax? GenerateSetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator)
30+
protected virtual AccessorDeclarationSyntax? GenerateSetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator, CancellationToken cancellationToken)
3131
=> (AccessorDeclarationSyntax)generator.SetAccessorDeclaration();
3232

3333
protected override bool IsValidSnippetLocation(in SnippetContext context, CancellationToken cancellationToken)
@@ -46,8 +46,8 @@ protected override async Task<PropertyDeclarationSyntax> GenerateSnippetSyntaxAs
4646
var syntaxContext = CSharpSyntaxContext.CreateContext(document, semanticModel, position, cancellationToken);
4747
var accessors = new AccessorDeclarationSyntax?[]
4848
{
49-
GenerateGetAccessorDeclaration(syntaxContext, generator),
50-
GenerateSetAccessorDeclaration(syntaxContext, generator),
49+
GenerateGetAccessorDeclaration(syntaxContext, generator, cancellationToken),
50+
GenerateSetAccessorDeclaration(syntaxContext, generator, cancellationToken),
5151
};
5252

5353
SyntaxTokenList modifiers = default;

src/Features/CSharp/Portable/Snippets/CSharpPropSnippetProvider.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System;
66
using System.Composition;
7+
using System.Threading;
78
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
89
using Microsoft.CodeAnalysis.CSharp.Syntax;
910
using Microsoft.CodeAnalysis.Editing;
@@ -22,16 +23,16 @@ internal sealed class CSharpPropSnippetProvider() : AbstractCSharpAutoPropertySn
2223

2324
public override string Description => FeaturesResources.property_;
2425

25-
protected override AccessorDeclarationSyntax? GenerateSetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator)
26+
protected override AccessorDeclarationSyntax? GenerateSetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator, CancellationToken cancellationToken)
2627
{
2728
// Having a property with `set` accessor in a readonly struct leads to a compiler error.
2829
// So if user executes snippet inside a readonly struct the right thing to do is to not generate `set` accessor at all
2930
if (syntaxContext.ContainingTypeDeclaration is StructDeclarationSyntax structDeclaration &&
30-
structDeclaration.Modifiers.Any(SyntaxKind.ReadOnlyKeyword))
31+
syntaxContext.SemanticModel.GetDeclaredSymbol(structDeclaration, cancellationToken) is { IsReadOnly: true })
3132
{
3233
return null;
3334
}
3435

35-
return base.GenerateSetAccessorDeclaration(syntaxContext, generator);
36+
return base.GenerateSetAccessorDeclaration(syntaxContext, generator, cancellationToken);
3637
}
3738
}

src/Features/CSharp/Portable/Snippets/CSharpPropgSnippetProvider.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System;
66
using System.Composition;
7+
using System.Threading;
78
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
89
using Microsoft.CodeAnalysis.CSharp.Syntax;
910
using Microsoft.CodeAnalysis.Editing;
@@ -22,7 +23,7 @@ internal sealed class CSharpPropgSnippetProvider() : AbstractCSharpAutoPropertyS
2223

2324
public override string Description => FeaturesResources.get_only_property;
2425

25-
protected override AccessorDeclarationSyntax? GenerateSetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator)
26+
protected override AccessorDeclarationSyntax? GenerateSetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator, CancellationToken cancellationToken)
2627
{
2728
// Interface cannot have properties with `private set` accessor.
2829
// So if we are inside an interface, we just return null here.
@@ -35,7 +36,7 @@ internal sealed class CSharpPropgSnippetProvider() : AbstractCSharpAutoPropertyS
3536
// Having a property with `set` accessor in a readonly struct leads to a compiler error.
3637
// So if user executes snippet inside a readonly struct the right thing to do is to not generate `set` accessor at all
3738
if (syntaxContext.ContainingTypeDeclaration is StructDeclarationSyntax structDeclaration &&
38-
structDeclaration.Modifiers.Any(SyntaxKind.ReadOnlyKeyword))
39+
syntaxContext.SemanticModel.GetDeclaredSymbol(structDeclaration, cancellationToken) is { IsReadOnly: true })
3940
{
4041
return null;
4142
}

src/Features/CSharp/Portable/Snippets/CSharpPropiSnippetProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System;
66
using System.Composition;
7+
using System.Threading;
78
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
89
using Microsoft.CodeAnalysis.CSharp.Syntax;
910
using Microsoft.CodeAnalysis.Editing;
@@ -24,6 +25,6 @@ internal sealed class CSharpPropiSnippetProvider() : AbstractCSharpAutoPropertyS
2425

2526
public override string Description => CSharpFeaturesResources.init_only_property;
2627

27-
protected override AccessorDeclarationSyntax? GenerateSetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator)
28+
protected override AccessorDeclarationSyntax? GenerateSetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator, CancellationToken cancellationToken)
2829
=> SyntaxFactory.AccessorDeclaration(SyntaxKind.InitAccessorDeclaration).WithSemicolonToken(SemicolonToken);
2930
}

0 commit comments

Comments
 (0)