IDE0044 incorrectly identifies generic types as immutable
#37,981 opened on Aug 14, 2019
Description
Version Used: 3.3.0-beta3-19409-05+ed92d532473db83c1db47b313ee1c1bd7520aa08
Steps to Reproduce: Copy this into a new project:
using System;
using System.Collections;
using System.Collections.Generic;
class Program
{
static void Main()
{
var e = Wrap(new List<int> { 1, 2, 3, 4, 5 }.GetEnumerator());
while (e.MoveNext()) Console.WriteLine(e.Current);
}
private static EnumeratorWrapper<T> Wrap<T>(T enumerator)
where T : IEnumerator<int> =>
new EnumeratorWrapper<T>(enumerator);
}
internal sealed class EnumeratorWrapper<T> :
IEnumerator<int>
where T : IEnumerator<int>
{
private T _enumerator;
public EnumeratorWrapper(T enumerator) => _enumerator = enumerator;
public int Current => _enumerator.Current;
object? IEnumerator.Current => _enumerator.Current;
public void Dispose() => _enumerator.Dispose();
public bool MoveNext() => _enumerator.MoveNext();
public void Reset() => _enumerator.Reset();
}
Run it. As expected, it prints out:
1
2
3
4
5
Then apply IDE0044, which triggers for the _enumerator field, and run it. The program will never end, repeatedly printing out an infinite number of 0s.
Expected Behavior:
IDE0044 should not trigger for the _enumerator field. The rule has logic to identify fields known to be of types that are either reference types or immutable structs, and it needs to be conservative in its estimation of whether a struct is immutable, always erring on the side of mutable if it can't be sure, since automatically making a field of a mutable struct type readonly can lead to erroneous behavior, as the repro demonstrates. The rule should consider generic parameters to be mutable, unless they're constrained to be reference types.
Actual Behavior:
IDE0044 triggers for _enumerator, marking the field readonly, and leading to buggy program behavior.