Skip to content

Commit

Permalink
Allow parameter renames in Edit and Continue (#54856)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidwengier authored Aug 10, 2021
1 parent 3d0d948 commit 9683739
Show file tree
Hide file tree
Showing 32 changed files with 701 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -760,8 +760,7 @@ private bool AreNamespacesEqual(NamespaceSymbol @namespace, NamespaceSymbol othe
private bool AreParametersEqual(ParameterSymbol parameter, ParameterSymbol other)
{
Debug.Assert(parameter.Ordinal == other.Ordinal);
return StringOrdinalComparer.Equals(parameter.MetadataName, other.MetadataName) &&
(parameter.RefKind == other.RefKind) &&
return (parameter.RefKind == other.RefKind) &&
_comparer.Equals(parameter.Type, other.Type);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,97 @@ static void Main() { }
Handle(2, TableIndex.AssemblyRef));
}

[Fact]
public void ModifyMethod_RenameParameter()
{
var source0 =
@"class C
{
static string F(int a) { return a.ToString(); }
}";
var source1 =
@"class C
{
static string F(int x) { return x.ToString(); }
}";
var source2 =
@"class C
{
static string F(int b) { return b.ToString(); }
}";

var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
var compilation1 = compilation0.WithSource(source1);
var compilation2 = compilation1.WithSource(source2);

var method0 = compilation0.GetMember<MethodSymbol>("C.F");

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

CheckNames(reader0, reader0.GetTypeDefNames(), "<Module>", "C");
CheckNames(reader0, reader0.GetMethodDefNames(), "F", ".ctor");
CheckNames(reader0, reader0.GetParameterDefNames(), "a");

var generation0 = EmitBaseline.CreateInitialBaseline(
md0,
EmptyLocalsProvider);
var method1 = compilation1.GetMember<MethodSymbol>("C.F");

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(), "F");
CheckNames(readers, reader1.GetParameterDefNames(), "x");

CheckEncLogDefinitions(reader1,
Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(1, TableIndex.Param, EditAndContinueOperation.Default));

CheckEncMapDefinitions(reader1,
Handle(1, TableIndex.MethodDef),
Handle(1, TableIndex.Param),
Handle(2, TableIndex.StandAloneSig));

var method2 = compilation2.GetMember<MethodSymbol>("C.F");

var diff2 = compilation2.EmitDifference(
diff1.NextGeneration,
ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Update, method1, method2)));

// Verify delta metadata contains expected rows.
using var md2 = diff2.GetMetadata();
var reader2 = md2.Reader;
readers = new[] { reader0, reader1, reader2 };

CheckNames(readers, reader2.GetTypeDefNames());
CheckNames(readers, reader2.GetMethodDefNames(), "F");
CheckNames(readers, reader2.GetMemberRefNames(), "ToString");
CheckNames(readers, reader2.GetParameterDefNames(), "b");

CheckEncLogDefinitions(reader2,
Row(3, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(1, TableIndex.Param, EditAndContinueOperation.Default));

CheckEncMapDefinitions(reader2,
Handle(1, TableIndex.MethodDef),
Handle(1, TableIndex.Param),
Handle(3, TableIndex.StandAloneSig));
}

[CompilerTrait(CompilerFeature.Tuples)]
[Fact]
public void ModifyMethod_WithTuples()
Expand Down Expand Up @@ -1404,6 +1495,10 @@ class C
EncValidation.VerifyModuleMvid(2, reader1, reader2);

CheckNames(readers, reader2.GetTypeDefNames());
CheckNames(readers, reader2.GetMethodDefNames(), "F");
CheckNames(readers, reader2.GetParameterDefNames(), "", "arg");
CheckNames(readers, diff2.EmitResult.ChangedTypes, "C");
CheckNames(readers, diff2.EmitResult.UpdatedMethods, "F");

CheckEncLogDefinitions(reader2,
Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), // C.F2
Expand Down Expand Up @@ -12043,5 +12138,89 @@ public void TopLevelStatement_Update()
Handle(8, TableIndex.MemberRef),
Handle(2, TableIndex.AssemblyRef));
}

[Fact]
public void LambdaParameterToDiscard()
{
var source0 = MarkedSource(@"
using System;
class C
{
void M()
{
var x = new Func<int, int, int>(<N:0>(a, b) => a + b + 1</N:0>);
Console.WriteLine(x(1, 2));
}
}");
var source1 = MarkedSource(@"
using System;
class C
{
void M()
{
var x = new Func<int, int, int>(<N:0>(_, _) => 10</N:0>);
Console.WriteLine(x(1, 2));
}
}");

var compilation0 = CreateCompilation(source0.Tree, options: ComSafeDebugDll);
var compilation1 = compilation0.WithSource(source1.Tree);

var method0 = compilation0.GetMember<MethodSymbol>("C.M");
var method1 = compilation1.GetMember<MethodSymbol>("C.M");

var v0 = CompileAndVerify(compilation0, verify: Verification.Skipped);

using var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData);

var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo);

var diff = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(
SemanticEdit.Create(SemanticEditKind.Update, method0, method1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true)));

// There should be no diagnostics from rude edits
diff.EmitResult.Diagnostics.Verify();

diff.VerifySynthesizedMembers(
"C: {<>c}",
"C.<>c: {<>9__0_0, <M>b__0_0}");

diff.VerifyIL("C.M",
@"
{
// Code size 48 (0x30)
.maxstack 3
.locals init ([unchanged] V_0,
System.Func<int, int, int> V_1) //x
IL_0000: nop
IL_0001: ldsfld ""System.Func<int, int, int> C.<>c.<>9__0_0""
IL_0006: dup
IL_0007: brtrue.s IL_0020
IL_0009: pop
IL_000a: ldsfld ""C.<>c C.<>c.<>9""
IL_000f: ldftn ""int C.<>c.<M>b__0_0(int, int)""
IL_0015: newobj ""System.Func<int, int, int>..ctor(object, System.IntPtr)""
IL_001a: dup
IL_001b: stsfld ""System.Func<int, int, int> C.<>c.<>9__0_0""
IL_0020: stloc.1
IL_0021: ldloc.1
IL_0022: ldc.i4.1
IL_0023: ldc.i4.2
IL_0024: callvirt ""int System.Func<int, int, int>.Invoke(int, int)""
IL_0029: call ""void System.Console.WriteLine(int)""
IL_002e: nop
IL_002f: ret
}");

diff.VerifyIL("C.<>c.<M>b__0_0(int, int)", @"
{
// Code size 3 (0x3)
.maxstack 1
IL_0000: ldc.i4.s 10
IL_0002: ret
}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,68 @@ class C
Assert.NotNull(other);
}

[Fact]
public void Method_ParameterRename()
{
var source0 = @"
using System.Collections.Generic;
class C
{
string M(string s) => s.ToString();
}";
var source1 = @"
using System.Collections.Generic;
class C
{
string M(string m) => m.ToString();
}";
var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
var compilation1 = compilation0.WithSource(source1);

var matcher = new CSharpSymbolMatcher(
null,
compilation1.SourceAssembly,
default,
compilation0.SourceAssembly,
default,
null);

var member = compilation1.GetMember<MethodSymbol>("C.M");
var other = matcher.MapDefinition(member.GetCciAdapter());
Assert.NotNull(other);
}

[Fact]
public void Method_ParameterRenameToDiscard()
{
var source0 = @"
using System.Collections.Generic;
class C
{
string M(string s) => s.ToString();
}";
var source1 = @"
using System.Collections.Generic;
class C
{
string M(string _) => ""Hello"";
}";
var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
var compilation1 = compilation0.WithSource(source1);

var matcher = new CSharpSymbolMatcher(
null,
compilation1.SourceAssembly,
default,
compilation0.SourceAssembly,
default,
null);

var member = compilation1.GetMember<MethodSymbol>("C.M");
var other = matcher.MapDefinition(member.GetCciAdapter());
Assert.NotNull(other);
}

[Fact]
public void Field_NullableChange()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit

Private Function AreParametersEqual(parameter As ParameterSymbol, other As ParameterSymbol) As Boolean
Debug.Assert(parameter.Ordinal = other.Ordinal)
Return s_nameComparer.Equals(parameter.Name, other.Name) AndAlso parameter.IsByRef = other.IsByRef AndAlso Me._comparer.Equals(parameter.Type, other.Type)
Return parameter.IsByRef = other.IsByRef AndAlso Me._comparer.Equals(parameter.Type, other.Type)
End Function

Private Function ArePropertiesEqual([property] As PropertySymbol, other As PropertySymbol) As Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests
End Sub

Friend Shared Sub CheckEncLogDefinitions(reader As MetadataReader, ParamArray rows As EditAndContinueLogEntry())
AssertEx.Equal(rows, reader.GetEditAndContinueLogEntries().Where(Function(entry) IsDefinition(entry)), itemInspector:=AddressOf EncLogRowToString)
AssertEx.Equal(rows, reader.GetEditAndContinueLogEntries().Where(Function(entry) IsDefinition(entry.Handle.Kind)), itemInspector:=AddressOf EncLogRowToString)
End Sub

Friend Shared Function IsDefinition(entry As EditAndContinueLogEntry) As Boolean
Friend Shared Function IsDefinition(kind As HandleKind) As Boolean
Dim index As TableIndex
Assert.True(MetadataTokens.TryGetTableIndex(entry.Handle.Kind, index))
Assert.True(MetadataTokens.TryGetTableIndex(kind, index))

Select Case index
Case TableIndex.MethodDef,
Expand Down Expand Up @@ -232,6 +232,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests
AssertEx.Equal([handles], reader.GetEditAndContinueMapEntries(), itemInspector:=AddressOf EncMapRowToString)
End Sub

Friend Shared Sub CheckEncMapDefinitions(reader As MetadataReader, ParamArray [handles] As EntityHandle())
AssertEx.Equal([handles], reader.GetEditAndContinueMapEntries().Where(Function(e) IsDefinition(e.Kind)), itemInspector:=AddressOf EncMapRowToString)
End Sub

Friend Shared Sub CheckNames(reader As MetadataReader, [handles] As StringHandle(), ParamArray expectedNames As String())
CheckNames({reader}, [handles], expectedNames)
End Sub
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,60 @@ End Class
End Using
End Sub

<Fact>
Public Sub ModifyMethod_RenameParameter()
Dim source0 =
"
Class C
Shared Function F(i As Integer) As Integer
Return i
End Function
End Class
"
Dim source1 =
"
Class C
Shared Function F(x As Integer) As Integer
Return x
End Function
End Class
"
Dim compilation0 = CreateCompilationWithMscorlib40({source0}, options:=TestOptions.DebugDll, references:={ValueTupleRef, SystemRuntimeFacadeRef})
Dim compilation1 = compilation0.WithSource(source1)

Dim bytes0 = compilation0.EmitToArray()
Using md0 = ModuleMetadata.CreateFromImage(bytes0)
Dim reader0 = md0.MetadataReader
Dim method0 = compilation0.GetMember(Of MethodSymbol)("C.F")
Dim generation0 = EmitBaseline.CreateInitialBaseline(md0, EmptyLocalsProvider)

CheckNames(reader0, reader0.GetParameterDefNames(), "i")

Dim method1 = compilation1.GetMember(Of MethodSymbol)("C.F")
Dim diff1 = compilation1.EmitDifference(generation0, ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, method0, method1)))

' Verify delta metadata contains expected rows.
Using md1 = diff1.GetMetadata()
Dim reader1 = md1.Reader
Dim readers = {reader0, reader1}
EncValidation.VerifyModuleMvid(1, reader0, reader1)
CheckNames(readers, reader1.GetTypeDefNames())
CheckNames(readers, reader1.GetMethodDefNames(), "F")
CheckNames(readers, reader1.GetParameterDefNames(), "x")

CheckEncLogDefinitions(reader1,
Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(1, TableIndex.Param, EditAndContinueOperation.Default))

CheckEncMapDefinitions(reader1,
Handle(2, TableIndex.MethodDef),
Handle(1, TableIndex.Param),
Handle(2, TableIndex.StandAloneSig))
End Using
End Using
End Sub

<WorkItem(962219, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/962219")>
<Fact>
Public Sub PartialMethod()
Expand Down
Loading

0 comments on commit 9683739

Please sign in to comment.