Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

EnC - Support top level statements #54102

Merged
merged 30 commits into from
Jul 9, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2ad0f53
Add failing tests
davidwengier Jun 4, 2021
69d304b
Classify global statement syntax
davidwengier Jun 4, 2021
f11e7be
Few more failing tests, and one passing.
davidwengier Jun 7, 2021
8905dc8
Issue semantic edits for the synthesized main method
davidwengier Jun 7, 2021
2f59e3a
Rude edits for creating or deleting the synthesized main method
davidwengier Jun 7, 2021
6d016fc
Basic regression test
davidwengier Jun 7, 2021
3651ed7
Rerport rude errors for return type changes
davidwengier Jun 8, 2021
2a28331
Analyze semantics of top level statements as though they're method bo…
davidwengier Jun 11, 2021
8a30d3d
Get correct breakpoint envelope
davidwengier Jun 15, 2021
ae7899c
Add some random tests
davidwengier Jun 15, 2021
9c5c916
Fix whitespace
davidwengier Jun 15, 2021
087a5e9
Namespace or type declarations can come after global statements
davidwengier Jun 15, 2021
49a7373
Ignore declarations after global statements
davidwengier Jun 15, 2021
d8dd4aa
Minor PR feedback
davidwengier Jun 24, 2021
4525937
Add tests for adding/removing awaits
davidwengier Jun 24, 2021
fca8a77
Move return type checking to semantics
davidwengier Jun 24, 2021
e54f08f
Merge remote-tracking branch 'upstream/main' into EnCTopLevelStatements
davidwengier Jun 24, 2021
a9c6ce4
Add new rude edit
davidwengier Jun 24, 2021
b6ab537
Report better diagnostic spans
davidwengier Jun 24, 2021
3789ba9
Update tests
davidwengier Jun 24, 2021
27e63f8
Allow moving global statements from one file to another
davidwengier Jun 25, 2021
19de225
Basic code flow tests
davidwengier Jun 25, 2021
884c8ea
Merge remote-tracking branch 'upstream/main' into EnCTopLevelStatements
davidwengier Jun 30, 2021
774b1be
Validate global statements are part of the same compilation unit
davidwengier Jul 2, 2021
afc1ca7
Fix condition
davidwengier Jul 4, 2021
458e9fe
Neater fix
davidwengier Jul 4, 2021
f4b3120
Fix typos (thanks Chuck!!)
davidwengier Jul 7, 2021
2a7b75d
PR feedback
davidwengier Jul 8, 2021
9ef630a
Whitespace
davidwengier Jul 9, 2021
8eb3471
Expand test
davidwengier Jul 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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) &&
davidwengier marked this conversation as resolved.
Show resolved Hide resolved
// 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)
davidwengier marked this conversation as resolved.
Show resolved Hide resolved
davidwengier marked this conversation as resolved.
Show resolved Hide resolved
{
throw new ArgumentException("statements not within the same statement list");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(@"
<N:0>
davidwengier marked this conversation as resolved.
Show resolved Hide resolved
using System;

Func<string> x = <N:1>() => args[0]</N:1>;
Console.WriteLine(x());
</N:0>
");
var source1 = MarkedSource(@"
<N:0>
using System;

Func<string> x = <N:1>() => args[1]</N:1>;
Console.WriteLine(x());
</N:0>
");
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<MethodSymbol>("<Program>$.<Main>$");
var f1 = compilation1.GetMember<MethodSymbol>("<Program>$.<Main>$");

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(
"<Program>$.<>c__DisplayClass0_0: {args, <<Main>$>b__0}",
"<Program>$: {<>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));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

davidwengier marked this conversation as resolved.
Show resolved Hide resolved
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<MethodSymbol>("<Program>$.<Main>$");

// Verify full metadata contains expected rows.
var bytes0 = compilation0.EmitToArray();
using var md0 = ModuleMetadata.CreateFromImage(bytes0);
var reader0 = md0.MetadataReader;

CheckNames(reader0, reader0.GetTypeDefNames(), "<Module>", "<Program>$");
CheckNames(reader0, reader0.GetMethodDefNames(), "<Main>$");
CheckNames(reader0, reader0.GetMemberRefNames(), /*CompilationRelaxationsAttribute.*/".ctor", /*RuntimeCompatibilityAttribute.*/".ctor", /*Object.*/".ctor", /*DebuggableAttribute*/".ctor", /*Console.*/"WriteLine");

var generation0 = EmitBaseline.CreateInitialBaseline(
md0,
EmptyLocalsProvider);
Copy link
Contributor

Choose a reason for hiding this comment

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

I was confused about why GetEncMethodDebugInfo is needed in the new test in EditAndContinueClosureTests but not here. Is it because the method we're editing doesn't have any locals?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not to pretend I understand it and didn't just copy an existing test, but yes GetEncMethodDebugInfo reads the lambda map and locals map.

var method1 = compilation1.GetMember<MethodSymbol>("<Program>$.<Main>$");

davidwengier marked this conversation as resolved.
Show resolved Hide resolved
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(), "<Main>$");
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));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

<AS:0>Console.WriteLine(1);</AS:0>
void M() { Console.WriteLine(2); }
";
var src2 = @"
using System;

<AS:0>Console.WriteLine(1);</AS:0>
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;

<AS:0>Console.WriteLine(1);</AS:0>
M();
";
var src2 = @"
using System;

<AS:0>Console.WriteLine(1);</AS:0>
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;

<AS:1>Goo(1);</AS:1>
davidwengier marked this conversation as resolved.
Show resolved Hide resolved

static void Goo(int a)
{
<AS:0>Console.WriteLine(a);</AS:0>
}
";
var src2 = @"
using System;

while (true)
{
<AS:1>Goo(2);</AS:1>
}

static void Goo(int a)
{
<AS:0>Console.WriteLine(a);</AS:0>
}
";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);

edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.ActiveStatementUpdate, "Goo(2);"));
}

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5147,5 +5147,38 @@ void Goo()
}
}");
}

#region Top Level Statements

[Fact]
public void TopLevelStatements()
{
VerifyAllSpansInDeclaration<CompilationUnitSyntax>(@"
$$[|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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10553,5 +10553,67 @@ public void WithExpression_PropertyValueReorder()
}

#endregion

#region Top Level Statements

[Fact]
public void TopLevelStatement_CaptureArgs()
{
var src1 = @"
using System;

var x = new Func<string>(() => ""Hello"");

Console.WriteLine(x());
";
var src2 = @"
using System;

var x = new Func<string>(() => ""Hello"" + args[0]);

Console.WriteLine(x());
";
var edits = GetTopEdits(src1, src2);

// TODO: allow creating a new leaf closure
davidwengier marked this conversation as resolved.
Show resolved Hide resolved
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.CapturingVariable, "using System;\r\n\r\nvar x = new Func<string>(() => \"Hello\" + args[0]);\r\n\r\nConsole.WriteLine(x());\r\n", "args"));
davidwengier marked this conversation as resolved.
Show resolved Hide resolved
}

[Fact, WorkItem(21499, "https://github.com/dotnet/roslyn/issues/21499")]
public void TopLevelStatement_UpdateLocalFunction_ForEach1()
davidwengier marked this conversation as resolved.
Show resolved Hide resolved
{
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;
davidwengier marked this conversation as resolved.
Show resolved Hide resolved
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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Loading