// semmle-extractor-options: --cil /langversion:8.0 /r:System.Xml.dll /r:System.Xml.ReaderWriter.dll /r:System.Private.Xml.dll /r:System.ComponentModel.Primitives.dll /r:System.IO.Compression.dll /r:System.Runtime.Extensions.dll

using System;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.Xml;
using System.Threading;

class Test
{
    IDisposable d;
    IDisposable dProp { get; set; }
    IDisposable this[int i] { get => null; set { } }

    public IDisposable Method()
    {
        // GOOD: Disposed automatically.
        using (var c1 = new Timer(TimerProc))
        {
        }

        // GOOD: Dispose called in finally
        Timer c1a = null;
        try
        {
            c1a = new Timer(TimerProc);
        }
        finally
        {
            if (c1a != null)
            {
                c1a.Dispose();
            }
        }

        // GOOD: Close called in finally
        FileStream c1b = null;
        try
        {
            c1b = new FileStream("", FileMode.CreateNew, FileAccess.Write);
        }
        finally
        {
            if (c1b != null)
            {
                c1b.Close();
            }
        }

        // BAD: No Dispose call
        var c1d = new Timer(TimerProc);
        var fs = new FileStream("", FileMode.CreateNew, FileAccess.Write);
        new FileStream("", FileMode.CreateNew, FileAccess.Write).Fluent();

        // GOOD: Disposed via wrapper
        fs = new FileStream("", FileMode.CreateNew, FileAccess.Write);
        var z = new GZipStream(fs, CompressionMode.Compress);
        z.Close();

        // GOOD: Escapes
        d = new Timer(TimerProc);
        if (d == null)
            return new Timer(TimerProc);
        fs = new FileStream("", FileMode.CreateNew, FileAccess.Write);
        d = new GZipStream(fs, CompressionMode.Compress);
        dProp = new Timer(TimerProc);
        this[0] = new Timer(TimerProc);
        d = new FileStream("", FileMode.CreateNew, FileAccess.Write).Fluent();

        // GOOD: Passed to another IDisposable
        using (var reader = new StreamReader(new FileStream("", FileMode.Open)))
            ;

        // GOOD: XmlDocument.Load disposes incoming XmlReader (according to CIL)
        var xmlReader = XmlReader.Create(new StringReader("xml"), null);
        var xmlDoc = new XmlDocument();
        xmlDoc.Load(xmlReader);

        // GOOD: Passed to a library. This is only detected in CIL.
        DisposalTests.Class1.Dispose(new StreamWriter("output.txt"));

        // GOOD: Disposed automatically.
        using var c2 = new Timer(TimerProc);

        // GOOD: ownership taken via ??
        StringReader source = null;
        using (XmlReader.Create(source ?? new StringReader("xml"), null))
            ;

        return null;
    }

    // GOOD: Escapes
    IDisposable Create() => new Timer(TimerProc);

    void TimerProc(object obj)
    {
    }

    public void Dispose() { }
}

static class Extensions
{
    public static FileStream Fluent(this FileStream fs) => fs;
}
