diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs index 58256706ad272..c9170321bc2cf 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs @@ -292,7 +292,7 @@ public static void Main() var baseActiveStatements = ImmutableArray.Create(ActiveStatementsDescription.CreateActiveStatement(ActiveStatementFlags.IsLeafFrame, oldStatementSpan, DocumentId.CreateNewId(ProjectId.CreateNewId()))); var analyzer = new CSharpEditAndContinueAnalyzer(); - var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, ImmutableArray.Empty, CancellationToken.None); + var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, ImmutableArray.Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None); Assert.True(result.HasChanges); var syntaxMap = result.SemanticEdits[0].SyntaxMap; @@ -338,7 +338,7 @@ public static void Main() var baseActiveStatements = ImmutableArray.Create(); var analyzer = new CSharpEditAndContinueAnalyzer(); - var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray.Empty, CancellationToken.None); + var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray.Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None); Assert.True(result.HasChanges); Assert.True(result.HasChangesAndErrors); @@ -364,7 +364,7 @@ public static void Main() var baseActiveStatements = ImmutableArray.Create(); var analyzer = new CSharpEditAndContinueAnalyzer(); - var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, oldDocument, ImmutableArray.Empty, CancellationToken.None); + var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, oldDocument, ImmutableArray.Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None); Assert.False(result.HasChanges); Assert.False(result.HasChangesAndErrors); @@ -405,7 +405,7 @@ public static void Main() var baseActiveStatements = ImmutableArray.Create(); var analyzer = new CSharpEditAndContinueAnalyzer(); - var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray.Empty, CancellationToken.None); + var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray.Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None); Assert.False(result.HasChanges); Assert.False(result.HasChangesAndErrors); @@ -438,7 +438,7 @@ public static void Main() var baseActiveStatements = ImmutableArray.Create(); var analyzer = new CSharpEditAndContinueAnalyzer(); - var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, oldDocument, ImmutableArray.Empty, CancellationToken.None); + var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, oldDocument, ImmutableArray.Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None); Assert.False(result.HasChanges); Assert.False(result.HasChangesAndErrors); @@ -489,7 +489,7 @@ public static void Main() var baseActiveStatements = ImmutableArray.Create(); var analyzer = new CSharpEditAndContinueAnalyzer(); - var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray.Empty, CancellationToken.None); + var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray.Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None); Assert.True(result.HasChanges); Assert.True(result.HasChangesAndErrors); @@ -522,7 +522,7 @@ public static void Main() var baseActiveStatements = ImmutableArray.Create(); var analyzer = new CSharpEditAndContinueAnalyzer(); - var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, oldDocument, ImmutableArray.Empty, CancellationToken.None); + var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, oldDocument, ImmutableArray.Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None); Assert.False(result.HasChanges); Assert.False(result.HasChangesAndErrors); @@ -565,7 +565,7 @@ public static void Main() var baseActiveStatements = ImmutableArray.Create(); var analyzer = new CSharpEditAndContinueAnalyzer(); - var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray.Empty, CancellationToken.None); + var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray.Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None); Assert.True(result.HasChanges); @@ -608,7 +608,7 @@ public static void Main(Bar x) var baseActiveStatements = ImmutableArray.Create(); var analyzer = new CSharpEditAndContinueAnalyzer(); - var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray.Empty, CancellationToken.None); + var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray.Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None); Assert.True(result.HasChanges); @@ -665,7 +665,7 @@ public class D foreach (var changedDocumentId in changedDocuments) { - result.Add(await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newProject.GetDocument(changedDocumentId), ImmutableArray.Empty, CancellationToken.None)); + result.Add(await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newProject.GetDocument(changedDocumentId), ImmutableArray.Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None)); } Assert.True(result.IsSingle()); @@ -717,7 +717,7 @@ class D foreach (var changedDocumentId in changedDocuments) { - result.Add(await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newProject.GetDocument(changedDocumentId), ImmutableArray.Empty, CancellationToken.None)); + result.Add(await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newProject.GetDocument(changedDocumentId), ImmutableArray.Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None)); } Assert.True(result.IsSingle()); @@ -751,7 +751,7 @@ public async Task AnalyzeDocumentAsync_InternalError(bool outOfMemory) } }); - var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, ImmutableArray.Empty, CancellationToken.None); + var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, ImmutableArray.Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None); var expectedDiagnostic = outOfMemory ? $"ENC0089: {string.Format(FeaturesResources.Modifying_source_file_will_prevent_the_debug_session_from_continuing_because_the_file_is_too_big, "src.cs")}" : @@ -762,5 +762,45 @@ public async Task AnalyzeDocumentAsync_InternalError(bool outOfMemory) AssertEx.Equal(new[] { expectedDiagnostic }, result.RudeEditErrors.Select(d => d.ToDiagnostic(newSyntaxTree)) .Select(d => $"{d.Id}: {d.GetMessage().Split(new[] { Environment.NewLine }, StringSplitOptions.None).First()}")); } + + [Fact] + public async Task AnalyzeDocumentAsync_NotSupportedByRuntime() + { + var source1 = @" +class C +{ + public static void Main() + { + System.Console.WriteLine(1); + } +} +"; + var source2 = @" +class C +{ + public static void Main() + { + System.Console.WriteLine(2); + } +} +"; + + using var workspace = TestWorkspace.CreateCSharp(source1, composition: s_composition); + + var oldSolution = workspace.CurrentSolution; + var oldProject = oldSolution.Projects.Single(); + var documentId = oldProject.Documents.Single().Id; + var newSolution = workspace.CurrentSolution.WithDocumentText(documentId, SourceText.From(source2)); + var newDocument = newSolution.GetDocument(documentId); + + var baseActiveStatements = ImmutableArray.Create(); + var analyzer = new CSharpEditAndContinueAnalyzer(); + + var capabilities = EditAndContinueCapabilities.None; + + var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, ImmutableArray.Empty, capabilities, CancellationToken.None); + + Assert.Equal(RudeEditKind.NotSupportedByRuntime, result.RudeEditErrors.Single().Kind); + } } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditAndContinueValidation.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditAndContinueValidation.cs index b45ad8c484eaa..7d06ca85b30a2 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditAndContinueValidation.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditAndContinueValidation.cs @@ -32,7 +32,23 @@ internal static void VerifyRudeDiagnostics( this EditScript editScript, params RudeEditDiagnosticDescription[] expectedDiagnostics) { - VerifyRudeDiagnostics(editScript, ActiveStatementsDescription.Empty, expectedDiagnostics); + VerifySemanticDiagnostics( + editScript, + ActiveStatementsDescription.Empty, + capabilities: null, + expectedDiagnostics); + } + + internal static void VerifyRudeDiagnostics( + this EditScript editScript, + EditAndContinueCapabilities? capabilities = null, + params RudeEditDiagnosticDescription[] expectedDiagnostics) + { + VerifySemanticDiagnostics( + editScript, + ActiveStatementsDescription.Empty, + capabilities, + expectedDiagnostics); } internal static void VerifyRudeDiagnostics( @@ -43,6 +59,7 @@ internal static void VerifyRudeDiagnostics( VerifySemanticDiagnostics( editScript, description, + capabilities: null, expectedDiagnostics); } @@ -68,14 +85,24 @@ internal static void VerifySemanticDiagnostics( new[] { new DocumentAnalysisResultsDescription(diagnostics: expectedDiagnostics) }); } + internal static void VerifySemanticDiagnostics( + this EditScript editScript, + ActiveStatementsDescription activeStatements, + params RudeEditDiagnosticDescription[] expectedDiagnostics) + { + VerifySemanticDiagnostics(editScript, activeStatements, capabilities: null, expectedDiagnostics); + } + internal static void VerifySemanticDiagnostics( this EditScript editScript, ActiveStatementsDescription activeStatements, + EditAndContinueCapabilities? capabilities = null, params RudeEditDiagnosticDescription[] expectedDiagnostics) { VerifySemantics( new[] { editScript }, - new[] { new DocumentAnalysisResultsDescription(activeStatements: activeStatements, diagnostics: expectedDiagnostics) }); + new[] { new DocumentAnalysisResultsDescription(activeStatements: activeStatements, diagnostics: expectedDiagnostics) }, + capabilities: capabilities); } internal static void VerifySemanticDiagnostics( @@ -92,28 +119,31 @@ internal static void VerifySemanticDiagnostics( internal static void VerifySemantics( this EditScript editScript, ActiveStatementsDescription activeStatements, - SemanticEditDescription[] expectedSemanticEdits) + SemanticEditDescription[] expectedSemanticEdits, + EditAndContinueCapabilities? capabilities = null) { VerifySemantics( new[] { editScript }, - new[] { new DocumentAnalysisResultsDescription(activeStatements, semanticEdits: expectedSemanticEdits) }); + new[] { new DocumentAnalysisResultsDescription(activeStatements, semanticEdits: expectedSemanticEdits) }, + capabilities: capabilities); } internal static void VerifySemantics( this EditScript editScript, params SemanticEditDescription[] expectedSemanticEdits) { - VerifySemantics(editScript, ActiveStatementsDescription.Empty, expectedSemanticEdits); + VerifySemantics(editScript, ActiveStatementsDescription.Empty, expectedSemanticEdits, capabilities: null); } internal static void VerifySemantics( EditScript[] editScripts, DocumentAnalysisResultsDescription[] expected, - TargetFramework[]? targetFrameworks = null) + TargetFramework[]? targetFrameworks = null, + EditAndContinueCapabilities? capabilities = null) { foreach (var targetFramework in targetFrameworks ?? new[] { TargetFramework.NetStandard20, TargetFramework.NetCoreApp }) { - new CSharpEditAndContinueTestHelpers().VerifySemantics(editScripts, targetFramework, expected); + new CSharpEditAndContinueTestHelpers().VerifySemantics(editScripts, targetFramework, expected, capabilities); } } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs index a68572a77607f..672d583157694 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs @@ -2626,6 +2626,110 @@ static void F() ); } + [Fact] + public void Lambdas_Insert_NotSupported() + { + var src1 = @" +using System; + +class C +{ + void F() + { + } +} +"; + var src2 = @" +using System; + +class C +{ + void F() + { + var f = new Func(a => a); + } +} +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifySemanticDiagnostics( + activeStatements: ActiveStatementsDescription.Empty, + capabilities: EditAndContinueTestHelpers.BaselineCapabilities, + Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "a", CSharpFeaturesResources.lambda)); + } + + [Fact] + public void Lambdas_Insert_Second_NotSupported() + { + var src1 = @" +using System; + +class C +{ + void F() + { + var f = new Func(a => a); + } +} +"; + var src2 = @" +using System; + +class C +{ + void F() + { + var f = new Func(a => a); + var g = new Func(b => b); + } +} +"; + var capabilities = EditAndContinueCapabilities.Baseline | EditAndContinueCapabilities.NewTypeDefinition; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemanticDiagnostics( + activeStatements: ActiveStatementsDescription.Empty, + capabilities, + Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "b", CSharpFeaturesResources.lambda)); + } + + [Fact] + public void Lambdas_Insert_FirstInClass_NotSupported() + { + var src1 = @" +using System; + +class C +{ + void F() + { + } +} +"; + var src2 = @" +using System; + +class C +{ + void F() + { + var g = new Func(b => b); + } +} +"; + var capabilities = EditAndContinueCapabilities.Baseline | EditAndContinueCapabilities.NewTypeDefinition; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemanticDiagnostics( + activeStatements: ActiveStatementsDescription.Empty, + capabilities, + // TODO: https://github.com/dotnet/roslyn/issues/52759 + // This is incorrect, there should be no rude edit reported + Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "b", CSharpFeaturesResources.lambda)); + } + [Fact] public void Lambdas_Update_CeaseCapture_This() { @@ -5484,6 +5588,40 @@ static void F() Diagnostic(RudeEditKind.InsertLambdaWithMultiScopeCapture, "x1", CSharpFeaturesResources.local_function, "x0", "x1")); } + [Fact] + public void LocalFunctions_Insert_NotSupported() + { + var src1 = @" +using System; + +class C +{ + void F() + { + } +} +"; + var src2 = @" +using System; + +class C +{ + void F() + { + void M() + { + } + } +} +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifySemanticDiagnostics( + activeStatements: ActiveStatementsDescription.Empty, + capabilities: EditAndContinueTestHelpers.BaselineCapabilities, + Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "M", FeaturesResources.local_function)); + } + [Fact, WorkItem(21499, "https://github.com/dotnet/roslyn/issues/21499")] public void LocalFunctions_Update_CeaseCapture_This() { diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index f9b904bff620a..c99c831ff6c7a 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -1034,6 +1034,37 @@ public override void H() {} edits.VerifyRudeDiagnostics(); } + [Fact] + public void ClassInsert_NotSupportedByRuntime() + { + var src1 = @" +public class C +{ + void F() + { + } +}"; + var src2 = @" +public class C +{ + void F() + { + } +} + +public class D +{ + void M() + { + } +}"; + + var edits = GetTopEdits(src1, src2); + edits.VerifyRudeDiagnostics( + capabilities: EditAndContinueTestHelpers.BaselineCapabilities, + Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "public class D", FeaturesResources.class_)); + } + [Fact] public void InterfaceInsert() { @@ -5091,6 +5122,19 @@ class C Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.method, "puts(string)"))); } + [Fact] + public void MethodInsert_NotSupportedByRuntime() + { + var src1 = "class C { }"; + var src2 = "class C { void goo() { } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + capabilities: EditAndContinueTestHelpers.BaselineCapabilities, + Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "void goo()", FeaturesResources.method)); + } + [Fact] public void PrivateMethodInsert() { @@ -5605,6 +5649,32 @@ public async Task WaitAsync() VerifyPreserveLocalVariables(edits, preserveLocalVariables: false); } + [Fact] + public void MethodUpdate_Modifier_Async_Add_NotSupported() + { + var src1 = @" +class Test +{ + public Task WaitAsync() + { + return 1; + } +}"; + var src2 = @" +class Test +{ + public async Task WaitAsync() + { + await Task.Delay(1000); + return 1; + } +}"; + var edits = GetTopEdits(src1, src2); + edits.VerifyRudeDiagnostics( + capabilities: EditAndContinueTestHelpers.BaselineCapabilities, + Diagnostic(RudeEditKind.MakeMethodAsync, "public async Task WaitAsync()")); + } + [Fact] public void MethodUpdate_AsyncMethod0() { @@ -6348,6 +6418,19 @@ public void MethodUpdate_AddYieldReturn() VerifyPreserveLocalVariables(edits, preserveLocalVariables: false); } + [Fact] + public void MethodUpdate_AddYieldReturn_NotSupported() + { + var src1 = "class C { IEnumerable M() { return new[] { 1, 2, 3}; } }"; + var src2 = "class C { IEnumerable M() { yield return 2; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + capabilities: EditAndContinueTestHelpers.BaselineCapabilities, + Diagnostic(RudeEditKind.MakeMethodIterator, "IEnumerable M()")); + } + [Fact] public void MethodUpdate_Iterator_YieldBreak() { @@ -10424,6 +10507,32 @@ class C }); } + [Fact] + public void FieldInsert_NotSupportedByRuntime() + { + var src1 = "class C { }"; + var src2 = "class C { public int a = 1; }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + capabilities: EditAndContinueTestHelpers.BaselineCapabilities | EditAndContinueCapabilities.AddStaticFieldToExistingType, + Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "a = 1", FeaturesResources.field)); + } + + [Fact] + public void FieldInsert_Static_NotSupportedByRuntime() + { + var src1 = "class C { }"; + var src2 = "class C { public static int a = 1; }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + capabilities: EditAndContinueTestHelpers.BaselineCapabilities | EditAndContinueCapabilities.AddInstanceFieldToExistingType, + Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "a = 1", FeaturesResources.field)); + } + [Fact] public void FieldDelete1() { @@ -10901,6 +11010,19 @@ public void PropertyInsert() SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").GetMember("P"))); } + [Fact] + public void PropertyInsert_NotSupportedByRuntime() + { + var src1 = "class C { }"; + var src2 = "class C { int P { get => 1; set { } } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + capabilities: EditAndContinueTestHelpers.BaselineCapabilities, + Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int P", FeaturesResources.auto_property)); + } + [WorkItem(835827, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/835827")] [Fact] public void PropertyInsert_PInvoke() diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index e84925d301667..d9e3b9b4dca14 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -3318,5 +3318,48 @@ public async Task WatchHotReloadServiceTest() hotReload.EndSession(); } + + [Fact] + public void ParseCapabilities() + { + var capabilities = ImmutableArray.Create("Baseline"); + + var service = EditAndContinueWorkspaceService.ParseCapabilities(capabilities); + + Assert.True(service.HasFlag(EditAndContinueCapabilities.Baseline)); + Assert.False(service.HasFlag(EditAndContinueCapabilities.NewTypeDefinition)); + } + + [Fact] + public void ParseCapabilities_CaseSensitive() + { + var capabilities = ImmutableArray.Create("BaseLine"); + + var service = EditAndContinueWorkspaceService.ParseCapabilities(capabilities); + + Assert.False(service.HasFlag(EditAndContinueCapabilities.Baseline)); + } + + [Fact] + public void ParseCapabilities_IgnoreInvalid() + { + var capabilities = ImmutableArray.Create("Baseline", "Invalid", "NewTypeDefinition"); + + var service = EditAndContinueWorkspaceService.ParseCapabilities(capabilities); + + Assert.True(service.HasFlag(EditAndContinueCapabilities.Baseline)); + Assert.True(service.HasFlag(EditAndContinueCapabilities.NewTypeDefinition)); + } + + [Fact] + public void ParseCapabilities_IgnoreInvalidNumeric() + { + var capabilities = ImmutableArray.Create("Baseline", "90", "NewTypeDefinition"); + + var service = EditAndContinueWorkspaceService.ParseCapabilities(capabilities); + + Assert.True(service.HasFlag(EditAndContinueCapabilities.Baseline)); + Assert.True(service.HasFlag(EditAndContinueCapabilities.NewTypeDefinition)); + } } } diff --git a/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs index 63377aad61c5d..34d88aa0f4ea6 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs @@ -49,6 +49,7 @@ private static EditSession CreateEditSession( var debuggingSession = new DebuggingSession( solution, mockDebuggerService, + EditAndContinueTestHelpers.Net5RuntimeCapabilities, mockCompilationOutputsProvider, SpecializedCollections.EmptyEnumerable>(), new DebuggingSessionTelemetry(), diff --git a/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs b/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs index 63a037312c79f..f46977f745bb9 100644 --- a/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs @@ -51,6 +51,9 @@ public void ToDiagnostic() RudeEditKind.InsertMethodWithExplicitInterfaceSpecifier, RudeEditKind.AddRecordPositionalParameter, RudeEditKind.DeleteRecordPositionalParameter, + RudeEditKind.NotSupportedByRuntime, + RudeEditKind.MakeMethodAsync, + RudeEditKind.MakeMethodIterator }; var arg2 = new HashSet() diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs index eaae72b51290e..518683dd6e38f 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs @@ -29,6 +29,13 @@ namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests { internal abstract class EditAndContinueTestHelpers { + public static readonly EditAndContinueCapabilities BaselineCapabilities = EditAndContinueCapabilities.Baseline; + public static readonly EditAndContinueCapabilities Net5RuntimeCapabilities = EditAndContinueCapabilities.Baseline | + EditAndContinueCapabilities.AddInstanceFieldToExistingType | + EditAndContinueCapabilities.AddStaticFieldToExistingType | + EditAndContinueCapabilities.AddMethodToExistingType | + EditAndContinueCapabilities.NewTypeDefinition; + public abstract AbstractEditAndContinueAnalyzer Analyzer { get; } public abstract SyntaxNode FindNode(SyntaxNode root, TextSpan span); public abstract SyntaxTree ParseText(string source); @@ -143,7 +150,7 @@ internal void VerifyLineEdits( AssertEx.Equal(expectedNodeUpdates, actualNodeUpdates, itemSeparator: ",\r\n"); } - internal void VerifySemantics(EditScript[] editScripts, TargetFramework targetFramework, DocumentAnalysisResultsDescription[] expectedResults) + internal void VerifySemantics(EditScript[] editScripts, TargetFramework targetFramework, DocumentAnalysisResultsDescription[] expectedResults, EditAndContinueCapabilities? capabilities = null) { Assert.True(editScripts.Length == expectedResults.Length); var documentCount = expectedResults.Length; @@ -185,7 +192,7 @@ internal void VerifySemantics(EditScript[] editScripts, TargetFramew Contract.ThrowIfNull(oldModel); Contract.ThrowIfNull(newModel); - var result = Analyzer.AnalyzeDocumentAsync(oldProject, oldActiveStatements, newDocument, newActiveStatementSpans, CancellationToken.None).Result; + var result = Analyzer.AnalyzeDocumentAsync(oldProject, oldActiveStatements, newDocument, newActiveStatementSpans, capabilities ?? Net5RuntimeCapabilities, CancellationToken.None).Result; var oldText = oldDocument.GetTextSynchronously(default); var newText = newDocument.GetTextSynchronously(default); diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb index d940b1d771828..a3fbf6a28291c 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb @@ -470,7 +470,7 @@ End Class Dim analyzer = New VisualBasicEditAndContinueAnalyzer() Dim baseActiveStatements = ImmutableArray.Create(ActiveStatementsDescription.CreateActiveStatement(ActiveStatementFlags.IsLeafFrame, oldStatementSpan, DocumentId.CreateNewId(ProjectId.CreateNewId()))) - Dim result = Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, ImmutableArray(Of TextSpan).Empty, CancellationToken.None) + Dim result = Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, ImmutableArray(Of TextSpan).Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None) Assert.True(result.HasChanges) Dim syntaxMap = result.SemanticEdits(0).SyntaxMap @@ -500,7 +500,7 @@ End Class Dim oldDocument = oldProject.Documents.Single() Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatement)() Dim analyzer = New VisualBasicEditAndContinueAnalyzer() - Dim result = Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, oldDocument, ImmutableArray(Of TextSpan).Empty, CancellationToken.None) + Dim result = Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, oldDocument, ImmutableArray(Of TextSpan).Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None) Assert.False(result.HasChanges) Assert.False(result.HasChangesAndErrors) @@ -534,7 +534,7 @@ End Class Dim analyzer = New VisualBasicEditAndContinueAnalyzer() Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatement)() - Dim result = Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray(Of TextSpan).Empty, CancellationToken.None) + Dim result = Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray(Of TextSpan).Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None) Assert.False(result.HasChanges) Assert.False(result.HasChangesAndErrors) @@ -558,7 +558,7 @@ End Class Dim oldDocument = oldProject.Documents.Single() Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatement)() Dim analyzer = New VisualBasicEditAndContinueAnalyzer() - Dim result = Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, oldDocument, ImmutableArray(Of TextSpan).Empty, CancellationToken.None) + Dim result = Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, oldDocument, ImmutableArray(Of TextSpan).Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None) Assert.False(result.HasChanges) Assert.False(result.HasChangesAndErrors) @@ -594,7 +594,7 @@ End Class Dim analyzer = New VisualBasicEditAndContinueAnalyzer() Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatement)() - Dim result = Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray(Of TextSpan).Empty, CancellationToken.None) + Dim result = Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray(Of TextSpan).Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None) ' no declaration errors (error in method body is only reported when emitting) Assert.False(result.HasChangesAndErrors) @@ -627,7 +627,7 @@ End Class Dim analyzer = New VisualBasicEditAndContinueAnalyzer() Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatement)() - Dim result = Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray(Of TextSpan).Empty, CancellationToken.None) + Dim result = Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newSolution.GetDocument(documentId), ImmutableArray(Of TextSpan).Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None) Assert.True(result.HasChanges) @@ -697,7 +697,7 @@ End Class Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatement)() Dim analyzer = New VisualBasicEditAndContinueAnalyzer() For Each changedDocumentId In changedDocuments - result.Add(Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newProject.GetDocument(changedDocumentId), ImmutableArray(Of TextSpan).Empty, CancellationToken.None)) + result.Add(Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newProject.GetDocument(changedDocumentId), ImmutableArray(Of TextSpan).Empty, EditAndContinueTestHelpers.Net5RuntimeCapabilities, CancellationToken.None)) Next Assert.True(result.IsSingle()) diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index e36af4dc5caf1..b9061cb1fe236 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -451,6 +451,7 @@ public async Task AnalyzeDocumentAsync( ImmutableArray oldActiveStatements, Document newDocument, ImmutableArray newActiveStatementSpans, + EditAndContinueCapabilities capabilities, CancellationToken cancellationToken) { DocumentAnalysisResults.Log.Write("Analyzing document {0}", newDocument.Name); @@ -535,6 +536,13 @@ public async Task AnalyzeDocumentAsync( return DocumentAnalysisResults.Unchanged(newDocument.Id, newActiveStatements.MoveToImmutable(), newExceptionRegions.MoveToImmutable()); } + // If the document has changed at all, lets make sure Edit and Continue is supported + if (!capabilities.HasFlag(EditAndContinueCapabilities.Baseline)) + { + return DocumentAnalysisResults.SyntaxErrors(newDocument.Id, ImmutableArray.Create( + new RudeEditDiagnostic(RudeEditKind.NotSupportedByRuntime, default)), hasChanges); + } + // Disallow modification of a file with experimental features enabled. // These features may not be handled well by the analysis below. if (ExperimentalFeaturesEnabled(newTree)) @@ -611,6 +619,7 @@ public async Task AnalyzeDocumentAsync( diagnostics, newActiveStatements, newExceptionRegions, + capabilities, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); @@ -904,6 +913,7 @@ private void AnalyzeChangedMemberBody( ISymbol newSymbol, ImmutableArray oldActiveStatements, ImmutableArray newActiveStatementSpans, + EditAndContinueCapabilities capabilities, [Out] ImmutableArray.Builder newActiveStatements, [Out] ImmutableArray>.Builder newExceptionRegions, [Out] ArrayBuilder diagnostics, @@ -1008,6 +1018,20 @@ private void AnalyzeChangedMemberBody( { ReportStateMachineRudeEdits(oldModel.Compilation, oldSymbol, newBody, diagnostics); } + else if (newHasStateMachineSuspensionPoint && + !capabilities.HasFlag(EditAndContinueCapabilities.NewTypeDefinition)) + { + // Adding a state machine, either for async or iterator, will require creating a new helper class + // so is a rude edit if the runtime doesn't support it + if (newSymbol is IMethodSymbol { IsAsync: true }) + { + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.MakeMethodAsync, GetDiagnosticSpan(newDeclaration, EditKind.Insert))); + } + else + { + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.MakeMethodIterator, GetDiagnosticSpan(newDeclaration, EditKind.Insert))); + } + } ReportLambdaAndClosureRudeEdits( oldModel, @@ -1017,6 +1041,7 @@ private void AnalyzeChangedMemberBody( newSymbol, lazyActiveOrMatchedLambdas, map, + capabilities, diagnostics, out var newBodyHasLambdas, cancellationToken); @@ -2194,6 +2219,7 @@ private async Task> AnalyzeSemanticsAsync( ArrayBuilder diagnostics, ImmutableArray.Builder newActiveStatements, ImmutableArray>.Builder newExceptionRegions, + EditAndContinueCapabilities capabilities, CancellationToken cancellationToken) { if (editScript.Edits.Length == 0 && triviaEdits.Count == 0) @@ -2527,6 +2553,7 @@ private async Task> AnalyzeSemanticsAsync( newSymbol, oldActiveStatements: ImmutableArray.Empty, newActiveStatementSpans: ImmutableArray.Empty, + capabilities: capabilities, newActiveStatements, newExceptionRegions, diagnostics, @@ -2567,6 +2594,15 @@ private async Task> AnalyzeSemanticsAsync( var containingSymbolKey = SymbolKey.Create(newContainingType, cancellationToken); oldContainingType = containingSymbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol as INamedTypeSymbol; + if (oldContainingType != null && !CanAddNewMember(newSymbol, capabilities)) + { + diagnostics.Add(new RudeEditDiagnostic( + RudeEditKind.InsertNotSupportedByRuntime, + GetDiagnosticSpan(edit.NewNode, EditKind.Insert), + edit.NewNode, + arguments: new[] { GetDisplayName(edit.NewNode, EditKind.Insert) })); + } + // Check rude edits for each member even if it is inserted into a new type. ReportInsertedMemberSymbolRudeEdits(diagnostics, newSymbol, edit.NewNode, insertingIntoExistingContainingType: oldContainingType != null); @@ -2597,6 +2633,15 @@ private async Task> AnalyzeSemanticsAsync( // adds a new top-level type Contract.ThrowIfFalse(newSymbol is INamedTypeSymbol); + if (!capabilities.HasFlag(EditAndContinueCapabilities.NewTypeDefinition)) + { + diagnostics.Add(new RudeEditDiagnostic( + RudeEditKind.InsertNotSupportedByRuntime, + GetDiagnosticSpan(edit.NewNode, EditKind.Insert), + edit.NewNode, + arguments: new[] { GetDisplayName(edit.NewNode, EditKind.Insert) })); + } + oldContainingType = null; ReportInsertedMemberSymbolRudeEdits(diagnostics, newSymbol, edit.NewNode, insertingIntoExistingContainingType: false); } @@ -2674,6 +2719,7 @@ private async Task> AnalyzeSemanticsAsync( newSymbol, oldActiveStatements, newActiveStatementSpans, + capabilities, newActiveStatements, newExceptionRegions, diagnostics, @@ -2835,6 +2881,25 @@ private async Task> AnalyzeSemanticsAsync( return semanticEdits.ToImmutable(); } + private static bool CanAddNewMember(ISymbol newSymbol, EditAndContinueCapabilities capabilities) + { + if (newSymbol is IMethodSymbol or IPropertySymbol) // Properties are just get_ and set_ methods + { + return capabilities.HasFlag(EditAndContinueCapabilities.AddMethodToExistingType); + } + else if (newSymbol is IFieldSymbol field) + { + if (field.IsStatic) + { + return capabilities.HasFlag(EditAndContinueCapabilities.AddStaticFieldToExistingType); + } + + return capabilities.HasFlag(EditAndContinueCapabilities.AddInstanceFieldToExistingType); + } + + return true; + } + private static void AddEditsForSynthesizedRecordMembers(Compilation compilation, INamedTypeSymbol recordType, ArrayBuilder semanticEdits) { foreach (var member in GetRecordUpdatedSynthesizedMembers(compilation, recordType)) @@ -3355,6 +3420,7 @@ private void ReportLambdaAndClosureRudeEdits( ISymbol newMember, IReadOnlyDictionary? matchedLambdas, BidirectionalMap map, + EditAndContinueCapabilities capabilities, ArrayBuilder diagnostics, out bool syntaxMapRequired, CancellationToken cancellationToken) @@ -3548,6 +3614,11 @@ select clausesByQuery.First()) { if (!map.Reverse.ContainsKey(newLambda)) { + if (!CanAddNewLambda(newLambda, capabilities, matchedLambdas)) + { + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.InsertNotSupportedByRuntime, GetDiagnosticSpan(newLambda, EditKind.Insert), newLambda, new string[] { GetDisplayName(newLambda, EditKind.Insert) })); + } + // TODO: https://github.com/dotnet/roslyn/issues/37128 // Local functions are emitted directly to the type containing the containing method. // Although local functions are non-virtual the Core CLR currently does not support adding any method to an interface. @@ -3588,6 +3659,31 @@ select clausesByQuery.First()) oldCapturesToClosureScopes.Free(); } + private bool CanAddNewLambda(SyntaxNode newLambda, EditAndContinueCapabilities capabilities, IReadOnlyDictionary? matchedLambdas) + { + // New local functions mean new methods in existing classes + if (IsLocalFunction(newLambda)) + { + return capabilities.HasFlag(EditAndContinueCapabilities.AddMethodToExistingType); + } + + // New lambdas sometimes mean creating new helper classes, and sometimes mean new methods in exising helper classes + // Unfortunately we are limited here in what we can do here. See: https://github.com/dotnet/roslyn/issues/52759 + + // If there is already a lambda in the method then the new lambda would result in a new method in the existing helper class. + // This check is redundant with the below, once the limitation in the referenced issue is resolved + if (matchedLambdas is { Count: > 0 }) + { + return capabilities.HasFlag(EditAndContinueCapabilities.AddMethodToExistingType); + } + + // If there is already a lambda in the class then the new lambda would result in a new method in the existing helper class. + // If there isn't already a lambda in the class then the new lambda would result in a new helper class. + // Unfortunately right now we can't determine which of these is true so we have to just check both capabilities instead. + return capabilities.HasFlag(EditAndContinueCapabilities.NewTypeDefinition) && + capabilities.HasFlag(EditAndContinueCapabilities.AddMethodToExistingType); + } + private void ReportMultiScopeCaptures( SyntaxNode lambdaBody, SemanticModel model, diff --git a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs index a741aa2245304..8984b580e3a12 100644 --- a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs @@ -97,13 +97,18 @@ internal sealed class DebuggingSession : IDisposable internal readonly IManagedEditAndContinueDebuggerService DebuggerService; - private readonly DebuggingSessionTelemetry _telemetry; + /// + /// Gets the capabilities of the runtime with respect to applying code changes. + /// + internal readonly EditAndContinueCapabilities Capabilities; + private readonly DebuggingSessionTelemetry _telemetry; internal EditSession EditSession; internal DebuggingSession( Solution solution, IManagedEditAndContinueDebuggerService debuggerService, + EditAndContinueCapabilities capabilities, Func compilationOutputsProvider, IEnumerable> initialDocumentStates, DebuggingSessionTelemetry debuggingSessionTelemetry, @@ -112,6 +117,7 @@ internal DebuggingSession( _compilationOutputsProvider = compilationOutputsProvider; _telemetry = debuggingSessionTelemetry; + Capabilities = capabilities; DebuggerService = debuggerService; LastCommittedSolution = new CommittedSolution(this, solution, initialDocumentStates); NonRemappableRegions = ImmutableDictionary>.Empty; diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueCapabilities.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueCapabilities.cs new file mode 100644 index 0000000000000..02978f6882471 --- /dev/null +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueCapabilities.cs @@ -0,0 +1,42 @@ +// 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; + +namespace Microsoft.CodeAnalysis.EditAndContinue +{ + /// + /// The capabilities that the runtime has with respect to edit and continue + /// + [Flags] + internal enum EditAndContinueCapabilities + { + None = 0, + + /// + /// Edit and continue is generally available with the set of capabilities that Mono 6, .NET Framework and .NET 5 have in common. + /// + Baseline = 1 << 0, + + /// + /// Adding a static or instance method to an existing type. + /// + AddMethodToExistingType = 1 << 1, + + /// + /// Adding a static field to an existing type. + /// + AddStaticFieldToExistingType = 1 << 2, + + /// + /// Adding an instance field to an existing type. + /// + AddInstanceFieldToExistingType = 1 << 3, + + /// + /// Creating a new type definition. + /// + NewTypeDefinition = 1 << 2 + } +} diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs index b85a9329191ce..ca3206918b2bc 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs @@ -151,6 +151,11 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.DeleteRecordPositionalParameter, nameof(FeaturesResources.Deleting_a_positional_parameter_from_a_record_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.ExplicitRecordMethodParameterNamesMustMatch, nameof(FeaturesResources.Explicitly_implemented_methods_of_records_must_have_parameter_names_that_match_the_compiler_generated_equivalent_0)); + AddRudeEdit(RudeEditKind.NotSupportedByRuntime, nameof(FeaturesResources.Edit_and_continue_is_not_supported_by_the_runtime)); + AddRudeEdit(RudeEditKind.MakeMethodAsync, nameof(FeaturesResources.Making_a_method_async_will_prevent_the_debug_session_from_continuing)); + AddRudeEdit(RudeEditKind.MakeMethodIterator, nameof(FeaturesResources.Making_a_method_an_iterator_will_prevent_the_debug_session_from_continuing)); + AddRudeEdit(RudeEditKind.InsertNotSupportedByRuntime, nameof(FeaturesResources.Adding_0_will_prevent_the_debug_session_from_continuing)); + // VB specific AddRudeEdit(RudeEditKind.HandlesClauseUpdate, nameof(FeaturesResources.Updating_the_Handles_clause_of_0_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.ImplementsClauseUpdate, nameof(FeaturesResources.Updating_the_Implements_clause_of_a_0_will_prevent_the_debug_session_from_continuing)); diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs index 622b89a776cfd..7cf43a6c5f469 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs @@ -22,7 +22,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue internal sealed class EditAndContinueDocumentAnalysesCache { private readonly object _guard = new(); - private readonly Dictionary results, Project baseProject, Document document, ImmutableArray activeStatementSpans)> _analyses = new(); + private readonly Dictionary results, Project baseProject, Document document, ImmutableArray activeStatementSpans, EditAndContinueCapabilities capabilities)> _analyses = new(); private readonly AsyncLazy _baseActiveStatements; public EditAndContinueDocumentAnalysesCache(AsyncLazy baseActiveStatements) @@ -30,11 +30,11 @@ public EditAndContinueDocumentAnalysesCache(AsyncLazy baseA _baseActiveStatements = baseActiveStatements; } - public async ValueTask> GetActiveStatementsAsync(Document baseDocument, Document document, ImmutableArray activeStatementSpans, CancellationToken cancellationToken) + public async ValueTask> GetActiveStatementsAsync(Document baseDocument, Document document, ImmutableArray activeStatementSpans, EditAndContinueCapabilities capabilities, CancellationToken cancellationToken) { try { - var results = await GetDocumentAnalysisAsync(baseDocument.Project, document, activeStatementSpans, cancellationToken).ConfigureAwait(false); + var results = await GetDocumentAnalysisAsync(baseDocument.Project, document, activeStatementSpans, capabilities, cancellationToken).ConfigureAwait(false); return results.ActiveStatements; } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) @@ -46,6 +46,7 @@ public async ValueTask> GetActiveStatementsAsync public async ValueTask> GetDocumentAnalysesAsync( Project oldProject, IReadOnlyList<(Document newDocument, ImmutableArray newActiveStatementSpans)> documentInfos, + EditAndContinueCapabilities capabilities, CancellationToken cancellationToken) { try @@ -55,7 +56,7 @@ public async ValueTask> GetDocumentAnaly return ImmutableArray.Empty; } - var tasks = documentInfos.Select(info => Task.Run(() => GetDocumentAnalysisAsync(oldProject, info.newDocument, info.newActiveStatementSpans, cancellationToken).AsTask(), cancellationToken)); + var tasks = documentInfos.Select(info => Task.Run(() => GetDocumentAnalysisAsync(oldProject, info.newDocument, info.newActiveStatementSpans, capabilities, cancellationToken).AsTask(), cancellationToken)); var allResults = await Task.WhenAll(tasks).ConfigureAwait(false); return allResults.ToImmutableArray(); @@ -72,7 +73,7 @@ public async ValueTask> GetDocumentAnaly /// Base project. /// Document snapshot to analyze. /// Active statement spans tracked by the editor. - public async ValueTask GetDocumentAnalysisAsync(Project baseProject, Document document, ImmutableArray activeStatementSpans, CancellationToken cancellationToken) + public async ValueTask GetDocumentAnalysisAsync(Project baseProject, Document document, ImmutableArray activeStatementSpans, EditAndContinueCapabilities capabilities, CancellationToken cancellationToken) { try { @@ -80,7 +81,7 @@ public async ValueTask GetDocumentAnalysisAsync(Project lock (_guard) { - lazyResults = GetDocumentAnalysisNoLock(baseProject, document, activeStatementSpans); + lazyResults = GetDocumentAnalysisNoLock(baseProject, document, activeStatementSpans, capabilities); } return await lazyResults.GetValueAsync(cancellationToken).ConfigureAwait(false); @@ -91,7 +92,7 @@ public async ValueTask GetDocumentAnalysisAsync(Project } } - private AsyncLazy GetDocumentAnalysisNoLock(Project baseProject, Document document, ImmutableArray activeStatementSpans) + private AsyncLazy GetDocumentAnalysisNoLock(Project baseProject, Document document, ImmutableArray activeStatementSpans, EditAndContinueCapabilities capabilities) { // Do not reuse an analysis of the document unless its snasphot is exactly the same as was used to calculate the results. // Note that comparing document snapshots in effect compares the entire solution snapshots (when another document is changed a new solution snapshot is created @@ -107,7 +108,8 @@ private AsyncLazy GetDocumentAnalysisNoLock(Project bas if (_analyses.TryGetValue(document.Id, out var analysis) && analysis.baseProject == baseProject && analysis.document == document && - analysis.activeStatementSpans.SequenceEqual(activeStatementSpans)) + analysis.activeStatementSpans.SequenceEqual(activeStatementSpans) && + analysis.capabilities == capabilities) { return analysis.results; } @@ -125,7 +127,7 @@ private AsyncLazy GetDocumentAnalysisNoLock(Project bas documentBaseActiveStatements = ImmutableArray.Empty; } - return await analyzer.AnalyzeDocumentAsync(baseProject, documentBaseActiveStatements, document, activeStatementSpans, cancellationToken).ConfigureAwait(false); + return await analyzer.AnalyzeDocumentAsync(baseProject, documentBaseActiveStatements, document, activeStatementSpans, capabilities, cancellationToken).ConfigureAwait(false); } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { @@ -138,7 +140,7 @@ private AsyncLazy GetDocumentAnalysisNoLock(Project bas // The only relevant analysis is for the latest base and document snapshots. // Note that the base snapshot may evolve if documents are dicovered that were previously // out-of-sync with the compiled outputs and are now up-to-date. - _analyses[document.Id] = (lazyResults, baseProject, document, activeStatementSpans); + _analyses[document.Id] = (lazyResults, baseProject, document, activeStatementSpans, capabilities); return lazyResults; } diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs index 9d1fc38fcad36..02a1146aad429 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs @@ -85,11 +85,38 @@ public async ValueTask StartDebuggingSessionAsync(Solution solution, IManagedEdi captureMatchingDocuments ? await CommittedSolution.GetMatchingDocumentsAsync(solution, _compilationOutputsProvider, cancellationToken).ConfigureAwait(false) : SpecializedCollections.EmptyEnumerable>(); - var newSession = new DebuggingSession(solution, debuggerService, _compilationOutputsProvider, initialDocumentStates, _debuggingSessionTelemetry, _editSessionTelemetry); + var runtimeCapabilities = await debuggerService.GetCapabilitiesAsync(cancellationToken).ConfigureAwait(false); + var capabilities = ParseCapabilities(runtimeCapabilities); + + var newSession = new DebuggingSession(solution, debuggerService, capabilities, _compilationOutputsProvider, initialDocumentStates, _debuggingSessionTelemetry, _editSessionTelemetry); var previousSession = Interlocked.CompareExchange(ref _debuggingSession, newSession, null); Contract.ThrowIfFalse(previousSession == null, "New debugging session can't be started until the existing one has ended."); } + // internal for testing + internal static EditAndContinueCapabilities ParseCapabilities(ImmutableArray capabilities) + { + var caps = EditAndContinueCapabilities.None; + + foreach (var capability in capabilities) + { + caps |= capability switch + { + "Baseline" => EditAndContinueCapabilities.Baseline, + "AddMethodToExistingType" => EditAndContinueCapabilities.AddMethodToExistingType, + "AddStaticFieldToExistingType" => EditAndContinueCapabilities.AddStaticFieldToExistingType, + "AddInstanceFieldToExistingType" => EditAndContinueCapabilities.AddInstanceFieldToExistingType, + "NewTypeDefinition" => EditAndContinueCapabilities.NewTypeDefinition, + + // To make it eaiser for runtimes to specify more broad capabilities + "AddDefinitionToExistingType" => EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddStaticFieldToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType, + + _ => EditAndContinueCapabilities.None + }; + } + + return caps; + } public void EndDebuggingSession(out ImmutableArray documentsToReanalyze) { var debuggingSession = Interlocked.Exchange(ref _debuggingSession, null); @@ -173,7 +200,7 @@ public async ValueTask> GetDocumentDiagnosticsAsync(D var editSession = debuggingSession.EditSession; var documentActiveStatementSpans = await activeStatementSpanProvider(cancellationToken).ConfigureAwait(false); - var analysis = await editSession.Analyses.GetDocumentAnalysisAsync(oldProject, document, documentActiveStatementSpans, cancellationToken).ConfigureAwait(false); + var analysis = await editSession.Analyses.GetDocumentAnalysisAsync(oldProject, document, documentActiveStatementSpans, debuggingSession.Capabilities, cancellationToken).ConfigureAwait(false); if (analysis.HasChanges) { // Once we detected a change in a document let the debugger know that the corresponding loaded module @@ -284,6 +311,7 @@ public void DiscardSolutionUpdate() var lastCommittedSolution = debuggingSession.LastCommittedSolution; var baseActiveStatements = await debuggingSession.EditSession.BaseActiveStatements.GetValueAsync(cancellationToken).ConfigureAwait(false); + using var _ = ArrayBuilder>.GetInstance(out var spans); foreach (var documentId in documentIds) @@ -311,6 +339,7 @@ public void DiscardSolutionUpdate() documentBaseActiveStatements, document, newActiveStatementSpans: ImmutableArray.Empty, + debuggingSession.Capabilities, cancellationToken).ConfigureAwait(false); if (!analysis.ActiveStatements.IsDefault) @@ -350,7 +379,7 @@ public void DiscardSolutionUpdate() } var documentActiveStatementSpans = await activeStatementSpanProvider(cancellationToken).ConfigureAwait(false); - var activeStatements = await debuggingSession.EditSession.Analyses.GetActiveStatementsAsync(baseDocument, document, documentActiveStatementSpans, cancellationToken).ConfigureAwait(false); + var activeStatements = await debuggingSession.EditSession.Analyses.GetActiveStatementsAsync(baseDocument, document, documentActiveStatementSpans, debuggingSession.Capabilities, cancellationToken).ConfigureAwait(false); if (activeStatements.IsDefault) { return default; @@ -395,7 +424,7 @@ public void DiscardSolutionUpdate() } var activeStatementSpans = await activeStatementSpanProvider(primaryDocument.Id, cancellationToken).ConfigureAwait(false); - var currentActiveStatements = await debuggingSession.EditSession.Analyses.GetActiveStatementsAsync(oldPrimaryDocument, primaryDocument, activeStatementSpans, cancellationToken).ConfigureAwait(false); + var currentActiveStatements = await debuggingSession.EditSession.Analyses.GetActiveStatementsAsync(oldPrimaryDocument, primaryDocument, activeStatementSpans, debuggingSession.Capabilities, cancellationToken).ConfigureAwait(false); if (currentActiveStatements.IsDefault) { // The document has syntax errors. diff --git a/src/Features/Core/Portable/EditAndContinue/EditSession.cs b/src/Features/Core/Portable/EditAndContinue/EditSession.cs index a7aa726b6f0ab..6d1adc2be1962 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditSession.cs @@ -444,7 +444,7 @@ private static async Task PopulateChangedAndAddedDocumentsAsync(CommittedSolutio var oldProject = DebuggingSession.LastCommittedSolution.GetProject(newProject.Id); Contract.ThrowIfNull(oldProject); - var analyses = await Analyses.GetDocumentAnalysesAsync(oldProject, builder, cancellationToken).ConfigureAwait(false); + var analyses = await Analyses.GetDocumentAnalysesAsync(oldProject, builder, DebuggingSession.Capabilities, cancellationToken).ConfigureAwait(false); return (analyses, documentDiagnostics.ToImmutable()); } diff --git a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueAnalyzer.cs index fc3602b9525f5..c4a277d2a788b 100644 --- a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueAnalyzer.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue { internal interface IEditAndContinueAnalyzer : ILanguageService { - Task AnalyzeDocumentAsync(Project baseProject, ImmutableArray baseActiveStatements, Document document, ImmutableArray newActiveStatementSpans, CancellationToken cancellationToken); + Task AnalyzeDocumentAsync(Project baseProject, ImmutableArray baseActiveStatements, Document document, ImmutableArray newActiveStatementSpans, EditAndContinueCapabilities capabilities, CancellationToken cancellationToken); ImmutableArray GetExceptionRegions(SourceText text, SyntaxNode syntaxRoot, LinePositionSpan activeStatementSpan, bool isLeaf, out bool isCovered); } } diff --git a/src/Features/Core/Portable/EditAndContinue/ManagedEditAndContinueDebuggerServiceExtensions_REMOVE.cs b/src/Features/Core/Portable/EditAndContinue/ManagedEditAndContinueDebuggerServiceExtensions_REMOVE.cs new file mode 100644 index 0000000000000..aa8cfe1b00e6f --- /dev/null +++ b/src/Features/Core/Portable/EditAndContinue/ManagedEditAndContinueDebuggerServiceExtensions_REMOVE.cs @@ -0,0 +1,22 @@ +// 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.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Debugger.Contracts.EditAndContinue; + +namespace Microsoft.CodeAnalysis.EditAndContinue +{ + internal static class ManagedEditAndContinueDebuggerServiceExtensions_REMOVE + { + /// + /// This will be removed when IManagedEditAndContinueDebuggerService gets the method for real + /// + public static ValueTask> GetCapabilitiesAsync(this IManagedEditAndContinueDebuggerService _1, CancellationToken _2) + { + return new(ImmutableArray.Create("Baseline", "AddDefinitionToExistingType", "NewTypeDefinition")); + } + } +} diff --git a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs index 297456012e4ab..6e1b6ff1db8bd 100644 --- a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs +++ b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs @@ -122,6 +122,11 @@ internal enum RudeEditKind : ushort ImplementRecordParameterWithSet = 93, AddRecordPositionalParameter = 94, DeleteRecordPositionalParameter = 95, - ExplicitRecordMethodParameterNamesMustMatch = 96 + ExplicitRecordMethodParameterNamesMustMatch = 96, + + NotSupportedByRuntime = 97, + MakeMethodAsync = 98, + MakeMethodIterator = 99, + InsertNotSupportedByRuntime = 100 } } diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 15be011d73e46..0832da9dc11e4 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -2855,4 +2855,13 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Explicitly implemented methods of records must have parameter names that match the compiler generated equivalent '{0}' + + Edit and continue is not supported by the runtime. + + + Making a method an iterator will prevent the debug session from continuing. + + + Making a method 'async' will prevent the debug session from continuing. + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index cc68e91d98834..16890fcc2c495 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -345,6 +345,11 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Změny provedené v projektu {0} znemožní relaci ladění pokračovat dále: {1} + + Edit and continue is not supported by the runtime. + Edit and continue is not supported by the runtime. + + Error while reading file '{0}': {1} Při čtení souboru {0} došlo k chybě: {1} @@ -600,6 +605,16 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Převrátit podmínku + + Making a method an iterator will prevent the debug session from continuing. + Making a method an iterator will prevent the debug session from continuing. + + + + Making a method 'async' will prevent the debug session from continuing. + Making a method 'async' will prevent the debug session from continuing. + + malformed chybný formát diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index 922bd8a12bbd7..0ca26dd36d72f 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -345,6 +345,11 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Die im Projekt "{0}" vorgenommenen Änderungen verhindern, dass die Debugsitzung fortgesetzt wird: {1} + + Edit and continue is not supported by the runtime. + Edit and continue is not supported by the runtime. + + Error while reading file '{0}': {1} Fehler beim Lesen der Datei "{0}": {1} @@ -600,6 +605,16 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Bedingten Operator umkehren + + Making a method an iterator will prevent the debug session from continuing. + Making a method an iterator will prevent the debug session from continuing. + + + + Making a method 'async' will prevent the debug session from continuing. + Making a method 'async' will prevent the debug session from continuing. + + malformed fehlerhaft diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index 3fb1679bc0cff..c4eeaf6516511 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -345,6 +345,11 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Los cambios realizados en el proyecto "{0}" impedirán que continúe la sesión de depuración: {1} + + Edit and continue is not supported by the runtime. + Edit and continue is not supported by the runtime. + + Error while reading file '{0}': {1} Error al leer el archivo "{0}": {1} @@ -600,6 +605,16 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Invertir condicional + + Making a method an iterator will prevent the debug session from continuing. + Making a method an iterator will prevent the debug session from continuing. + + + + Making a method 'async' will prevent the debug session from continuing. + Making a method 'async' will prevent the debug session from continuing. + + malformed con formato incorrecto diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index b6a370422253d..9f9c99607e1e2 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -345,6 +345,11 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Les changements apportés au projet '{0}' empêchent la session de débogage de se poursuivre : {1} + + Edit and continue is not supported by the runtime. + Edit and continue is not supported by the runtime. + + Error while reading file '{0}': {1} Erreur durant la lecture du fichier '{0}' : {1} @@ -600,6 +605,16 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Inverser un élément conditionnel + + Making a method an iterator will prevent the debug session from continuing. + Making a method an iterator will prevent the debug session from continuing. + + + + Making a method 'async' will prevent the debug session from continuing. + Making a method 'async' will prevent the debug session from continuing. + + malformed incorrecte diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index 0665349c39b8d..6a5ad3d2bf151 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -345,6 +345,11 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Le modifiche apportate al progetto '{0}' impediranno la continuazione della sessione di debug: {1} + + Edit and continue is not supported by the runtime. + Edit and continue is not supported by the runtime. + + Error while reading file '{0}': {1} Si è verificato un errore durante la lettura del file '{0}': {1} @@ -600,6 +605,16 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Inverti espressione condizionale + + Making a method an iterator will prevent the debug session from continuing. + Making a method an iterator will prevent the debug session from continuing. + + + + Making a method 'async' will prevent the debug session from continuing. + Making a method 'async' will prevent the debug session from continuing. + + malformed non valido diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index a40344b670a01..efcd55f9b7328 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -345,6 +345,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma プロジェクト '{0}' で変更を行うと、デバッグ セッションは続行されません。{1} + + Edit and continue is not supported by the runtime. + Edit and continue is not supported by the runtime. + + Error while reading file '{0}': {1} ファイル {0}' の読み取り中にエラーが発生しました: {1} @@ -600,6 +605,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 条件を反転します + + Making a method an iterator will prevent the debug session from continuing. + Making a method an iterator will prevent the debug session from continuing. + + + + Making a method 'async' will prevent the debug session from continuing. + Making a method 'async' will prevent the debug session from continuing. + + malformed 不正な形式 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index 0605ebddedcd9..fc0630fe3ccf2 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -345,6 +345,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma '{0}' 프로젝트에서 수행한 변경으로 디버그 세션을 계속할 수 없습니다. {1} + + Edit and continue is not supported by the runtime. + Edit and continue is not supported by the runtime. + + Error while reading file '{0}': {1} '{0}' 파일을 읽는 동안 오류가 발생했습니다. {1} @@ -600,6 +605,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 조건 반전 + + Making a method an iterator will prevent the debug session from continuing. + Making a method an iterator will prevent the debug session from continuing. + + + + Making a method 'async' will prevent the debug session from continuing. + Making a method 'async' will prevent the debug session from continuing. + + malformed 형식이 잘못되었습니다. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index c37875eacce17..c161f6f5a3f00 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -345,6 +345,11 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Zmiany wprowadzone w projekcie „{0}” uniemożliwią kontynuowanie sesji debugowania: {1} + + Edit and continue is not supported by the runtime. + Edit and continue is not supported by the runtime. + + Error while reading file '{0}': {1} Błąd podczas odczytywania pliku „{0}”: {1} @@ -600,6 +605,16 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Odwróć warunkowe + + Making a method an iterator will prevent the debug session from continuing. + Making a method an iterator will prevent the debug session from continuing. + + + + Making a method 'async' will prevent the debug session from continuing. + Making a method 'async' will prevent the debug session from continuing. + + malformed źle sformułowane diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index 7152bac06dc28..ee3e3b4a0da9d 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -345,6 +345,11 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess As alterações feitas no projeto '{0}' impedirão que a sessão de depuração continue: {1} + + Edit and continue is not supported by the runtime. + Edit and continue is not supported by the runtime. + + Error while reading file '{0}': {1} Erro ao ler o arquivo '{0}': {1} @@ -600,6 +605,16 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Inverter condicional + + Making a method an iterator will prevent the debug session from continuing. + Making a method an iterator will prevent the debug session from continuing. + + + + Making a method 'async' will prevent the debug session from continuing. + Making a method 'async' will prevent the debug session from continuing. + + malformed malformado diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index fd99539c61bb7..110471324c20b 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -345,6 +345,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Изменения, внесенные в проект "{0}", препятствуют продолжению сеанса отладки: {1} + + Edit and continue is not supported by the runtime. + Edit and continue is not supported by the runtime. + + Error while reading file '{0}': {1} Ошибка при чтении файла "{0}": {1} @@ -600,6 +605,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Инвертировать условный оператор + + Making a method an iterator will prevent the debug session from continuing. + Making a method an iterator will prevent the debug session from continuing. + + + + Making a method 'async' will prevent the debug session from continuing. + Making a method 'async' will prevent the debug session from continuing. + + malformed Ошибка в регулярном выражении diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 52f39dbc7753d..77c6b65e49ab8 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -345,6 +345,11 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be '{0}' projesinde yapılan değişiklikler hata ayıklama oturumunun devam etmesini engelleyecek: {1} + + Edit and continue is not supported by the runtime. + Edit and continue is not supported by the runtime. + + Error while reading file '{0}': {1} '{0}' dosyası okunurken hata: {1} @@ -600,6 +605,16 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Koşullu öğeyi ters çevir + + Making a method an iterator will prevent the debug session from continuing. + Making a method an iterator will prevent the debug session from continuing. + + + + Making a method 'async' will prevent the debug session from continuing. + Making a method 'async' will prevent the debug session from continuing. + + malformed Hatalı biçimlendirilmiş diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index 906c6650eeb49..bd6bbf4927eb2 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -345,6 +345,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 在项目“{0}”中所作的更改将阻止调试会话继续: {1} + + Edit and continue is not supported by the runtime. + Edit and continue is not supported by the runtime. + + Error while reading file '{0}': {1} 读取文件“{0}”时出错: {1} @@ -600,6 +605,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 反转条件 + + Making a method an iterator will prevent the debug session from continuing. + Making a method an iterator will prevent the debug session from continuing. + + + + Making a method 'async' will prevent the debug session from continuing. + Making a method 'async' will prevent the debug session from continuing. + + malformed 格式错误 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 7be1353dd9beb..af78b544ba084 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -345,6 +345,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 在專案 '{0}' 中所做的變更將使偵錯工作階段無法繼續: {1} + + Edit and continue is not supported by the runtime. + Edit and continue is not supported by the runtime. + + Error while reading file '{0}': {1} 讀取檔案 '{0}' 時發生錯誤: {1} @@ -600,6 +605,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 反轉條件 + + Making a method an iterator will prevent the debug session from continuing. + Making a method an iterator will prevent the debug session from continuing. + + + + Making a method 'async' will prevent the debug session from continuing. + Making a method 'async' will prevent the debug session from continuing. + + malformed 語式錯誤