Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Record-structs: address misc other IDE scenarios #52464

Merged
merged 7 commits into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CodeStyle/Core/Analyzers/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind.Parameter = 20 -> Micros
Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind.Property = 13 -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind
Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind.RaiseAccessor = 28 -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind
Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind.RecordClass = 29 -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind
Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind.RecordStruct = 30 -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind
Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind.RemoveAccessor = 27 -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind
Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind.SetAccessor = 25 -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind
Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind.Struct = 3 -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationKind
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,87 +348,6 @@ private static bool IsInAStringOrCharacter(SyntaxNode currentNode, SnapshotPoint
&& caret.Position < currentNode.Span.End
&& caret.Position > currentNode.SpanStart;

private static bool SemicolonIsMissing(SyntaxNode currentNode)
{
switch (currentNode.Kind())
{
case SyntaxKind.LocalDeclarationStatement:
return ((LocalDeclarationStatementSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.ReturnStatement:
return ((ReturnStatementSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.VariableDeclaration:
return SemicolonIsMissing(currentNode.Parent);
case SyntaxKind.ThrowStatement:
return ((ThrowStatementSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.DoStatement:
return ((DoStatementSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.GetAccessorDeclaration:
case SyntaxKind.SetAccessorDeclaration:
return ((AccessorDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.FieldDeclaration:
return ((FieldDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.ForStatement:
return ((ForStatementSyntax)currentNode).FirstSemicolonToken.IsMissing;
case SyntaxKind.ExpressionStatement:
return ((ExpressionStatementSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.EmptyStatement:
return ((EmptyStatementSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.GotoStatement:
return ((GotoStatementSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.BreakStatement:
return ((BreakStatementSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.ContinueStatement:
return ((ContinueStatementSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.YieldReturnStatement:
case SyntaxKind.YieldBreakStatement:
return ((YieldStatementSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.LocalFunctionStatement:
return ((LocalFunctionStatementSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.NamespaceDeclaration:
return ((NamespaceDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.UsingDirective:
return ((UsingDirectiveSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.ExternAliasDirective:
return ((ExternAliasDirectiveSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.ClassDeclaration:
return ((ClassDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.StructDeclaration:
return ((StructDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.InterfaceDeclaration:
return ((InterfaceDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.EnumDeclaration:
return ((EnumDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.DelegateDeclaration:
return ((DelegateDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.EventFieldDeclaration:
return ((EventFieldDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.MethodDeclaration:
return ((MethodDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.OperatorDeclaration:
return ((OperatorDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.ConversionOperatorDeclaration:
return ((ConversionOperatorDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.ConstructorDeclaration:
return ((ConstructorDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.BaseConstructorInitializer:
case SyntaxKind.ThisConstructorInitializer:
return ((ConstructorDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.DestructorDeclaration:
return ((DestructorDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.PropertyDeclaration:
return ((PropertyDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.IndexerDeclaration:
return ((IndexerDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
case SyntaxKind.AddAccessorDeclaration:
return ((AccessorDeclarationSyntax)currentNode).SemicolonToken.IsMissing;
default:
// At this point, the node should be empty or its children should not end with a semicolon.
Debug.Assert(!currentNode.ChildNodesAndTokens().Any()
|| !currentNode.ChildNodesAndTokens().Last().IsKind(SyntaxKind.SemicolonToken));
return false;
}
}

/// <summary>
/// Determines if a statement ends with a closing delimiter, and that closing delimiter exists.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4443,5 +4443,33 @@ class C
testHost,
Record("R"));
}

[Theory]
[CombinatorialData]
public async Task BasicRecordClassClassification(TestHost testHost)
{
await TestAsync(
@"record class R
{
R r;

R() { }
}",
testHost,
Record("R"));
}

[Theory]
[CombinatorialData]
public async Task BasicRecordStructClassification(TestHost testHost)
{
await TestAsync(
@"record struct R
{
R property { get; set; }
}",
testHost,
RecordStruct("R"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,56 @@ await TestAsync(
Punctuation.CloseCurly);
}

[Theory]
[CombinatorialData]
public async Task TestRecordClass(TestHost testHost)
{
await TestAsync(
@"record class R
{
R()
{
}
}",
testHost,
Keyword("record"),
Keyword("class"),
Record("R"),
Punctuation.OpenCurly,
Record("R"),
Punctuation.OpenParen,
Punctuation.CloseParen,
Punctuation.OpenCurly,
Punctuation.CloseCurly,
Punctuation.CloseCurly);
}

[Theory]
[CombinatorialData]
public async Task TestRecordStruct(TestHost testHost)
{
await TestAsync(
@"record struct R
{
R(int i)
{
}
}",
testHost,
Keyword("record"),
Keyword("struct"),
RecordStruct("R"),
Punctuation.OpenCurly,
RecordStruct("R"),
Punctuation.OpenParen,
Keyword("int"),
Parameter("i"),
Punctuation.CloseParen,
Punctuation.OpenCurly,
Punctuation.CloseCurly,
Punctuation.CloseCurly);
}

[Theory]
[CombinatorialData]
public async Task UsingAliasGlobalNamespace(TestHost testHost)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4206,24 +4206,50 @@ void Test()
}

[WorkItem(48453, "https://github.com/dotnet/roslyn/issues/48453")]
[Theory, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)]
[InlineData("record")]
[InlineData("record class")]
public async Task TestInRecord(string record)
{
await TestInRegularAndScript1Async($@"
{record} Program
{{
int field;

public int this[int i] => [|this.field|];
}}",
$@"
{record} Program
{{
int field;

public int this[int i] => {{|Rename:GetField|}}();

private int GetField()
{{
return this.field;
}}
}}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)]
public async Task TestInRecord()
public async Task TestInRecordStruct()
{
await TestInRegularAndScript1Async(@"
record Program
record struct Program
{
int field;

public int this[int i] => [|this.field|];
}",
@"
record Program
record struct Program
{
int field;

public int this[int i] => {|Rename:GetField|}();

private int GetField()
private readonly int GetField()
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Apr 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting. i didn't know we did that. #Resolved

{
return this.field;
}
Expand Down
45 changes: 24 additions & 21 deletions src/EditorFeatures/CSharpTest/CodeLens/CSharpCodeLensTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,35 +215,38 @@ public class A
await RunMethodReferenceTest(input);
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeLens)]
public async Task TestFullyQualifiedName()
[Theory, Trait(Traits.Feature, Traits.Features.CodeLens)]
[InlineData("class")]
[InlineData("record class")]
[InlineData("record struct")]
public async Task TestFullyQualifiedName(string typeKind)
{
const string input = @"<Workspace>
var input = $@"<Workspace>
<Project Language=""C#"" CommonReferences=""true"" AssemblyName=""Proj1"">
<Document FilePath=""CurrentDocument.cs""><![CDATA[
public class A
{
{|A.C: public void C()
{
public {typeKind} A
{{
{{|A.C: public void C()
{{
C();
}|}
}}|}}

public class B
{
{|A+B.C: public void C()
{
public {typeKind} B
{{
{{|A+B.C: public void C()
{{
C();
}|}
}}|}}

public class D
{
{|A+B+D.C: public void C()
{
public {typeKind} D
{{
{{|A+B+D.C: public void C()
{{
C();
}|}
}
}
}
}}|}}
}}
}}
}}
]]>
</Document>
</Project>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,22 @@ class C : IList
await VerifyItemExistsAsync(markup, "IList");
}

[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task TestAtStartOfRecord()
[Theory, Trait(Traits.Feature, Traits.Features.Completion)]
[InlineData("record")]
[InlineData("record class")]
[InlineData("record struct")]
public async Task TestAtStartOfRecord(string record)
{
var markup = @"
var markup = $@"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"" LanguageVersion=""Preview"">
<Document>
using System.Collections;

record C : IList
{
{record} C : IList
{{
int $$
}
}}
</Document>
</Project>
</Workspace>";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,10 @@ public async Task SuggestEventAfterReadonlyInStruct()
[Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
[WorkItem(39265, "https://github.com/dotnet/roslyn/issues/39265")]
[InlineData("struct", true)]
[InlineData("record struct", true)]
[InlineData("class", false)]
[InlineData("record", false)]
[InlineData("record class", false)]
[InlineData("interface", false)]
public async Task SuggestReadonlyPropertyAccessor(string declarationType, bool present)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8724,30 +8724,33 @@ public void M1()
}

[WorkItem(48295, "https://github.com/dotnet/roslyn/issues/48295")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
public async Task TestImplementOnRecord_WithSemiColonAndTrivia()
[Theory, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
[InlineData("record")]
[InlineData("record class")]
[InlineData("record struct")]
public async Task TestImplementOnRecord_WithSemiColonAndTrivia(string record)
{
await TestInRegularAndScriptAsync(@"
await TestInRegularAndScriptAsync($@"
interface I
{
{{
void M1();
}
}}

record C : [|I|]; // hello
{record} C : [|I|]; // hello
",
@"
$@"
interface I
{
{{
void M1();
}
}}

record C : [|I|] // hello
{
{record} C : [|I|] // hello
{{
public void M1()
{
{{
throw new System.NotImplementedException();
}
}
}}
}}
");
}

Expand Down
Loading