icsharpcode / ILSpy

.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
21.27k stars 3.34k forks source link

Incorrect deconstruction of foreach item #2322

Open mmusu3 opened 3 years ago

mmusu3 commented 3 years ago

Input code

static class DeconstructForeachItemTest
{
    static void Test(Dictionary<string, object> data)
    {
        var dictionary = new Dictionary<string, object>();

        foreach (var (key, value) in data)
        {
            dictionary[key] = value;
        }
    }

    static void Deconstruct<K, V>(this KeyValuePair<K, V> pair, out K k, out V v)
    {
        k = pair.Key;
        v = pair.Value;
    }
}

Erroneous output

internal static class DeconstructForeachItemTest
{
    private static void Test(Dictionary<string, object> data)
    {
        Dictionary<string, object> dictionary = new Dictionary<string, object>();
        foreach (KeyValuePair<string, object> datum in data)
        {
            string key;
            (key, dictionary[key]) = datum;
        }
    }

    private static void Deconstruct<K, V>(this KeyValuePair<K, V> pair, out K k, out V v)
    {
        k = pair.Key;
        v = pair.Value;
    }
}

Error CS0165: Use of unassigned local variable 'key'

Details

siegfriedpammer commented 2 years ago

Fixing #2378 improves this somewhat as now this does no longer produce invalid code, however, the deconstruction is no longer detected, most likely because of the inline assignment.

// DeconstructForeachItemTest
using System.Collections.Generic;

private static void Test(Dictionary<string, object> data)
{
    Dictionary<string, object> dictionary = new Dictionary<string, object>();
    foreach (KeyValuePair<string, object> datum in data)
    {
        datum.Deconstruct(out var key2, out var value2);
        string key = key2;
        object value = (dictionary[key] = value2);
    }
}
mmusu3 commented 2 years ago

The fixes only seem to apply for projects targeting newer .NET versions (tested with Core 3.1 and Net 5). It still produces the old wrong output for older versions (tested with netstandard2 and netfx 4.8).