diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs index 00aea94d8..e05fbaccc 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs @@ -184,16 +184,45 @@ namespace ICSharpCode.Decompiler.FlowAnalysis protected internal override void VisitLdLoca(LdLoca inst) { // A variable needs to be initialized before we can take it by reference. - // The exception is if the variable is passed to an out parameter. - if (!IsOutArgument(inst)) { - EnsureInitialized(inst.Variable); - } + // The exception is if the variable is passed to an out parameter (handled in VisitCall). + EnsureInitialized(inst.Variable); } - static bool IsOutArgument(ILInstruction inst) + protected internal override void VisitCall(Call inst) { - return inst.Parent is CallInstruction call - && call.GetParameter(inst.ChildIndex)?.IsOut == true; + HandleCall(inst); + } + + protected internal override void VisitCallVirt(CallVirt inst) + { + HandleCall(inst); + } + + protected internal override void VisitNewObj(NewObj inst) + { + HandleCall(inst); + } + + void HandleCall(CallInstruction call) + { + bool hasOutArgs = false; + foreach (var arg in call.Arguments) { + if (arg.MatchLdLoca(out var v) && call.GetParameter(arg.ChildIndex)?.IsOut == true) { + // Visiting ldloca would require the variable to be initialized, + // but we don't need out arguments to be initialized. + hasOutArgs = true; + } else { + arg.AcceptVisitor(this); + } + } + // Mark out arguments as initialized, but only after the whole call: + if (hasOutArgs) { + foreach (var arg in call.Arguments) { + if (arg.MatchLdLoca(out var v) && call.GetParameter(arg.ChildIndex)?.IsOut == true) { + HandleStore(v); + } + } + } } } }