// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class VarianceTests : CompilingTestBase
    {
        /// <summary>
        /// Test generic interface assignment with type parameter variance.
        /// </summary>
        [Fact]
        public void TestInterfaceAssignment()
        {
            var text = @"
interface I<in TIn, out TOut, T> {{ }}

class A {{ }}
class B : A {{ }}
class C : B {{ }}

class Test
{{
    static I<A, A, A> i01 = null;
    static I<A, A, B> i02 = null;
    static I<A, A, C> i03 = null;
    static I<A, B, A> i04 = null;
    static I<A, B, B> i05 = null;
    static I<A, B, C> i06 = null;
    static I<A, C, A> i07 = null;
    static I<A, C, B> i08 = null;
    static I<A, C, C> i09 = null;

    static I<B, A, A> i10 = null;
    static I<B, A, B> i11 = null;
    static I<B, A, C> i12 = null;
    static I<B, B, A> i13 = null;
    static I<B, B, B> i14 = null;
    static I<B, B, C> i15 = null;
    static I<B, C, A> i16 = null;
    static I<B, C, B> i17 = null;
    static I<B, C, C> i18 = null;

    static I<C, A, A> i19 = null;
    static I<C, A, B> i20 = null;
    static I<C, A, C> i21 = null;
    static I<C, B, A> i22 = null;
    static I<C, B, B> i23 = null;
    static I<C, B, C> i24 = null;
    static I<C, C, A> i25 = null;
    static I<C, C, B> i26 = null;
    static I<C, C, C> i27 = null;

    static void Main()
    {{
        i{0:d2} = i{1:d2};
    }}
}}";

            // Table comes from manual Dev10 testing
            int[][] validAssignments = new int[][]
            {
                /*filler for 1-indexing*/ new int[0],
                /*01*/
                       new [] { 1, 4, 7 },
                /*02*/ new [] { 2, 5, 8 },
                /*03*/ new [] { 3, 6, 9 },
                /*04*/ new [] { 4, 7  },
                /*05*/ new [] { 5, 8 },
                /*06*/ new [] { 6, 9 },
                /*07*/ new [] { 7 },
                /*08*/ new [] { 8 },
                /*09*/ new [] { 9 },
                /*10*/ new [] { 1, 4, 7, 10, 13, 16 },
                /*11*/ new [] { 2, 5, 8, 11, 14, 17 },
                /*12*/ new [] { 3, 6, 9, 12, 15, 18 },
                /*13*/ new [] { 4, 7, 13, 16 },
                /*14*/ new [] { 5, 8, 14, 17 },
                /*15*/ new [] { 6, 9, 15, 18 },
                /*16*/ new [] { 7, 16 },
                /*17*/ new [] { 8, 17 },
                /*18*/ new [] { 9, 18 },
                /*19*/ new [] { 1, 4, 7, 10, 13, 16, 19, 22, 25 },
                /*20*/ new [] { 2, 5, 8, 11, 14, 17, 20, 23, 26 },
                /*21*/ new [] { 3, 6, 9, 12, 15, 18, 21, 24, 27 },
                /*22*/ new [] { 4, 7, 13, 16, 22, 25 },
                /*23*/ new [] { 5, 8, 14, 17, 23, 26 },
                /*24*/ new [] { 6, 9, 15, 18, 24, 27 },
                /*25*/ new [] { 7, 16, 25 },
                /*26*/ new [] { 8, 17, 26 },
                /*27*/ new [] { 9, 18, 27 },
            };

            int numFields = validAssignments.Length - 1;

            for (int i = 1; i <= numFields; i++)
            {
                for (int j = 1; j <= numFields; j++)
                {
                    try
                    {
                        var comp = CreateCompilation(string.Format(text, i, j));
                        var errors = comp.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error);
                        if (!validAssignments[i].Contains(j))
                        {
                            Assert.Equal(ErrorCode.ERR_NoImplicitConvCast, (ErrorCode)errors.Single().Code);
                        }
                        else
                        {
                            Assert.Empty(errors);
                        }
                    }
                    catch (Exception)
                    {
                        Console.WriteLine("Failed on assignment i{0:d2} = i{1:d2}", i, j);
                        throw;
                    }
                }
            }
        }

        /// <summary>
        /// Test generic interface assignment with type parameter variance.
        /// </summary>
        [Fact]
        public void TestDelegateAssignment()
        {
            var text = @"
delegate TOut D<in TIn, out TOut, T>(TIn tIn, T t);

class A {{ }}
class B : A {{ }}
class C : B {{ }}

class Test
{{
    static D<A, A, A> d01 = null;
    static D<A, A, B> d02 = null;
    static D<A, A, C> d03 = null;
    static D<A, B, A> d04 = null;
    static D<A, B, B> d05 = null;
    static D<A, B, C> d06 = null;
    static D<A, C, A> d07 = null;
    static D<A, C, B> d08 = null;
    static D<A, C, C> d09 = null;

    static D<B, A, A> d10 = null;
    static D<B, A, B> d11 = null;
    static D<B, A, C> d12 = null;
    static D<B, B, A> d13 = null;
    static D<B, B, B> d14 = null;
    static D<B, B, C> d15 = null;
    static D<B, C, A> d16 = null;
    static D<B, C, B> d17 = null;
    static D<B, C, C> d18 = null;

    static D<C, A, A> d19 = null;
    static D<C, A, B> d20 = null;
    static D<C, A, C> d21 = null;
    static D<C, B, A> d22 = null;
    static D<C, B, B> d23 = null;
    static D<C, B, C> d24 = null;
    static D<C, C, A> d25 = null;
    static D<C, C, B> d26 = null;
    static D<C, C, C> d27 = null;

    static void Main()
    {{
        d{0:d2} = d{1:d2};
    }}
}}";

            // Table comes from manual Dev10 testing
            int[][] validAssignments = new int[][]
            {
                /*filler for 1-indexing*/ new int[0],
                /*01*/
                       new [] { 1, 4, 7 },
                /*02*/ new [] { 2, 5, 8 },
                /*03*/ new [] { 3, 6, 9 },
                /*04*/ new [] { 4, 7  },
                /*05*/ new [] { 5, 8 },
                /*06*/ new [] { 6, 9 },
                /*07*/ new [] { 7 },
                /*08*/ new [] { 8 },
                /*09*/ new [] { 9 },
                /*10*/ new [] { 1, 4, 7, 10, 13, 16 },
                /*11*/ new [] { 2, 5, 8, 11, 14, 17 },
                /*12*/ new [] { 3, 6, 9, 12, 15, 18 },
                /*13*/ new [] { 4, 7, 13, 16 },
                /*14*/ new [] { 5, 8, 14, 17 },
                /*15*/ new [] { 6, 9, 15, 18 },
                /*16*/ new [] { 7, 16 },
                /*17*/ new [] { 8, 17 },
                /*18*/ new [] { 9, 18 },
                /*19*/ new [] { 1, 4, 7, 10, 13, 16, 19, 22, 25 },
                /*20*/ new [] { 2, 5, 8, 11, 14, 17, 20, 23, 26 },
                /*21*/ new [] { 3, 6, 9, 12, 15, 18, 21, 24, 27 },
                /*22*/ new [] { 4, 7, 13, 16, 22, 25 },
                /*23*/ new [] { 5, 8, 14, 17, 23, 26 },
                /*24*/ new [] { 6, 9, 15, 18, 24, 27 },
                /*25*/ new [] { 7, 16, 25 },
                /*26*/ new [] { 8, 17, 26 },
                /*27*/ new [] { 9, 18, 27 },
            };

            int numFields = validAssignments.Length - 1;

            for (int i = 1; i <= numFields; i++)
            {
                for (int j = 1; j <= numFields; j++)
                {
                    try
                    {
                        var comp = CreateCompilation(string.Format(text, i, j));
                        var errors = comp.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error);
                        if (!validAssignments[i].Contains(j))
                        {
                            var code = (ErrorCode)errors.Single().Code;
                            // UNDONE: which one will be used is predictable, but confirming that the behavior
                            // exactly matches dev10 is probably too tedious to be worthwhile
                            Assert.True(code == ErrorCode.ERR_NoImplicitConvCast || code == ErrorCode.ERR_NoImplicitConv, "Unexpected error code " + code);
                        }
                        else
                        {
                            Assert.Empty(errors);
                        }
                    }
                    catch (Exception)
                    {
                        Console.WriteLine("Failed on assignment d{0:d2} = d{1:d2}", i, j);
                        throw;
                    }
                }
            }
        }

        /// <remarks>Based on LambdaTests.TestLambdaErrors03</remarks>
        [Fact]
        [WorkItem(539538, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539538")]
        public void TestVarianceConversionCycle()
        {
            // To determine which overload is better, we have to try to convert D<IIn<I>> to D<I>
            // and vice versa.  The former is impossible and the latter is possible if and only
            // if it is possible (i.e. cycle).
            var text = @"
interface IIn<in TIn> { }
interface I : IIn<IIn<I>> { }

delegate T D<out T>();

class C
{
    static void Goo(D<IIn<I>> x) { }
    static void Goo(D<I> x) { }
    static void M()
    {
        Goo(null);
    }
}
";
            CreateCompilation(text).VerifyDiagnostics(
                // (13,9): error CS0121: The call is ambiguous between the following methods or properties: 'C.Goo(D<IIn<I>>)' and 'C.Goo(D<I>)'
                Diagnostic(ErrorCode.ERR_AmbigCall, "Goo").WithArguments("C.Goo(D<IIn<I>>)", "C.Goo(D<I>)"));
        }

        /// <remarks>http://blogs.msdn.com/b/ericlippert/archive/2008/05/07/covariance-and-contravariance-part-twelve-to-infinity-but-not-beyond.aspx</remarks>
        [Fact]
        [WorkItem(539538, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539538")]
        public void TestVarianceConversionInfiniteExpansion01()
        {
            // IC<double> is convertible to IN<IC<string>> if and only
            // if IC<IC<double>> is convertible to IN<IC<IC<string>>>.
            var text = @"
public interface IN<in U> {}
public interface IC<X> : IN<IN<IC<IC<X>>>> {}

class C
{
    static void M()
    {
        IC<double> bar = null;
        IN<IC<string>> goo = bar;
    }
}
";
            CreateCompilation(text).VerifyDiagnostics(
                // (10,30): error CS0266: Cannot implicitly convert type 'IC<double>' to 'IN<IC<string>>'. An explicit conversion exists (are you missing a cast?)
                Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "bar").WithArguments("IC<double>", "IN<IC<string>>"));
        }

        /// <remarks>http://blogs.msdn.com/b/ericlippert/archive/2008/05/07/covariance-and-contravariance-part-twelve-to-infinity-but-not-beyond.aspx</remarks>
        [Fact]
        [WorkItem(539538, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539538")]
        public void TestVarianceConversionInfiniteExpansion02()
        {
            var text = @"
interface A<in B> where B : class
{
}

interface B<in A> where A : class
{
}

class X : A<B<X>>
{
}

class Y : B<A<Y>>
{
}

class Test
{
     static void Main ()
     {
         A<Y> x = new X ();
     }
}
";
            CreateCompilation(text).VerifyDiagnostics(
                // (22,19): error CS0266: Cannot implicitly convert type 'X' to 'A<Y>'. An explicit conversion exists (are you missing a cast?)
                //          A<Y> x = new X ();
                Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "new X ()").WithArguments("X", "A<Y>")
                );
        }

        [WorkItem(539538, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539538")]
        [Fact]
        public void TestVarianceConversionLongFailure()
        {
            // IC<double> is convertible to IN<IC<string>> if and only
            // if IC<IC<double>> is convertible to IN<IC<IC<string>>>.
            var text = @"
interface A : B<B<A>> {}
interface B<T> : C<C<T>> {}
interface C<T> : D<D<T>> {}
interface D<T> : E<E<T>> {}
interface E<T> : F<F<T>> {}
interface F<T> : N<N<T>> {}

interface X : Y<Y<N<N<X>>>> {}
interface Y<T> : Z<Z<T>> {}
interface Z<T> : W<W<T>> {}
interface W<T> : U<U<T>> {}
interface U<T> : V<V<T>> {}
interface V<T> : N<N<T>> {}

interface N<in T> {}

class M {

  static void Main() {
    A a  = null;
    N<X> nx = a;
  }
}
";
            CreateCompilation(text).VerifyDiagnostics(
                // (22,15): error CS0266: Cannot implicitly convert type 'A' to 'N<X>'. An explicit conversion exists (are you missing a cast?)
                Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "a").WithArguments("A", "N<X>"));
        }

        [WorkItem(539538, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539538")]
        [WorkItem(529488, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529488")]
        [Fact]
        public void TestVarianceConversionLongSuccess_Breaking()
        {
            // IC<double> is convertible to IN<IC<string>> if and only
            // if IC<IC<double>> is convertible to IN<IC<IC<string>>>.
            var text = @"
interface A : B<B<A>> { }
interface B<T> : C<C<T>> { }
interface C<T> : D<D<T>> { }
interface D<T> : E<E<T>> { }
interface E<T> : F<F<T>> { }
interface F<T> : N<N<T>> { }

interface X : Y<Y<N<F<E<D<C<B<A>>>>>>>> { }
interface Y<T> : Z<Z<T>> { }
interface Z<T> : W<W<T>> { }
interface W<T> : U<U<T>> { }
interface U<T> : V<V<T>> { }
interface V<T> : N<N<T>> { }

interface N<in T> { }

class M
{

    static void Main()
    {
        A a = null;
        N<X> nx = a;
    }
}
";
            // There should not be any diagnostics, but we blow our stack and make a guess.
            // NB: this is a breaking change.
            CreateCompilation(text).VerifyDiagnostics(
                // (24,19): error CS0266: Cannot implicitly convert type 'A' to 'N<X>'. An explicit conversion exists (are you missing a cast?)
                //         N<X> nx = a;
                Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "a").WithArguments("A", "N<X>"));
        }

        [WorkItem(542482, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542482")]
        [Fact]
        public void CS1961ERR_UnexpectedVariance_ConstraintTypes()
        {
            var text =
@"interface IIn<in T> { }
interface IOut<out T> { }
class C<T> { }
interface I1<out T, in U, V>
{
    void M<X>() where X : T, U, V;
}
interface I2<out T, in U, V>
{
    void M<X>() where X : IOut<T>, IOut<U>, IOut<V>;
}
interface I3<out T, in U, V>
{
    void M<X>() where X : IIn<T>, IIn<U>, IIn<V>;
}
interface I4<out T, in U, V>
{
    void M1<X>() where X : C<T>;
    void M2<X>() where X : C<U>;
    void M3<X>() where X : C<V>;
}";
            CreateCompilation(text).VerifyDiagnostics(
                // (6,12): error CS1961: Invalid variance: The type parameter 'T' must be contravariantly valid on 'I1<T, U, V>.M<X>()'. 'T' is covariant.
                //     void M<X>() where X : T, U, V;
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "X").WithArguments("I1<T, U, V>.M<X>()", "T", "covariant", "contravariantly").WithLocation(6, 12),
                // (10,12): error CS1961: Invalid variance: The type parameter 'T' must be contravariantly valid on 'I2<T, U, V>.M<X>()'. 'T' is covariant.
                //     void M<X>() where X : IOut<T>, IOut<U>, IOut<V>;
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "X").WithArguments("I2<T, U, V>.M<X>()", "T", "covariant", "contravariantly").WithLocation(10, 12),
                // (14,12): error CS1961: Invalid variance: The type parameter 'U' must be covariantly valid on 'I3<T, U, V>.M<X>()'. 'U' is contravariant.
                //     void M<X>() where X : IIn<T>, IIn<U>, IIn<V>;
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "X").WithArguments("I3<T, U, V>.M<X>()", "U", "contravariant", "covariantly").WithLocation(14, 12),
                // (18,13): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'I4<T, U, V>.M1<X>()'. 'T' is covariant.
                //     void M1<X>() where X : C<T>;
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "X").WithArguments("I4<T, U, V>.M1<X>()", "T", "covariant", "invariantly").WithLocation(18, 13),
                // (19,13): error CS1961: Invalid variance: The type parameter 'U' must be invariantly valid on 'I4<T, U, V>.M2<X>()'. 'U' is contravariant.
                //     void M2<X>() where X : C<U>;
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "X").WithArguments("I4<T, U, V>.M2<X>()", "U", "contravariant", "invariantly").WithLocation(19, 13));
        }

        [Fact]
        public void CS1961ERR_UnexpectedVariance_ClassesAndStructs()
        {
            var text =
@"class C<T> { }
struct S<T> { }
interface I1<out T, U>
{
    C<T> M(C<U> o);
}
interface I2<out T, U>
{
    C<U> M(C<T> o);
}
interface I3<out T, U>
{
    S<T> M(S<U> o);
}
interface I4<out T, U>
{
    S<U> M(S<T> o);
}
interface I5<in T, U>
{
    C<T> M(C<U> o);
}
interface I6<in T, U>
{
    C<U> M(C<T> o);
}
interface I7<in T, U>
{
    S<T> M(S<U> o);
}
interface I8<in T, U>
{
    S<U> M(S<T> o);
}";
            CreateCompilation(text).VerifyDiagnostics(
                // (5,5): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'I1<T, U>.M(C<U>)'. 'T' is covariant.
                //     C<T> M(C<U> o);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "C<T>").WithArguments("I1<T, U>.M(C<U>)", "T", "covariant", "invariantly").WithLocation(5, 5),
                // (9,12): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'I2<T, U>.M(C<T>)'. 'T' is covariant.
                //     C<U> M(C<T> o);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "C<T>").WithArguments("I2<T, U>.M(C<T>)", "T", "covariant", "invariantly").WithLocation(9, 12),
                // (13,5): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'I3<T, U>.M(S<U>)'. 'T' is covariant.
                //     S<T> M(S<U> o);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "S<T>").WithArguments("I3<T, U>.M(S<U>)", "T", "covariant", "invariantly").WithLocation(13, 5),
                // (17,12): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'I4<T, U>.M(S<T>)'. 'T' is covariant.
                //     S<U> M(S<T> o);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "S<T>").WithArguments("I4<T, U>.M(S<T>)", "T", "covariant", "invariantly").WithLocation(17, 12),
                // (21,5): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'I5<T, U>.M(C<U>)'. 'T' is contravariant.
                //     C<T> M(C<U> o);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "C<T>").WithArguments("I5<T, U>.M(C<U>)", "T", "contravariant", "invariantly").WithLocation(21, 5),
                // (25,12): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'I6<T, U>.M(C<T>)'. 'T' is contravariant.
                //     C<U> M(C<T> o);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "C<T>").WithArguments("I6<T, U>.M(C<T>)", "T", "contravariant", "invariantly").WithLocation(25, 12),
                // (29,5): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'I7<T, U>.M(S<U>)'. 'T' is contravariant.
                //     S<T> M(S<U> o);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "S<T>").WithArguments("I7<T, U>.M(S<U>)", "T", "contravariant", "invariantly").WithLocation(29, 5),
                // (33,12): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'I8<T, U>.M(S<T>)'. 'T' is contravariant.
                //     S<U> M(S<T> o);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "S<T>").WithArguments("I8<T, U>.M(S<T>)", "T", "contravariant", "invariantly").WithLocation(33, 12));
        }

        [WorkItem(602022, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/602022")]
        [Fact]
        public void CS1961ERR_UnexpectedVariance_Enums()
        {
            var text =
@"class C<T>
{
    public enum E { }
}
interface I1<out T, U>
{
    C<T>.E M(C<U>.E o);
}
interface I2<out T, U>
{
    C<U>.E M(C<T>.E o);
}
interface I3<in T, U>
{
    C<T>.E M(C<U>.E o);
}
interface I4<in T, U>
{
    C<U>.E M(C<T>.E o);
}";
            CreateCompilation(text).VerifyDiagnostics(
                // (7,5): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'I1<T, U>.M(C<U>.E)'. 'T' is covariant.
                //     C<T>.E M(C<U>.E o);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "C<T>.E").WithArguments("I1<T, U>.M(C<U>.E)", "T", "covariant", "invariantly").WithLocation(7, 5),
                // (11,14): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'I2<T, U>.M(C<T>.E)'. 'T' is covariant.
                //     C<U>.E M(C<T>.E o);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "C<T>.E").WithArguments("I2<T, U>.M(C<T>.E)", "T", "covariant", "invariantly").WithLocation(11, 14),
                // (15,5): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'I3<T, U>.M(C<U>.E)'. 'T' is contravariant.
                //     C<T>.E M(C<U>.E o);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "C<T>.E").WithArguments("I3<T, U>.M(C<U>.E)", "T", "contravariant", "invariantly").WithLocation(15, 5),
                // (19,14): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'I4<T, U>.M(C<T>.E)'. 'T' is contravariant.
                //     C<U>.E M(C<T>.E o);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "C<T>.E").WithArguments("I4<T, U>.M(C<T>.E)", "T", "contravariant", "invariantly").WithLocation(19, 14));
        }

        [WorkItem(542794, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542794")]
        [Fact]
        public void ContravariantBaseInterface()
        {
            var text =
@"
interface IA<in T> { }
interface IB<in T> : IA<IB<T>> { }
";
            CreateCompilation(text).VerifyDiagnostics(
                // (3,17): error CS1961: Invalid variance: The type parameter 'T' must be covariantly valid on 'IA<IB<T>>'. 'T' is contravariant.
                // interface IB<in T> : IA<IB<T>> { }
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "T").WithArguments("IA<IB<T>>", "T", "contravariant", "covariantly").WithLocation(3, 17));
        }

        /// <summary>
        /// Report errors on type parameter use
        /// rather than declaration.
        /// </summary>
        [WorkItem(855750, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/855750")]
        [Fact]
        public void ErrorLocations()
        {
            var text =
@"interface IIn<in T> { }
interface IOut<out T> { }
class C<T> { }
delegate void D<in T>();
interface I<out T, in U>
{
    C<U> M(C<T> o);
    IIn<T>[] P { get; }
    U this[object o] { get; }
    event D<U> E;
    void M<X>()
        where X : C<IIn<U>>, IOut<T>;
}
interface I<out T> :
    I<T, T>
{
}";
            CreateCompilation(text).VerifyDiagnostics(
                // (7,5): error CS1961: Invalid variance: The type parameter 'U' must be invariantly valid on 'I<T, U>.M(C<T>)'. 'U' is contravariant.
                //     C<U> M(C<T> o);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "C<U>").WithArguments("I<T, U>.M(C<T>)", "U", "contravariant", "invariantly").WithLocation(7, 5),
                // (7,12): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'I<T, U>.M(C<T>)'. 'T' is covariant.
                //     C<U> M(C<T> o);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "C<T>").WithArguments("I<T, U>.M(C<T>)", "T", "covariant", "invariantly").WithLocation(7, 12),
                // (8,5): error CS1961: Invalid variance: The type parameter 'T' must be contravariantly valid on 'I<T, U>.P'. 'T' is covariant.
                //     IIn<T>[] P { get; }
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "IIn<T>[]").WithArguments("I<T, U>.P", "T", "covariant", "contravariantly").WithLocation(8, 5),
                // (9,5): error CS1961: Invalid variance: The type parameter 'U' must be covariantly valid on 'I<T, U>.this[object]'. 'U' is contravariant.
                //     U this[object o] { get; }
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "U").WithArguments("I<T, U>.this[object]", "U", "contravariant", "covariantly").WithLocation(9, 5),
                // (10,16): error CS1961: Invalid variance: The type parameter 'U' must be covariantly valid on 'I<T, U>.E'. 'U' is contravariant.
                //     event D<U> E;
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "E").WithArguments("I<T, U>.E", "U", "contravariant", "covariantly").WithLocation(10, 16),
                // (11,12): error CS1961: Invalid variance: The type parameter 'U' must be invariantly valid on 'I<T, U>.M<X>()'. 'U' is contravariant.
                //     void M<X>()
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "X").WithArguments("I<T, U>.M<X>()", "U", "contravariant", "invariantly").WithLocation(11, 12),
                // (11,12): error CS1961: Invalid variance: The type parameter 'T' must be contravariantly valid on 'I<T, U>.M<X>()'. 'T' is covariant.
                //     void M<X>()
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "X").WithArguments("I<T, U>.M<X>()", "T", "covariant", "contravariantly").WithLocation(11, 12),
                // (14,17): error CS1961: Invalid variance: The type parameter 'T' must be contravariantly valid on 'I<T, T>'. 'T' is covariant.
                // interface I<out T> :
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "T").WithArguments("I<T, T>", "T", "covariant", "contravariantly").WithLocation(14, 17));
        }

        [Fact]
        public void CovarianceBoundariesForRefReadOnly_Parameters()
        {
            CreateCompilation(@"
interface ITest<in T>
{
    void M(in T p);
}").VerifyDiagnostics(
                // (4,15): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'ITest<T>.M(in T)'. 'T' is contravariant.
                //     void M(in T p);
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "T").WithArguments("ITest<T>.M(in T)", "T", "contravariant", "invariantly").WithLocation(4, 15));
        }

        [Fact]
        public void CovarianceBoundariesForRefReadOnly_ReturnType()
        {
            CreateCompilation(@"
interface ITest<in T>
{
    ref readonly T M();
}").VerifyDiagnostics(
                // (4,5): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'ITest<T>.M()'. 'T' is contravariant.
                //     ref readonly T M();
                Diagnostic(ErrorCode.ERR_UnexpectedVariance, "ref readonly T").WithArguments("ITest<T>.M()", "T", "contravariant", "invariantly").WithLocation(4, 5));
        }
    }
}
