diff --git a/src/CodeStyle/Core/Analyzers/PublicAPI.Unshipped.txt b/src/CodeStyle/Core/Analyzers/PublicAPI.Unshipped.txt
index b79a079b6d63e..a03401416d958 100644
--- a/src/CodeStyle/Core/Analyzers/PublicAPI.Unshipped.txt
+++ b/src/CodeStyle/Core/Analyzers/PublicAPI.Unshipped.txt
@@ -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
diff --git a/src/EditorFeatures/CSharp/CompleteStatement/CompleteStatementCommandHandler.cs b/src/EditorFeatures/CSharp/CompleteStatement/CompleteStatementCommandHandler.cs
index 23f86ad16c197..26416a850248f 100644
--- a/src/EditorFeatures/CSharp/CompleteStatement/CompleteStatementCommandHandler.cs
+++ b/src/EditorFeatures/CSharp/CompleteStatement/CompleteStatementCommandHandler.cs
@@ -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;
- }
- }
-
///
/// Determines if a statement ends with a closing delimiter, and that closing delimiter exists.
///
diff --git a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs
index c99915b6ec525..8d76e7dce789b 100644
--- a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs
+++ b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs
@@ -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"));
+ }
}
}
diff --git a/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs
index ee85fc5dac98a..51ef31753f7fd 100644
--- a/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs
+++ b/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs
@@ -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)
diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs
index 41475a258b766..5cf6db0f41970 100644
--- a/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs
+++ b/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs
@@ -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()
{
return this.field;
}
diff --git a/src/EditorFeatures/CSharpTest/CodeLens/CSharpCodeLensTests.cs b/src/EditorFeatures/CSharpTest/CodeLens/CSharpCodeLensTests.cs
index 8f43e4be2e8df..efb3df175f11f 100644
--- a/src/EditorFeatures/CSharpTest/CodeLens/CSharpCodeLensTests.cs
+++ b/src/EditorFeatures/CSharpTest/CodeLens/CSharpCodeLensTests.cs
@@ -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 = @"
+ var input = $@"
diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExplicitInterfaceTypeCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExplicitInterfaceTypeCompletionProviderTests.cs
index c9fccc90daae5..21bec8998c987 100644
--- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExplicitInterfaceTypeCompletionProviderTests.cs
+++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExplicitInterfaceTypeCompletionProviderTests.cs
@@ -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 = $@"
using System.Collections;
-record C : IList
-{
+{record} C : IList
+{{
int $$
-}
+}}
";
diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/KeywordCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/KeywordCompletionProviderTests.cs
index 9607425cfaf0a..de1176e685356 100644
--- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/KeywordCompletionProviderTests.cs
+++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/KeywordCompletionProviderTests.cs
@@ -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)
{
diff --git a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs b/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs
index 55566b6cc9e86..8e20dbcb817b8 100644
--- a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs
+++ b/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs
@@ -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();
- }
-}
+ }}
+}}
");
}
diff --git a/src/EditorFeatures/CSharpTest/MakeRefStruct/MakeRefStructTests.cs b/src/EditorFeatures/CSharpTest/MakeRefStruct/MakeRefStructTests.cs
index d9c1ae3943071..4ab058e0cb278 100644
--- a/src/EditorFeatures/CSharpTest/MakeRefStruct/MakeRefStructTests.cs
+++ b/src/EditorFeatures/CSharpTest/MakeRefStruct/MakeRefStructTests.cs
@@ -61,6 +61,18 @@ ref struct S
await TestInRegularAndScriptAsync(text, expected, parseOptions: s_parseOptions);
}
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeRefStruct)]
+ public async Task FieldInRecordStruct()
+ {
+ var text = CreateTestSource(@"
+record struct S
+{
+ Span[||] m;
+}
+");
+ await TestMissingInRegularAndScriptAsync(text, new TestParameters(CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview)));
+ }
+
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeRefStruct)]
public async Task FieldInNestedClassInsideNotRefStruct()
{
diff --git a/src/EditorFeatures/CSharpTest/Organizing/OrganizeTypeDeclarationTests.cs b/src/EditorFeatures/CSharpTest/Organizing/OrganizeTypeDeclarationTests.cs
index 6a382abadd4b5..99ca6817b62fd 100644
--- a/src/EditorFeatures/CSharpTest/Organizing/OrganizeTypeDeclarationTests.cs
+++ b/src/EditorFeatures/CSharpTest/Organizing/OrganizeTypeDeclarationTests.cs
@@ -24,6 +24,8 @@ public class OrganizeTypeDeclarationTests : AbstractOrganizerTests
[Theory, Trait(Traits.Feature, Traits.Features.Organizing)]
[InlineData("class")]
[InlineData("record")]
+ [InlineData("record class")]
+ [InlineData("record struct")]
public async Task TestFieldsWithoutInitializers1(string typeKind)
{
var initial =
@@ -42,6 +44,30 @@ public async Task TestFieldsWithoutInitializers1(string typeKind)
await CheckAsync(initial, final);
}
+ [Theory, Trait(Traits.Feature, Traits.Features.Organizing)]
+ [InlineData("class")]
+ [InlineData("struct")]
+ [InlineData("record")]
+ [InlineData("record class")]
+ [InlineData("record struct")]
+ public async Task TestNestedTypes(string typeKind)
+ {
+ var initial =
+$@"class C {{
+ {typeKind} Nested1 {{ }}
+ {typeKind} Nested2 {{ }}
+ int A;
+}}";
+
+ var final =
+$@"class C {{
+ int A;
+ {typeKind} Nested1 {{ }}
+ {typeKind} Nested2 {{ }}
+}}";
+ await CheckAsync(initial, final);
+ }
+
[Theory, Trait(Traits.Feature, Traits.Features.Organizing)]
[InlineData("class")]
[InlineData("record")]
@@ -66,6 +92,7 @@ public async Task TestFieldsWithoutInitializers2(string typeKind)
[Theory, Trait(Traits.Feature, Traits.Features.Organizing)]
[InlineData("class")]
[InlineData("record")]
+ [InlineData("record struct")]
public async Task TestFieldsWithInitializers1(string typeKind)
{
var initial =
@@ -287,6 +314,7 @@ public async Task TestStaticInstance(string typeKind)
[Theory, Trait(Traits.Feature, Traits.Features.Organizing)]
[InlineData("class")]
[InlineData("record")]
+ [InlineData("record struct")]
public async Task TestAccessibility(string typeKind)
{
var initial =
diff --git a/src/EditorFeatures/CSharpTest/Structure/MetadataAsSource/TypeDeclarationStructureTests.cs b/src/EditorFeatures/CSharpTest/Structure/MetadataAsSource/TypeDeclarationStructureTests.cs
index c5d7ae8e19b59..5e29cd7b93a29 100644
--- a/src/EditorFeatures/CSharpTest/Structure/MetadataAsSource/TypeDeclarationStructureTests.cs
+++ b/src/EditorFeatures/CSharpTest/Structure/MetadataAsSource/TypeDeclarationStructureTests.cs
@@ -74,6 +74,23 @@ public async Task RecordWithCommentsAndAttributes()
// This is a doc comment.
[Bar, Baz]
|}{|#0:public record $$C|}{|textspan2:
+{
+ void M();
+}|}|#0}";
+
+ await VerifyBlockSpansAsync(code,
+ Region("textspan", "hint", CSharpStructureHelpers.Ellipsis, autoCollapse: true),
+ Region("textspan2", "#0", CSharpStructureHelpers.Ellipsis, autoCollapse: false));
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.MetadataAsSource)]
+ public async Task RecordStructWithCommentsAndAttributes()
+ {
+ const string code = @"
+{|hint:{|textspan:// Summary:
+// This is a doc comment.
+[Bar, Baz]
+|}{|#0:public record struct $$C|}{|textspan2:
{
void M();
}|}|#0}";
diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/FieldKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/FieldKeywordRecommenderTests.cs
index 25271dd32928b..63f7c60a79858 100644
--- a/src/EditorFeatures/CSharpTest2/Recommendations/FieldKeywordRecommenderTests.cs
+++ b/src/EditorFeatures/CSharpTest2/Recommendations/FieldKeywordRecommenderTests.cs
@@ -66,13 +66,16 @@ await VerifyKeywordAsync(
[$$");
}
- [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
- public async Task TestInAttributeInsideRecord()
+ [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ [InlineData("record")]
+ [InlineData("record class")]
+ [InlineData("record struct")]
+ public async Task TestInAttributeInsideRecord(string record)
{
// The recommender doesn't work in record in script
// Tracked by https://github.com/dotnet/roslyn/issues/44865
await VerifyWorkerAsync(
-@"record C {
+$@"{record} C {{
[$$", absent: false, TestOptions.RegularPreview);
}
diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/FixedKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/FixedKeywordRecommenderTests.cs
index b3e32c3a2955a..33a3e93a1ea3e 100644
--- a/src/EditorFeatures/CSharpTest2/Recommendations/FixedKeywordRecommenderTests.cs
+++ b/src/EditorFeatures/CSharpTest2/Recommendations/FixedKeywordRecommenderTests.cs
@@ -97,6 +97,14 @@ public async Task TestNotInStruct()
{
await VerifyAbsenceAsync(
@"struct S {
+ $$");
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestNotInRecordStruct()
+ {
+ await VerifyAbsenceAsync(
+@"record struct S {
$$");
}
diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/VoidKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/VoidKeywordRecommenderTests.cs
index 0e2ff6ade57c3..ba23401ec98d5 100644
--- a/src/EditorFeatures/CSharpTest2/Recommendations/VoidKeywordRecommenderTests.cs
+++ b/src/EditorFeatures/CSharpTest2/Recommendations/VoidKeywordRecommenderTests.cs
@@ -785,6 +785,15 @@ struct S
public readonly $$");
}
+ [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestAfterReadonlyInRecordStruct()
+ {
+ await VerifyKeywordAsync(@"
+record struct S
+{
+ public readonly $$");
+ }
+
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
[WorkItem(43295, "https://github.com/dotnet/roslyn/issues/43295")]
public async Task TestNotAfterReadonlyInClass()
diff --git a/src/EditorFeatures/Core.Wpf/Classification/ClassificationTypeFormatDefinitions.cs b/src/EditorFeatures/Core.Wpf/Classification/ClassificationTypeFormatDefinitions.cs
index 995659f5580e7..07003fb2ad364 100644
--- a/src/EditorFeatures/Core.Wpf/Classification/ClassificationTypeFormatDefinitions.cs
+++ b/src/EditorFeatures/Core.Wpf/Classification/ClassificationTypeFormatDefinitions.cs
@@ -204,6 +204,25 @@ public UserTypeRecordsFormatDefinition()
}
}
#endregion
+ #region User Types - Record structs
+ [Export(typeof(EditorFormatDefinition))]
+ [ClassificationType(ClassificationTypeNames = ClassificationTypeNames.RecordStructName)]
+ [Name(ClassificationTypeNames.RecordStructName)]
+ [Order(After = PredefinedClassificationTypeNames.Identifier)]
+ [Order(After = PredefinedClassificationTypeNames.Keyword)]
+ [Order(Before = ClassificationTypeNames.StaticSymbol)]
+ [UserVisible(true)]
+ [ExcludeFromCodeCoverage]
+ private class UserTypeRecordStructsFormatDefinition : ClassificationFormatDefinition
+ {
+ [ImportingConstructor]
+ [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
+ public UserTypeRecordStructsFormatDefinition()
+ {
+ this.DisplayName = EditorFeaturesResources.User_Types_Record_Structs;
+ }
+ }
+ #endregion
#region User Types - Delegates
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = ClassificationTypeNames.DelegateName)]
diff --git a/src/EditorFeatures/Core/EditorFeaturesResources.resx b/src/EditorFeatures/Core/EditorFeaturesResources.resx
index 2f6899e590dce..655e59d820ec5 100644
--- a/src/EditorFeatures/Core/EditorFeaturesResources.resx
+++ b/src/EditorFeatures/Core/EditorFeaturesResources.resx
@@ -939,6 +939,9 @@ Do you want to proceed?
User Types - Records
+
+ User Types - Record Structs
+
Get help for '{0}'
diff --git a/src/EditorFeatures/Core/Implementation/Classification/ClassificationTypeDefinitions.cs b/src/EditorFeatures/Core/Implementation/Classification/ClassificationTypeDefinitions.cs
index 122bee88aa0fb..5f5227bff4bdd 100644
--- a/src/EditorFeatures/Core/Implementation/Classification/ClassificationTypeDefinitions.cs
+++ b/src/EditorFeatures/Core/Implementation/Classification/ClassificationTypeDefinitions.cs
@@ -58,6 +58,12 @@ internal sealed class ClassificationTypeDefinitions
[BaseDefinition(ClassificationTypeNames.ClassName)]
internal readonly ClassificationTypeDefinition UserTypeRecordsTypeDefinition;
#endregion
+ #region User Types - Record Structs
+ [Export]
+ [Name(ClassificationTypeNames.RecordStructName)]
+ [BaseDefinition(ClassificationTypeNames.StructName)]
+ internal readonly ClassificationTypeDefinition UserTypeRecordStructsTypeDefinition;
+ #endregion
#region User Types - Delegates
[Export]
[Name(ClassificationTypeNames.DelegateName)]
diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf
index 6539fa8dcfff7..20a0010739633 100644
--- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf
+++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf
@@ -457,6 +457,11 @@
Uživatelské typy – rozhraní
+
+
+ User Types - Record Structs
+
+
Typy uživatelů – Záznamy
diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf
index 7dab8aa18dd1f..e73d787ccc8f1 100644
--- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf
+++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf
@@ -457,6 +457,11 @@
Benutzertypen - Schnittstellen
+
+
+ User Types - Record Structs
+
+
Benutzertypen – Datensätze
diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf
index 91f0299c0d109..ed9f89eb81174 100644
--- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf
+++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf
@@ -457,6 +457,11 @@
Tipos de usuario: interfaces
+
+
+ User Types - Record Structs
+
+
Tipos de usuario: registros
diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf
index 734ca3ccf60db..2d3c94fd04137 100644
--- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf
+++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf
@@ -457,6 +457,11 @@
Types d'utilisateurs - Interfaces
+
+
+ User Types - Record Structs
+
+
Types d'utilisateur - Enregistrements
diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf
index ca26b60ca9ecd..155f6d1fd0cb8 100644
--- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf
+++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf
@@ -457,6 +457,11 @@
Tipi utente - Interfacce
+
+
+ User Types - Record Structs
+
+
Tipi utente - Record
diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf
index 5508feabfa76b..5a1c9d998aaf7 100644
--- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf
+++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf
@@ -457,6 +457,11 @@
ユーザー タイプ - インターフェイス
+
+
+ User Types - Record Structs
+
+
ユーザー タイプ - レコード
diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf
index 295e6f802a05b..5fac4f47d28c8 100644
--- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf
+++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf
@@ -457,6 +457,11 @@
사용자 형식 - 인터페이스
+
+
+ User Types - Record Structs
+
+
사용자 유형 - 레코드
diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf
index 823fd239c3d54..362f172ecbc79 100644
--- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf
+++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf
@@ -457,6 +457,11 @@
Typy użytkownika — interfejsy
+
+
+ User Types - Record Structs
+
+
Typy użytkownika — rekordy
diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf
index 00eb77d4bcf95..63992526de0d0 100644
--- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf
+++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf
@@ -457,6 +457,11 @@
Tipos de Usuário - Interfaces
+
+
+ User Types - Record Structs
+
+
Tipos de Usuário – Registros
diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf
index e797ae4690a91..0971262a56d41 100644
--- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf
+++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf
@@ -457,6 +457,11 @@
Пользовательские типы — интерфейсы
+
+
+ User Types - Record Structs
+
+
Пользовательские типы — записи
diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf
index 42dd9347671f5..e9ee571938d91 100644
--- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf
+++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf
@@ -457,6 +457,11 @@
Kullanıcı Türleri - Arabirimler
+
+
+ User Types - Record Structs
+
+
Kullanıcı Türleri - Kayıtlar
diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf
index 01cd292694cf0..5e265f962febb 100644
--- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf
+++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf
@@ -457,6 +457,11 @@
用户类型 - 接口
+
+
+ User Types - Record Structs
+
+
用户类型 - 记录
diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf
index 285e7d9493df4..9cbcd387ae693 100644
--- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf
+++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf
@@ -457,6 +457,11 @@
使用者類型 - 介面
+
+
+ User Types - Record Structs
+
+
使用者類型 - 記錄
diff --git a/src/EditorFeatures/Test2/IntelliSense/IntellisenseQuickInfoBuilderTests.vb b/src/EditorFeatures/Test2/IntelliSense/IntellisenseQuickInfoBuilderTests.vb
index b03efe20b2bda..6a896dc783796 100644
--- a/src/EditorFeatures/Test2/IntelliSense/IntellisenseQuickInfoBuilderTests.vb
+++ b/src/EditorFeatures/Test2/IntelliSense/IntellisenseQuickInfoBuilderTests.vb
@@ -1158,5 +1158,107 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
ToolTipAssert.EqualContent(expected, container)
End Function
+
+
+ Public Async Function QuickInfoForRecordClass() As Task
+ Dim workspace =
+
+
+
+ public sealed record class TestRecord(int X, int Y) { }
+
+ class C
+ {
+ void M()
+ {
+ var x = new Test$$Record(1, 2);
+ }
+ }
+
+
+
+
+ Dim intellisenseQuickInfo = Await GetQuickInfoItemAsync(workspace, LanguageNames.CSharp)
+ Assert.NotNull(intellisenseQuickInfo)
+
+ Dim container = Assert.IsType(Of ContainerElement)(intellisenseQuickInfo.Item)
+
+ Dim expected = New ContainerElement(
+ ContainerElementStyle.Stacked Or ContainerElementStyle.VerticalPadding,
+ New ContainerElement(
+ ContainerElementStyle.Wrapped,
+ New ImageElement(New ImageId(KnownImageIds.ImageCatalogGuid, KnownImageIds.MethodPublic)),
+ New ClassifiedTextElement(
+ New ClassifiedTextRun(ClassificationTypeNames.RecordClassName, "TestRecord", navigationAction:=Sub() Return, "TestRecord"),
+ New ClassifiedTextRun(ClassificationTypeNames.Punctuation, "."),
+ New ClassifiedTextRun(ClassificationTypeNames.RecordClassName, "TestRecord", navigationAction:=Sub() Return, "TestRecord.TestRecord(int X, int Y)"),
+ New ClassifiedTextRun(ClassificationTypeNames.Punctuation, "("),
+ New ClassifiedTextRun(ClassificationTypeNames.Keyword, "int", navigationAction:=Sub() Return, "int"),
+ New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "),
+ New ClassifiedTextRun(ClassificationTypeNames.ParameterName, "X", navigationAction:=Sub() Return, "int X"),
+ New ClassifiedTextRun(ClassificationTypeNames.Punctuation, ","),
+ New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "),
+ New ClassifiedTextRun(ClassificationTypeNames.Keyword, "int", navigationAction:=Sub() Return, "int"),
+ New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "),
+ New ClassifiedTextRun(ClassificationTypeNames.ParameterName, "Y", navigationAction:=Sub() Return, "int Y"),
+ New ClassifiedTextRun(ClassificationTypeNames.Punctuation, ")"))))
+
+ ToolTipAssert.EqualContent(expected, container)
+ End Function
+
+
+ Public Async Function QuickInfoForRecordStructs() As Task
+ Dim workspace =
+
+
+
+ public sealed record struct TestRecord(int X, int Y) { }
+
+ class C
+ {
+ void M()
+ {
+ var x = new Test$$Record(1, 2);
+ }
+ }
+
+
+
+
+ Dim intellisenseQuickInfo = Await GetQuickInfoItemAsync(workspace, LanguageNames.CSharp)
+ Assert.NotNull(intellisenseQuickInfo)
+
+ Dim container = Assert.IsType(Of ContainerElement)(intellisenseQuickInfo.Item)
+
+ Dim expected = New ContainerElement(
+ ContainerElementStyle.Stacked Or ContainerElementStyle.VerticalPadding,
+ New ContainerElement(
+ ContainerElementStyle.Wrapped,
+ New ImageElement(New ImageId(KnownImageIds.ImageCatalogGuid, KnownImageIds.MethodPublic)),
+ New ClassifiedTextElement(
+ New ClassifiedTextRun(ClassificationTypeNames.RecordStructName, "TestRecord", navigationAction:=Sub() Return, "TestRecord"),
+ New ClassifiedTextRun(ClassificationTypeNames.Punctuation, "."),
+ New ClassifiedTextRun(ClassificationTypeNames.RecordStructName, "TestRecord", navigationAction:=Sub() Return, "TestRecord.TestRecord(int X, int Y)"),
+ New ClassifiedTextRun(ClassificationTypeNames.Punctuation, "("),
+ New ClassifiedTextRun(ClassificationTypeNames.Keyword, "int", navigationAction:=Sub() Return, "int"),
+ New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "),
+ New ClassifiedTextRun(ClassificationTypeNames.ParameterName, "X", navigationAction:=Sub() Return, "int X"),
+ New ClassifiedTextRun(ClassificationTypeNames.Punctuation, ","),
+ New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "),
+ New ClassifiedTextRun(ClassificationTypeNames.Keyword, "int", navigationAction:=Sub() Return, "int"),
+ New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "),
+ New ClassifiedTextRun(ClassificationTypeNames.ParameterName, "Y", navigationAction:=Sub() Return, "int Y"),
+ New ClassifiedTextRun(ClassificationTypeNames.Punctuation, ")"),
+ New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "),
+ New ClassifiedTextRun(ClassificationTypeNames.Punctuation, "("),
+ New ClassifiedTextRun(ClassificationTypeNames.Punctuation, "+"),
+ New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "),
+ New ClassifiedTextRun(ClassificationTypeNames.Text, "1"),
+ New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "),
+ New ClassifiedTextRun(ClassificationTypeNames.Text, FeaturesResources.overload),
+ New ClassifiedTextRun(ClassificationTypeNames.Punctuation, ")"))))
+
+ ToolTipAssert.EqualContent(expected, container)
+ End Function
End Class
End Namespace
diff --git a/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs b/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs
index a1488330e57be..0a05fc8cdad27 100644
--- a/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs
+++ b/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs
@@ -34,6 +34,10 @@ public static FormattedClassification Class(string text)
public static FormattedClassification Record(string text)
=> New(text, ClassificationTypeNames.RecordClassName);
+ [DebuggerStepThrough]
+ public static FormattedClassification RecordStruct(string text)
+ => New(text, ClassificationTypeNames.RecordStructName);
+
[DebuggerStepThrough]
public static FormattedClassification Delegate(string text)
=> New(text, ClassificationTypeNames.DelegateName);
diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceTypeCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceTypeCompletionProvider.cs
index 754cb0310009c..fd435e1bf5f3f 100644
--- a/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceTypeCompletionProvider.cs
+++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceTypeCompletionProvider.cs
@@ -142,7 +142,7 @@ private static bool IsPreviousTokenValid(SyntaxToken tokenBeforeType)
}
private static bool IsClassOrStructOrInterfaceOrRecord(SyntaxNode node)
- => node.Kind() == SyntaxKind.ClassDeclaration || node.Kind() == SyntaxKind.StructDeclaration ||
- node.Kind() == SyntaxKind.InterfaceDeclaration || node.Kind() == SyntaxKind.RecordDeclaration;
+ => node.Kind() is SyntaxKind.ClassDeclaration or SyntaxKind.StructDeclaration or
+ SyntaxKind.InterfaceDeclaration or SyntaxKind.RecordDeclaration or SyntaxKind.RecordStructDeclaration;
}
}
diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FieldKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FieldKeywordRecommender.cs
index 26afaf5f39551..e098eb5de0c35 100644
--- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FieldKeywordRecommender.cs
+++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FieldKeywordRecommender.cs
@@ -18,6 +18,7 @@ internal class FieldKeywordRecommender : AbstractSyntacticSingleKeywordRecommend
SyntaxKind.StructDeclaration,
SyntaxKind.ClassDeclaration,
SyntaxKind.RecordDeclaration,
+ SyntaxKind.RecordStructDeclaration,
SyntaxKind.EnumDeclaration,
};
diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs
index 18a5aae547cf4..4f52472c83899 100644
--- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs
+++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs
@@ -52,7 +52,11 @@ private static bool IsValidContextForType(CSharpSyntaxContext context, Cancellat
}
private static bool IsStructAccessorContext(CSharpSyntaxContext context)
- => context.ContainingTypeDeclaration.IsKind(SyntaxKind.StructDeclaration) &&
+ {
+ var type = context.ContainingTypeDeclaration;
+ return type is not null &&
+ type.Kind() is SyntaxKind.StructDeclaration or SyntaxKind.RecordStructDeclaration &&
context.TargetToken.IsAnyAccessorDeclarationContext(context.Position, SyntaxKind.ReadOnlyKeyword);
+ }
}
}
diff --git a/src/Features/CSharp/Portable/Diagnostics/Analyzers/CSharpSimplifyTypeNamesDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/Diagnostics/Analyzers/CSharpSimplifyTypeNamesDiagnosticAnalyzer.cs
index 3011dacc7a5ad..d625cd68869d3 100644
--- a/src/Features/CSharp/Portable/Diagnostics/Analyzers/CSharpSimplifyTypeNamesDiagnosticAnalyzer.cs
+++ b/src/Features/CSharp/Portable/Diagnostics/Analyzers/CSharpSimplifyTypeNamesDiagnosticAnalyzer.cs
@@ -39,6 +39,7 @@ protected override bool IsIgnoredCodeBlock(SyntaxNode codeBlock)
SyntaxKind.ClassDeclaration,
SyntaxKind.RecordDeclaration,
SyntaxKind.StructDeclaration,
+ SyntaxKind.RecordStructDeclaration,
SyntaxKind.InterfaceDeclaration,
SyntaxKind.DelegateDeclaration,
SyntaxKind.EnumDeclaration);
diff --git a/src/Features/CSharp/Portable/DocumentationComments/DocumentationCommentSnippetService.cs b/src/Features/CSharp/Portable/DocumentationComments/DocumentationCommentSnippetService.cs
index 079e46c889145..a908916e98648 100644
--- a/src/Features/CSharp/Portable/DocumentationComments/DocumentationCommentSnippetService.cs
+++ b/src/Features/CSharp/Portable/DocumentationComments/DocumentationCommentSnippetService.cs
@@ -39,8 +39,10 @@ protected override bool SupportsDocumentationComments(MemberDeclarationSyntax me
switch (member.Kind())
{
case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.RecordDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.StructDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
case SyntaxKind.DelegateDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.EnumMemberDeclaration:
diff --git a/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs b/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs
index 2517806df62f5..4f1c785724200 100644
--- a/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs
+++ b/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs
@@ -564,6 +564,7 @@ private static Label ClassifyTopSyntax(SyntaxKind kind, out bool isLeaf)
return Label.NamespaceDeclaration;
// Need to add support for records (tracked by https://github.com/dotnet/roslyn/issues/44877)
+ // Need to add support for record structs (tracked by https://github.com/dotnet/roslyn/issues/44877)
case SyntaxKind.ClassDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.InterfaceDeclaration:
@@ -1343,6 +1344,7 @@ private static double CombineOptional(
return ((NamespaceDeclarationSyntax)node).Name;
// Need to add support for records (tracked by https://github.com/dotnet/roslyn/issues/44877)
+ // Need to add support for record structs (tracked by https://github.com/dotnet/roslyn/issues/44877)
case SyntaxKind.ClassDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.InterfaceDeclaration:
diff --git a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs
index ae496e28361cf..56739f2c2e9c7 100644
--- a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs
+++ b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs
@@ -386,6 +386,16 @@ public override SyntaxNode VisitRecordDeclaration(RecordDeclarationSyntax node)
return GetUpdatedTypeDeclaration(node);
}
+ public override SyntaxNode VisitRecordStructDeclaration(RecordStructDeclarationSyntax node)
+ {
+ if (node != ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitRecordStructDeclaration(node);
+ }
+
+ return GetUpdatedTypeDeclaration(node);
+ }
+
public override SyntaxNode VisitStructDeclaration(StructDeclarationSyntax node)
{
if (node != ContainerOfStatementsOrFieldToReplace)
diff --git a/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs b/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs
index 2bbebe4eb7164..89dfa3f31afc5 100644
--- a/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs
+++ b/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs
@@ -100,9 +100,7 @@ expression.Parent is BaseTypeSyntax baseType &&
// If it's in the base list of an interface or struct, then it's definitely an
// interface.
- return
- baseList.IsParentKind(SyntaxKind.InterfaceDeclaration) ||
- baseList.IsParentKind(SyntaxKind.StructDeclaration);
+ return baseList.IsParentKind(SyntaxKind.InterfaceDeclaration, SyntaxKind.StructDeclaration, SyntaxKind.RecordStructDeclaration);
}
if (expression is TypeSyntax &&
@@ -673,7 +671,7 @@ internal override bool TryGetBaseList(ExpressionSyntax expression, out TypeKindO
{
if (node is BaseListSyntax)
{
- if (node.Parent != null && (node.Parent is InterfaceDeclarationSyntax || node.Parent is StructDeclarationSyntax))
+ if (node.Parent is InterfaceDeclarationSyntax or StructDeclarationSyntax or RecordStructDeclarationSyntax)
{
typeKindValue = TypeKindOptions.Interface;
return true;
diff --git a/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementInterfaceService.cs b/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementInterfaceService.cs
index 74bdae4b8143a..644850fb6aa4f 100644
--- a/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementInterfaceService.cs
+++ b/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementInterfaceService.cs
@@ -41,7 +41,7 @@ protected override bool TryInitializeState(
baseType.IsParentKind(SyntaxKind.BaseList) &&
baseType.Type == interfaceNode)
{
- if (interfaceNode.Parent.Parent.IsParentKind(SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration, SyntaxKind.RecordDeclaration))
+ if (interfaceNode.Parent.Parent.IsParentKind(SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration, SyntaxKind.RecordDeclaration, SyntaxKind.RecordStructDeclaration))
{
var interfaceSymbolInfo = model.GetSymbolInfo(interfaceNode, cancellationToken);
if (interfaceSymbolInfo.CandidateReason != CandidateReason.WrongArity)
diff --git a/src/Features/CSharp/Portable/Organizing/Organizers/MemberDeclarationsOrganizer.Comparer.cs b/src/Features/CSharp/Portable/Organizing/Organizers/MemberDeclarationsOrganizer.Comparer.cs
index 72c40758ffd84..c362b4b3d1338 100644
--- a/src/Features/CSharp/Portable/Organizing/Organizers/MemberDeclarationsOrganizer.Comparer.cs
+++ b/src/Features/CSharp/Portable/Organizing/Organizers/MemberDeclarationsOrganizer.Comparer.cs
@@ -171,6 +171,7 @@ private static OuterOrdering GetOuterOrdering(MemberDeclarationSyntax x)
case SyntaxKind.EnumDeclaration:
case SyntaxKind.DelegateDeclaration:
case SyntaxKind.RecordDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
return OuterOrdering.Types;
default:
return OuterOrdering.Remaining;
diff --git a/src/Features/CSharp/Portable/Organizing/Organizers/RecordDeclarationOrganizer.cs b/src/Features/CSharp/Portable/Organizing/Organizers/RecordDeclarationOrganizer.cs
index 91202cd480449..b8775103c79fe 100644
--- a/src/Features/CSharp/Portable/Organizing/Organizers/RecordDeclarationOrganizer.cs
+++ b/src/Features/CSharp/Portable/Organizing/Organizers/RecordDeclarationOrganizer.cs
@@ -28,6 +28,7 @@ protected override RecordDeclarationSyntax Organize(
syntax.AttributeLists,
ModifiersOrganizer.Organize(syntax.Modifiers),
syntax.Keyword,
+ syntax.ClassKeyword,
syntax.Identifier,
syntax.TypeParameterList,
syntax.ParameterList,
diff --git a/src/Features/CSharp/Portable/Organizing/Organizers/RecordStructDeclarationOrganizer.cs b/src/Features/CSharp/Portable/Organizing/Organizers/RecordStructDeclarationOrganizer.cs
new file mode 100644
index 0000000000000..f8527cb732bae
--- /dev/null
+++ b/src/Features/CSharp/Portable/Organizing/Organizers/RecordStructDeclarationOrganizer.cs
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+using System.Composition;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Organizing.Organizers;
+
+namespace Microsoft.CodeAnalysis.CSharp.Organizing.Organizers
+{
+ [ExportSyntaxNodeOrganizer(LanguageNames.CSharp), Shared]
+ internal class RecordStructDeclarationOrganizer : AbstractSyntaxNodeOrganizer
+ {
+ [ImportingConstructor]
+ [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
+ public RecordStructDeclarationOrganizer()
+ {
+ }
+
+ protected override RecordStructDeclarationSyntax Organize(
+ RecordStructDeclarationSyntax syntax,
+ CancellationToken cancellationToken)
+ {
+ return syntax.Update(
+ syntax.AttributeLists,
+ ModifiersOrganizer.Organize(syntax.Modifiers),
+ syntax.Keyword,
+ syntax.StructKeyword,
+ syntax.Identifier,
+ syntax.TypeParameterList,
+ syntax.ParameterList,
+ syntax.BaseList,
+ syntax.ConstraintClauses,
+ syntax.OpenBraceToken,
+ MemberDeclarationsOrganizer.Organize(syntax.Members, cancellationToken),
+ syntax.CloseBraceToken,
+ syntax.SemicolonToken);
+ }
+ }
+}
diff --git a/src/Features/CSharp/Portable/Structure/CSharpBlockStructureProvider.cs b/src/Features/CSharp/Portable/Structure/CSharpBlockStructureProvider.cs
index 241a13e5e895c..9b168d5d91d46 100644
--- a/src/Features/CSharp/Portable/Structure/CSharpBlockStructureProvider.cs
+++ b/src/Features/CSharp/Portable/Structure/CSharpBlockStructureProvider.cs
@@ -42,6 +42,7 @@ private static ImmutableDictionary();
builder.Add();
builder.Add();
+ builder.Add();
builder.Add();
builder.Add();
builder.Add();
diff --git a/src/Features/CSharp/Portable/Structure/CSharpStructureHelpers.cs b/src/Features/CSharp/Portable/Structure/CSharpStructureHelpers.cs
index cc20341acbd9a..6b23a4f9ae71e 100644
--- a/src/Features/CSharp/Portable/Structure/CSharpStructureHelpers.cs
+++ b/src/Features/CSharp/Portable/Structure/CSharpStructureHelpers.cs
@@ -352,11 +352,10 @@ static SyntaxToken GetEndToken(SyntaxNode node)
{
return propertyDeclaration.Modifiers.FirstOrNull() ?? propertyDeclaration.Type.GetFirstToken();
}
- else if (node.IsKind(SyntaxKind.ClassDeclaration, out TypeDeclarationSyntax typeDeclaration)
- || node.IsKind(SyntaxKind.RecordDeclaration, out typeDeclaration)
- || node.IsKind(SyntaxKind.StructDeclaration, out typeDeclaration)
- || node.IsKind(SyntaxKind.InterfaceDeclaration, out typeDeclaration))
+ else if (node.Kind() is SyntaxKind.ClassDeclaration or SyntaxKind.RecordDeclaration or
+ SyntaxKind.RecordStructDeclaration or SyntaxKind.StructDeclaration or SyntaxKind.InterfaceDeclaration)
{
+ var typeDeclaration = (TypeDeclarationSyntax)node;
return typeDeclaration.Modifiers.FirstOrNull() ?? typeDeclaration.Keyword;
}
else
@@ -371,11 +370,10 @@ static SyntaxToken GetHintTextEndToken(SyntaxNode node)
{
return enumDeclaration.OpenBraceToken.GetPreviousToken();
}
- else if (node.IsKind(SyntaxKind.ClassDeclaration, out TypeDeclarationSyntax typeDeclaration)
- || node.IsKind(SyntaxKind.RecordDeclaration, out typeDeclaration)
- || node.IsKind(SyntaxKind.StructDeclaration, out typeDeclaration)
- || node.IsKind(SyntaxKind.InterfaceDeclaration, out typeDeclaration))
+ else if (node.Kind() is SyntaxKind.ClassDeclaration or SyntaxKind.RecordDeclaration or
+ SyntaxKind.RecordStructDeclaration or SyntaxKind.StructDeclaration or SyntaxKind.InterfaceDeclaration)
{
+ var typeDeclaration = (TypeDeclarationSyntax)node;
return typeDeclaration.OpenBraceToken.GetPreviousToken();
}
else
diff --git a/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs b/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs
index 662d91d4751f0..5d9fdf7a35864 100644
--- a/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs
+++ b/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs
@@ -312,6 +312,7 @@ public async Task GetFullyQualifiedNameAsync(Solution solution, Document
case SymbolDisplayPartKind.ErrorTypeName:
case SymbolDisplayPartKind.InterfaceName:
case SymbolDisplayPartKind.StructName:
+ case SymbolDisplayPartKind.RecordStructName:
actualBuilder.Append('+');
break;
@@ -328,7 +329,8 @@ public async Task GetFullyQualifiedNameAsync(Solution solution, Document
previousWasClass = part.Kind == SymbolDisplayPartKind.ClassName ||
part.Kind == SymbolDisplayPartKind.RecordClassName ||
part.Kind == SymbolDisplayPartKind.InterfaceName ||
- part.Kind == SymbolDisplayPartKind.StructName;
+ part.Kind == SymbolDisplayPartKind.StructName ||
+ part.Kind == SymbolDisplayPartKind.RecordStructName;
}
return actualBuilder.ToString();
diff --git a/src/Features/Core/Portable/Common/SymbolDisplayPartKindTags.cs b/src/Features/Core/Portable/Common/SymbolDisplayPartKindTags.cs
index 54a95beca287f..410a0b7fb928f 100644
--- a/src/Features/Core/Portable/Common/SymbolDisplayPartKindTags.cs
+++ b/src/Features/Core/Portable/Common/SymbolDisplayPartKindTags.cs
@@ -43,6 +43,7 @@ public static string GetTag(SymbolDisplayPartKind kind)
SymbolDisplayPartKind.ExtensionMethodName => TextTags.ExtensionMethod,
SymbolDisplayPartKind.ConstantName => TextTags.Constant,
SymbolDisplayPartKind.RecordClassName => TextTags.Record,
+ SymbolDisplayPartKind.RecordStructName => TextTags.RecordStruct,
_ => string.Empty,
};
}
diff --git a/src/Features/Core/Portable/Common/TaggedText.cs b/src/Features/Core/Portable/Common/TaggedText.cs
index dba173e80f714..ac1560445d897 100644
--- a/src/Features/Core/Portable/Common/TaggedText.cs
+++ b/src/Features/Core/Portable/Common/TaggedText.cs
@@ -230,6 +230,9 @@ public static string ToClassificationTypeName(this string taggedTextTag)
case TextTags.Record:
return ClassificationTypeNames.RecordClassName;
+ case TextTags.RecordStruct:
+ return ClassificationTypeNames.RecordStructName;
+
case TextTags.ContainerStart:
case TextTags.ContainerEnd:
// These tags are not visible so classify them as whitespace
diff --git a/src/Features/Core/Portable/Common/TextTags.cs b/src/Features/Core/Portable/Common/TextTags.cs
index db749099cc6ea..6b726768ad041 100644
--- a/src/Features/Core/Portable/Common/TextTags.cs
+++ b/src/Features/Core/Portable/Common/TextTags.cs
@@ -44,6 +44,7 @@ public static class TextTags
public const string ExtensionMethod = nameof(ExtensionMethod);
public const string Constant = nameof(Constant);
public const string Record = nameof(Record);
+ public const string RecordStruct = nameof(RecordStruct);
///
/// Indicates the start of a text container. The elements after through (but not
diff --git a/src/Features/Core/Portable/PublicAPI.Unshipped.txt b/src/Features/Core/Portable/PublicAPI.Unshipped.txt
index d62edd3868059..5542acfb736be 100644
--- a/src/Features/Core/Portable/PublicAPI.Unshipped.txt
+++ b/src/Features/Core/Portable/PublicAPI.Unshipped.txt
@@ -1,4 +1,5 @@
const Microsoft.CodeAnalysis.TextTags.Record = "Record" -> string
+const Microsoft.CodeAnalysis.TextTags.RecordStruct = "RecordStruct" -> string
Microsoft.CodeAnalysis.Completion.CompletionItem.IsComplexTextEdit.get -> bool
Microsoft.CodeAnalysis.Completion.CompletionItem.WithIsComplexTextEdit(bool isComplexTextEdit) -> Microsoft.CodeAnalysis.Completion.CompletionItem
static Microsoft.CodeAnalysis.Completion.CompletionChange.Create(Microsoft.CodeAnalysis.Text.TextChange textChange, System.Collections.Immutable.ImmutableArray textChanges, int? newPosition = null, bool includesCommitCharacter = false) -> Microsoft.CodeAnalysis.Completion.CompletionChange
diff --git a/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.NodeLocator.cs b/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.NodeLocator.cs
index f9f39b3d1d5d5..c987308a0c697 100644
--- a/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.NodeLocator.cs
+++ b/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.NodeLocator.cs
@@ -41,8 +41,10 @@ private class NodeLocator : AbstractNodeLocator
case SyntaxKind.AttributeArgument:
return GetStartPoint(text, (AttributeArgumentSyntax)node, part);
case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.RecordDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.StructDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
case SyntaxKind.EnumDeclaration:
return GetStartPoint(text, (BaseTypeDeclarationSyntax)node, part);
case SyntaxKind.MethodDeclaration:
@@ -89,8 +91,10 @@ private class NodeLocator : AbstractNodeLocator
case SyntaxKind.AttributeArgument:
return GetEndPoint(text, (AttributeArgumentSyntax)node, part);
case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.RecordDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.StructDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
case SyntaxKind.EnumDeclaration:
return GetEndPoint(text, (BaseTypeDeclarationSyntax)node, part);
case SyntaxKind.MethodDeclaration:
diff --git a/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.NodeNameGenerator.cs b/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.NodeNameGenerator.cs
index 26716a4b8856d..cc5a3f2436740 100644
--- a/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.NodeNameGenerator.cs
+++ b/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.NodeNameGenerator.cs
@@ -169,7 +169,9 @@ protected override void AppendNodeName(StringBuilder builder, SyntaxNode node)
break;
case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.RecordDeclaration:
case SyntaxKind.StructDeclaration:
+ case SyntaxKind.RecordStructDeclaration: // PROTOTYPE(record-structs): not sure how to test
case SyntaxKind.InterfaceDeclaration:
var typeDeclaration = (TypeDeclarationSyntax)node;
builder.Append(typeDeclaration.Identifier.ValueText);
diff --git a/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs b/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs
index 1c41e712d2b92..c8df01b305fec 100644
--- a/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs
+++ b/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs
@@ -88,6 +88,7 @@ private static bool IsNameableNode(SyntaxNode node)
switch (node.Kind())
{
case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.RecordDeclaration:
case SyntaxKind.ConstructorDeclaration:
case SyntaxKind.ConversionOperatorDeclaration:
case SyntaxKind.DelegateDeclaration:
@@ -102,6 +103,7 @@ private static bool IsNameableNode(SyntaxNode node)
case SyntaxKind.OperatorDeclaration:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.StructDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
return true;
case SyntaxKind.VariableDeclarator:
@@ -139,7 +141,7 @@ public override bool MatchesScope(SyntaxNode node, EnvDTE.vsCMElement scope)
break;
- case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.ClassDeclaration: // PROTOTYPE(record-structs): no sure what this is for (and elsewhere in this file)
if (scope == EnvDTE.vsCMElement.vsCMElementClass)
{
return true;
diff --git a/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs b/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs
index 79bff73c599db..f96a666c8a024 100644
--- a/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs
+++ b/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs
@@ -301,6 +301,7 @@ private static bool IsVerbatimStringToken(SyntaxToken token)
SyntaxKind.ClassDeclaration => ClassificationTypeNames.ClassName,
SyntaxKind.RecordDeclaration => ClassificationTypeNames.RecordClassName,
SyntaxKind.StructDeclaration => ClassificationTypeNames.StructName,
+ SyntaxKind.RecordStructDeclaration => ClassificationTypeNames.RecordStructName,
_ => null
};
@@ -354,6 +355,7 @@ private static bool IsExtensionMethod(MethodDeclarationSyntax methodDeclaration)
SyntaxKind.StructDeclaration => ClassificationTypeNames.StructName,
SyntaxKind.InterfaceDeclaration => ClassificationTypeNames.InterfaceName,
SyntaxKind.RecordDeclaration => ClassificationTypeNames.RecordClassName,
+ SyntaxKind.RecordStructDeclaration => ClassificationTypeNames.RecordStructName,
_ => null,
};
diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpDeclarationComparer.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpDeclarationComparer.cs
index 8d2ba875c3ba7..232b470c730b7 100644
--- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpDeclarationComparer.cs
+++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpDeclarationComparer.cs
@@ -118,7 +118,9 @@ public int Compare(SyntaxNode x, SyntaxNode y)
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.StructDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.RecordDeclaration:
return Compare((BaseTypeDeclarationSyntax)x, (BaseTypeDeclarationSyntax)y);
case SyntaxKind.ConversionOperatorDeclaration:
@@ -358,7 +360,7 @@ private static int GetAccessibilityPrecedence(SyntaxTokenList modifiers, SyntaxN
// All interface members are public
return (int)Accessibility.Public;
}
- else if (node.Kind() == SyntaxKind.StructDeclaration || node.Kind() == SyntaxKind.ClassDeclaration)
+ else if (node.Kind() is SyntaxKind.StructDeclaration or SyntaxKind.ClassDeclaration or SyntaxKind.RecordDeclaration or SyntaxKind.RecordStructDeclaration)
{
// Members and nested types default to private
return (int)Accessibility.Private;
diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs
index 36f009884ee5c..b397349d6d43f 100644
--- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs
+++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs
@@ -150,6 +150,7 @@ private static SyntaxNode AsNamespaceMember(SyntaxNode declaration)
case SyntaxKind.EnumDeclaration:
case SyntaxKind.DelegateDeclaration:
case SyntaxKind.RecordDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
return declaration;
default:
return null;
@@ -1282,6 +1283,13 @@ private static SyntaxNode EnsureRecordDeclarationHasBody(SyntaxNode declaration)
.WithOpenBraceToken(recordDeclaration.OpenBraceToken == default ? SyntaxFactory.Token(SyntaxKind.OpenBraceToken) : recordDeclaration.OpenBraceToken)
.WithCloseBraceToken(recordDeclaration.CloseBraceToken == default ? SyntaxFactory.Token(SyntaxKind.CloseBraceToken) : recordDeclaration.CloseBraceToken);
}
+ else if (declaration is RecordStructDeclarationSyntax recordStructDeclaration)
+ {
+ return recordStructDeclaration
+ .WithSemicolonToken(default)
+ .WithOpenBraceToken(recordStructDeclaration.OpenBraceToken == default ? SyntaxFactory.Token(SyntaxKind.OpenBraceToken) : recordStructDeclaration.OpenBraceToken)
+ .WithCloseBraceToken(recordStructDeclaration.CloseBraceToken == default ? SyntaxFactory.Token(SyntaxKind.CloseBraceToken) : recordStructDeclaration.CloseBraceToken);
+ }
return declaration;
}
@@ -1473,6 +1481,7 @@ private static DeclarationModifiers GetAllowedModifiers(SyntaxKind kind)
return s_interfaceModifiers;
case SyntaxKind.StructDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
return s_structModifiers;
case SyntaxKind.MethodDeclaration:
@@ -1967,6 +1976,7 @@ private SyntaxNode AsNodeLike(SyntaxNode existingNode, SyntaxNode newNode)
case DeclarationKind.RecordClass:
case DeclarationKind.Interface:
case DeclarationKind.Struct:
+ case DeclarationKind.RecordStruct:
case DeclarationKind.Enum:
case DeclarationKind.Namespace:
case DeclarationKind.CompilationUnit:
diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/NamedTypeGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/NamedTypeGenerator.cs
index 08baff5438364..38dcd702b90ff 100644
--- a/src/Workspaces/CSharp/Portable/CodeGeneration/NamedTypeGenerator.cs
+++ b/src/Workspaces/CSharp/Portable/CodeGeneration/NamedTypeGenerator.cs
@@ -79,7 +79,7 @@ public static MemberDeclarationSyntax GenerateNamedTypeDeclaration(
var members = GetMembers(namedType).Where(s => s.Kind != SymbolKind.Property || PropertyGenerator.CanBeGenerated((IPropertySymbol)s))
.ToImmutableArray();
- if (namedType.IsRecord)
+ if (namedType.IsRecord && namedType.TypeKind is TypeKind.Class)
{
declaration = GenerateRecordMembers(service, options, (RecordDeclarationSyntax)declaration, members, cancellationToken);
}
@@ -170,8 +170,10 @@ private static MemberDeclarationSyntax RemoveAllMembers(MemberDeclarationSyntax
return ((EnumDeclarationSyntax)declaration).WithMembers(default);
case SyntaxKind.StructDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.RecordDeclaration:
return ((TypeDeclarationSyntax)declaration).WithMembers(default);
default:
@@ -196,7 +198,9 @@ private static MemberDeclarationSyntax GetDeclarationSyntaxWithoutMembersWorker(
TypeDeclarationSyntax typeDeclaration;
if (namedType.IsRecord)
{
- typeDeclaration = SyntaxFactory.RecordDeclaration(SyntaxFactory.Token(SyntaxKind.RecordKeyword), namedType.Name.ToIdentifierToken());
+ typeDeclaration = namedType.TypeKind is TypeKind.Class
+ ? SyntaxFactory.RecordDeclaration(SyntaxFactory.Token(SyntaxKind.RecordKeyword), namedType.Name.ToIdentifierToken())
+ : SyntaxFactory.RecordStructDeclaration(SyntaxFactory.Token(SyntaxKind.RecordKeyword), namedType.Name.ToIdentifierToken());
}
else
{
diff --git a/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs b/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs
index 90e00243ec042..f28de456bd124 100644
--- a/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs
+++ b/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs
@@ -508,7 +508,9 @@ private static Accessibility GetAccessibility(SyntaxNode node, SyntaxTokenList m
switch (node.Parent.Kind())
{
case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.RecordDeclaration:
case SyntaxKind.StructDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
// Anything without modifiers is private if it's in a class/struct declaration.
return Accessibility.Private;
case SyntaxKind.InterfaceDeclaration:
diff --git a/src/Workspaces/CSharp/Portable/Simplification/Simplifiers/ExpressionSimplifier.cs b/src/Workspaces/CSharp/Portable/Simplification/Simplifiers/ExpressionSimplifier.cs
index ca02f36c95ac4..a6457f3d5ab50 100644
--- a/src/Workspaces/CSharp/Portable/Simplification/Simplifiers/ExpressionSimplifier.cs
+++ b/src/Workspaces/CSharp/Portable/Simplification/Simplifiers/ExpressionSimplifier.cs
@@ -402,7 +402,7 @@ private static bool AccessMethodWithDynamicArgumentInsideStructConstructor(Membe
{
var constructor = memberAccess.Ancestors().OfType().SingleOrDefault();
- if (constructor == null || constructor.Parent.Kind() != SyntaxKind.StructDeclaration)
+ if (constructor == null || !constructor.Parent.IsKind(SyntaxKind.StructDeclaration, SyntaxKind.RecordStructDeclaration))
{
return false;
}
diff --git a/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs b/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs
index 102627684df1c..feb5c07f7689a 100644
--- a/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs
+++ b/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs
@@ -3023,11 +3023,13 @@ public void TestMultiFieldDeclarations()
}");
}
- [Fact, WorkItem(48789, "https://github.com/dotnet/roslyn/issues/48789")]
- public void TestInsertMembersOnRecord_SemiColon()
+ [Theory, WorkItem(48789, "https://github.com/dotnet/roslyn/issues/48789")]
+ [InlineData("record")]
+ [InlineData("record class")]
+ public void TestInsertMembersOnRecord_SemiColon(string typeKind)
{
var comp = Compile(
-@"public record C;
+$@"public {typeKind} C;
");
var symbolC = (INamedTypeSymbol)comp.GlobalNamespace.GetMembers("C").First();
@@ -3035,7 +3037,28 @@ public void TestInsertMembersOnRecord_SemiColon()
VerifySyntax(
Generator.InsertMembers(declC, 0, Generator.FieldDeclaration("A", Generator.IdentifierName("T"))),
-@"public record C
+$@"public {typeKind} C
+{{
+ T A;
+}}");
+ }
+
+ [Fact, WorkItem(48789, "https://github.com/dotnet/roslyn/issues/48789")]
+ public void TestInsertMembersOnRecordStruct_SemiColon()
+ {
+ var src =
+@"public record struct C;
+";
+ var comp = CSharpCompilation.Create("test")
+ .AddReferences(TestMetadata.Net451.mscorlib)
+ .AddSyntaxTrees(SyntaxFactory.ParseSyntaxTree(src, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview)));
+
+ var symbolC = (INamedTypeSymbol)comp.GlobalNamespace.GetMembers("C").First();
+ var declC = Generator.GetDeclaration(symbolC.DeclaringSyntaxReferences.Select(x => x.GetSyntax()).First());
+
+ VerifySyntax(
+ Generator.InsertMembers(declC, 0, Generator.FieldDeclaration("A", Generator.IdentifierName("T"))),
+@"public record struct C
{
T A;
}");
diff --git a/src/Workspaces/Core/Portable/Classification/ClassificationExtensions.cs b/src/Workspaces/Core/Portable/Classification/ClassificationExtensions.cs
index acfdc86125492..7d7e6bdbe3061 100644
--- a/src/Workspaces/Core/Portable/Classification/ClassificationExtensions.cs
+++ b/src/Workspaces/Core/Portable/Classification/ClassificationExtensions.cs
@@ -11,7 +11,7 @@ internal static class ClassificationExtensions
{
TypeKind.Class => type.IsRecord ? ClassificationTypeNames.RecordClassName : ClassificationTypeNames.ClassName,
TypeKind.Module => ClassificationTypeNames.ModuleName,
- TypeKind.Struct => ClassificationTypeNames.StructName,
+ TypeKind.Struct => type.IsRecord ? ClassificationTypeNames.RecordStructName : ClassificationTypeNames.StructName,
TypeKind.Interface => ClassificationTypeNames.InterfaceName,
TypeKind.Enum => ClassificationTypeNames.EnumName,
TypeKind.Delegate => ClassificationTypeNames.DelegateName,
diff --git a/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs b/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs
index cd41916f2b2a8..3f58888422123 100644
--- a/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs
+++ b/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs
@@ -44,6 +44,7 @@ public static class ClassificationTypeNames
public const string InterfaceName = "interface name";
public const string ModuleName = "module name";
public const string StructName = "struct name";
+ public const string RecordStructName = "record struct name";
public const string TypeParameterName = "type parameter name";
public const string FieldName = "field name";
diff --git a/src/Workspaces/Core/Portable/Editing/DeclarationKind.cs b/src/Workspaces/Core/Portable/Editing/DeclarationKind.cs
index 1f7b3dc181bfc..c3426c37a9976 100644
--- a/src/Workspaces/Core/Portable/Editing/DeclarationKind.cs
+++ b/src/Workspaces/Core/Portable/Editing/DeclarationKind.cs
@@ -42,5 +42,6 @@ public enum DeclarationKind
RemoveAccessor,
RaiseAccessor,
RecordClass,
+ RecordStruct,
}
}
diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt
index 86a95dde75144..d38ec6de5d767 100644
--- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt
+++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt
@@ -1,9 +1,11 @@
abstract Microsoft.CodeAnalysis.CodeFixes.DocumentBasedFixAllProvider.FixAllAsync(Microsoft.CodeAnalysis.CodeFixes.FixAllContext fixAllContext, Microsoft.CodeAnalysis.Document document, System.Collections.Immutable.ImmutableArray diagnostics) -> System.Threading.Tasks.Task
const Microsoft.CodeAnalysis.Classification.ClassificationTypeNames.RecordClassName = "record class name" -> string
+const Microsoft.CodeAnalysis.Classification.ClassificationTypeNames.RecordStructName = "record struct name" -> string
Microsoft.CodeAnalysis.CodeActions.CodeAction.CustomTags.get -> System.Collections.Immutable.ImmutableArray
Microsoft.CodeAnalysis.CodeActions.CodeAction.CustomTags.set -> void
Microsoft.CodeAnalysis.CodeFixes.DocumentBasedFixAllProvider
Microsoft.CodeAnalysis.CodeFixes.DocumentBasedFixAllProvider.DocumentBasedFixAllProvider() -> void
+Microsoft.CodeAnalysis.Editing.DeclarationKind.RecordStruct = 30 -> Microsoft.CodeAnalysis.Editing.DeclarationKind
Microsoft.CodeAnalysis.Host.IPersistentStorageService.GetStorageAsync(Microsoft.CodeAnalysis.Solution solution, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
Microsoft.CodeAnalysis.Project.GetSourceGeneratedDocumentAsync(Microsoft.CodeAnalysis.DocumentId documentId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
Microsoft.CodeAnalysis.Project.GetSourceGeneratedDocumentsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask>
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs
index f2ef822ad2493..fdcee439963e4 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs
@@ -44,6 +44,7 @@ public static SyntaxToken GetNameToken(this MemberDeclarationSyntax member)
case SyntaxKind.RecordDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.StructDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
return ((TypeDeclarationSyntax)member).Identifier;
case SyntaxKind.DelegateDeclaration:
return ((DelegateDeclarationSyntax)member).Identifier;
@@ -82,6 +83,7 @@ public static int GetArity(this MemberDeclarationSyntax member)
case SyntaxKind.RecordDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.StructDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
return ((TypeDeclarationSyntax)member).Arity;
case SyntaxKind.DelegateDeclaration:
return ((DelegateDeclarationSyntax)member).Arity;
@@ -103,6 +105,7 @@ public static TypeParameterListSyntax GetTypeParameterList(this MemberDeclaratio
case SyntaxKind.RecordDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.StructDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
return ((TypeDeclarationSyntax)member).TypeParameterList;
case SyntaxKind.DelegateDeclaration:
return ((DelegateDeclarationSyntax)member).TypeParameterList;
@@ -189,6 +192,7 @@ public static MemberDeclarationSyntax WithAttributeLists(
case SyntaxKind.RecordDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.StructDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
return ((TypeDeclarationSyntax)member).WithAttributeLists(attributeLists);
case SyntaxKind.DelegateDeclaration:
return ((DelegateDeclarationSyntax)member).WithAttributeLists(attributeLists);
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs
index abf316d7a1e17..b245786d4db86 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs
@@ -1136,6 +1136,9 @@ private void AppendConstructors(SyntaxList members, Lis
case StructDeclarationSyntax @struct:
AppendConstructors(@struct.Members, constructors, cancellationToken);
break;
+ case RecordStructDeclarationSyntax recordStruct:
+ AppendConstructors(recordStruct.Members, constructors, cancellationToken);
+ break;
}
}
}
@@ -1778,6 +1781,7 @@ public override bool CanHaveAccessibility(SyntaxNode declaration)
case SyntaxKind.ClassDeclaration:
case SyntaxKind.RecordDeclaration:
case SyntaxKind.StructDeclaration:
+ case SyntaxKind.RecordStructDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.DelegateDeclaration:
@@ -1914,6 +1918,8 @@ public override DeclarationKind GetDeclarationKind(SyntaxNode declaration)
return DeclarationKind.RecordClass;
case SyntaxKind.StructDeclaration:
return DeclarationKind.Struct;
+ case SyntaxKind.RecordStructDeclaration:
+ return DeclarationKind.RecordStruct;
case SyntaxKind.InterfaceDeclaration:
return DeclarationKind.Interface;
case SyntaxKind.EnumDeclaration:
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs
index a946ffa7d3426..ef4cbce90fe0c 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs
@@ -73,6 +73,7 @@ internal class SyntaxKindSet
SyntaxKind.ClassDeclaration,
SyntaxKind.RecordDeclaration,
SyntaxKind.StructDeclaration,
+ SyntaxKind.RecordStructDeclaration,
SyntaxKind.EnumDeclaration,
};
@@ -82,6 +83,7 @@ internal class SyntaxKindSet
SyntaxKind.ClassDeclaration,
SyntaxKind.RecordDeclaration,
SyntaxKind.StructDeclaration,
+ SyntaxKind.RecordStructDeclaration,
};
public static readonly ISet ClassInterfaceRecordTypeDeclarations = new HashSet(SyntaxFacts.EqualityComparer)
@@ -96,11 +98,13 @@ internal class SyntaxKindSet
SyntaxKind.ClassDeclaration,
SyntaxKind.RecordDeclaration,
SyntaxKind.StructDeclaration,
+ SyntaxKind.RecordStructDeclaration,
};
public static readonly ISet StructOnlyTypeDeclarations = new HashSet(SyntaxFacts.EqualityComparer)
{
SyntaxKind.StructDeclaration,
+ SyntaxKind.RecordStructDeclaration,
};
}
}