// semmle-extractor-options: --cil

using System;

class Test
{
    static void Main(string[] args)
    {
        // Indirect call to method
        var c1 = "abc".Contains("a");          // Calls string.IndexOf()
        var c2 = "123".CompareTo("b");         // Calls string.Compare()
        var c3 = Tuple.Create("c", "d", "e");    // Calls Tuple constructor
    }
    
    void DataFlowThroughFramework()
    {
        // Dataflow through call
        var f1 = "tainted".ToString();
        var f2 = Math.Abs(12);
        var f3 = Math.Max(2, 3);
        var f4 = Math.Round(0.5);
        var f5 = System.IO.Path.GetFullPath("tainted");

        // Tainted dataflow (there is no untainted dataflow path)
        int remainder;
        var t1 = System.Math.DivRem(1, 2, out remainder);

        // Tainted indirect call to method (there is no untainted dataflow path)
        var t2 = System.Math.IEEERemainder(1.0, 2.0);

        // Miscellaneous examples
        var m1 = System.Math.DivRem(Math.Abs(-1), Math.Max(1, 2), out remainder);
        var m2 = "tainted".ToString().Contains("t");
    }

    void DataFlowThroughAssembly()
    {
        // Dataflow through test assembly
        var dataflow = new Dataflow.DataFlow();
        var d1 = dataflow.Taint1("d1");
        var d2 = dataflow.Taint2("d2");
        var d3 = dataflow.Taint3("d3");

        // Taint tracking
        var tt = new Dataflow.TaintFlow();
        var t1 = tt.Taint1("t1a", "t1b");
        var t2 = tt.Taint2(2, 3);
        var t3 = tt.Taint3(1);
        var t4 = tt.TaintIndirect("t6", "t6");
    }

    void DeadCode() { }

    void CilAlwaysThrows()
    {
        System.Reflection.Assembly.LoadFrom("", null, System.Configuration.Assemblies.AssemblyHashAlgorithm.SHA1);  // Throws NotSupportedException
        DeadCode();
    }

    void Throw() => throw new InvalidCastException();

    void CsAlwaysThrows()
    {
        Throw();
        DeadCode();
    }

    void Nullness()
    {
        var @null = default(object);
        var nonNull1 = default(int);
        var nullFromCil = this.GetType().DeclaringMethod;
        var nonNullFromCil = true.ToString();
        var null2 = NullFunction() ?? IndirectNull();

        // Null from dataflow assembly
        var nullMethods = new Dataflow.NullMethods();
        var null3 = nullMethods.ReturnsNull(); // Null
        var null4 = nullMethods.ReturnsNull2();
        var null5 = nullMethods.NullProperty;

        // NotNull
        var nonNull = new Dataflow.NonNullMethods();
        var nonNull2 = nonNull.ReturnsNonNull();
        var nonNull3 = nonNull.ReturnsNonNullIndirect();
        var nonNull4 = nonNull.NonNullProperty;

        // The following are not always null:
        var notNull1 = cond ? NullFunction() : this;
        var notNull2 = nullMethods.VirtualReturnsNull();
        var notNull3 = nullMethods.VirtualNullProperty;
        var notNonNull = nonNull.VirtualNonNull;

        // The following are maybe null
        var maybeNull = nonNull.MaybeNull();
        var maybeNull2 = nonNull.MaybeNull2();
        var maybeNullMethods = new DataflowUnoptimized.MaybeNullMethods();
        maybeNull = maybeNullMethods.MaybeNull();
        maybeNull2 = maybeNullMethods.MaybeNull2();
    }

    object IndirectNull() => null;

    bool cond;

    object NullFunction()
    {
        object x = IndirectNull();
        if (cond) x = null;
        return x;
    }
}
