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

Allow parameter renames in Edit and Continue #54856

Merged
merged 15 commits into from
Aug 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -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");
Copy link
Member

Choose a reason for hiding this comment

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

""

Curious, where does this empty name come from?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The method return

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 @@ -11807,5 +11902,89 @@ public void TopLevelStatement_Update()
Handle(7, 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