Skip to content

Commit de9d72e

Browse files
authored
RefSafetyAnalysis: Fix handling of nested deconstruction utilizing modern extensions (#80231)
* Fix nested extension deconstruction * Add more tests * Verify emit diagnostics
1 parent 05e636f commit de9d72e

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed

src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ private void VisitDeconstructionArguments(ArrayBuilder<DeconstructionVariable> v
11471147
{
11481148
var (placeholder, placeholderConversion) = conversion.DeconstructConversionInfo[i];
11491149
var underlyingConversion = BoundNode.GetConversion(placeholderConversion, placeholder);
1150-
VisitDeconstructionArguments(nestedVariables, syntax, underlyingConversion, right: invocation.Arguments[i + offset]);
1150+
VisitDeconstructionArguments(nestedVariables, syntax, underlyingConversion, right: methodInvocationInfo.ArgsOpt[i + offset]);
11511151
}
11521152
}
11531153
}

src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,102 @@ static class E
7676
Diagnostic(ErrorCode.ERR_MissingDeconstruct, @"""""").WithArguments("string", "2").WithLocation(1, 14));
7777
}
7878

79+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80217")]
80+
public void Deconstruct_Nested_01()
81+
{
82+
var source = """
83+
var (a, b, c) = 42;
84+
var ((d, _, _), (_, e, _), (_, _, f)) = 42;
85+
System.Console.WriteLine($"{a} {b} {c} {d} {e} {f}");
86+
87+
static class E
88+
{
89+
extension(int instance)
90+
{
91+
public void Deconstruct(out int a, out int b, out int c)
92+
=> (a, b, c) = (1, 2, 3);
93+
}
94+
}
95+
""";
96+
CompileAndVerify(source, expectedOutput: "1 2 3 1 2 3").VerifyDiagnostics();
97+
}
98+
99+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80217")]
100+
public void Deconstruct_Nested_02()
101+
{
102+
var source = """
103+
((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42;
104+
105+
static class E
106+
{
107+
extension(int instance)
108+
{
109+
public void Deconstruct(out int a, out int b, out int c)
110+
=> (a, b, c) = (1, 2, 3);
111+
}
112+
}
113+
""";
114+
CreateCompilation(source).VerifyEmitDiagnostics(
115+
// (1,73): error CS7036: There is no argument given that corresponds to the required parameter 'c' of 'E.extension(int).Deconstruct(out int, out int, out int)'
116+
// ((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42;
117+
Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "42").WithArguments("c", "E.extension(int).Deconstruct(out int, out int, out int)").WithLocation(1, 73),
118+
// (1,73): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int', with 2 out parameters and a void return type.
119+
// ((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42;
120+
Diagnostic(ErrorCode.ERR_MissingDeconstruct, "42").WithArguments("int", "2").WithLocation(1, 73),
121+
// (1,73): error CS1501: No overload for method 'Deconstruct' takes 4 arguments
122+
// ((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42;
123+
Diagnostic(ErrorCode.ERR_BadArgCount, "42").WithArguments("Deconstruct", "4").WithLocation(1, 73),
124+
// (1,73): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int', with 4 out parameters and a void return type.
125+
// ((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42;
126+
Diagnostic(ErrorCode.ERR_MissingDeconstruct, "42").WithArguments("int", "4").WithLocation(1, 73));
127+
}
128+
129+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80217")]
130+
public void Deconstruct_Nested_03()
131+
{
132+
var source = """
133+
((int d, int _, int _), (int _, int e, int _), (int _, int _, int f), (int _, int _, int _)) = 42;
134+
135+
static class E
136+
{
137+
extension(int instance)
138+
{
139+
public void Deconstruct(out int a, out int b, out int c)
140+
=> (a, b, c) = (1, 2, 3);
141+
}
142+
}
143+
""";
144+
CreateCompilation(source).VerifyEmitDiagnostics(
145+
// (1,96): error CS1501: No overload for method 'Deconstruct' takes 4 arguments
146+
// ((int d, int _, int _), (int _, int e, int _), (int _, int _, int f), (int _, int _, int _)) = 42;
147+
Diagnostic(ErrorCode.ERR_BadArgCount, "42").WithArguments("Deconstruct", "4").WithLocation(1, 96),
148+
// (1,96): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int', with 4 out parameters and a void return type.
149+
// ((int d, int _, int _), (int _, int e, int _), (int _, int _, int f), (int _, int _, int _)) = 42;
150+
Diagnostic(ErrorCode.ERR_MissingDeconstruct, "42").WithArguments("int", "4").WithLocation(1, 96));
151+
}
152+
153+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80217")]
154+
public void Deconstruct_Nested_04()
155+
{
156+
var source = """
157+
var ((a, b, c), (d, e), (f, g)) = 42;
158+
System.Console.WriteLine($"{a} {b} {c} {d} {e} {f} {g}");
159+
160+
static class E
161+
{
162+
extension(int instance)
163+
{
164+
public void Deconstruct(out int a, out int b, out int c)
165+
=> (a, b, c) = (1, 2, 3);
166+
167+
public void Deconstruct(out int a, out int b)
168+
=> (a, b) = (4, 5);
169+
}
170+
}
171+
""";
172+
CompileAndVerify(source, expectedOutput: "1 2 3 4 5 4 5").VerifyDiagnostics();
173+
}
174+
79175
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75484")]
80176
public void Deconstruction_UnscopedRef_ExtensionMethod()
81177
{

0 commit comments

Comments
 (0)