Skip to content

Commit 46c99f1

Browse files
Fix #3452: More bugs with primary constructors.
1 parent 9c8d1e4 commit 46c99f1

File tree

4 files changed

+106
-18
lines changed

4 files changed

+106
-18
lines changed

ICSharpCode.Decompiler.Tests/TestCases/Pretty/Playstation.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Playstation
66
{
7+
#pragma warning disable CS0414, CS9113, CS9124
8+
79
public record struct CopilotContextId
810
{
911
public Guid Id { get; }
@@ -176,6 +178,54 @@ public FromConvert(double dummy1, double dummy2)
176178
Leet = (int)Math.Min(dummy1, dummy2);
177179
}
178180
}
181+
182+
internal class Issue3452
183+
{
184+
private struct Data
185+
{
186+
public object Obj;
187+
}
188+
189+
private class C1(object obj)
190+
{
191+
internal Data d = new Data {
192+
Obj = obj
193+
};
194+
}
195+
196+
private class C2(object obj)
197+
{
198+
public object Obj => obj;
199+
}
200+
201+
private class C3(StringComparison comparison)
202+
{
203+
private StringComparison _comparison = comparison;
204+
205+
internal StringComparison Test()
206+
{
207+
return comparison;
208+
}
209+
}
210+
211+
private struct S1
212+
{
213+
internal Data d;
214+
215+
public S1(object obj)
216+
{
217+
d = new Data {
218+
Obj = obj
219+
};
220+
}
221+
}
222+
223+
private struct S2(object obj)
224+
{
225+
public object Obj => obj;
226+
}
227+
}
228+
179229
public record NamedParameter(string name, object? value, bool encode = true) : Parameter(Ensure.NotEmptyString(name, "name"), value, encode);
180230

181231
[DebuggerDisplay("{DebuggerDisplay()}")]

ICSharpCode.Decompiler.Tests/TestCases/Pretty/PlaystationPreferPrimary.cs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.PlaystationPreferPrimary
66
{
7+
#pragma warning disable CS0414, CS9113, CS9124
8+
79
public record struct CopilotContextId
810
{
911
public Guid Id { get; }
@@ -157,6 +159,48 @@ public struct FromConvert(double dummy1, double dummy2)
157159
public int Leet = (int)Math.Min(dummy1, dummy2);
158160
}
159161

162+
internal class Issue3452
163+
{
164+
private struct Data
165+
{
166+
public object Obj;
167+
}
168+
169+
private class C1(object obj)
170+
{
171+
internal Data d = new Data {
172+
Obj = obj
173+
};
174+
}
175+
176+
private class C2(object obj)
177+
{
178+
public object Obj => obj;
179+
}
180+
181+
private class C3(StringComparison comparison)
182+
{
183+
private StringComparison _comparison = comparison;
184+
185+
internal StringComparison Test()
186+
{
187+
return comparison;
188+
}
189+
}
190+
191+
private struct S1(object obj)
192+
{
193+
internal Data d = new Data {
194+
Obj = obj
195+
};
196+
}
197+
198+
private struct S2(object obj)
199+
{
200+
public object Obj => obj;
201+
}
202+
}
203+
160204
public record NamedParameter(string name, object? value, bool encode = true) : Parameter(Ensure.NotEmptyString(name, "name"), value, encode);
161205

162206
[DebuggerDisplay("{DebuggerDisplay()}")]
@@ -238,14 +282,10 @@ public PersonRegular1(string name, int age)
238282
}
239283
}
240284

241-
public class PersonRegular2
285+
public class PersonRegular2(string name, int age)
242286
{
243287
private readonly string _name = "name" + Environment.GetEnvironmentVariable("Path");
244288
private readonly int _age = Environment.GetEnvironmentVariable("Path")?.Length ?? (-1);
245-
246-
public PersonRegular2(string name, int age)
247-
{
248-
}
249289
}
250290

251291
public record QueryParameter(string name, object? value, bool encode = true) : NamedParameter(name, value, encode);

ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ IMethod DetectPrimaryConstructor()
185185
if (method.IsStatic || !method.IsConstructor)
186186
continue;
187187
var m = method.Specialize(subst);
188-
var body = DecompileBody(method);
188+
var body = DecompileBody(method, allTransforms: method.Accessibility == Accessibility.Public && !recordTypeDef.IsRecord);
189189
if (body == null)
190190
continue;
191191
if (primaryCtor == null && method.Accessibility == Accessibility.Public && IsPrimaryConstructorBody(m, method, body))
@@ -268,7 +268,7 @@ bool IsPrimaryConstructorBody(IMethod method, IMethod unspecializedMethod, Block
268268
else
269269
{
270270
backingMember = field;
271-
referencesMembersDeclaredByPrimaryConstructor |= valueReferencesParameter && recordTypeDef.IsReferenceType == true;
271+
referencesMembersDeclaredByPrimaryConstructor |= valueReferencesParameter && (FieldIsGenerated(field) || recordTypeDef.IsReferenceType == true);
272272
}
273273
if (offset < method.Parameters.Count)
274274
{
@@ -1206,7 +1206,7 @@ bool MatchMemberAccess(ILInstruction inst, out ILInstruction target, out IMember
12061206
}
12071207
}
12081208

1209-
Block DecompileBody(IMethod method)
1209+
Block DecompileBody(IMethod method, bool allTransforms = false)
12101210
{
12111211
if (method == null || method.MetadataToken.IsNil)
12121212
return null;
@@ -1223,7 +1223,7 @@ Block DecompileBody(IMethod method)
12231223
var body = typeSystem.MainModule.MetadataFile.GetMethodBody(methodDef.RelativeVirtualAddress);
12241224
var ilReader = new ILReader(typeSystem.MainModule);
12251225
var il = ilReader.ReadIL(methodDefHandle, body, genericContext, ILFunctionKind.TopLevelFunction, cancellationToken);
1226-
var settings = new DecompilerSettings(LanguageVersion.CSharp1);
1226+
var settings = allTransforms ? this.settings : new DecompilerSettings(LanguageVersion.CSharp1);
12271227
var transforms = CSharpDecompiler.GetILTransforms();
12281228
// Remove the last couple transforms -- we don't need variable names etc. here
12291229
int lastBlockTransform = transforms.FindLastIndex(t => t is BlockILTransform);

ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,13 @@ record = null;
227227
// Recognize field or property initializers:
228228
// Translate first statement in all ctors (if all ctors have the same statement) into an initializer.
229229
bool allSame;
230-
bool isStructPrimaryCtor = false;
231230
do
232231
{
233232
Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault());
234233
if (!m.Success)
235234
break;
236235
IMember fieldOrPropertyOrEvent = (m.Get<AstNode>("fieldAccess").Single().GetSymbol() as IMember)?.MemberDefinition;
237-
if (!(fieldOrPropertyOrEvent is IField) && !(fieldOrPropertyOrEvent is IProperty) && !(fieldOrPropertyOrEvent is IEvent))
236+
if (fieldOrPropertyOrEvent is not (IField or IProperty or IEvent))
238237
break;
239238
var fieldOrPropertyOrEventDecl = members.FirstOrDefault(f => f.GetSymbol() == fieldOrPropertyOrEvent) as EntityDeclaration;
240239
// Cannot transform if it is a custom event.
@@ -245,12 +244,11 @@ record = null;
245244
// 'this'/'base' cannot be used in initializers
246245
if (initializer.DescendantsAndSelf.Any(n => n is ThisReferenceExpression || n is BaseReferenceExpression))
247246
break;
248-
var v = initializer.Annotation<ILVariableResolveResult>()?.Variable;
249-
if (v?.Kind == IL.VariableKind.Parameter && IsPropertyDeclaredByPrimaryCtor(fieldOrPropertyOrEvent, record))
247+
248+
if (record != null && ctorMethodDef.Equals(record.PrimaryConstructor))
250249
{
251250
// remove record ctor parameter assignments
252-
isStructPrimaryCtor = true;
253-
if (fieldOrPropertyOrEvent is IField f)
251+
if (fieldOrPropertyOrEvent is IField f && initializer.Annotation<ILVariableResolveResult>()?.Variable is ILVariable v)
254252
fieldToVariableMap.Add(f, v);
255253
}
256254
else
@@ -259,14 +257,14 @@ record = null;
259257
if (fieldOrPropertyOrEventDecl == null)
260258
break;
261259
// or if this is a struct record, but not the primary ctor
262-
if (declaringTypeDefinition.IsRecord && declaringTypeDefinition.IsReferenceType == false && !isStructPrimaryCtor)
260+
if (declaringTypeDefinition.IsRecord && declaringTypeDefinition.IsReferenceType == false)
263261
break;
264262
// or if this is not a primary constructor and the initializer contains any parameter reference, we have to abort
265263
if (!ctorMethodDef.Equals(record?.PrimaryConstructor))
266264
{
267-
bool referencesParameter = initializer.Annotation<ILInstruction>()?.Descendants
265+
bool referencesParameter = initializer.Annotations.OfType<ILInstruction>().Any(inst => inst.Descendants
268266
.OfType<IInstructionWithVariableOperand>()
269-
.Any(inst => inst.Variable.Kind == VariableKind.Parameter) ?? false;
267+
.Any(inst => inst.Variable.Kind == VariableKind.Parameter));
270268
if (referencesParameter)
271269
break;
272270
}

0 commit comments

Comments
 (0)