From 2ad0f5385af6f67cbcbf751e5343f073e1e80bce Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 4 Jun 2021 17:16:28 +1000 Subject: [PATCH 01/28] Add failing tests --- .../EditAndContinue/TopLevelEditingTests.cs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index dd495ff5fd626..995166273cb84 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -13270,5 +13270,69 @@ public void TypeConstraintUpdateAndReorder() } #endregion + + #region Top Level Statements + + // This test don't _really_ belong here, but it's actually the top level syntax comparer that is aware of global statements + // in order to recognize that an edit has actually occured. + + [Fact] + public void TopLevelStatements_Update() + { + var src1 = @" +using System; + +Console.WriteLine(""Hello""); +"; + var src2 = @" +using System; + +Console.WriteLine(""Hello World""); +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits("Update [Console.WriteLine(\"Hello\");]@19 -> [Console.WriteLine(\"Hello World\");]@19"); + + edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"))); + } + + [Fact] + public void TopLevelStatements_Insert() + { + var src1 = @" +using System; +"; + var src2 = @" +using System; + +Console.WriteLine(""Hello World""); +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits("Insert [Console.WriteLine(\"Hello World\");]@19"); + + edits.VerifySemantics(SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("$.
$"))); + } + + [Fact] + public void TopLevelStatements_Delete() + { + var src1 = @" +using System; + +Console.WriteLine(""Hello World""); +"; + var src2 = @" +using System; + +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits("Delete [Console.WriteLine(\"Hello World\");]@19"); + + edits.VerifySemantics(SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("$.
$"))); + } + + #endregion } } From 69d304bb5227046b987ad5eeb510a146873f22f6 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 4 Jun 2021 17:16:48 +1000 Subject: [PATCH 02/28] Classify global statement syntax --- .../CSharpEditAndContinueAnalyzer.cs | 15 +++++++-------- .../Portable/EditAndContinue/SyntaxComparer.cs | 5 +++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index dd102f7e42bfa..1ff763ab7c0ed 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -1424,7 +1424,6 @@ private static bool GroupBySignatureComparer(ImmutableArray ol return default(TextSpan); case SyntaxKind.GlobalStatement: - // TODO: return node.Span; case SyntaxKind.ExternAliasDirective: @@ -2150,8 +2149,6 @@ private void ClassifyReorder(SyntaxNode newNode) switch (newNode.Kind()) { case SyntaxKind.GlobalStatement: - // TODO: - ReportError(RudeEditKind.Move); return; case SyntaxKind.ExternAliasDirective: @@ -2216,8 +2213,7 @@ private void ClassifyInsert(SyntaxNode node) switch (node.Kind()) { case SyntaxKind.GlobalStatement: - // TODO: - ReportError(RudeEditKind.Insert); + ClassifyUpdate((GlobalStatementSyntax)node); return; case SyntaxKind.ExternAliasDirective: @@ -2305,8 +2301,6 @@ private void ClassifyDelete(SyntaxNode oldNode) switch (oldNode.Kind()) { case SyntaxKind.GlobalStatement: - // TODO: - ReportError(RudeEditKind.Delete); return; case SyntaxKind.ExternAliasDirective: @@ -2409,7 +2403,7 @@ private void ClassifyUpdate(SyntaxNode oldNode, SyntaxNode newNode) switch (newNode.Kind()) { case SyntaxKind.GlobalStatement: - ReportError(RudeEditKind.Update); + ClassifyUpdate((GlobalStatementSyntax)newNode); return; case SyntaxKind.ExternAliasDirective: @@ -2538,6 +2532,11 @@ private void ClassifyUpdate(SyntaxNode oldNode, SyntaxNode newNode) } } + private void ClassifyUpdate(GlobalStatementSyntax newNode) + { + ClassifyDeclarationBodyRudeUpdates(newNode.Statement); + } + private void ClassifyUpdate(NamespaceDeclarationSyntax oldNode, NamespaceDeclarationSyntax newNode) { Debug.Assert(!SyntaxFactory.AreEquivalent(oldNode.Name, newNode.Name)); diff --git a/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs b/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs index 11a4bfd82d71f..74d3daa716f73 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs @@ -53,6 +53,8 @@ internal enum Label // Top level syntax kinds CompilationUnit, + GlobalStatement, + NamespaceDeclaration, ExternAliasDirective, // tied to parent UsingDirective, // tied to parent @@ -548,9 +550,8 @@ private static Label ClassifyTopSyntax(SyntaxKind kind, out bool isLeaf) switch (kind) { case SyntaxKind.GlobalStatement: - // TODO: isLeaf = true; - return Label.Ignored; + return Label.GlobalStatement; case SyntaxKind.ExternAliasDirective: isLeaf = true; From f11e7be1bfcbb5d0ef3615160ef6b65c6acf0005 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 7 Jun 2021 11:25:22 +1000 Subject: [PATCH 03/28] Few more failing tests, and one passing. --- .../EditAndContinue/TopLevelEditingTests.cs | 46 ++++++++++++++++++- .../CSharpEditAndContinueAnalyzer.cs | 13 ++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index 995166273cb84..834d80afc21bb 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -13297,7 +13297,7 @@ public void TopLevelStatements_Update() } [Fact] - public void TopLevelStatements_Insert() + public void TopLevelStatements_Insert_NoImplicitMain() { var src1 = @" using System; @@ -13315,7 +13315,28 @@ public void TopLevelStatements_Insert() } [Fact] - public void TopLevelStatements_Delete() + public void TopLevelStatements_Insert_ImplicitMain() + { + var src1 = @" +using System; + +Console.WriteLine(""Hello""); +"; + var src2 = @" +using System; + +Console.WriteLine(""Hello""); +Console.WriteLine(""World""); +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits("Insert [Console.WriteLine(\"World\");]@48"); + + edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"))); + } + + [Fact] + public void TopLevelStatements_Delete_NoImplicitMain() { var src1 = @" using System; @@ -13333,6 +13354,27 @@ public void TopLevelStatements_Delete() edits.VerifySemantics(SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("$.
$"))); } + [Fact] + public void TopLevelStatements_Delete_ImplicitMain() + { + var src1 = @" +using System; + +Console.WriteLine(""Hello""); +Console.WriteLine(""World""); +"; + var src2 = @" +using System; + +Console.WriteLine(""Hello""); +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits("Delete [Console.WriteLine(\"World\");]@48"); + + edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"))); + } + #endregion } } diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index 1ff763ab7c0ed..bc2f0eec990b7 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -641,6 +641,13 @@ private static IEnumerable GetChildNodes(SyntaxNode root, SyntaxNode internal override void ReportDeclarationInsertDeleteRudeEdits(ArrayBuilder diagnostics, SyntaxNode oldNode, SyntaxNode newNode, ISymbol oldSymbol, ISymbol newSymbol) { + // Global statements have a declaring syntax reference to the compilation unit itself, which we can just ignore + // for the purposes of declaration rude edits + if (oldNode.IsKind(SyntaxKind.CompilationUnit) || newNode.IsKind(SyntaxKind.CompilationUnit)) + { + return; + } + // Compiler generated methods of records have a declaring syntax reference to the record declaration itself // but their explicitly implemented counterparts reference the actual member. Compiler generated properties // of records reference the parameter that names them. @@ -1249,6 +1256,12 @@ protected override SyntaxNode GetSymbolDeclarationSyntax(SyntaxReference referen return null; } + // Top level code always lives in a synthesized Main method + if (node.IsKind(SyntaxKind.GlobalStatement)) + { + return model.GetEnclosingSymbol(node.SpanStart, cancellationToken); + } + var symbol = model.GetDeclaredSymbol(node, cancellationToken); // Ignore partial method definition parts. From 8905dc89e81a4b46df5c354719d3f48fd47b3738 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 7 Jun 2021 17:29:27 +1000 Subject: [PATCH 04/28] Issue semantic edits for the synthesized main method --- .../EditAndContinue/TopLevelEditingTests.cs | 25 +++++++++++++++++++ .../CSharpEditAndContinueAnalyzer.cs | 3 +++ .../AbstractEditAndContinueAnalyzer.cs | 14 ++++++++++- .../VisualBasicEditAndContinueAnalyzer.vb | 4 +++ 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index 834d80afc21bb..a505f865fae48 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -13296,6 +13296,31 @@ public void TopLevelStatements_Update() edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"))); } + [Fact] + public void TopLevelStatements_InsertAndUpdate() + { + var src1 = @" +using System; + +Console.WriteLine(""Hello""); +"; + var src2 = @" +using System; + +Console.WriteLine(""Hello World""); +Console.WriteLine(""What is your name?""); +var name = Console.ReadLine(); +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [Console.WriteLine(\"Hello\");]@19 -> [Console.WriteLine(\"Hello World\");]@19", + "Insert [Console.WriteLine(\"What is your name?\");]@54", + "Insert [var name = Console.ReadLine();]@96"); + + edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"))); + } + [Fact] public void TopLevelStatements_Insert_NoImplicitMain() { diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index bc2f0eec990b7..d4817ad895b80 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -773,6 +773,9 @@ protected override bool TryMatchActiveStatement( #region Syntax and Semantic Utils + protected override bool IsGlobalStatement(SyntaxNode node) + => node.IsKind(SyntaxKind.GlobalStatement); + protected override string LineDirectiveKeyword => "line"; diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 0f1cdff914830..142579856ad80 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -260,6 +260,8 @@ protected virtual bool StateMachineSuspensionPointKindEquals(SyntaxNode suspensi /// protected abstract bool AreEquivalentActiveStatements(SyntaxNode oldStatement, SyntaxNode newStatement, int statementPart); + protected abstract bool IsGlobalStatement(SyntaxNode node); + /// /// Returns all symbols associated with an edit. /// Returns an empty set if the edit is not associated with any symbols. @@ -2478,7 +2480,12 @@ private async Task> AnalyzeSemanticsAsync( continue; } - if (!newSymbol.IsImplicitlyDeclared) + if (IsGlobalStatement(edit.OldNode)) + { + // A delete of a global statement, when newSymbol isn't null, is an update to the implicit Main method + editKind = SemanticEditKind.Update; + } + else if (!newSymbol.IsImplicitlyDeclared) { // Ignore the delete. The new symbol is explicitly declared and thus there will be an insert edit that will issue a semantic update. // Note that this could also be the case for deleting properties of records, but they will be handled when we see @@ -2650,6 +2657,11 @@ private async Task> AnalyzeSemanticsAsync( continue; } + else if (IsGlobalStatement(edit.NewNode)) + { + // An insert of a global statement, when oldSymbol isn't null, is an update to the implicit Main method + editKind = SemanticEditKind.Update; + } else if (oldSymbol.DeclaringSyntaxReferences.Length == 1 && newSymbol.DeclaringSyntaxReferences.Length == 1) { // Handles partial methods and explicitly implemented properties that implement positional parameters of records diff --git a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb index 85075aaf5a143..647218fc640cf 100644 --- a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb +++ b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb @@ -799,6 +799,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue #Region "Syntax And Semantic Utils" + Protected Overrides Function IsGlobalStatement(node As SyntaxNode) As Boolean + Return False + End Function + Protected Overrides ReadOnly Property LineDirectiveKeyword As String Get Return "ExternalSource" From 2f59e3aa669621ee3cd21f23210311a9a75954a4 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 7 Jun 2021 17:29:48 +1000 Subject: [PATCH 05/28] Rude edits for creating or deleting the synthesized main method --- .../EditAndContinue/TopLevelEditingTests.cs | 4 ++-- .../CSharpEditAndContinueAnalyzer.cs | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index a505f865fae48..c844ad0be6c64 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -13336,7 +13336,7 @@ public void TopLevelStatements_Insert_NoImplicitMain() edits.VerifyEdits("Insert [Console.WriteLine(\"Hello World\");]@19"); - edits.VerifySemantics(SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("$.
$"))); + edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Insert, "Console.WriteLine(\"Hello World\");", CSharpFeaturesResources.global_statement)); } [Fact] @@ -13376,7 +13376,7 @@ public void TopLevelStatements_Delete_NoImplicitMain() edits.VerifyEdits("Delete [Console.WriteLine(\"Hello World\");]@19"); - edits.VerifySemantics(SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("$.
$"))); + edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Delete, null, CSharpFeaturesResources.global_statement)); } [Fact] diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index d4817ad895b80..cd558618bf8bd 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -2229,6 +2229,13 @@ private void ClassifyInsert(SyntaxNode node) switch (node.Kind()) { case SyntaxKind.GlobalStatement: + // Global statements can be inserted but only if there is at least one other global statement, + // otherwise this would be an insert of the synthesized entry point itself. + if ((node.Parent as CompilationUnitSyntax)?.Members.Count == 1) + { + ReportError(RudeEditKind.Insert); + } + ClassifyUpdate((GlobalStatementSyntax)node); return; @@ -2317,6 +2324,12 @@ private void ClassifyDelete(SyntaxNode oldNode) switch (oldNode.Kind()) { case SyntaxKind.GlobalStatement: + // Global statements can be deleted unless they were the only global statement, + // otherwise it would be a delete of the synthesized entry point itself + if ((oldNode.Parent as CompilationUnitSyntax)?.Members.Count == 1) + { + ReportError(RudeEditKind.Delete); + } return; case SyntaxKind.ExternAliasDirective: From 6d016fc322d34dfe8a703d3224408d0e54bc1f20 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 7 Jun 2021 17:48:58 +1000 Subject: [PATCH 06/28] Basic regression test --- .../EditAndContinue/EditAndContinueTests.cs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index 0652a0d96bc13..942445cbdbf38 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -10775,5 +10775,71 @@ record R(int X) "Microsoft.CodeAnalysis: {EmbeddedAttribute}", "System.Runtime.CompilerServices: {NullableAttribute, NullableContextAttribute}"); } + + [Fact] + public void TopLevelStatement_Update() + { + var source0 = + @"using System; + +Console.WriteLine(""Hello""); +"; + var source1 = + @"using System; + +Console.WriteLine(""Hello World""); +"; + var compilation0 = CreateCompilation(source0, options: TestOptions.DebugExe); + var compilation1 = compilation0.WithSource(source1); + + var method0 = compilation0.GetMember("$.
$"); + + // Verify full metadata contains expected rows. + var bytes0 = compilation0.EmitToArray(); + using var md0 = ModuleMetadata.CreateFromImage(bytes0); + var reader0 = md0.MetadataReader; + + CheckNames(reader0, reader0.GetTypeDefNames(), "", "$"); + CheckNames(reader0, reader0.GetMethodDefNames(), "
$"); + CheckNames(reader0, reader0.GetMemberRefNames(), /*CompilationRelaxationsAttribute.*/".ctor", /*RuntimeCompatibilityAttribute.*/".ctor", /*Object.*/".ctor", /*DebuggableAttribute*/".ctor", /*Console.*/"WriteLine"); + + var generation0 = EmitBaseline.CreateInitialBaseline( + md0, + EmptyLocalsProvider); + var method1 = compilation1.GetMember("$.
$"); + + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Update, method0, method1))); + + // Verify delta metadata contains expected rows. + using var md1 = diff1.GetMetadata(); + var reader1 = md1.Reader; + var readers = new[] { reader0, reader1 }; + + EncValidation.VerifyModuleMvid(1, reader0, reader1); + + CheckNames(readers, reader1.GetTypeDefNames()); + CheckNames(readers, reader1.GetMethodDefNames(), "
$"); + CheckNames(readers, reader1.GetMemberRefNames(), /*CompilerGenerated*/".ctor", /*Console.*/"WriteLine"); + + CheckEncLog(reader1, + Row(2, TableIndex.AssemblyRef, EditAndContinueOperation.Default), + Row(6, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(7, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(8, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(9, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(10, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default)); // Synthesized Main method + + CheckEncMap(reader1, + Handle(8, TableIndex.TypeRef), + Handle(9, TableIndex.TypeRef), + Handle(10, TableIndex.TypeRef), + Handle(1, TableIndex.MethodDef), + Handle(6, TableIndex.MemberRef), + Handle(7, TableIndex.MemberRef), + Handle(2, TableIndex.AssemblyRef)); + } } } From 3651ed78259dcc0c086dc11ae84983e40ae0085f Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 8 Jun 2021 14:38:18 +1000 Subject: [PATCH 07/28] Rerport rude errors for return type changes --- .../EditAndContinue/TopLevelEditingTests.cs | 315 ++++++++++++++++++ .../CSharpEditAndContinueAnalyzer.cs | 52 ++- 2 files changed, 363 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index c844ad0be6c64..31745766ccffc 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -13400,6 +13400,321 @@ public void TopLevelStatements_Delete_ImplicitMain() edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"))); } + [Fact] + public void TopLevelStatements_StackAlloc() + { + var src1 = @"unsafe { var x = stackalloc int[3]; System.Console.Write(1); }"; + var src2 = @"unsafe { var x = stackalloc int[3]; System.Console.Write(2); }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc", CSharpFeaturesResources.global_statement)); + } + + [Fact] + public void TopLevelStatements_VoidToInt1() + { + var src1 = @" +using System; + +Console.Write(1); +"; + var src2 = @" +using System; + +Console.Write(1); +return 1; +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, "return 1;", CSharpFeaturesResources.global_statement)); + } + + [Fact] + public void TopLevelStatements_VoidToInt2() + { + var src1 = @" +using System; + +Console.Write(1); + +return; +"; + var src2 = @" +using System; + +Console.Write(1); +return 1; +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, "return 1;", CSharpFeaturesResources.global_statement)); + } + + [Fact] + public void TopLevelStatements_VoidToInt3() + { + var src1 = @" +using System; + +Console.Write(1); + +int Goo() +{ + return 1; +} +"; + var src2 = @" +using System; + +Console.Write(1); +return 1; + +int Goo() +{ + return 1; +} +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, "return 1;", CSharpFeaturesResources.global_statement)); + } + + [Fact] + public void TopLevelStatements_VoidToTask() + { + var src1 = @" +using System; +using System.Threading.Tasks; + +Console.Write(1); +"; + var src2 = @" +using System; +using System.Threading.Tasks; + +await Task.Delay(100); +Console.Write(1); +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, "await Task.Delay(100);", CSharpFeaturesResources.global_statement)); + } + + [Fact] + public void TopLevelStatements_TaskToTaskInt() + { + var src1 = @" +using System; +using System.Threading.Tasks; + +await Task.Delay(100); +Console.Write(1); +"; + var src2 = @" +using System; +using System.Threading.Tasks; + +await Task.Delay(100); +Console.Write(1); +return 1; +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, "return 1;", CSharpFeaturesResources.global_statement)); + } + + [Fact] + public void TopLevelStatements_VoidToTaskInt() + { + var src1 = @" +using System; +using System.Threading.Tasks; + +Console.Write(1); +"; + var src2 = @" +using System; +using System.Threading.Tasks; + +Console.Write(1); +return await GetInt(); + +Task GetInt() +{ + return Task.FromResult(1); +} +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, "return await GetInt();", CSharpFeaturesResources.global_statement)); + } + + [Fact] + public void TopLevelStatements_IntToVoid1() + { + var src1 = @" +using System; + +Console.Write(1); + +return 1; +"; + var src2 = @" +using System; + +Console.Write(1); +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement)); + } + + [Fact] + public void TopLevelStatements_IntToVoid2() + { + var src1 = @" +using System; + +Console.Write(1); + +return 1; +"; + var src2 = @" +using System; + +Console.Write(1); +return; +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, "return;", CSharpFeaturesResources.global_statement)); + } + + [Fact] + public void TopLevelStatements_IntToVoid3() + { + var src1 = @" +using System; + +Console.Write(1); +return 1; + +int Goo() +{ + return 1; +} +"; + var src2 = @" +using System; + +Console.Write(1); + +int Goo() +{ + return 1; +} +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement)); + } + + [Fact] + public void TopLevelStatements_TaskToVoid() + { + var src1 = @" +using System; +using System.Threading.Tasks; + +await Task.Delay(100); +Console.Write(1); +"; + var src2 = @" +using System; +using System.Threading.Tasks; + +Console.Write(1); +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement)); + } + + [Fact] + public void TopLevelStatements_TaskIntToTask() + { + var src1 = @" +using System; +using System.Threading.Tasks; + +await Task.Delay(100); +Console.Write(1); +return 1; +"; + var src2 = @" +using System; +using System.Threading.Tasks; + +await Task.Delay(100); +Console.Write(1); +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement)); + } + + [Fact] + public void TopLevelStatements_TaskIntToVoid() + { + var src1 = @" +using System; +using System.Threading.Tasks; + +Console.Write(1); +return await GetInt(); + +Task GetInt() +{ + return Task.FromResult(1); +} +"; + var src2 = @" +using System; +using System.Threading.Tasks; + +Console.Write(1); +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement)); + } + #endregion } } diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index cd558618bf8bd..af97e2f417440 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -2234,9 +2234,11 @@ private void ClassifyInsert(SyntaxNode node) if ((node.Parent as CompilationUnitSyntax)?.Members.Count == 1) { ReportError(RudeEditKind.Insert); + return; } - ClassifyUpdate((GlobalStatementSyntax)node); + // An insert of a global statement is actually an update to the synthesized main so we need to check some extra things + ClassifyUpdate(oldNode: null, (GlobalStatementSyntax)node); return; case SyntaxKind.ExternAliasDirective: @@ -2329,7 +2331,11 @@ private void ClassifyDelete(SyntaxNode oldNode) if ((oldNode.Parent as CompilationUnitSyntax)?.Members.Count == 1) { ReportError(RudeEditKind.Delete); + return; } + + // A delete of a global statement is actually an update to the synthesized main so we need to check some extra things + ClassifyUpdate((GlobalStatementSyntax)oldNode, newNode: null); return; case SyntaxKind.ExternAliasDirective: @@ -2432,7 +2438,7 @@ private void ClassifyUpdate(SyntaxNode oldNode, SyntaxNode newNode) switch (newNode.Kind()) { case SyntaxKind.GlobalStatement: - ClassifyUpdate((GlobalStatementSyntax)newNode); + ClassifyUpdate((GlobalStatementSyntax)oldNode, (GlobalStatementSyntax)newNode); return; case SyntaxKind.ExternAliasDirective: @@ -2561,9 +2567,47 @@ private void ClassifyUpdate(SyntaxNode oldNode, SyntaxNode newNode) } } - private void ClassifyUpdate(GlobalStatementSyntax newNode) + private void ClassifyUpdate(GlobalStatementSyntax? oldNode, GlobalStatementSyntax? newNode) { - ClassifyDeclarationBodyRudeUpdates(newNode.Statement); + if (newNode is not null) + { + ClassifyDeclarationBodyRudeUpdates(newNode.Statement); + } + + var otherCompilationUnit = newNode is null ? _match?.NewRoot as CompilationUnitSyntax : _match?.OldRoot as CompilationUnitSyntax; + var nodeToAnalyze = newNode ?? oldNode; + + Debug.Assert(otherCompilationUnit is not null); + Debug.Assert(nodeToAnalyze is not null); + + // Any change to a global statement could mean the synthesized main method changes return type + // from void to int, Task or Task (or vice versa), so we might need to issue a TypeUpdate rude edit. + foreach (var node in nodeToAnalyze.DescendantNodesAndSelf(LambdaUtilities.IsNotLambda)) + { + switch (node.Kind()) + { + case SyntaxKind.AwaitExpression: + // If we found an await expression, there must be one in the other root + // or it would mean a change of return type from non-Task to Task (or vice versa) + if (!otherCompilationUnit.DescendantNodesAndSelf(LambdaUtilities.IsNotLambda).OfType().Any()) + { + ReportError(RudeEditKind.TypeUpdate, newNode, newNode ?? oldNode); + return; + } + break; + case SyntaxKind.ReturnStatement: + // If we found a return statement with a null expression, there must be one in the other root, or + // if we found a return statement with a non-null expression, there must be one in the other root + // or it would mean a change of return type from void or Task to int or Task (or vice versa) + var returnExpression = (ReturnStatementSyntax)node; + if (!otherCompilationUnit.DescendantNodesAndSelf(LambdaUtilities.IsNotLambda).OfType().Where(r => (r.Expression == null) == (returnExpression.Expression == null)).Any()) + { + ReportError(RudeEditKind.TypeUpdate, newNode, newNode ?? oldNode); + return; + } + break; + } + } } private void ClassifyUpdate(NamespaceDeclarationSyntax oldNode, NamespaceDeclarationSyntax newNode) From 2a283312995370478c706eeade707fe554b6ab0d Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 11 Jun 2021 17:28:01 +1000 Subject: [PATCH 08/28] Analyze semantics of top level statements as though they're method bodies --- .../Compilation/SyntaxTreeSemanticModel.cs | 5 +- .../EditAndContinue/TopLevelEditingTests.cs | 108 +++++++++++++++++- .../CSharpEditAndContinueAnalyzer.cs | 13 ++- .../EditAndContinue/SyntaxUtilities.cs | 16 +++ .../AbstractEditAndContinueAnalyzer.cs | 55 +++++++++ 5 files changed, 190 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 7592924da90f0..ec3f95f1a6eb0 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -2229,7 +2229,10 @@ private void ValidateStatementRange(StatementSyntax firstStatement, StatementSyn throw new ArgumentException("statements not within tree"); } - if (firstStatement.Parent == null || firstStatement.Parent != lastStatement.Parent) + if ((firstStatement.Parent == null || firstStatement.Parent != lastStatement.Parent) && + // Special case for global statements, since they have no common parent that is a statement syntax + firstStatement.Parent is not GlobalStatementSyntax && + lastStatement.Parent is not GlobalStatementSyntax) { throw new ArgumentException("statements not within the same statement list"); } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index 31745766ccffc..2957164769567 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -13273,9 +13273,6 @@ public void TypeConstraintUpdateAndReorder() #region Top Level Statements - // This test don't _really_ belong here, but it's actually the top level syntax comparer that is aware of global statements - // in order to recognize that an edit has actually occured. - [Fact] public void TopLevelStatements_Update() { @@ -13659,7 +13656,8 @@ public void TopLevelStatements_TaskToVoid() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement)); + Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement), + Diagnostic(RudeEditKind.Delete, null, CSharpFeaturesResources.await_expression)); } [Fact] @@ -13712,7 +13710,107 @@ Task GetInt() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement)); + Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement), + Diagnostic(RudeEditKind.Delete, null, CSharpFeaturesResources.await_expression)); + } + + [Fact] + public void TopLevelStatements_WithLambda_Insert() + { + var src1 = @" +using System; + +Func a = () => { return 1; }; +Func> b = () => () => { return 1; }; +"; + var src2 = @" +using System; + +Func a = () => { return 1; }; +Func> b = () => () => { return 1; }; + +Console.WriteLine(1); +"; + var edits = GetTopEdits(src1, src2); + var syntaxMap = GetSyntaxMap(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"), syntaxMap[0]) }); + } + + [Fact] + public void TopLevelStatements_WithLambda_Update() + { + var src1 = @" +using System; + +Func a = () => { return 1; }; +Func> b = () => () => { return 1; }; + +Console.WriteLine(1); +"; + var src2 = @" +using System; + +Func a = () => { return 1; }; +Func> b = () => () => { return 1; }; + +Console.WriteLine(2); +"; + var edits = GetTopEdits(src1, src2); + var syntaxMap = GetSyntaxMap(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"), syntaxMap[0]) }); + } + + [Fact] + public void TopLevelStatements_WithLambda_Delete() + { + var src1 = @" +using System; + +Func a = () => { return 1; }; +Func> b = () => () => { return 1; }; + +Console.WriteLine(1); +"; + var src2 = @" +using System; + +Func a = () => { return 1; }; +Func> b = () => () => { return 1; }; +"; + var edits = GetTopEdits(src1, src2); + var syntaxMap = GetSyntaxMap(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"), syntaxMap[0]) }); + } + + [Fact] + public void TopLevelStatements_UpdateMultiple() + { + var src1 = @" +using System; + +Console.WriteLine(1); +Console.WriteLine(2); +"; + var src2 = @" +using System; + +Console.WriteLine(3); +Console.WriteLine(4); +"; + var edits = GetTopEdits(src1, src2); + + // Since each individual statement is a separate update to a separate node, this just validates we correctly + // only anaylze the things once + edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"))); } #endregion diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index af97e2f417440..45f1bc575e87d 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -160,6 +160,12 @@ internal override bool TryFindMemberDeclaration(SyntaxNode? root, SyntaxNode nod { return variableDeclarator.Initializer?.Value; } + else if (node is CompilationUnitSyntax unit && unit.ContainsTopLevelStatements()) + { + // For top level statements, where there is no syntax node to represent the entire body of the synthesized + // main method we just use the compilation unit itself + return node; + } return SyntaxUtilities.TryGetMethodDeclarationBody(node); } @@ -169,6 +175,11 @@ internal override bool IsDeclarationWithSharedBody(SyntaxNode declaration) protected override ImmutableArray GetCapturedVariables(SemanticModel model, SyntaxNode memberBody) { + if (memberBody is CompilationUnitSyntax unit && unit.ContainsTopLevelStatements()) + { + return model.AnalyzeDataFlow(((GlobalStatementSyntax)unit.Members[0]).Statement, ((GlobalStatementSyntax)unit.Members[^1]).Statement)!.Captured; + } + Debug.Assert(memberBody.IsKind(SyntaxKind.Block) || memberBody is ExpressionSyntax); return model.AnalyzeDataFlow(memberBody).Captured; } @@ -1110,7 +1121,7 @@ internal override bool IsRecordDeclaration(SyntaxNode node) => node.IsKind(SyntaxKind.RecordDeclaration, SyntaxKind.RecordStructDeclaration); internal override SyntaxNode? TryGetContainingTypeDeclaration(SyntaxNode node) - => node.Parent!.FirstAncestorOrSelf(); + => node is CompilationUnitSyntax ? null : node.Parent!.FirstAncestorOrSelf(); internal override bool HasBackingField(SyntaxNode propertyOrIndexerDeclaration) => propertyOrIndexerDeclaration.IsKind(SyntaxKind.PropertyDeclaration, out PropertyDeclarationSyntax? propertyDecl) && diff --git a/src/Features/CSharp/Portable/EditAndContinue/SyntaxUtilities.cs b/src/Features/CSharp/Portable/EditAndContinue/SyntaxUtilities.cs index 333e146172c19..9ca36f86ba051 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/SyntaxUtilities.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/SyntaxUtilities.cs @@ -116,9 +116,25 @@ public static void AssertIsBody(SyntaxNode syntax, bool allowLambda) return; } + // special case for top level statements, which have no containing block other than the compilation unit + if (syntax is CompilationUnitSyntax unit && unit.ContainsTopLevelStatements()) + { + return; + } + Debug.Assert(false); } + public static bool ContainsTopLevelStatements(this CompilationUnitSyntax compilationUnit) + { + if (compilationUnit.Members.Count == 0) + { + return false; + } + + return compilationUnit.Members[0] is GlobalStatementSyntax; + } + public static void FindLeafNodeAndPartner(SyntaxNode leftRoot, int leftPosition, SyntaxNode rightRoot, out SyntaxNode leftNode, out SyntaxNode rightNodeOpt) { leftNode = leftRoot; diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 142579856ad80..f9e0e220d272d 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -2484,6 +2484,32 @@ private async Task> AnalyzeSemanticsAsync( { // A delete of a global statement, when newSymbol isn't null, is an update to the implicit Main method editKind = SemanticEditKind.Update; + + // Since we're deleting we don't have any new nodes, so we have to use the declaring syntax reference + Contract.ThrowIfTrue(oldDeclaration == null); + Contract.ThrowIfFalse(newDeclaration == null); + newDeclaration = GetSymbolDeclarationSyntax(newSymbol.DeclaringSyntaxReferences[0], cancellationToken); + var oldBody = oldDeclaration; + var newBody = newDeclaration; + + AnalyzeChangedMemberBody( + oldDeclaration, + newDeclaration, + oldBody, + newBody, + oldModel, + newModel, + oldSymbol, + newSymbol, + newText, + oldActiveStatements: ImmutableArray.Empty, + newActiveStatementSpans: ImmutableArray.Empty, + capabilities: capabilities, + newActiveStatements, + newExceptionRegions, + diagnostics, + out syntaxMap, + cancellationToken); } else if (!newSymbol.IsImplicitlyDeclared) { @@ -2661,6 +2687,35 @@ private async Task> AnalyzeSemanticsAsync( { // An insert of a global statement, when oldSymbol isn't null, is an update to the implicit Main method editKind = SemanticEditKind.Update; + + // Since we're inserting we don't have any old nodes, so we have to use the declaring syntax reference + Contract.ThrowIfFalse(oldDeclaration == null); + oldDeclaration = GetSymbolDeclarationSyntax(oldSymbol.DeclaringSyntaxReferences[0], cancellationToken); + var oldBody = oldDeclaration; + var newBody = newDeclaration; + + // The old symbol's declaration syntax may be located in a different document than the old version of the current document. + var oldSyntaxDocument = oldProject.Solution.GetRequiredDocument(oldDeclaration.SyntaxTree); + var oldSyntaxModel = await oldSyntaxDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + AnalyzeChangedMemberBody( + oldDeclaration, + newDeclaration, + oldBody, + newBody, + oldSyntaxModel, + newModel, + oldSymbol, + newSymbol, + newText, + oldActiveStatements: ImmutableArray.Empty, + newActiveStatementSpans: ImmutableArray.Empty, + capabilities: capabilities, + newActiveStatements, + newExceptionRegions, + diagnostics, + out syntaxMap, + cancellationToken); } else if (oldSymbol.DeclaringSyntaxReferences.Length == 1 && newSymbol.DeclaringSyntaxReferences.Length == 1) { From 8a30d3d1acd1d59b15bc76fd7471e4c9dbd2a274 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 15 Jun 2021 14:18:28 +1000 Subject: [PATCH 09/28] Get correct breakpoint envelope --- .../EditAndContinue/ActiveStatementTests.cs | 79 +++++++++++++++++++ .../EditAndContinue/BreakpointSpans.cs | 5 ++ 2 files changed, 84 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs index 4fa5b89fd2ade..20aa8c1038154 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs @@ -11035,5 +11035,84 @@ public static void H(int x) } #endregion + + #region Top Level Statements + + [Fact] + public void TopLevelStatements_UpdateAroundActiveStatement_LocalFunction() + { + var src1 = @" +using System; + +Console.WriteLine(1); +void M() { Console.WriteLine(2); } +"; + var src2 = @" +using System; + +Console.WriteLine(1); +void M() { Console.WriteLine(3); } +"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active); + } + + [Fact] + public void TopLevelStatements_UpdateAroundActiveStatement_OutVar() + { + var src1 = @" +using System; + +Console.WriteLine(1); +M(); +"; + var src2 = @" +using System; + +Console.WriteLine(1); +M(out var x); +"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active); + } + + [Fact] + public void TopLevelStatements_Inner() + { + var src1 = @" +using System; + +Goo(1); + +static void Goo(int a) +{ + Console.WriteLine(a); +} +"; + var src2 = @" +using System; + +while (true) +{ + Goo(2); +} + +static void Goo(int a) +{ + Console.WriteLine(a); +} +"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.ActiveStatementUpdate, "Goo(2);")); + } + + #endregion } } diff --git a/src/Features/CSharp/Portable/EditAndContinue/BreakpointSpans.cs b/src/Features/CSharp/Portable/EditAndContinue/BreakpointSpans.cs index c1b084b4c2b67..a7cfdc56e2d90 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/BreakpointSpans.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/BreakpointSpans.cs @@ -801,6 +801,11 @@ internal static TextSpan GetEnvelope(SyntaxNode declaration) return TextSpan.FromBounds(firstSpan.Start, lastSpan.End); } + if (declaration is CompilationUnitSyntax unit && unit.ContainsTopLevelStatements()) + { + return TextSpan.FromBounds(unit.Members[0].SpanStart, unit.Members[^1].Span.End); + } + var body = SyntaxUtilities.TryGetMethodDeclarationBody(declaration); if (body == null) { From ae7899cbed6e011fc9d5933119316998c682358c Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 15 Jun 2021 14:18:35 +1000 Subject: [PATCH 10/28] Add some random tests --- .../EditAndContinueClosureTests.cs | 49 ++++++++++++++ .../EditAndContinue/BreakpointSpansTests.cs | 35 ++++++++++ .../EditAndContinue/StatementEditingTests.cs | 64 +++++++++++++++++++ .../EditAndContinue/StatementMatchingTests.cs | 46 +++++++++++++ 4 files changed, 194 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs index 003401a3770ab..2d3a65a3dac35 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs @@ -4431,5 +4431,54 @@ .maxstack 2 Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(7, TableIndex.MethodDef, EditAndContinueOperation.Default)); } + + [Fact] + public void TopLevelStatement_Closure() + { + var source0 = MarkedSource(@" + +using System; + +Func x = () => args[0]; +Console.WriteLine(x()); + +"); + var source1 = MarkedSource(@" + +using System; + +Func x = () => args[1]; +Console.WriteLine(x()); + +"); + var compilation0 = CreateCompilation(source0.Tree, options: TestOptions.DebugExe); + var compilation1 = compilation0.WithSource(source1.Tree); + + var v0 = CompileAndVerify(compilation0); + var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + + var f0 = compilation0.GetMember("$.
$"); + var f1 = compilation1.GetMember("$.
$"); + + var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo); + + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true))); + + // no new synthesized members generated (with #1 in names): + diff1.VerifySynthesizedMembers( + "$.<>c__DisplayClass0_0: {args, <
$>b__0}", + "$: {<>c__DisplayClass0_0}"); + + var md1 = diff1.GetMetadata(); + var reader1 = md1.Reader; + + // Method updates + CheckEncLogDefinitions(reader1, + Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default), + Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default)); + } } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs index d4f26f5ad2fdc..3ca605e56511f 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs @@ -5147,5 +5147,40 @@ void Goo() } }"); } + + #region Top Level Statements + + + [Fact] + public void TopLevelStatements() + { + VerifyAllSpansInDeclaration(@" +$$[|int d = 5;|] +[|int a = 1|], [|b = 2|], [|c = 3|]; +for ([|int i = 0|], [|j = 1|], [|k = 2|]; [|i < 10|]; [|i++|], [|j++|], [|k--|]) + [|while (b > 0)|] + [|{|] + [|if (c < b)|] + try + [|{|] + [|System.Console.WriteLine(a);|] + [|}|] + [|catch (Exception e)|] + [|{|] + [|System.Console.WriteLine(e);|] + [|}|] + finally + [|{|] + [|}|] + else [|if (b < 10)|] + [|System.Console.WriteLine(b);|] + else + [|System.Console.WriteLine(c);|] + [|}|] +"); + } + + + #endregion } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs index 5658d7becc583..0a1084d8fc0e2 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs @@ -10552,6 +10552,70 @@ public void WithExpression_PropertyValueReorder() edits.VerifyEdits(@"Update [x = y with { X = 1, Y = 1 }]@6 -> [x = y with { Y = 1, X = 1 }]@6"); } + #endregion + + #region Top Level Statements + + [Fact] + public void TopLevelStatement_CaptureArgs() + { + var src1 = @" +using System; + +var x = new Func(() => ""Hello""); + +Console.WriteLine(x()); +"; + var src2 = @" +using System; + +var x = new Func(() => ""Hello"" + args[0]); + +Console.WriteLine(x()); +"; + var edits = GetTopEdits(src1, src2); + + // TODO: allow creating a new leaf closure + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.CapturingVariable, "using System;\r\n\r\nvar x = new Func(() => \"Hello\" + args[0]);\r\n\r\nConsole.WriteLine(x());\r\n", "args")); + } + + + [Fact, WorkItem(21499, "https://github.com/dotnet/roslyn/issues/21499")] + public void TopLevelStatement_UpdateLocalFunction_ForEach1() + { + var src1 = @" +using System; + +foreach (int x0 in new[] { 1 }) // Group #0 +{ // Group #1 + int x1 = 0; + + int f0(int a) => x0; + int f1(int a) => x1; +} +"; + var src2 = @" +using System; + +foreach (int x0 in new[] { 1 }) // Group #0 +{ // Group #1 + int x1 = 0; + + int f0(int a) => x0; + int f1(int a) => x1; + + int f0(int a) => x0; + int f2(int a) => x0 + x1; // error: connecting previously disconnected closures +} +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.InsertLambdaWithMultiScopeCapture, "x1", CSharpFeaturesResources.local_function, "x0", "x1")); + } + + #endregion } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementMatchingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementMatchingTests.cs index ca139c32c0195..87040bbf65213 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementMatchingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementMatchingTests.cs @@ -2118,5 +2118,51 @@ public void SwitchExpressionArms_NestedDissimilar() } #endregion + + #region Top Level Statements + + [Fact] + public void TopLevelStatements() + { + var src1 = @" +Console.WriteLine(1); +Console.WriteLine(2); + +var x = 0; +while (true) +{ + x++; +} + +Console.WriteLine(3); +"; + var src2 = @" +Console.WriteLine(4); +Console.WriteLine(5); + +var x = 1; +while (true) +{ + x--; +} + +Console.WriteLine(6); +"; + var match = GetTopEdits(src1, src2).Match; + var actual = ToMatchingPairs(match); + + var expected = new MatchingPairs + { + { "Console.WriteLine(1);", "Console.WriteLine(4);" }, + { "Console.WriteLine(2);", "Console.WriteLine(5);" }, + { "var x = 0;", "var x = 1;" }, + { "while (true) { x++; }", "while (true) { x--; }" }, + { "Console.WriteLine(3);", "Console.WriteLine(6);" } + }; + + expected.AssertEqual(actual); + } + + #endregion } } From 9c5c9161e3ee14c85811683ddbd9dc66378f6ebc Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 15 Jun 2021 14:31:17 +1000 Subject: [PATCH 11/28] Fix whitespace --- .../CSharpTest/EditAndContinue/BreakpointSpansTests.cs | 2 -- .../CSharpTest/EditAndContinue/StatementEditingTests.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs index 3ca605e56511f..1c76d916543a3 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs @@ -5150,7 +5150,6 @@ void Goo() #region Top Level Statements - [Fact] public void TopLevelStatements() { @@ -5180,7 +5179,6 @@ public void TopLevelStatements() "); } - #endregion } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs index 0a1084d8fc0e2..0130cabaddc1e 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs @@ -10580,7 +10580,6 @@ public void TopLevelStatement_CaptureArgs() Diagnostic(RudeEditKind.CapturingVariable, "using System;\r\n\r\nvar x = new Func(() => \"Hello\" + args[0]);\r\n\r\nConsole.WriteLine(x());\r\n", "args")); } - [Fact, WorkItem(21499, "https://github.com/dotnet/roslyn/issues/21499")] public void TopLevelStatement_UpdateLocalFunction_ForEach1() { @@ -10615,7 +10614,6 @@ public void TopLevelStatement_UpdateLocalFunction_ForEach1() Diagnostic(RudeEditKind.InsertLambdaWithMultiScopeCapture, "x1", CSharpFeaturesResources.local_function, "x0", "x1")); } - #endregion } } From 087a5e9da9b5dda4509eee2f4883d434f96a917b Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 15 Jun 2021 16:44:48 +1000 Subject: [PATCH 12/28] Namespace or type declarations can come after global statements --- .../CSharp/Portable/EditAndContinue/BreakpointSpans.cs | 2 +- .../EditAndContinue/CSharpEditAndContinueAnalyzer.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Features/CSharp/Portable/EditAndContinue/BreakpointSpans.cs b/src/Features/CSharp/Portable/EditAndContinue/BreakpointSpans.cs index a7cfdc56e2d90..207699bfdee64 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/BreakpointSpans.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/BreakpointSpans.cs @@ -803,7 +803,7 @@ internal static TextSpan GetEnvelope(SyntaxNode declaration) if (declaration is CompilationUnitSyntax unit && unit.ContainsTopLevelStatements()) { - return TextSpan.FromBounds(unit.Members[0].SpanStart, unit.Members[^1].Span.End); + return TextSpan.FromBounds(unit.Members[0].SpanStart, unit.Members.OfType().Last().Span.End); } var body = SyntaxUtilities.TryGetMethodDeclarationBody(declaration); diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index 45f1bc575e87d..98da5eecbc062 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -177,7 +177,7 @@ protected override ImmutableArray GetCapturedVariables(SemanticModel mo { if (memberBody is CompilationUnitSyntax unit && unit.ContainsTopLevelStatements()) { - return model.AnalyzeDataFlow(((GlobalStatementSyntax)unit.Members[0]).Statement, ((GlobalStatementSyntax)unit.Members[^1]).Statement)!.Captured; + return model.AnalyzeDataFlow(((GlobalStatementSyntax)unit.Members[0]).Statement, unit.Members.OfType().Last().Statement)!.Captured; } Debug.Assert(memberBody.IsKind(SyntaxKind.Block) || memberBody is ExpressionSyntax); @@ -2242,7 +2242,7 @@ private void ClassifyInsert(SyntaxNode node) case SyntaxKind.GlobalStatement: // Global statements can be inserted but only if there is at least one other global statement, // otherwise this would be an insert of the synthesized entry point itself. - if ((node.Parent as CompilationUnitSyntax)?.Members.Count == 1) + if ((node.Parent as CompilationUnitSyntax)?.Members.OfType().Take(2).Count() == 1) { ReportError(RudeEditKind.Insert); return; @@ -2339,7 +2339,7 @@ private void ClassifyDelete(SyntaxNode oldNode) case SyntaxKind.GlobalStatement: // Global statements can be deleted unless they were the only global statement, // otherwise it would be a delete of the synthesized entry point itself - if ((oldNode.Parent as CompilationUnitSyntax)?.Members.Count == 1) + if ((oldNode.Parent as CompilationUnitSyntax)?.Members.OfType().Take(2).Count() == 1) { ReportError(RudeEditKind.Delete); return; From 49a737330ffb83e7aeace5eb427006060801c241 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 15 Jun 2021 17:28:46 +1000 Subject: [PATCH 13/28] Ignore declarations after global statements --- .../EditAndContinue/TopLevelEditingTests.cs | 49 +++++++++++++++++++ .../CSharpEditAndContinueAnalyzer.cs | 8 ++- .../EditAndContinue/SyntaxComparer.cs | 11 +++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index 2957164769567..d8466f05c1315 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -13636,6 +13636,43 @@ int Goo() Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement)); } + [Fact] + public void TopLevelStatements_IntToVoid4() + { + var src1 = @" +using System; + +Console.Write(1); +return 1; + +public class C +{ + public int Goo() + { + return 1; + } +} +"; + var src2 = @" +using System; + +Console.Write(1); + +public class C +{ + public int Goo() + { + return 1; + } +} +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement)); + } + [Fact] public void TopLevelStatements_TaskToVoid() { @@ -13749,6 +13786,8 @@ public void TopLevelStatements_WithLambda_Update() Func> b = () => () => { return 1; }; Console.WriteLine(1); + +public class C { } "; var src2 = @" using System; @@ -13757,6 +13796,8 @@ public void TopLevelStatements_WithLambda_Update() Func> b = () => () => { return 1; }; Console.WriteLine(2); + +public class C { } "; var edits = GetTopEdits(src1, src2); var syntaxMap = GetSyntaxMap(src1, src2); @@ -13776,12 +13817,16 @@ public void TopLevelStatements_WithLambda_Delete() Func> b = () => () => { return 1; }; Console.WriteLine(1); + +public class C { } "; var src2 = @" using System; Func a = () => { return 1; }; Func> b = () => () => { return 1; }; + +public class C { } "; var edits = GetTopEdits(src1, src2); var syntaxMap = GetSyntaxMap(src1, src2); @@ -13799,12 +13844,16 @@ public void TopLevelStatements_UpdateMultiple() Console.WriteLine(1); Console.WriteLine(2); + +public class C { } "; var src2 = @" using System; Console.WriteLine(3); Console.WriteLine(4); + +public class C { } "; var edits = GetTopEdits(src1, src2); diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index 98da5eecbc062..289db3ba193e8 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -2600,7 +2600,7 @@ private void ClassifyUpdate(GlobalStatementSyntax? oldNode, GlobalStatementSynta case SyntaxKind.AwaitExpression: // If we found an await expression, there must be one in the other root // or it would mean a change of return type from non-Task to Task (or vice versa) - if (!otherCompilationUnit.DescendantNodesAndSelf(LambdaUtilities.IsNotLambda).OfType().Any()) + if (!otherCompilationUnit.DescendantNodesAndSelf(IsNotLambdaOrDeclaration).OfType().Any()) { ReportError(RudeEditKind.TypeUpdate, newNode, newNode ?? oldNode); return; @@ -2611,7 +2611,7 @@ private void ClassifyUpdate(GlobalStatementSyntax? oldNode, GlobalStatementSynta // if we found a return statement with a non-null expression, there must be one in the other root // or it would mean a change of return type from void or Task to int or Task (or vice versa) var returnExpression = (ReturnStatementSyntax)node; - if (!otherCompilationUnit.DescendantNodesAndSelf(LambdaUtilities.IsNotLambda).OfType().Where(r => (r.Expression == null) == (returnExpression.Expression == null)).Any()) + if (!otherCompilationUnit.DescendantNodesAndSelf(IsNotLambdaOrDeclaration).OfType().Where(r => (r.Expression == null) == (returnExpression.Expression == null)).Any()) { ReportError(RudeEditKind.TypeUpdate, newNode, newNode ?? oldNode); return; @@ -2619,6 +2619,10 @@ private void ClassifyUpdate(GlobalStatementSyntax? oldNode, GlobalStatementSynta break; } } + + // There could be namespace or type declarations after the global statements and we don't care about what is in those + bool IsNotLambdaOrDeclaration(SyntaxNode node) + => node is not MemberDeclarationSyntax && LambdaUtilities.IsNotLambda(node); } private void ClassifyUpdate(NamespaceDeclarationSyntax oldNode, NamespaceDeclarationSyntax newNode) diff --git a/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs b/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs index 74d3daa716f73..a6831a80a2cc1 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs @@ -319,6 +319,17 @@ private static Label ClassifyStatementSyntax(SyntaxKind kind, SyntaxNode? node, // Expressions are ignored but they may contain nodes that should be matched by tree comparer. // (e.g. lambdas, declaration expressions). Descending to these nodes is handled in EnumerateChildren. + case SyntaxKind.NamespaceDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.StructDeclaration: + case SyntaxKind.RecordDeclaration: + case SyntaxKind.RecordStructDeclaration: + // These declarations can come after global statements so we want to stop statement matching + // because no global statements can come after them + isLeaf = true; + return Label.Ignored; + case SyntaxKind.LocalDeclarationStatement: return Label.LocalDeclarationStatement; From d8dd4aad2c1656f2ca4c4dcfb09bd5b19a976f97 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Thu, 24 Jun 2021 14:57:30 +1000 Subject: [PATCH 14/28] Minor PR feedback --- .../Emit/Emit/EditAndContinue/EditAndContinueTests.cs | 10 +++++----- .../EditAndContinue/StatementEditingTests.cs | 2 +- .../EditAndContinue/CSharpEditAndContinueAnalyzer.cs | 4 +++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index 942445cbdbf38..093869f6c0844 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -10779,13 +10779,13 @@ record R(int X) [Fact] public void TopLevelStatement_Update() { - var source0 = - @"using System; + var source0 = @" +using System; Console.WriteLine(""Hello""); "; - var source1 = - @"using System; + var source1 = @" +using System; Console.WriteLine(""Hello World""); "; @@ -10793,6 +10793,7 @@ public void TopLevelStatement_Update() var compilation1 = compilation0.WithSource(source1); var method0 = compilation0.GetMember("$.
$"); + var method1 = compilation1.GetMember("$.
$"); // Verify full metadata contains expected rows. var bytes0 = compilation0.EmitToArray(); @@ -10806,7 +10807,6 @@ public void TopLevelStatement_Update() var generation0 = EmitBaseline.CreateInitialBaseline( md0, EmptyLocalsProvider); - var method1 = compilation1.GetMember("$.
$"); var diff1 = compilation1.EmitDifference( generation0, diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs index 0130cabaddc1e..1f6f509bbca39 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs @@ -10581,7 +10581,7 @@ public void TopLevelStatement_CaptureArgs() } [Fact, WorkItem(21499, "https://github.com/dotnet/roslyn/issues/21499")] - public void TopLevelStatement_UpdateLocalFunction_ForEach1() + public void TopLevelStatement_InsertMultiScopeCapture() { var src1 = @" using System; diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index 289db3ba193e8..9c78b2a58a834 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -151,6 +151,7 @@ internal override bool TryFindMemberDeclaration(SyntaxNode? root, SyntaxNode nod /// - for method-like member declarations with block bodies (methods, operators, constructors, destructors, accessors). /// - for variable declarators of fields, properties with an initializer expression, or /// for method-like member declarations with expression bodies (methods, properties, indexers, operators) + /// - for top level statements /// /// A null reference otherwise. /// @@ -160,7 +161,8 @@ internal override bool TryFindMemberDeclaration(SyntaxNode? root, SyntaxNode nod { return variableDeclarator.Initializer?.Value; } - else if (node is CompilationUnitSyntax unit && unit.ContainsTopLevelStatements()) + + if (node is CompilationUnitSyntax unit && unit.ContainsTopLevelStatements()) { // For top level statements, where there is no syntax node to represent the entire body of the synthesized // main method we just use the compilation unit itself From 4525937220ee8fb62f2dc5ed13d0865311851c58 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Thu, 24 Jun 2021 16:08:44 +1000 Subject: [PATCH 15/28] Add tests for adding/removing awaits --- .../EditAndContinue/TopLevelEditingTests.cs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index d8466f05c1315..32d5661c4c779 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -13484,6 +13484,48 @@ int Goo() Diagnostic(RudeEditKind.TypeUpdate, "return 1;", CSharpFeaturesResources.global_statement)); } + [Fact] + public void TopLevelStatements_AddAwait() + { + var src1 = @" +using System.Threading.Tasks; + +await Task.Delay(100); +"; + var src2 = @" +using System.Threading.Tasks; + +await Task.Delay(100); +await Task.Delay(200); +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "await", CSharpFeaturesResources.await_expression)); + } + + [Fact] + public void TopLevelStatements_DeleteAwait() + { + var src1 = @" +using System.Threading.Tasks; + +await Task.Delay(100); +await Task.Delay(200); +"; + var src2 = @" +using System.Threading.Tasks; + +await Task.Delay(100); +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, null, CSharpFeaturesResources.await_expression)); + } + [Fact] public void TopLevelStatements_VoidToTask() { From fca8a7776e7f11169e3e7dd5c706c2de0017bf26 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Thu, 24 Jun 2021 17:32:20 +1000 Subject: [PATCH 16/28] Move return type checking to semantics --- .../CSharpEditAndContinueAnalyzer.cs | 57 +++---------------- .../AbstractEditAndContinueAnalyzer.cs | 20 +++++++ 2 files changed, 29 insertions(+), 48 deletions(-) diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index 9c78b2a58a834..a9155004cad85 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -1450,6 +1450,11 @@ private static bool GroupBySignatureComparer(ImmutableArray ol switch (kind) { case SyntaxKind.CompilationUnit: + if (editKind == EditKind.Delete) + { + // When deleting something from a compilation unit we just report diagnostics for the last global statment + return ((CompilationUnitSyntax)node).Members.OfType().LastOrDefault()?.Span; + } return default(TextSpan); case SyntaxKind.GlobalStatement: @@ -2251,7 +2256,7 @@ private void ClassifyInsert(SyntaxNode node) } // An insert of a global statement is actually an update to the synthesized main so we need to check some extra things - ClassifyUpdate(oldNode: null, (GlobalStatementSyntax)node); + ClassifyUpdate((GlobalStatementSyntax)node); return; case SyntaxKind.ExternAliasDirective: @@ -2347,8 +2352,6 @@ private void ClassifyDelete(SyntaxNode oldNode) return; } - // A delete of a global statement is actually an update to the synthesized main so we need to check some extra things - ClassifyUpdate((GlobalStatementSyntax)oldNode, newNode: null); return; case SyntaxKind.ExternAliasDirective: @@ -2451,7 +2454,7 @@ private void ClassifyUpdate(SyntaxNode oldNode, SyntaxNode newNode) switch (newNode.Kind()) { case SyntaxKind.GlobalStatement: - ClassifyUpdate((GlobalStatementSyntax)oldNode, (GlobalStatementSyntax)newNode); + ClassifyUpdate((GlobalStatementSyntax)newNode); return; case SyntaxKind.ExternAliasDirective: @@ -2580,51 +2583,9 @@ private void ClassifyUpdate(SyntaxNode oldNode, SyntaxNode newNode) } } - private void ClassifyUpdate(GlobalStatementSyntax? oldNode, GlobalStatementSyntax? newNode) + private void ClassifyUpdate(GlobalStatementSyntax node) { - if (newNode is not null) - { - ClassifyDeclarationBodyRudeUpdates(newNode.Statement); - } - - var otherCompilationUnit = newNode is null ? _match?.NewRoot as CompilationUnitSyntax : _match?.OldRoot as CompilationUnitSyntax; - var nodeToAnalyze = newNode ?? oldNode; - - Debug.Assert(otherCompilationUnit is not null); - Debug.Assert(nodeToAnalyze is not null); - - // Any change to a global statement could mean the synthesized main method changes return type - // from void to int, Task or Task (or vice versa), so we might need to issue a TypeUpdate rude edit. - foreach (var node in nodeToAnalyze.DescendantNodesAndSelf(LambdaUtilities.IsNotLambda)) - { - switch (node.Kind()) - { - case SyntaxKind.AwaitExpression: - // If we found an await expression, there must be one in the other root - // or it would mean a change of return type from non-Task to Task (or vice versa) - if (!otherCompilationUnit.DescendantNodesAndSelf(IsNotLambdaOrDeclaration).OfType().Any()) - { - ReportError(RudeEditKind.TypeUpdate, newNode, newNode ?? oldNode); - return; - } - break; - case SyntaxKind.ReturnStatement: - // If we found a return statement with a null expression, there must be one in the other root, or - // if we found a return statement with a non-null expression, there must be one in the other root - // or it would mean a change of return type from void or Task to int or Task (or vice versa) - var returnExpression = (ReturnStatementSyntax)node; - if (!otherCompilationUnit.DescendantNodesAndSelf(IsNotLambdaOrDeclaration).OfType().Where(r => (r.Expression == null) == (returnExpression.Expression == null)).Any()) - { - ReportError(RudeEditKind.TypeUpdate, newNode, newNode ?? oldNode); - return; - } - break; - } - } - - // There could be namespace or type declarations after the global statements and we don't care about what is in those - bool IsNotLambdaOrDeclaration(SyntaxNode node) - => node is not MemberDeclarationSyntax && LambdaUtilities.IsNotLambda(node); + ClassifyDeclarationBodyRudeUpdates(node.Statement); } private void ClassifyUpdate(NamespaceDeclarationSyntax oldNode, NamespaceDeclarationSyntax newNode) diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index f9e0e220d272d..3eb42a38200bb 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -2489,6 +2489,11 @@ private async Task> AnalyzeSemanticsAsync( Contract.ThrowIfTrue(oldDeclaration == null); Contract.ThrowIfFalse(newDeclaration == null); newDeclaration = GetSymbolDeclarationSyntax(newSymbol.DeclaringSyntaxReferences[0], cancellationToken); + + // The symbols here are the compiler generated Main method so we don't have nodes to report the edit for + // so we'll just use the last global statement in the file + ReportMethodDeclarationRudeEdits((IMethodSymbol)oldSymbol, (IMethodSymbol)newSymbol, GetDiagnosticSpan(newDeclaration, EditKind.Delete), diagnostics); + var oldBody = oldDeclaration; var newBody = newDeclaration; @@ -2685,6 +2690,8 @@ private async Task> AnalyzeSemanticsAsync( } else if (IsGlobalStatement(edit.NewNode)) { + ReportMethodDeclarationRudeEdits((IMethodSymbol)oldSymbol, (IMethodSymbol)newSymbol, GetDiagnosticSpan(edit.NewNode, edit.Kind), diagnostics); + // An insert of a global statement, when oldSymbol isn't null, is an update to the implicit Main method editKind = SemanticEditKind.Update; @@ -2924,6 +2931,11 @@ private async Task> AnalyzeSemanticsAsync( continue; } + if (IsGlobalStatement(edit.NewNode)) + { + ReportMethodDeclarationRudeEdits((IMethodSymbol)oldSymbol, (IMethodSymbol)newSymbol, GetDiagnosticSpan(edit.NewNode, edit.Kind), diagnostics); + } + editKind = SemanticEditKind.Update; syntaxMap = null; @@ -3128,6 +3140,14 @@ private async Task> AnalyzeSemanticsAsync( } } + private static void ReportMethodDeclarationRudeEdits(IMethodSymbol oldSymbol, IMethodSymbol newSymbol, TextSpan diagnosticSpan, ArrayBuilder diagnostics) + { + if (!SymbolEqualityComparer.Default.Equals(oldSymbol.ReturnType, newSymbol.ReturnType)) + { + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.TypeUpdate, diagnosticSpan)); + } + } + private static bool CanAddNewMember(ISymbol newSymbol, EditAndContinueCapabilities capabilities) { if (newSymbol is IMethodSymbol or IPropertySymbol) // Properties are just get_ and set_ methods From a9c6ce4f8866f2d925ff55fede5fe4d92eed2cbd Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 25 Jun 2021 07:04:20 +1000 Subject: [PATCH 17/28] Add new rude edit --- .../Test/EditAndContinue/RudeEditDiagnosticTests.cs | 3 ++- .../EditAndContinue/EditAndContinueDiagnosticDescriptors.cs | 2 ++ src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs | 4 +++- src/Features/Core/Portable/FeaturesResources.resx | 3 +++ src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf | 5 +++++ src/Features/Core/Portable/xlf/FeaturesResources.de.xlf | 5 +++++ src/Features/Core/Portable/xlf/FeaturesResources.es.xlf | 5 +++++ src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf | 5 +++++ src/Features/Core/Portable/xlf/FeaturesResources.it.xlf | 5 +++++ src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf | 5 +++++ src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf | 5 +++++ src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf | 5 +++++ src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf | 5 +++++ src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf | 5 +++++ src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf | 5 +++++ src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf | 5 +++++ src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf | 5 +++++ 17 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs b/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs index f46977f745bb9..ea38546afd6a0 100644 --- a/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs @@ -53,7 +53,8 @@ public void ToDiagnostic() RudeEditKind.DeleteRecordPositionalParameter, RudeEditKind.NotSupportedByRuntime, RudeEditKind.MakeMethodAsync, - RudeEditKind.MakeMethodIterator + RudeEditKind.MakeMethodIterator, + RudeEditKind.ChangeImplicitMainReturnType }; var arg2 = new HashSet() diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs index 32c79d77a897f..315555f0d9c59 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs @@ -155,6 +155,8 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.InsertNotSupportedByRuntime, nameof(FeaturesResources.Adding_0_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.ChangingAttributesNotSupportedByRuntime, nameof(FeaturesResources.Updating_the_attributes_of_0_is_not_supported_by_the_runtime)); + AddRudeEdit(RudeEditKind.ChangeImplicitMainReturnType, FeaturesResources.An_update_that_causes_the_return_type_of_implicit_main_to_change_will_prevent_the_debug_session_from_contuing); + // 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/RudeEditKind.cs b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs index 98c571f9c6802..513a6d7423d20 100644 --- a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs +++ b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs @@ -128,6 +128,8 @@ internal enum RudeEditKind : ushort MakeMethodAsync = 98, MakeMethodIterator = 99, InsertNotSupportedByRuntime = 100, - ChangingAttributesNotSupportedByRuntime = 101 + ChangingAttributesNotSupportedByRuntime = 101, + + ChangeImplicitMainReturnType = 102 } } diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index a1e7d9653e735..51b66e63d0414 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -2888,4 +2888,7 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Updating the attributes of '{0}' is not supported by the runtime. + + An update that causes the return type of the implicit Main method to change 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 1df883b850441..29bf605412e1f 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -140,6 +140,11 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Podmínky alternativního výrazu nezachytávají a nejde je pojmenovat. This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Použít předvolby hlaviček souborů diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index fb78e23e5bce2..1549bdcc412fa 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -140,6 +140,11 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Wechselbedingungen werden nicht erfasst und können nicht benannt werden. This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Dateiheadereinstellungen anwenden diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index 50a5fd50630e5..cdc1e10ae76f6 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -140,6 +140,11 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Las condiciones de alternancia no se captan y se les puede poner un nombre This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Aplicar preferencias de encabezado de archivo diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index f88bebc705fb0..060357282e8dd 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -140,6 +140,11 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Les conditions d'alternance n'effectuent pas de capture et ne peuvent pas être nommées This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Appliquer les préférences d'en-tête de fichier diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index b3debbd02411a..e9efa19b5f299 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -140,6 +140,11 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Le condizioni di alternanza non consentono l'acquisizione e non possono essere denominate This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Applica le preferenze relative alle intestazioni di file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 670e4fa05740a..764bbb430d40c 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -140,6 +140,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 選択条件が捕捉されないため、名前を指定できません This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences ファイル ヘッダーの基本設定を適用する diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index 66b63e7857464..7d3d86df5dab5 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -140,6 +140,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 교체 조건은 캡처하지 않고 이름을 지정할 수 없습니다. This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences 파일 헤더 기본 설정 적용 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 53e1ce23a705a..5cfcd09bd9514 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -140,6 +140,11 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Warunki alternatywne nie przechwytują i nie mogą być nazywane This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Zastosuj preferencje nagłówka pliku diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index 379852007521a..1ea639a73cf2a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -140,6 +140,11 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Condições de alternância não capturam e não podem ser nomeadas This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Aplicar as preferências de cabeçalho de arquivo diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 7692686a359ca..a2764d27c1ba6 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -140,6 +140,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Условия чередования не выполняют запись, и им невозможно присвоить имя This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Применить параметры заголовка файла diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 77600d5869695..a96fc55b30559 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -140,6 +140,11 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Değişim koşulları yakalamak değil ve adlandırılamaz This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Dosya üst bilgisi tercihlerini uygula diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index cfefe8521ca2e..97ddc25fc1126 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -140,6 +140,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 替换条件不捕获且不能命名 This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences 应用文件头首选项 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 01fba5962147e..07ace85e029ff 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -140,6 +140,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 替代條件不會擷取,也無法命名 This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences 套用檔案標題的喜好設定 From b6ab5376a605ceffe867d2a03a4873bb199906de Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 25 Jun 2021 07:04:58 +1000 Subject: [PATCH 18/28] Report better diagnostic spans --- .../CSharpEditAndContinueAnalyzer.cs | 16 +++++++++++----- .../AbstractEditAndContinueAnalyzer.cs | 10 ++++++---- .../VisualBasicEditAndContinueAnalyzer.vb | 4 ++++ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index c1b0e1061bf5f..6a72babf05d2c 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -789,6 +789,17 @@ protected override bool TryMatchActiveStatement( protected override bool IsGlobalStatement(SyntaxNode node) => node.IsKind(SyntaxKind.GlobalStatement); + protected override TextSpan GetGlobalStatementDiagnosticSpan(SyntaxNode node) + { + if (node is CompilationUnitSyntax unit) + { + // When deleting something from a compilation unit we just report diagnostics for the last global statment + return unit.Members.OfType().LastOrDefault()?.Span ?? default; + } + + return GetDiagnosticSpan(node, EditKind.Delete); + } + protected override string LineDirectiveKeyword => "line"; @@ -1474,11 +1485,6 @@ private static bool GroupBySignatureComparer(ImmutableArray ol switch (kind) { case SyntaxKind.CompilationUnit: - if (editKind == EditKind.Delete) - { - // When deleting something from a compilation unit we just report diagnostics for the last global statment - return ((CompilationUnitSyntax)node).Members.OfType().LastOrDefault()?.Span; - } return default(TextSpan); case SyntaxKind.GlobalStatement: diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index caf67ebf9e800..e43f3c3b09647 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -262,6 +262,8 @@ protected virtual bool StateMachineSuspensionPointKindEquals(SyntaxNode suspensi protected abstract bool IsGlobalStatement(SyntaxNode node); + protected abstract TextSpan GetGlobalStatementDiagnosticSpan(SyntaxNode node); + /// /// Returns all symbols associated with an edit. /// Returns an empty set if the edit is not associated with any symbols. @@ -2492,7 +2494,7 @@ private async Task> AnalyzeSemanticsAsync( // The symbols here are the compiler generated Main method so we don't have nodes to report the edit for // so we'll just use the last global statement in the file - ReportMethodDeclarationRudeEdits((IMethodSymbol)oldSymbol, (IMethodSymbol)newSymbol, GetDiagnosticSpan(newDeclaration, EditKind.Delete), diagnostics); + ReportMethodDeclarationRudeEdits((IMethodSymbol)oldSymbol, (IMethodSymbol)newSymbol, GetGlobalStatementDiagnosticSpan(newDeclaration), diagnostics); var oldBody = oldDeclaration; var newBody = newDeclaration; @@ -2690,7 +2692,7 @@ private async Task> AnalyzeSemanticsAsync( } else if (IsGlobalStatement(edit.NewNode)) { - ReportMethodDeclarationRudeEdits((IMethodSymbol)oldSymbol, (IMethodSymbol)newSymbol, GetDiagnosticSpan(edit.NewNode, edit.Kind), diagnostics); + ReportMethodDeclarationRudeEdits((IMethodSymbol)oldSymbol, (IMethodSymbol)newSymbol, GetGlobalStatementDiagnosticSpan(edit.NewNode), diagnostics); // An insert of a global statement, when oldSymbol isn't null, is an update to the implicit Main method editKind = SemanticEditKind.Update; @@ -2933,7 +2935,7 @@ private async Task> AnalyzeSemanticsAsync( if (IsGlobalStatement(edit.NewNode)) { - ReportMethodDeclarationRudeEdits((IMethodSymbol)oldSymbol, (IMethodSymbol)newSymbol, GetDiagnosticSpan(edit.NewNode, edit.Kind), diagnostics); + ReportMethodDeclarationRudeEdits((IMethodSymbol)oldSymbol, (IMethodSymbol)newSymbol, GetGlobalStatementDiagnosticSpan(edit.NewNode), diagnostics); } editKind = SemanticEditKind.Update; @@ -3145,7 +3147,7 @@ private static void ReportMethodDeclarationRudeEdits(IMethodSymbol oldSymbol, IM { if (!SymbolEqualityComparer.Default.Equals(oldSymbol.ReturnType, newSymbol.ReturnType)) { - diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.TypeUpdate, diagnosticSpan)); + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.ChangeImplicitMainReturnType, diagnosticSpan)); } } diff --git a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb index de4744ed10a7c..1e82ad72b44fb 100644 --- a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb +++ b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb @@ -803,6 +803,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return False End Function + Protected Overrides Function GetGlobalStatementDiagnosticSpan(node As SyntaxNode) As TextSpan + Return Nothing + End Function + Protected Overrides ReadOnly Property LineDirectiveKeyword As String Get Return "ExternalSource" From 3789ba9c1cbec788fc9adb9010d58a0aa6059bc6 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 25 Jun 2021 07:05:08 +1000 Subject: [PATCH 19/28] Update tests --- .../EditAndContinue/TopLevelEditingTests.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index 61dd56cb6fbd8..fdf299b15e927 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -14052,7 +14052,7 @@ public void TopLevelStatements_VoidToInt1() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "return 1;", CSharpFeaturesResources.global_statement)); + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return 1;")); } [Fact] @@ -14075,7 +14075,7 @@ public void TopLevelStatements_VoidToInt2() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "return 1;", CSharpFeaturesResources.global_statement)); + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return 1;")); } [Fact] @@ -14106,7 +14106,7 @@ int Goo() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "return 1;", CSharpFeaturesResources.global_statement)); + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return 1;")); } [Fact] @@ -14171,7 +14171,7 @@ public void TopLevelStatements_VoidToTask() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "await Task.Delay(100);", CSharpFeaturesResources.global_statement)); + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "await Task.Delay(100);")); } [Fact] @@ -14196,7 +14196,7 @@ public void TopLevelStatements_TaskToTaskInt() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "return 1;", CSharpFeaturesResources.global_statement)); + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return 1;")); } [Fact] @@ -14224,7 +14224,7 @@ Task GetInt() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "return await GetInt();", CSharpFeaturesResources.global_statement)); + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return await GetInt();")); } [Fact] @@ -14246,7 +14246,7 @@ public void TopLevelStatements_IntToVoid1() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement)); + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);")); } [Fact] @@ -14269,7 +14269,7 @@ public void TopLevelStatements_IntToVoid2() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "return;", CSharpFeaturesResources.global_statement)); + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return;")); } [Fact] @@ -14300,7 +14300,7 @@ int Goo() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement)); + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "int Goo()\r\n{\r\n return 1;\r\n}")); } [Fact] @@ -14337,7 +14337,7 @@ public int Goo() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement)); + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);")); } [Fact] @@ -14360,7 +14360,7 @@ public void TopLevelStatements_TaskToVoid() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement), + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);"), Diagnostic(RudeEditKind.Delete, null, CSharpFeaturesResources.await_expression)); } @@ -14386,7 +14386,7 @@ public void TopLevelStatements_TaskIntToTask() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement)); + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);")); } [Fact] @@ -14414,7 +14414,7 @@ Task GetInt() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, null, CSharpFeaturesResources.global_statement), + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);"), Diagnostic(RudeEditKind.Delete, null, CSharpFeaturesResources.await_expression)); } From 27e63f899dd3fdfc036c97232729c1bf31f0d485 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 25 Jun 2021 10:54:19 +1000 Subject: [PATCH 20/28] Allow moving global statements from one file to another --- .../EditAndContinue/TopLevelEditingTests.cs | 44 ++++++++++++++++++- .../CSharpEditAndContinueAnalyzer.cs | 16 ------- .../AbstractEditAndContinueAnalyzer.cs | 24 ++++++++-- 3 files changed, 63 insertions(+), 21 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index fdf299b15e927..315f2c006e9de 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -13958,7 +13958,7 @@ public void TopLevelStatements_Insert_NoImplicitMain() edits.VerifyEdits("Insert [Console.WriteLine(\"Hello World\");]@19"); - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Insert, "Console.WriteLine(\"Hello World\");", CSharpFeaturesResources.global_statement)); + edits.VerifySemantics(SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("$.
$"))); } [Fact] @@ -14529,6 +14529,48 @@ public class C { } edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"))); } + [Fact] + public void TopLevelStatements_MoveToOtherFile() + { + var srcA1 = @" +using System; + +Console.WriteLine(1); + +public class A +{ +}"; + var srcB1 = @" +using System; + +public class B +{ +}"; + + var srcA2 = @" +using System; + +public class A +{ +}"; + var srcB2 = @" +using System; + +Console.WriteLine(2); + +public class B +{ +}"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults(semanticEdits: new [] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$")) }), + }); + } + #endregion } } diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index 6a72babf05d2c..572ead7eee66f 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -2277,14 +2277,6 @@ private void ClassifyInsert(SyntaxNode node) switch (node.Kind()) { case SyntaxKind.GlobalStatement: - // Global statements can be inserted but only if there is at least one other global statement, - // otherwise this would be an insert of the synthesized entry point itself. - if ((node.Parent as CompilationUnitSyntax)?.Members.OfType().Take(2).Count() == 1) - { - ReportError(RudeEditKind.Insert); - return; - } - // An insert of a global statement is actually an update to the synthesized main so we need to check some extra things ClassifyUpdate((GlobalStatementSyntax)node); return; @@ -2387,14 +2379,6 @@ private void ClassifyDelete(SyntaxNode oldNode) switch (oldNode.Kind()) { case SyntaxKind.GlobalStatement: - // Global statements can be deleted unless they were the only global statement, - // otherwise it would be a delete of the synthesized entry point itself - if ((oldNode.Parent as CompilationUnitSyntax)?.Members.OfType().Take(2).Count() == 1) - { - ReportError(RudeEditKind.Delete); - return; - } - return; case SyntaxKind.ExternAliasDirective: diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index e43f3c3b09647..64da2dddeb98e 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -2492,6 +2492,13 @@ private async Task> AnalyzeSemanticsAsync( Contract.ThrowIfFalse(newDeclaration == null); newDeclaration = GetSymbolDeclarationSyntax(newSymbol.DeclaringSyntaxReferences[0], cancellationToken); + // If all global statements are moved from one file to another then we'll get insert edits for them + // so we don't need to do anything for the deletes. + if (newDeclaration.SyntaxTree != newModel.SyntaxTree) + { + continue; + } + // The symbols here are the compiler generated Main method so we don't have nodes to report the edit for // so we'll just use the last global statement in the file ReportMethodDeclarationRudeEdits((IMethodSymbol)oldSymbol, (IMethodSymbol)newSymbol, GetGlobalStatementDiagnosticSpan(newDeclaration), diagnostics); @@ -2567,6 +2574,15 @@ private async Task> AnalyzeSemanticsAsync( } else { + var diagnosticSpan = GetDeletedNodeDiagnosticSpan(editScript.Match.Matches, oldDeclaration); + + // If we got here for a global statement then the actual edit is a delete of the synthesized Main method + if (IsGlobalStatement(edit.OldNode)) + { + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.Delete, diagnosticSpan, edit.OldNode, new[] { GetDisplayName(edit.OldNode, EditKind.Delete) })); + continue; + } + // Check if the symbol being deleted is a member of a type or associated with a property or event that's also being deleted. // If so, skip the member deletion and only report the containing symbol deletion. var oldContainingSymbol = (oldSymbol as IMethodSymbol)?.AssociatedSymbol ?? oldSymbol.ContainingType; @@ -2581,7 +2597,6 @@ private async Task> AnalyzeSemanticsAsync( } // deleting symbol is not allowed - var diagnosticSpan = GetDeletedNodeDiagnosticSpan(editScript.Match.Matches, oldDeclaration); diagnostics.Add(new RudeEditDiagnostic( RudeEditKind.Delete, @@ -2807,7 +2822,7 @@ private async Task> AnalyzeSemanticsAsync( editKind = SemanticEditKind.Update; } } - else if (newSymbol.ContainingType != null) + else if (newSymbol.ContainingType != null && !IsGlobalStatement(edit.NewNode)) { // The edit actually adds a new symbol into an existing or a new type. @@ -2857,8 +2872,9 @@ private async Task> AnalyzeSemanticsAsync( } else { - // adds a new top-level type - Contract.ThrowIfFalse(newSymbol is INamedTypeSymbol); + // adds a new top-level type, or a global statement where none existed before, which is + // therefore inserting the $ type + Contract.ThrowIfFalse(newSymbol is INamedTypeSymbol || IsGlobalStatement(edit.NewNode)); if (!capabilities.HasFlag(EditAndContinueCapabilities.NewTypeDefinition)) { From 19de225f5958e3e3bbe931af20b84595269c95cd Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 25 Jun 2021 17:12:59 +1000 Subject: [PATCH 21/28] Basic code flow tests --- .../FlowAnalysis/RegionAnalysisTests.cs | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs index a29e0821f920e..8a844ab78d74b 100644 --- a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs @@ -8574,6 +8574,88 @@ static void Local() Assert.Equal("Local, Sub", GetSymbolNamesJoined(results.UsedLocalFunctions)); } + #endregion + + #region "Top level statements" + + [Fact] + public void TestTopLevelStatements() + { + var analysisResults = CompileAndAnalyzeControlAndDataFlowStatements(@" +using System; +using System.Linq; + +/**/Console.Write(1);/**/ +"); + var controlFlowAnalysisResults = analysisResults.Item1; + var dataFlowAnalysisResults = analysisResults.Item2; + Assert.Equal(0, controlFlowAnalysisResults.EntryPoints.Count()); + Assert.Equal(0, controlFlowAnalysisResults.ExitPoints.Count()); + Assert.True(controlFlowAnalysisResults.EndPointIsReachable); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut)); + Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.DefinitelyAssignedOnEntry)); + Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.DefinitelyAssignedOnExit)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside)); + Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside)); + } + + [Fact] + public void TestTopLevelStatements_Lambda() + { + var analysisResults = CompileAndAnalyzeControlAndDataFlowStatements(@" +using System; +using System.Linq; + +int i = 1; +Func lambda = () => { /**/return i;/**/ }; +"); + var controlFlowAnalysisResults = analysisResults.Item1; + var dataFlowAnalysisResults = analysisResults.Item2; + Assert.Equal(0, controlFlowAnalysisResults.EntryPoints.Count()); + Assert.Equal(1, controlFlowAnalysisResults.ExitPoints.Count()); + Assert.False(controlFlowAnalysisResults.EndPointIsReachable); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared)); + Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut)); + Assert.Equal("i, args", GetSymbolNamesJoined(dataFlowAnalysisResults.DefinitelyAssignedOnEntry)); + Assert.Equal("i, args", GetSymbolNamesJoined(dataFlowAnalysisResults.DefinitelyAssignedOnExit)); + Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside)); + Assert.Equal("i, lambda, args", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside)); + } + + + [Fact] + public void TestTopLevelStatements_LambdaCapturingArgs() + { + var analysisResults = CompileAndAnalyzeControlAndDataFlowStatements(@" +using System; +using System.Linq; + +Func lambda = () => { /**/return args.Length;/**/ }; +"); + var controlFlowAnalysisResults = analysisResults.Item1; + var dataFlowAnalysisResults = analysisResults.Item2; + Assert.Equal(0, controlFlowAnalysisResults.EntryPoints.Count()); + Assert.Equal(1, controlFlowAnalysisResults.ExitPoints.Count()); + Assert.False(controlFlowAnalysisResults.EndPointIsReachable); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared)); + Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut)); + Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.DefinitelyAssignedOnEntry)); + Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.DefinitelyAssignedOnExit)); + Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside)); + Assert.Equal("lambda, args", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside)); + } + + #endregion } } From 774b1be17fcc6b9a0031273b52cd1925f7065732 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 2 Jul 2021 11:56:38 +1000 Subject: [PATCH 22/28] Validate global statements are part of the same compilation unit --- .../Portable/Compilation/SyntaxTreeSemanticModel.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index be394e2516532..34494f83aeb3e 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -2226,10 +2226,14 @@ private void ValidateStatementRange(StatementSyntax firstStatement, StatementSyn throw new ArgumentException("statements not within tree"); } - if ((firstStatement.Parent == null || firstStatement.Parent != lastStatement.Parent) && - // Special case for global statements, since they have no common parent that is a statement syntax - firstStatement.Parent is not GlobalStatementSyntax && - lastStatement.Parent is not GlobalStatementSyntax) + // Global statements don't have their parent in common, but should belong to the same compilation unit + // For everything else, the parents should be the same + if (firstStatement.Parent is GlobalStatementSyntax && + (lastStatement.Parent is not GlobalStatementSyntax || firstStatement.Parent.Parent != lastStatement.Parent.Parent)) + { + throw new ArgumentException("global statements not within the same compilation unit"); + } + else if (firstStatement.Parent == null || firstStatement.Parent != lastStatement.Parent) { throw new ArgumentException("statements not within the same statement list"); } From afc1ca7ab9337dfd016bd3fa0c9450723583b8d1 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Sun, 4 Jul 2021 17:17:35 +1000 Subject: [PATCH 23/28] Fix condition --- .../CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 34494f83aeb3e..01a19d35de78c 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -2228,12 +2228,14 @@ private void ValidateStatementRange(StatementSyntax firstStatement, StatementSyn // Global statements don't have their parent in common, but should belong to the same compilation unit // For everything else, the parents should be the same - if (firstStatement.Parent is GlobalStatementSyntax && + bool isGlobalStatement = firstStatement.Parent is GlobalStatementSyntax; + if (isGlobalStatement && (lastStatement.Parent is not GlobalStatementSyntax || firstStatement.Parent.Parent != lastStatement.Parent.Parent)) { throw new ArgumentException("global statements not within the same compilation unit"); } - else if (firstStatement.Parent == null || firstStatement.Parent != lastStatement.Parent) + else if (!isGlobalStatement && + (firstStatement.Parent == null || firstStatement.Parent != lastStatement.Parent)) { throw new ArgumentException("statements not within the same statement list"); } From 458e9fe45c644dd9241ccea896a0e6086d7bd2bc Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 5 Jul 2021 08:03:07 +1000 Subject: [PATCH 24/28] Neater fix --- .../Portable/Compilation/SyntaxTreeSemanticModel.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 01a19d35de78c..ddf4ae5f0f022 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -2227,15 +2227,14 @@ private void ValidateStatementRange(StatementSyntax firstStatement, StatementSyn } // Global statements don't have their parent in common, but should belong to the same compilation unit - // For everything else, the parents should be the same bool isGlobalStatement = firstStatement.Parent is GlobalStatementSyntax; - if (isGlobalStatement && - (lastStatement.Parent is not GlobalStatementSyntax || firstStatement.Parent.Parent != lastStatement.Parent.Parent)) + if (isGlobalStatement && (lastStatement.Parent is not GlobalStatementSyntax || firstStatement.Parent.Parent != lastStatement.Parent.Parent)) { throw new ArgumentException("global statements not within the same compilation unit"); } - else if (!isGlobalStatement && - (firstStatement.Parent == null || firstStatement.Parent != lastStatement.Parent)) + + // Non-global statements, the parents should be the same + if (!isGlobalStatement && (firstStatement.Parent == null || firstStatement.Parent != lastStatement.Parent)) { throw new ArgumentException("statements not within the same statement list"); } From f4b31200ccd185d63b327dc950bde7aa6f638bad Mon Sep 17 00:00:00 2001 From: David Wengier Date: Thu, 8 Jul 2021 08:51:10 +1000 Subject: [PATCH 25/28] Fix typos (thanks Chuck!!) --- .../CSharpTest/EditAndContinue/TopLevelEditingTests.cs | 2 +- .../Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs | 2 +- .../EditAndContinue/AbstractEditAndContinueAnalyzer.cs | 4 ++-- .../EditAndContinue/EditAndContinueDiagnosticDescriptors.cs | 2 +- src/Features/Core/Portable/FeaturesResources.resx | 2 +- src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf | 2 +- src/Features/Core/Portable/xlf/FeaturesResources.de.xlf | 2 +- src/Features/Core/Portable/xlf/FeaturesResources.es.xlf | 2 +- src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf | 2 +- src/Features/Core/Portable/xlf/FeaturesResources.it.xlf | 2 +- src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf | 2 +- src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf | 2 +- src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf | 2 +- src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf | 2 +- src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf | 2 +- src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf | 2 +- src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf | 2 +- src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf | 2 +- 18 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index 2a9b8f5ffa7c8..fafe59a470b22 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -15186,7 +15186,7 @@ public class C { } var edits = GetTopEdits(src1, src2); // Since each individual statement is a separate update to a separate node, this just validates we correctly - // only anaylze the things once + // only analyze the things once edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"))); } diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index 02c1d21f2fb2f..d9556afc5e772 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -793,7 +793,7 @@ protected override TextSpan GetGlobalStatementDiagnosticSpan(SyntaxNode node) { if (node is CompilationUnitSyntax unit) { - // When deleting something from a compilation unit we just report diagnostics for the last global statment + // When deleting something from a compilation unit we just report diagnostics for the last global statement return unit.Members.OfType().LastOrDefault()?.Span ?? default; } diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 4e83bfd566c28..8a536f1d98831 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -2591,8 +2591,8 @@ private async Task> AnalyzeSemanticsAsync( if (oldContainingSymbol != null) { var containingSymbolKey = SymbolKey.Create(oldContainingSymbol, cancellationToken); - var newContatiningSymbol = containingSymbolKey.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; - if (newContatiningSymbol == null) + var newContainingSymbol = containingSymbolKey.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + if (newContainingSymbol == null) { continue; } diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs index fbad56c3b38b2..4d3d202825a4d 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs @@ -155,7 +155,7 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.InsertNotSupportedByRuntime, nameof(FeaturesResources.Adding_0_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.ChangingAttributesNotSupportedByRuntime, nameof(FeaturesResources.Updating_the_attributes_of_0_is_not_supported_by_the_runtime)); - AddRudeEdit(RudeEditKind.ChangeImplicitMainReturnType, FeaturesResources.An_update_that_causes_the_return_type_of_implicit_main_to_change_will_prevent_the_debug_session_from_contuing); + AddRudeEdit(RudeEditKind.ChangeImplicitMainReturnType, FeaturesResources.An_update_that_causes_the_return_type_of_implicit_main_to_change_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)); diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 51b66e63d0414..f864c4ec5608d 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -2888,7 +2888,7 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Updating the attributes of '{0}' is not supported by the runtime. - + An update that causes the return type of the implicit Main method to change 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 29bf605412e1f..b1144f0f222b8 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -140,7 +140,7 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Podmínky alternativního výrazu nezachytávají a nejde je pojmenovat. This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) - + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index 1549bdcc412fa..caff483d618c2 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -140,7 +140,7 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Wechselbedingungen werden nicht erfasst und können nicht benannt werden. This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) - + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index cdc1e10ae76f6..5e20feea0f6a5 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -140,7 +140,7 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Las condiciones de alternancia no se captan y se les puede poner un nombre This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) - + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index 060357282e8dd..c234ae0a4f4a5 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -140,7 +140,7 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Les conditions d'alternance n'effectuent pas de capture et ne peuvent pas être nommées This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) - + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index e9efa19b5f299..2102fac157b71 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -140,7 +140,7 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Le condizioni di alternanza non consentono l'acquisizione e non possono essere denominate This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) - + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 764bbb430d40c..f586cfa94d0d6 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -140,7 +140,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 選択条件が捕捉されないため、名前を指定できません This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) - + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index 7d3d86df5dab5..a314787483b26 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -140,7 +140,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 교체 조건은 캡처하지 않고 이름을 지정할 수 없습니다. This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) - + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 5cfcd09bd9514..edd609a8ac006 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -140,7 +140,7 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Warunki alternatywne nie przechwytują i nie mogą być nazywane This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) - + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index 1ea639a73cf2a..1e7801e347d48 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -140,7 +140,7 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Condições de alternância não capturam e não podem ser nomeadas This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) - + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index a2764d27c1ba6..644272a7baae9 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -140,7 +140,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Условия чередования не выполняют запись, и им невозможно присвоить имя This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) - + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index a96fc55b30559..b50e0f8032484 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -140,7 +140,7 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Değişim koşulları yakalamak değil ve adlandırılamaz This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) - + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index 97ddc25fc1126..00bef307d4324 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -140,7 +140,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 替换条件不捕获且不能命名 This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) - + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 07ace85e029ff..30d9682e07a0a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -140,7 +140,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 替代條件不會擷取,也無法命名 This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) - + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. From 2a7b75d243bd49850437bf9a5b2134b38db1b106 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Thu, 8 Jul 2021 10:02:25 +1000 Subject: [PATCH 26/28] PR feedback --- .../CSharpTest/EditAndContinue/StatementEditingTests.cs | 8 +++----- .../EditAndContinue/StatementEditingTests.vb | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs index fce5476cd6dc3..cc9a90f554180 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs @@ -2090,7 +2090,7 @@ void F() "; var edits = GetTopEdits(src1, src2); - // TODO: allow creating a new leaf closure + // TODO: allow creating a new leaf closure: https://github.com/dotnet/roslyn/issues/54672 edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.CapturingVariable, "F", "this")); } @@ -5254,7 +5254,7 @@ void F() "; var edits = GetTopEdits(src1, src2); - // TODO: allow creating a new leaf closure + // TODO: allow creating a new leaf closure: https://github.com/dotnet/roslyn/issues/54672 edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.CapturingVariable, "F", "this")); } @@ -5516,7 +5516,6 @@ void F() int f0(int a) => x0; int f1(int a) => x1; - int f0(int a) => x0; int f2(int a) => x0 + x1; // error: connecting previously disconnected closures } } @@ -10559,7 +10558,7 @@ public void TopLevelStatement_CaptureArgs() "; var edits = GetTopEdits(src1, src2); - // TODO: allow creating a new leaf closure + // TODO: allow creating a new leaf closure: https://github.com/dotnet/roslyn/issues/54672 edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.CapturingVariable, "using System;\r\n\r\nvar x = new Func(() => \"Hello\" + args[0]);\r\n\r\nConsole.WriteLine(x());\r\n", "args")); } @@ -10588,7 +10587,6 @@ public void TopLevelStatement_InsertMultiScopeCapture() int f0(int a) => x0; int f1(int a) => x1; - int f0(int a) => x0; int f2(int a) => x0 + x1; // error: connecting previously disconnected closures } "; diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/StatementEditingTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/StatementEditingTests.vb index 17c7b3e9dfd88..fb6bc5c551a2e 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/StatementEditingTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/StatementEditingTests.vb @@ -2421,7 +2421,7 @@ Class C End Class" Dim edits = GetTopEdits(src1, src2) - ' TODO allow creating a new leaf closure + ' TODO allow creating a new leaf closure: : https://github.com/dotnet/roslyn/issues/54672 edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.CapturingVariable, "F", "Me")) End Sub From 9ef630aff81d7af5917b210eedf1b3aa7cc09595 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 9 Jul 2021 14:25:51 +1000 Subject: [PATCH 27/28] Whitespace --- .../CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs | 2 -- .../CSharpTest/EditAndContinue/ActiveStatementTests.cs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs index 8a844ab78d74b..23b0c0ec06eae 100644 --- a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs @@ -8629,7 +8629,6 @@ public void TestTopLevelStatements_Lambda() Assert.Equal("i, lambda, args", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside)); } - [Fact] public void TestTopLevelStatements_LambdaCapturingArgs() { @@ -8655,7 +8654,6 @@ public void TestTopLevelStatements_LambdaCapturingArgs() Assert.Equal("lambda, args", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside)); } - #endregion } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs index 8fc14098ffaae..7dd34c2e5a188 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs @@ -11086,7 +11086,7 @@ public void TopLevelStatements_Inner() var src1 = @" using System; -Goo(1); +Goo(1); static void Goo(int a) { From 8eb3471937dcee577bf4930546d7b95033b5c7de Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 9 Jul 2021 14:26:57 +1000 Subject: [PATCH 28/28] Expand test --- .../Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs index 23b0c0ec06eae..6b6b577e777c0 100644 --- a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs @@ -8585,7 +8585,13 @@ public void TestTopLevelStatements() using System; using System.Linq; -/**/Console.Write(1);/**/ +/**/ +Console.Write(1); +Console.Write(2); +Console.Write(3); +Console.Write(4); +Console.Write(5); +/**/ "); var controlFlowAnalysisResults = analysisResults.Item1; var dataFlowAnalysisResults = analysisResults.Item2;