|
// 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.Collections.Immutable;
using System.Linq;
using Basic.Reference.Assemblies;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
/// <summary>
/// Tests related to binding (but not lowering) foreach loops.
/// </summary>
public class ForEachTests : CompilingTestBase
{
[Fact]
public void TestErrorBadElementType()
{
// See bug 7419.
var text = @"
class C
{
static void Main()
{
System.Collections.IEnumerable sequence = null;
foreach (MissingType x in sequence)
{
bool b = !x.Equals(null);
}
}
}";
CreateCompilation(text).VerifyDiagnostics(
// (7,18): error CS0246: The type or namespace name 'MissingType' could not be found (are you missing a using directive or an assembly reference?)
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "MissingType").WithArguments("MissingType"));
}
[Fact]
public void TestErrorNullLiteralCollection()
{
var text = @"
class C
{
static void Main()
{
foreach (int x in null)
{
}
}
}";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0186: Use of null is not valid in this context
Diagnostic(ErrorCode.ERR_NullNotValid, "null"));
}
[Fact]
public void TestErrorNullConstantCollection()
{
var text = @"
class C
{
static void Main()
{
const object NULL = null;
foreach (int x in NULL)
{
}
}
}";
CreateCompilation(text).VerifyDiagnostics(
// (7,27): error CS1579: foreach statement cannot operate on variables of type 'object' because 'object' does not contain a public instance or extension definition for 'GetEnumerator'
// foreach (int x in NULL)
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "NULL").WithArguments("object", "GetEnumerator").WithLocation(7, 27)
);
}
[WorkItem(540957, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540957")]
[Fact]
public void TestErrorDefaultOfArrayType()
{
var text = @"
class C
{
static void Main()
{
foreach (int x in default(int[]))
{
}
}
}";
CreateCompilation(text).VerifyDiagnostics(
// (7,27): error CS0186: Use of null is not valid in this context
Diagnostic(ErrorCode.ERR_NullNotValid, "default(int[])"));
}
[Fact]
public void TestErrorLambdaCollection()
{
var text = @"
class C
{
static void Main()
{
foreach (int x in (() => {}))
{
}
}
}";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0446: Foreach cannot operate on a 'lambda expression'. Did you intend to invoke the 'lambda expression'?
Diagnostic(ErrorCode.ERR_AnonMethGrpInForEach, "(() => {})").WithArguments("lambda expression"));
}
[Fact]
public void TestErrorMethodGroupCollection()
{
var text = @"
class C
{
static void Main()
{
foreach (int x in Main)
{
}
}
}";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0446: Foreach cannot operate on a 'method group'. Did you intend to invoke the 'method group'?
Diagnostic(ErrorCode.ERR_AnonMethGrpInForEach, "Main").WithArguments("method group"));
}
[Fact]
public void TestErrorNoElementConversion()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in args)
{
}
}
}";
CreateCompilation(text).VerifyDiagnostics(
// (6,9): error CS0030: Cannot convert type 'string' to 'int'
Diagnostic(ErrorCode.ERR_NoExplicitConv, "foreach").WithArguments("string", "int"));
}
[Fact]
public void TestErrorPatternNoGetEnumerator()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
//public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public int Current { get { return 1; } }
public bool MoveNext() { return false; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS1579: foreach statement cannot operate on variables of type 'Enumerable' because 'Enumerable' does not contain a public definition for 'GetEnumerator'
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "new Enumerable()").WithArguments("Enumerable", "GetEnumerator"));
}
[Fact]
public void TestErrorPatternInaccessibleGetEnumerator()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
private Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public int Current { get { return 1; } }
public bool MoveNext() { return false; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS1579: foreach statement cannot operate on variables of type 'Enumerable' because 'Enumerable' does not contain a public definition for 'GetEnumerator'
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "new Enumerable()").WithArguments("Enumerable", "GetEnumerator"));
}
[Fact]
public void TestErrorPatternNonPublicGetEnumerator()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
internal Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public int Current { get { return 1; } }
public bool MoveNext() { return false; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): warning CS0279: 'Enumerable' does not implement the 'collection' pattern. 'Enumerable.GetEnumerator()' is not a public instance or extension method.
Diagnostic(ErrorCode.WRN_PatternNotPublicOrNotInstance, "new Enumerable()").WithArguments("Enumerable", "collection", "Enumerable.GetEnumerator()"),
// (6,27): error CS1579: foreach statement cannot operate on variables of type 'Enumerable' because 'Enumerable' does not contain a public definition for 'GetEnumerator'
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "new Enumerable()").WithArguments("Enumerable", "GetEnumerator"));
}
[Fact]
public void TestErrorPatternStaticGetEnumerator()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
public static Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public int Current { get { return 1; } }
public bool MoveNext() { return false; }
}
";
CreateCompilation(text, parseOptions: TestOptions.Regular7).VerifyDiagnostics(
// (6,27): warning CS0279: 'Enumerable' does not implement the 'collection' pattern. 'Enumerable.GetEnumerator()' is not a public instance or extension method.
Diagnostic(ErrorCode.WRN_PatternNotPublicOrNotInstance, "new Enumerable()").WithArguments("Enumerable", "collection", "Enumerable.GetEnumerator()"),
// (6,27): error CS1579: foreach statement cannot operate on variables of type 'Enumerable' because 'Enumerable' does not contain a public definition for 'GetEnumerator'
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "new Enumerable()").WithArguments("Enumerable", "GetEnumerator"));
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS1579: foreach statement cannot operate on variables of type 'Enumerable' because 'Enumerable' does not contain a public instance or extension definition for 'GetEnumerator'
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "new Enumerable()").WithArguments("Enumerable", "GetEnumerator"));
}
[Fact]
public void TestErrorPatternNullableGetEnumerator()
{
var text = @"
struct C
{
static void Goo(Enumerable? e)
{
foreach (long x in e) { }
}
static void Main()
{
Goo(new Enumerable());
}
}
struct Enumerable
{
public Enumerator? GetEnumerator() { return new Enumerator(); }
}
struct Enumerator
{
public int Current { get { return 1; } }
public bool MoveNext() { return false; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,28): error CS0117: 'Enumerator?' does not contain a definition for 'Current'
Diagnostic(ErrorCode.ERR_NoSuchMember, "e").WithArguments("Enumerator?", "Current"),
// (6,28): error CS0202: foreach requires that the return type 'Enumerator?' of 'Enumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "e").WithArguments("Enumerator?", "Enumerable.GetEnumerator()"));
}
[Fact]
public void TestErrorPatternNoCurrent()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
//public int Current { get { return 1; } }
public bool MoveNext() { return false; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0117: 'Enumerator' does not contain a definition for 'Current'
Diagnostic(ErrorCode.ERR_NoSuchMember, "new Enumerable()").WithArguments("Enumerator", "Current"),
// (6,27): error CS0202: foreach requires that the return type 'Enumerator' of 'Enumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new Enumerable()").WithArguments("Enumerator", "Enumerable.GetEnumerator()"));
}
[Fact]
public void TestErrorPatternInaccessibleCurrent()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
private int Current { get { return 1; } }
public bool MoveNext() { return false; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0122: 'Enumerator.Current' is inaccessible due to its protection level
Diagnostic(ErrorCode.ERR_BadAccess, "new Enumerable()").WithArguments("Enumerator.Current"),
// (6,27): error CS0202: foreach requires that the return type 'Enumerator' of 'Enumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new Enumerable()").WithArguments("Enumerator", "Enumerable.GetEnumerator()"));
}
[Fact]
public void TestErrorPatternNonPublicCurrent()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
internal int Current { get { return 1; } }
public bool MoveNext() { return false; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0202: foreach requires that the return type 'Enumerator' of 'Enumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new Enumerable()").WithArguments("Enumerator", "Enumerable.GetEnumerator()"));
}
[Fact]
public void TestErrorPatternStaticCurrent()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public static int Current { get { return 1; } }
public bool MoveNext() { return false; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0202: foreach requires that the return type 'Enumerator' of 'Enumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new Enumerable()").WithArguments("Enumerator", "Enumerable.GetEnumerator()"));
}
[Fact]
public void TestErrorPatternNonPropertyCurrent()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public int Current;
public bool MoveNext() { return false; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0202: foreach requires that the return type 'Enumerator' of 'Enumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
// foreach (int x in new Enumerable())
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new Enumerable()").WithArguments("Enumerator", "Enumerable.GetEnumerator()"),
// (19,16): warning CS0649: Field 'Enumerator.Current' is never assigned to, and will always have its default value 0
// public int Current;
Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Current").WithArguments("Enumerator.Current", "0")
);
}
[Fact]
public void TestErrorPatternNoMoveNext()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public int Current { get { return 1; } }
//public bool MoveNext() { return false; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0117: 'Enumerator' does not contain a definition for 'MoveNext'
Diagnostic(ErrorCode.ERR_NoSuchMember, "new Enumerable()").WithArguments("Enumerator", "MoveNext"),
// (6,27): error CS0202: foreach requires that the return type 'Enumerator' of 'Enumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new Enumerable()").WithArguments("Enumerator", "Enumerable.GetEnumerator()"));
}
[Fact]
public void TestErrorPatternInaccessibleMoveNext()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public int Current { get { return 1; } }
private bool MoveNext() { return false; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0122: 'Enumerator.MoveNext()' is inaccessible due to its protection level
Diagnostic(ErrorCode.ERR_BadAccess, "new Enumerable()").WithArguments("Enumerator.MoveNext()"),
// (6,27): error CS0202: foreach requires that the return type 'Enumerator' of 'Enumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new Enumerable()").WithArguments("Enumerator", "Enumerable.GetEnumerator()"));
}
[Fact]
public void TestErrorPatternNonPublicMoveNext()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public int Current { get { return 1; } }
internal bool MoveNext() { return false; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0202: foreach requires that the return type 'Enumerator' of 'Enumerable.GetEnumerator()' must have a suitable public 'MoveNext' method and public 'Current' property
// foreach (int x in new Enumerable())
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new Enumerable()").WithArguments("Enumerator", "Enumerable.GetEnumerator()").WithLocation(6, 27)
);
}
[Fact]
public void TestErrorPatternStaticMoveNext()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public int Current { get { return 1; } }
public static bool MoveNext() { return false; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0202: foreach requires that the return type 'Enumerator' of 'Enumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new Enumerable()").WithArguments("Enumerator", "Enumerable.GetEnumerator()"));
}
[Fact]
public void TestErrorPatternNonMethodMoveNext()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public int Current { get { return 1; } }
public bool MoveNext;
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0202: foreach requires that the return type 'Enumerator' of 'Enumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
// foreach (int x in new Enumerable())
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new Enumerable()").WithArguments("Enumerator", "Enumerable.GetEnumerator()"),
// (20,17): warning CS0649: Field 'Enumerator.MoveNext' is never assigned to, and will always have its default value false
// public bool MoveNext;
Diagnostic(ErrorCode.WRN_UnassignedInternalField, "MoveNext").WithArguments("Enumerator.MoveNext", "false")
);
}
[Fact]
public void TestErrorPatternNonBoolMoveNext()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public int Current { get { return 1; } }
public int MoveNext() { return 1; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0202: foreach requires that the return type 'Enumerator' of 'Enumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new Enumerable()").WithArguments("Enumerator", "Enumerable.GetEnumerator()"));
}
[Fact]
public void TestErrorPatternNullableBoolMoveNext()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public int Current { get { return 1; } }
public bool? MoveNext() { return true; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0202: foreach requires that the return type 'Enumerator' of 'Enumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new Enumerable()").WithArguments("Enumerator", "Enumerable.GetEnumerator()"));
}
[Fact]
public void TestErrorPatternNoMoveNextOrCurrent()
{
var text = @"
class C
{
static void Main(string[] args)
{
foreach (int x in new Enumerable())
{
}
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
//public int Current { get { return 1; } }
//public bool MoveNext() { return false; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0117: 'Enumerator' does not contain a definition for 'Current'
Diagnostic(ErrorCode.ERR_NoSuchMember, "new Enumerable()").WithArguments("Enumerator", "Current"),
// (6,27): error CS0202: foreach requires that the return type 'Enumerator' of 'Enumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new Enumerable()").WithArguments("Enumerator", "Enumerable.GetEnumerator()"));
}
[Fact]
public void TestErrorMultipleIEnumerableT()
{
var text = @"
using System.Collections;
using System.Collections.Generic;
class C
{
void Goo(Enumerable e)
{
foreach (int x in e) { }
}
}
class Enumerable : IEnumerable<int>, IEnumerable<float>
{
IEnumerator<float> IEnumerable<float>.GetEnumerator() { throw null; }
IEnumerator<int> IEnumerable<int>.GetEnumerator() { throw null; }
IEnumerator IEnumerable.GetEnumerator() { throw null; }
}
";
CreateCompilation(text).VerifyDiagnostics(
// (9,27): error CS1640: foreach statement cannot operate on variables of type 'Enumerable' because it implements multiple instantiations of 'System.Collections.Generic.IEnumerable<T>'; try casting to a specific interface instantiation
Diagnostic(ErrorCode.ERR_MultipleIEnumOfT, "e").WithArguments("Enumerable", "System.Collections.Generic.IEnumerable<T>"));
}
[Fact]
public void TestErrorMultipleIEnumerableT2()
{
var text = @"
using System.Collections;
using System.Collections.Generic;
class C
{
void Goo(Enumerable e)
{
foreach (int x in e) { }
}
}
class Enumerable : IEnumerable<int>, I
{
IEnumerator<float> IEnumerable<float>.GetEnumerator() { throw null; }
IEnumerator<int> IEnumerable<int>.GetEnumerator() { throw null; }
IEnumerator IEnumerable.GetEnumerator() { throw null; }
}
interface I : IEnumerable<float> { }
";
CreateCompilation(text).VerifyDiagnostics(
// (9,27): error CS1640: foreach statement cannot operate on variables of type 'Enumerable' because it implements multiple instantiations of 'System.Collections.Generic.IEnumerable<T>'; try casting to a specific interface instantiation
Diagnostic(ErrorCode.ERR_MultipleIEnumOfT, "e").WithArguments("Enumerable", "System.Collections.Generic.IEnumerable<T>"));
}
/// <summary>
/// Type parameter with constraints containing
/// IEnumerable<T> with explicit implementations.
/// </summary>
[Fact]
public void TestErrorExplicitIEnumerableTOnTypeParameter()
{
var text =
@"using System.Collections;
using System.Collections.Generic;
struct S { }
interface I : IEnumerable<S> { }
class A : IEnumerable<int>
{
IEnumerator<int> IEnumerable<int>.GetEnumerator() { return null; }
IEnumerator IEnumerable.GetEnumerator() { return null; }
}
class B : A, I
{
IEnumerator<S> IEnumerable<S>.GetEnumerator() { return null; }
IEnumerator IEnumerable.GetEnumerator() { return null; }
}
class C
{
static void M<T1, T2, T3, T4, T5, T6>(A a, B b, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
where T1 : A
where T2 : B
where T3 : I
where T4 : T1, I
where T5 : A, I
where T6 : A, IEnumerable<string>
{
foreach (int o in a) { }
foreach (var o in b) { }
foreach (int o in t1) { }
foreach (var o in t2) { }
foreach (S o in t3) { }
foreach (S o in t4) { }
foreach (S o in t5) { }
foreach (string o in t6) { }
}
}";
CreateCompilation(text).VerifyDiagnostics(
// (26,27): error CS1640: foreach statement cannot operate on variables of type 'B' because it implements multiple instantiations of 'System.Collections.Generic.IEnumerable<T>'; try casting to a specific interface instantiation
Diagnostic(ErrorCode.ERR_MultipleIEnumOfT, "b").WithArguments("B", "System.Collections.Generic.IEnumerable<T>").WithLocation(26, 27),
// (28,27): error CS1640: foreach statement cannot operate on variables of type 'T2' because it implements multiple instantiations of 'System.Collections.Generic.IEnumerable<T>'; try casting to a specific interface instantiation
Diagnostic(ErrorCode.ERR_MultipleIEnumOfT, "t2").WithArguments("T2", "System.Collections.Generic.IEnumerable<T>").WithLocation(28, 27));
}
/// <summary>
/// Type parameter with constraints containing
/// IEnumerable<T> with implicit implementations.
/// </summary>
[Fact]
public void TestErrorImplicitIEnumerableTOnTypeParameter()
{
var text =
@"using System.Collections;
using System.Collections.Generic;
struct S { }
interface I : IEnumerable<S> { }
class A : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator() { return null; }
IEnumerator IEnumerable.GetEnumerator() { return null; }
}
class B : A, I
{
public new IEnumerator<S> GetEnumerator() { return null; }
IEnumerator IEnumerable.GetEnumerator() { return null; }
}
class C
{
static void M<T1, T2, T3, T4, T5, T6>(A a, B b, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
where T1 : A
where T2 : B
where T3 : I
where T4 : T1, I
where T5 : A, I
where T6 : A, IEnumerable<string>
{
foreach (int o in a) { }
foreach (var o in b) { }
foreach (int o in t1) { }
foreach (var o in t2) { }
foreach (S o in t3) { }
foreach (int o in t4) { }
foreach (S o in t5) { }
foreach (string o in t6) { }
}
}";
CreateCompilation(text).VerifyDiagnostics(
// (31,9): error CS0030: Cannot convert type 'int' to 'S'
Diagnostic(ErrorCode.ERR_NoExplicitConv, "foreach").WithArguments("int", "S").WithLocation(31, 9),
// (32,9): error CS0030: Cannot convert type 'int' to 'string'
Diagnostic(ErrorCode.ERR_NoExplicitConv, "foreach").WithArguments("int", "string").WithLocation(32, 9));
}
/// <summary>
/// Type parameter with constraints
/// using enumerable pattern.
/// </summary>
[Fact]
public void TestErrorEnumerablePatternOnTypeParameter()
{
var text =
@"using System.Collections.Generic;
interface I
{
IEnumerator<object> GetEnumerator();
}
class E : I
{
IEnumerator<object> I.GetEnumerator() { return null; }
}
class C
{
static void M<T, U, V>(T t, U u, V v)
where T : E
where U : E, I
where V : T, I
{
foreach (var o in t) { }
foreach (var o in u) { }
foreach (var o in v) { }
}
}";
CreateCompilation(text).VerifyDiagnostics(
// (17,27): error CS1579: foreach statement cannot operate on variables of type 'T' because 'T' does not contain a public definition for 'GetEnumerator'
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "t").WithArguments("T", "GetEnumerator").WithLocation(17, 27));
}
[Fact]
public void TestErrorNonEnumerable()
{
var text = @"
class C
{
void Goo(int i)
{
foreach (int x in i) { }
}
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS1579: foreach statement cannot operate on variables of type 'int' because 'int' does not contain a public definition for 'GetEnumerator'
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "i").WithArguments("int", "GetEnumerator"));
}
[Fact]
public void TestErrorModifyIterationVariable()
{
var text = @"
class C
{
void Goo(int[] a)
{
foreach (int x in a) { x++; }
}
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,32): error CS1656: Cannot assign to 'x' because it is a 'foreach iteration variable'
Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "x").WithArguments("x", "foreach iteration variable"));
}
[Fact]
public void TestErrorImplicitlyTypedCycle()
{
var text = @"
class C
{
System.Collections.IEnumerable Goo(object y)
{
foreach (var x in Goo(x)) { }
return null;
}
}
";
CreateCompilation(text).VerifyDiagnostics(
// (6,31): error CS0103: The name 'x' does not exist in the current context
Diagnostic(ErrorCode.ERR_NameNotInContext, "x").WithArguments("x"));
}
[Fact]
public void TestErrorDynamicEnumerator()
{
var text = @"
class C
{
void Goo(DynamicEnumerable e)
{
foreach (int x in e) { }
}
}
public class DynamicEnumerable
{
public dynamic GetEnumerator() { return null; }
}
";
// It's not entirely clear why this doesn't work, but it doesn't work in Dev10 either.
CreateCompilationWithMscorlib40AndSystemCore(text).VerifyDiagnostics(
// (6,27): error CS0117: 'dynamic' does not contain a definition for 'Current'
Diagnostic(ErrorCode.ERR_NoSuchMember, "e").WithArguments("dynamic", "Current"),
// (6,27): error CS0202: foreach requires that the return type 'dynamic' of 'DynamicEnumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "e").WithArguments("dynamic", "DynamicEnumerable.GetEnumerator()"));
}
[Fact]
public void TestErrorTypeEnumerable()
{
var text = @"
class C
{
static void Main()
{
foreach (var x in System.Collections.IEnumerable) { }
}
}
";
// It's not entirely clear why this doesn't work, but it doesn't work in Dev10 either.
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0119: 'System.Collections.IEnumerable' is a 'type', which is not valid in the given context
// foreach (var x in System.Collections.IEnumerable) { }
Diagnostic(ErrorCode.ERR_BadSKunknown, "System.Collections.IEnumerable").WithArguments("System.Collections.IEnumerable", "type"));
}
[Fact]
public void TestErrorTypeNonEnumerable()
{
var text = @"
class C
{
void Goo()
{
foreach (int x in System.Console) { }
}
}
";
// It's not entirely clear why this doesn't work, but it doesn't work in Dev10 either.
CreateCompilation(text).VerifyDiagnostics(
// (6,27): error CS0119: 'System.Console' is a 'type', which is not valid in the given context
// foreach (int x in System.Console) { }
Diagnostic(ErrorCode.ERR_BadSKunknown, "System.Console").WithArguments("System.Console", "type"));
}
[WorkItem(545123, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545123")]
[Fact]
public void TestErrorForEachLoopWithSyntaxErrors()
{
string source = @"
public static partial class Extensions
{
public static ((System.Linq.Expressions.Expression<System.Func<int>>)(() => int )).Compile()()Fib(this int i)
{
}
}
public class ExtensionMethodTest
{
public static void Run()
{
int i = 0;
var Fib = new[]
{
i++.Fib()
};
foreach (var j in Fib)
{
}
}
}";
Assert.NotEmpty(CreateCompilation(source).GetDiagnostics());
}
[WorkItem(545123, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545123")]
[Fact]
public void TestErrorForEachOverVoidArray1()
{
string source = @"
class C
{
static void Main()
{
var array = new[] { Main() };
foreach (var element in array)
{
}
}
}
";
CreateCompilation(source).VerifyDiagnostics(
// (6,21): error CS0826: No best type found for implicitly-typed array
// var array = new[] { Main() };
Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, "new[] { Main() }"));
}
[WorkItem(545123, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545123")]
[Fact]
public void TestErrorForEachOverVoidArray2()
{
string source = @"
class C
{
static void Main()
{
var array = new[] { Main() };
foreach (int element in array)
{
}
}
}
";
CreateCompilation(source).VerifyDiagnostics(
// (6,21): error CS0826: No best type found for implicitly-typed array
// var array = new[] { Main() };
Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, "new[] { Main() }"),
// CONSIDER: Could eliminate this cascading diagnostic.
// (7,9): error CS0030: Cannot convert type '?' to 'int'
// foreach (int element in array)
Diagnostic(ErrorCode.ERR_NoExplicitConv, "foreach").WithArguments("?", "int"));
}
[WorkItem(545123, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545123")]
[Fact]
public void TestErrorForEachWithVoidIterationVariable()
{
string source = @"
class C
{
static void Main()
{
foreach (void element in new int[1])
{
}
}
}
";
CreateCompilation(source).VerifyDiagnostics(
// (6,18): error CS1547: Keyword 'void' cannot be used in this context
// foreach (void element in new int[1])
Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(6, 18),
// (6,9): error CS0030: Cannot convert type 'int' to 'void'
// foreach (void element in new int[1])
Diagnostic(ErrorCode.ERR_NoExplicitConv, "foreach").WithArguments("int", "void").WithLocation(6, 9)
);
}
[WorkItem(545123, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545123")]
[Fact]
public void TestErrorForEachOverErrorTypeArray()
{
string source = @"
class C
{
static void Main()
{
foreach (var element in new Unknown[1])
{
}
}
}
";
CreateCompilation(source).VerifyDiagnostics(
// (6,37): error CS0246: The type or namespace name 'Unknown' could not be found (are you missing a using directive or an assembly reference?)
// foreach (var element in new Unknown[1])
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Unknown").WithArguments("Unknown"));
}
[Fact]
public void TestSuccessArray()
{
var text = @"
class C
{
void Goo(int[] a)
{
foreach (int x in a) { }
}
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal("System.Collections.IEnumerable", info.CollectionType.ToTestDisplayString()); //NB: differs from expression type
Assert.Equal(SpecialType.System_Int32, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Object System.Collections.IEnumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.ImplicitReference, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Unboxing, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal("System.Int32 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal(SpecialType.System_Collections_IEnumerable, boundNode.Expression.Type.SpecialType);
Assert.Equal(SymbolKind.ArrayType, ((BoundConversion)boundNode.Expression).Operand.Type.Kind);
}
private static Conversion GetCollectionConversion(BoundForEachStatement boundNode)
{
return ((BoundConversion)boundNode.Expression).Conversion;
}
[Fact]
public void TestSuccessString()
{
var text = @"
class C
{
void Goo(string s)
{
foreach (char c in s) { }
}
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal(SpecialType.System_String, info.CollectionType.SpecialType);
Assert.Equal(SpecialType.System_Char, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("System.CharEnumerator System.String.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Char System.CharEnumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean System.CharEnumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.Identity, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal("System.Char c", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal(SpecialType.System_String, boundNode.Expression.Type.SpecialType);
Assert.Equal(SpecialType.System_String, ((BoundConversion)boundNode.Expression).Operand.Type.SpecialType);
}
[Fact]
public void TestSuccessPattern()
{
var text = @"
class C
{
void Goo(Enumerable e)
{
foreach (long x in e) { }
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public int Current { get { return 1; } }
public bool MoveNext() { return false; }
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal("Enumerable", info.CollectionType.ToTestDisplayString());
Assert.Equal(SpecialType.System_Int32, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("Enumerator Enumerable.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Int32 Enumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean Enumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.Identity, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.ImplicitNumeric, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal("System.Int64 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("Enumerable", boundNode.Expression.Type.ToTestDisplayString());
Assert.Equal("Enumerable", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
[Fact]
public void TestSuccessPatternStruct()
{
var text = @"
struct C
{
void Goo(Enumerable e)
{
foreach (long x in e) { }
}
}
struct Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
struct Enumerator
{
public int Current { get { return 1; } }
public bool MoveNext() { return false; }
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal("Enumerable", info.CollectionType.ToTestDisplayString());
Assert.Equal(SpecialType.System_Int32, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("Enumerator Enumerable.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Int32 Enumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean Enumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.False(info.NeedsDisposal); // Definitely not disposable
Assert.Equal(ConversionKind.Identity, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.ImplicitNumeric, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal("System.Int64 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("Enumerable", boundNode.Expression.Type.ToTestDisplayString());
Assert.Equal("Enumerable", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
[Fact]
public void TestSuccessInterfacePattern()
{
var text = @"
class C
{
void Goo(System.Collections.IEnumerable e)
{
foreach (long x in e) { }
}
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal("System.Collections.IEnumerable", info.CollectionType.ToTestDisplayString());
Assert.Equal(SpecialType.System_Object, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Object System.Collections.IEnumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.Identity, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.Unboxing, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal("System.Int64 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("System.Collections.IEnumerable", boundNode.Expression.Type.ToTestDisplayString());
Assert.Equal("System.Collections.IEnumerable", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
[Fact]
public void TestSuccessInterfaceNonPatternGeneric()
{
var text = @"
class C
{
void Goo(Enumerable e)
{
foreach (long x in e) { }
}
}
class Enumerable : System.Collections.Generic.IEnumerable<int>
{
// Explicit implementations won't match pattern.
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return null; }
System.Collections.Generic.IEnumerator<int> System.Collections.Generic.IEnumerable<int>.GetEnumerator() { return null; }
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal("System.Collections.Generic.IEnumerable<System.Int32>", info.CollectionType.ToTestDisplayString()); //NB: differs from expression type
Assert.Equal(SpecialType.System_Int32, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("System.Collections.Generic.IEnumerator<System.Int32> System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Int32 System.Collections.Generic.IEnumerator<System.Int32>.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString()); //NB: not on generic interface
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.ImplicitReference, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.ImplicitNumeric, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal("System.Int64 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("System.Collections.Generic.IEnumerable<System.Int32>", boundNode.Expression.Type.ToTestDisplayString());
Assert.Equal("Enumerable", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
[Fact]
public void TestSuccessInterfaceNonPatternInaccessibleGeneric()
{
var text = @"
class C
{
void Goo(Enumerable e)
{
foreach (object x in e) { }
}
}
class Enumerable : System.Collections.Generic.IEnumerable<Enumerable.Hidden>
{
// Explicit implementations won't match pattern.
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return null; }
System.Collections.Generic.IEnumerator<Hidden> System.Collections.Generic.IEnumerable<Hidden>.GetEnumerator() { return null; }
private class Hidden { }
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal("System.Collections.IEnumerable", info.CollectionType.ToTestDisplayString()); //NB: fall back on non-generic, since generic is inaccessible
Assert.Equal(SpecialType.System_Object, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Object System.Collections.IEnumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.ImplicitReference, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal("System.Object x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal(SpecialType.System_Collections_IEnumerable, boundNode.Expression.Type.SpecialType);
Assert.Equal("Enumerable", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
[Fact]
public void TestSuccessInterfaceNonPatternNonGeneric()
{
var text = @"
class C
{
void Goo(Enumerable e)
{
foreach (long x in e) { }
}
}
class Enumerable : System.Collections.IEnumerable
{
// Explicit implementation won't match pattern.
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return null; }
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal("System.Collections.IEnumerable", info.CollectionType.ToTestDisplayString()); //NB: differs from expression type
Assert.Equal(SpecialType.System_Object, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Object System.Collections.IEnumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.ImplicitReference, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.Unboxing, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal("System.Int64 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal(SpecialType.System_Collections_IEnumerable, boundNode.Expression.Type.SpecialType);
Assert.Equal("Enumerable", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
[Fact]
public void TestSuccessImplicitlyTypedArray()
{
var text = @"
class C
{
void Goo(int[] a)
{
foreach (var x in a) { }
}
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal("System.Collections.IEnumerable", info.CollectionType.ToTestDisplayString()); //NB: differs from expression type
Assert.Equal(SpecialType.System_Int32, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Object System.Collections.IEnumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.ImplicitReference, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Unboxing, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal(SpecialType.System_Int32, boundNode.IterationVariables.Single().Type.SpecialType);
}
[Fact]
public void TestSuccessImplicitlyTypedString()
{
var text = @"
class C
{
void Goo(string s)
{
foreach (var x in s) { }
}
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal(SpecialType.System_String, info.CollectionType.SpecialType);
Assert.Equal(SpecialType.System_Char, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("System.CharEnumerator System.String.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Char System.CharEnumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean System.CharEnumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.Identity, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal(SpecialType.System_Char, boundNode.IterationVariables.Single().Type.SpecialType);
}
[Fact]
public void TestSuccessImplicitlyTypedPattern()
{
var text = @"
class C
{
void Goo(Enumerable e)
{
foreach (var x in e) { }
}
}
class Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
class Enumerator
{
public int Current { get { return 1; } }
public bool MoveNext() { return false; }
}
";
var boundNode = GetBoundForEachStatement(text);
Assert.NotNull(boundNode.EnumeratorInfoOpt);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal(SpecialType.System_Int32, boundNode.IterationVariables.Single().Type.SpecialType);
}
[Fact]
public void TestSuccessImplicitlyTypedInterface()
{
var text = @"
class C
{
void Goo(Enumerable e)
{
foreach (var x in e) { }
}
}
class Enumerable : System.Collections.IEnumerable
{
// Explicit implementation won't match pattern.
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return null; }
}
";
var boundNode = GetBoundForEachStatement(text);
Assert.NotNull(boundNode.EnumeratorInfoOpt);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal(SpecialType.System_Object, boundNode.IterationVariables.Single().Type.SpecialType);
}
[Fact]
public void TestSuccessExplicitlyTypedVar()
{
var text = @"
class C
{
void Goo(var[] a)
{
foreach (var x in a) { }
}
class @var { }
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal("System.Collections.IEnumerable", info.CollectionType.ToTestDisplayString()); //NB: differs from expression type
Assert.Equal("C.var", info.ElementTypeWithAnnotations.ToTestDisplayString());
Assert.Equal("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Object System.Collections.IEnumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.ImplicitReference, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.ExplicitReference, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind); //object to C.var
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal("C.var", boundNode.IterationVariables.Single().TypeWithAnnotations.ToTestDisplayString());
}
[Fact]
public void TestSuccessDynamicEnumerable()
{
var text = @"
class C
{
void Goo(dynamic d)
{
foreach (int x in d) { }
}
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal(SpecialType.System_Collections_IEnumerable, info.CollectionType.SpecialType);
Assert.Equal(SpecialType.System_Object, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Object System.Collections.IEnumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.ImplicitDynamic, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.ExplicitDynamic, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal("System.Int32 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal(SpecialType.System_Collections_IEnumerable, boundNode.Expression.Type.SpecialType);
Assert.Equal(TypeKind.Dynamic, ((BoundConversion)boundNode.Expression).Operand.Type.TypeKind);
}
[Fact]
public void TestSuccessImplicitlyTypedDynamicEnumerable()
{
var text = @"
class C
{
void Goo(dynamic d)
{
foreach (var x in d) { }
}
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal(SpecialType.System_Collections_IEnumerable, info.CollectionType.SpecialType);
Assert.Equal(TypeKind.Dynamic, info.ElementType.TypeKind); //NB: differs from explicit case
Assert.Equal("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Object System.Collections.IEnumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.ImplicitDynamic, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind); //NB: differs from explicit case
Assert.Equal("dynamic x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal(SpecialType.System_Collections_IEnumerable, boundNode.Expression.Type.SpecialType);
Assert.Equal(SymbolKind.DynamicType, ((BoundConversion)boundNode.Expression).Operand.Type.Kind);
}
[Fact]
public void TestSuccessTypeParameterConstrainedToInterface()
{
var text = @"
class C
{
static void Test<T>() where T : System.Collections.IEnumerator
{
foreach (object x in new Enumerable<T>())
{
System.Console.WriteLine(x);
}
}
}
public class Enumerable<T>
{
public T GetEnumerator() { return default(T); }
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal("Enumerable<T>", info.CollectionType.ToTestDisplayString());
Assert.Equal(SpecialType.System_Object, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("T Enumerable<T>.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Object System.Collections.IEnumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.Identity, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal("System.Object x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("Enumerable<T>", boundNode.Expression.Type.ToTestDisplayString());
Assert.Equal("Enumerable<T>", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
[Fact]
public void TestSuccessTypeParameterConstrainedToClass()
{
var text =
@"using System.Collections;
using System.Collections.Generic;
class A<T> : IEnumerable<T>
{
IEnumerator<T> IEnumerable<T>.GetEnumerator() { return null; }
IEnumerator IEnumerable.GetEnumerator() { return null; }
}
class A0 : A<string> { }
class C<T>
{
static void M<U, V>(A0 a, U u, V v)
where U : A0
where V : A<T>
{
foreach (var o in a)
{
M<string>(o);
}
foreach (var o in u)
{
M<string>(o);
}
foreach (var o in v)
{
M<T>(o);
}
}
static void M<U>(U u) { }
}";
var compilation = CreateCompilation(text);
compilation.VerifyDiagnostics();
}
[Fact]
public void TestSuccessTypeParameterConstrainedToPattern()
{
var text = @"
class C
{
static void Test<T>() where T : MyEnumerator
{
foreach (object x in new Enumerable<T>())
{
System.Console.WriteLine(x);
}
}
}
public class Enumerable<T>
{
public T GetEnumerator() { return default(T); }
}
interface MyEnumerator
{
object Current { get; }
bool MoveNext();
}
";
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal("Enumerable<T>", info.CollectionType.ToTestDisplayString());
Assert.Equal(SpecialType.System_Object, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("T Enumerable<T>.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Object MyEnumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean MyEnumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.Identity, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal("System.Object x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("Enumerable<T>", boundNode.Expression.Type.ToTestDisplayString());
Assert.Equal("Enumerable<T>", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
// Copied from TestSuccessPatternStruct - only change is that Goo parameter is now nullable.
[WorkItem(544908, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544908")]
[Fact]
public void TestSuccessNullableCollection()
{
var text = @"
struct C
{
void Goo(Enumerable? e)
{
foreach (long x in e) { }
}
}
struct Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
struct Enumerator
{
public int Current { get { return 1; } }
public bool MoveNext() { return false; }
}
";
var boundNode = GetBoundForEachStatement(text);
// NOTE: info is exactly as if the collection was not nullable.
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal("Enumerable", info.CollectionType.ToTestDisplayString());
Assert.Equal(SpecialType.System_Int32, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("Enumerator Enumerable.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Int32 Enumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean Enumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.False(info.NeedsDisposal); // Definitely not disposable
Assert.Equal(ConversionKind.Identity, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.ImplicitNumeric, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal("System.Int64 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("Enumerable", boundNode.Expression.Type.ToTestDisplayString());
Assert.Equal("Enumerable", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
[WorkItem(542193, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542193")]
[Fact]
public void ForEachStmtWithSyntaxError1()
{
var text = @"
public class Test
{
public static void Main(string [] args)
{
foreach(int; i < 5; i++)
{
}
}
}
";
var boundNode = GetBoundForEachStatement(text, TestOptions.Regular,
// (6,13): error CS1525: Invalid expression term 'int'
// foreach(int; i < 5; i++)
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(6, 13),
// (6,16): error CS1515: 'in' expected
// foreach(int; i < 5; i++)
Diagnostic(ErrorCode.ERR_InExpected, ";").WithLocation(6, 16),
// (6,16): error CS0230: Type and identifier are both required in a foreach statement
// foreach(int; i < 5; i++)
Diagnostic(ErrorCode.ERR_BadForeachDecl, ";").WithLocation(6, 16),
// (6,16): error CS1525: Invalid expression term ';'
// foreach(int; i < 5; i++)
Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(6, 16),
// (6,16): error CS1026: ) expected
// foreach(int; i < 5; i++)
Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(6, 16),
// (6,28): error CS1002: ; expected
// foreach(int; i < 5; i++)
Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(6, 28),
// (6,28): error CS1513: } expected
// foreach(int; i < 5; i++)
Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(6, 28),
// (6,18): error CS0103: The name 'i' does not exist in the current context
// foreach(int; i < 5; i++)
Diagnostic(ErrorCode.ERR_NameNotInContext, "i").WithArguments("i").WithLocation(6, 18),
// (6,18): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
// foreach(int; i < 5; i++)
Diagnostic(ErrorCode.ERR_IllegalStatement, "i < 5").WithLocation(6, 18),
// (6,25): error CS0103: The name 'i' does not exist in the current context
// foreach(int; i < 5; i++)
Diagnostic(ErrorCode.ERR_NameNotInContext, "i").WithArguments("i").WithLocation(6, 25));
Assert.Null(boundNode.EnumeratorInfoOpt);
}
[WorkItem(545489, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545489")]
[Fact]
public void ForEachOnErrorTypesWithoutReferences()
{
var text = @"
public class condGenClass<T> { }
public class Test
{
static void Main()
{
Type[] types = {typeof(condGenClass<int>)};
foreach (Type t in types)
{
}
}
}
";
var compilation = CreateEmptyCompilation(text);
Assert.NotEmpty(compilation.GetDiagnostics());
}
[WorkItem(545489, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545489")]
[Fact]
public void ForEachWithoutCorlib_Array()
{
var text = @"
public class Test
{
static void Main()
{
Test[] array = new Test[] { new Test() };
foreach (Test t in array)
{
}
}
}
";
var compilation = CreateEmptyCompilation(text);
Assert.NotEmpty(compilation.GetDiagnostics());
}
[WorkItem(545489, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545489")]
[Fact]
public void ForEachWithoutCorlib_String()
{
var text = @"
public class Test
{
static void Main()
{
foreach (char ch in ""hello"")
{
}
}
}
";
var compilation = CreateEmptyCompilation(text);
Assert.NotEmpty(compilation.GetDiagnostics());
}
[WorkItem(545186, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545186")]
[Fact]
public void TestForEachWithConditionalMethod()
{
string source = @"
using System;
using System.Collections;
namespace ForEachTest
{
public class BaseEnumerator
{
public bool MoveNext() { Console.WriteLine(""BaseEnumerator::MoveNext()""); return false; }
public int Current { get { Console.WriteLine(""BaseEnumerator::Current""); return 1; } }
}
public class BaseEnumeratorImpl : IEnumerator
{
public bool MoveNext() { Console.WriteLine(""BaseEnumeratorImpl::MoveNext()""); return false; }
public Object Current { get { Console.WriteLine(""BaseEnumeratorImpl::Current""); return 1; } }
public void Reset() { Console.WriteLine(""BaseEnumeratorImpl::Reset""); }
}
public class BasePattern
{
public BaseEnumerator GetEnumerator() { Console.WriteLine(""BasePattern::GetEnumerator()""); return new BaseEnumerator(); }
}
namespace ValidBaseTest
{
public class Derived5 : BasePattern, IEnumerable
{
IEnumerator IEnumerable.GetEnumerator() { Console.WriteLine(""<Interface> Derived5.GetEnumerator()""); return new BaseEnumeratorImpl(); }
[System.Diagnostics.Conditional(""CONDITIONAL"")]
new public void GetEnumerator() { Console.WriteLine(""ERROR: <Conditional Method not in scope> Derived5.GetEnumerator()""); }
}
}
public class Logger
{
public static void Main()
{
foreach (int i in new ValidBaseTest.Derived5()) { }
}
}
}
";
// Without "CONDITIONAL" defined: Succeed
string expectedOutput = @"<Interface> Derived5.GetEnumerator()
BaseEnumeratorImpl::MoveNext()";
CompileAndVerify(source, expectedOutput: expectedOutput);
// With "CONDITIONAL" defined: Fail
// (a) Preprocessor symbol defined through command line parse options
var options = new CSharpParseOptions(preprocessorSymbols: ImmutableArray.Create("CONDITIONAL"), documentationMode: DocumentationMode.None);
CreateCompilation(source, parseOptions: options).VerifyDiagnostics(
// (35,31): error CS0117: 'void' does not contain a definition for 'Current'
// foreach (int i in new ValidBaseTest.Derived5()) { }
Diagnostic(ErrorCode.ERR_NoSuchMember, "new ValidBaseTest.Derived5()").WithArguments("void", "Current").WithLocation(35, 31),
// (35,31): error CS0202: foreach requires that the return type 'void' of 'ForEachTest.ValidBaseTest.Derived5.GetEnumerator()' must have a suitable public MoveNext method and public Current property
// foreach (int i in new ValidBaseTest.Derived5()) { }
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new ValidBaseTest.Derived5()").WithArguments("void", "ForEachTest.ValidBaseTest.Derived5.GetEnumerator()").WithLocation(35, 31));
// (b) Preprocessor symbol defined in source
string condDefSource = "#define CONDITIONAL" + source;
CreateCompilation(condDefSource).VerifyDiagnostics(
// (35,31): error CS0117: 'void' does not contain a definition for 'Current'
// foreach (int i in new ValidBaseTest.Derived5()) { }
Diagnostic(ErrorCode.ERR_NoSuchMember, "new ValidBaseTest.Derived5()").WithArguments("void", "Current").WithLocation(35, 31),
// (35,31): error CS0202: foreach requires that the return type 'void' of 'ForEachTest.ValidBaseTest.Derived5.GetEnumerator()' must have a suitable public MoveNext method and public Current property
// foreach (int i in new ValidBaseTest.Derived5()) { }
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new ValidBaseTest.Derived5()").WithArguments("void", "ForEachTest.ValidBaseTest.Derived5.GetEnumerator()").WithLocation(35, 31));
}
[WorkItem(649809, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/649809")]
[Fact]
public void ArrayOfNullableOfError()
{
var source = @"
class F
{
void Test()
{
E[] a1 = null;
E?[] a2 = null;
S<E>[] a3 = null;
IEnumerable<E> e1 = null;
IEnumerable<E?> e2 = null;
IEnumerable<S<E>> e3 = null;
foreach (E e in a1) { }
foreach (E e in a2) { }
foreach (E e in a2) { }
foreach (E e in e1) { }
foreach (E e in e2) { }
foreach (E? e in a1) { }
foreach (E? e in a2) { } // used to assert
foreach (E? e in e1) { }
foreach (E? e in e2) { }
foreach (S<E> e in a3) { }
foreach (S<E> e in e3) { }
}
}
public struct S<T> { }
";
Assert.NotEmpty(CreateCompilation(source).GetDiagnostics());
}
[WorkItem(667616, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/667616")]
[Fact]
public void PortableLibraryStringForEach()
{
var source = @"
class C
{
void Test(string s)
{
foreach (var c in s) { }
}
}
";
var comp = CreateEmptyCompilation(source, [SystemRuntimePP7Ref]);
comp.VerifyDiagnostics();
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var loopSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var loopInfo = model.GetForEachStatementInfo(loopSyntax);
Assert.False(loopInfo.IsAsynchronous);
Assert.Equal<ISymbol>(comp.GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerable__GetEnumerator).GetPublicSymbol(), loopInfo.GetEnumeratorMethod);
Assert.Equal<ISymbol>(comp.GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__Current).GetPublicSymbol(), loopInfo.CurrentProperty);
Assert.Equal<ISymbol>(comp.GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext).GetPublicSymbol(), loopInfo.MoveNextMethod);
Assert.Equal<ISymbol>(comp.GetSpecialTypeMember(SpecialMember.System_IDisposable__Dispose).GetPublicSymbol(), loopInfo.DisposeMethod);
// The spec says that the element type is object.
// Therefore, we should infer object for "var".
Assert.Equal(SpecialType.System_Object, loopInfo.CurrentProperty.Type.SpecialType);
// However, to match dev11, we actually infer "char" for "var".
var typeInfo = model.GetTypeInfo(loopSyntax.Type);
Assert.Equal(SpecialType.System_Char, typeInfo.Type.SpecialType);
Assert.Equal(typeInfo.Type, typeInfo.ConvertedType);
var conv = model.GetConversion(loopSyntax.Type);
Assert.Equal(ConversionKind.Identity, conv.Kind);
}
[WorkItem(529956, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529956")]
[Fact]
public void CastArrayToIEnumerable()
{
var source = @"
using System.Collections;
class C
{
static void Main(string[] args)
{
foreach (C x in args) { }
foreach (C x in (IEnumerable)args) { }
}
public static implicit operator C(string s)
{
return new C();
}
}
";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics();
var udc = comp.GlobalNamespace.GetMember<NamedTypeSymbol>("C").GetMember<MethodSymbol>(WellKnownMemberNames.ImplicitConversionName);
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var loopSyntaxes = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().ToArray();
Assert.Equal(2, loopSyntaxes.Length);
var loopInfo0 = model.GetForEachStatementInfo(loopSyntaxes[0]);
Assert.Equal<ISymbol>(comp.GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerable__GetEnumerator).GetPublicSymbol(), loopInfo0.GetEnumeratorMethod);
Assert.Equal<ISymbol>(comp.GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__Current).GetPublicSymbol(), loopInfo0.CurrentProperty);
Assert.Equal<ISymbol>(comp.GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext).GetPublicSymbol(), loopInfo0.MoveNextMethod);
Assert.Equal<ISymbol>(comp.GetSpecialTypeMember(SpecialMember.System_IDisposable__Dispose).GetPublicSymbol(), loopInfo0.DisposeMethod);
Assert.Equal(SpecialType.System_String, loopInfo0.ElementType.SpecialType);
Assert.Equal(udc, loopInfo0.ElementConversion.Method);
Assert.Equal(ConversionKind.ExplicitReference, loopInfo0.CurrentConversion.Kind);
var loopInfo1 = model.GetForEachStatementInfo(loopSyntaxes[1]);
Assert.Equal(loopInfo0.GetEnumeratorMethod, loopInfo1.GetEnumeratorMethod);
Assert.Equal(loopInfo0.CurrentProperty, loopInfo1.CurrentProperty);
Assert.Equal(loopInfo0.MoveNextMethod, loopInfo1.MoveNextMethod);
Assert.Equal(loopInfo0.DisposeMethod, loopInfo1.DisposeMethod);
Assert.Equal(SpecialType.System_Object, loopInfo1.ElementType.SpecialType); // No longer string.
Assert.Null(loopInfo1.ElementConversion.Method); // No longer using UDC.
Assert.Equal(ConversionKind.Identity, loopInfo1.CurrentConversion.Kind); // Now identity.
}
[WorkItem(762179, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/762179")]
[Fact]
public void MissingObjectType()
{
var text = @"
class C
{
void Goo(Enumerable e)
{
foreach (Element x in e) { }
}
}
struct Enumerable
{
public Enumerator GetEnumerator() { return new Enumerator(); }
}
struct Enumerator
{
public Element Current { get { return null; } }
public bool MoveNext() { return false; }
}
class Element
{
}
";
var comp = CreateEmptyCompilation(text);
comp.GetDiagnostics();
}
[WorkItem(798000, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/798000")]
[WorkItem(39948, "https://github.com/dotnet/roslyn/issues/39948")]
[Fact]
public void MissingNullableValue()
{
var text = @"
namespace System
{
public class Object { }
public class ValueType {}
public struct Void { }
public struct Nullable<T> { }
public struct Boolean { }
public struct Int32 { }
public class Attribute { }
public class AttributeUsageAttribute : Attribute
{
public AttributeUsageAttribute(AttributeTargets t) { }
public bool AllowMultiple { get; set; }
public bool Inherited { get; set; }
}
public struct Enum { }
public enum AttributeTargets { }
}
namespace System.Collections.Generic
{
public interface IEnumerable<T>
{
IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<T>
{
T Current { get; }
bool MoveNext();
}
}
class C
{
void Goo(System.Collections.Generic.IEnumerable<C>? e)
{
foreach (var c in e) { }
}
}
";
var comp = CreateEmptyCompilation(text);
comp.VerifyDiagnostics(
// (38,55): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
// void Goo(System.Collections.Generic.IEnumerable<C>? e)
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(38, 55)
);
comp.VerifyEmitDiagnostics(
// warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options.
Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1),
// error CS0518: Predefined type 'System.Byte' is not defined or imported
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Byte").WithLocation(1, 1),
// (38,55): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
// void Goo(System.Collections.Generic.IEnumerable<C>? e)
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(38, 55),
// The following error is unexpected - https://github.com/dotnet/roslyn/issues/39948
// (40,9): error CS0656: Missing compiler required member 'System.IDisposable.Dispose'
// foreach (var c in e) { }
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "foreach (var c in e) { }").WithArguments("System.IDisposable", "Dispose").WithLocation(40, 9)
);
}
[WorkItem(798000, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/798000")]
[Fact]
public void MissingNullableValue_CSharp7()
{
var text = @"
namespace System
{
public class Object { }
public class ValueType {}
public struct Void { }
public struct Nullable<T> { }
public struct Boolean { }
}
namespace System.Collections.Generic
{
public interface IEnumerable<T>
{
IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<T>
{
T Current { get; }
bool MoveNext();
}
}
class C
{
void Goo(System.Collections.Generic.IEnumerable<C>? e)
{
foreach (var c in e) { }
}
}
";
var expected = new[]
{
// (28,55): error CS8652: The feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater.
// void Goo(System.Collections.Generic.IEnumerable<C>? e)
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "?").WithArguments("nullable reference types", "8.0").WithLocation(28, 55)
};
var comp = CreateEmptyCompilation(text, parseOptions: TestOptions.Regular7_3, skipUsesIsNullable: true);
comp.VerifyDiagnostics(expected);
comp = CreateEmptyCompilation(text, skipUsesIsNullable: true);
comp.VerifyDiagnostics(
// (28,55): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context.
// void Goo(System.Collections.Generic.IEnumerable<C>? e)
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(28, 55)
);
}
[WorkItem(798000, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/798000")]
[Fact]
public void MissingIEnumerableTGetEnumerator()
{
var text = @"
namespace System
{
public class Object { }
public class ValueType {}
public struct Void { }
public struct Boolean { }
}
namespace System.Collections.Generic
{
public interface IEnumerable<T> { }
}
public class Enumerable<T> : System.Collections.Generic.IEnumerable<T> { }
class C
{
void Goo(System.Collections.Generic.IEnumerable<C> e1, Enumerable<C> e2)
{
foreach (var c in e1) { } // Pattern
foreach (var c in e2) { } // Interface
}
}
";
var comp = CreateEmptyCompilation(text);
comp.VerifyDiagnostics(
// For pattern:
// (22,27): error CS1579: foreach statement cannot operate on variables of type 'System.Collections.Generic.IEnumerable<C>' because 'System.Collections.Generic.IEnumerable<C>' does not contain a public definition for 'GetEnumerator'
// foreach (var c in e1) { }
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "e1").WithArguments("System.Collections.Generic.IEnumerable<C>", "GetEnumerator"),
// For interface:
// (23,27): error CS0656: Missing compiler required member 'System.Collections.Generic.IEnumerable`1.GetEnumerator'
// foreach (var c in e2) { }
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e2").WithArguments("System.Collections.Generic.IEnumerable`1", "GetEnumerator"),
// (23,27): error CS0656: Missing compiler required member 'System.Collections.IEnumerator.MoveNext'
// foreach (var c in e2) { }
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e2").WithArguments("System.Collections.IEnumerator", "MoveNext"));
}
[WorkItem(798000, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/798000")]
[Fact]
public void MissingIEnumeratorTMoveNext()
{
var text = @"
namespace System
{
public class Object { }
public class ValueType {}
public struct Void { }
public struct Boolean { }
}
namespace System.Collections.Generic
{
public interface IEnumerable<T>
{
IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<T>
{
T Current { get; }
}
}
public class Enumerable<T> : System.Collections.Generic.IEnumerable<T>
{
System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()
{
return null;
}
}
class C
{
void Goo(System.Collections.Generic.IEnumerable<C> e1, Enumerable<C> e2)
{
foreach (var c in e1) { } // Pattern
foreach (var c in e2) { } // Interface
}
}
";
var comp = CreateEmptyCompilation(text);
comp.VerifyDiagnostics(
// For pattern:
// (37,27): error CS0117: 'System.Collections.Generic.IEnumerator<C>' does not contain a definition for 'MoveNext'
// foreach (var c in e1) { } // Pattern
Diagnostic(ErrorCode.ERR_NoSuchMember, "e1").WithArguments("System.Collections.Generic.IEnumerator<C>", "MoveNext"),
// For interface:
// (37,27): error CS0202: foreach requires that the return type 'System.Collections.Generic.IEnumerator<C>' of 'System.Collections.Generic.IEnumerable<C>.GetEnumerator()' must have a suitable public MoveNext method and public Current property
// foreach (var c in e1) { } // Pattern
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "e1").WithArguments("System.Collections.Generic.IEnumerator<C>", "System.Collections.Generic.IEnumerable<C>.GetEnumerator()"),
// (38,27): error CS0656: Missing compiler required member 'System.Collections.IEnumerator.MoveNext'
// foreach (var c in e2) { } // Interface
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e2").WithArguments("System.Collections.IEnumerator", "MoveNext"));
}
[WorkItem(798000, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/798000")]
[Fact]
public void MissingIEnumeratorTCurrent()
{
var text = @"
namespace System
{
public class Object { }
public class ValueType {}
public struct Void { }
public struct Boolean { }
}
namespace System.Collections
{
public interface IEnumerator
{
bool MoveNext();
}
}
namespace System.Collections.Generic
{
public interface IEnumerable<T>
{
IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<T> : IEnumerator
{
//T Current { get; }
}
}
public class Enumerable<T> : System.Collections.Generic.IEnumerable<T>
{
System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()
{
return null;
}
}
class C
{
void Goo(System.Collections.Generic.IEnumerable<C> e1, Enumerable<C> e2)
{
foreach (var c in e1) { } // Pattern
foreach (var c in e2) { } // Interface
}
}
";
var comp = CreateEmptyCompilation(text);
comp.VerifyDiagnostics(
// For pattern:
// (44,27): error CS0117: 'System.Collections.Generic.IEnumerator<C>' does not contain a definition for 'Current'
// foreach (var c in e1) { } // Pattern
Diagnostic(ErrorCode.ERR_NoSuchMember, "e1").WithArguments("System.Collections.Generic.IEnumerator<C>", "Current"),
// (44,27): error CS0202: foreach requires that the return type 'System.Collections.Generic.IEnumerator<C>' of 'System.Collections.Generic.IEnumerable<C>.GetEnumerator()' must have a suitable public MoveNext method and public Current property
// foreach (var c in e1) { } // Pattern
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "e1").WithArguments("System.Collections.Generic.IEnumerator<C>", "System.Collections.Generic.IEnumerable<C>.GetEnumerator()"),
// For interface:
// (45,27): error CS0656: Missing compiler required member 'System.Collections.Generic.IEnumerator`1.get_Current'
// foreach (var c in e2) { } // Interface
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e2").WithArguments("System.Collections.Generic.IEnumerator`1", "get_Current"));
}
[WorkItem(798000, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/798000")]
[Fact]
public void MissingIEnumeratorTCurrentGetter()
{
var text = @"
namespace System
{
public class Object { }
public class ValueType {}
public struct Void { }
public struct Boolean { }
}
namespace System.Collections
{
public interface IEnumerator
{
bool MoveNext();
}
}
namespace System.Collections.Generic
{
public interface IEnumerable<T>
{
IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<T> : IEnumerator
{
T Current { /* get; */ set; }
}
}
public class Enumerable<T> : System.Collections.Generic.IEnumerable<T>
{
System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()
{
return null;
}
}
class C
{
void Goo(System.Collections.Generic.IEnumerable<C> e1, Enumerable<C> e2)
{
foreach (var c in e1) { } // Pattern
foreach (var c in e2) { } // Interface
}
}
";
var comp = CreateEmptyCompilation(text);
comp.VerifyDiagnostics(
// For pattern:
// (44,27): error CS0202: foreach requires that the return type 'System.Collections.Generic.IEnumerator<C>' of 'System.Collections.Generic.IEnumerable<C>.GetEnumerator()' must have a suitable public MoveNext method and public Current property
// foreach (var c in e1) { } // Pattern
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "e1").WithArguments("System.Collections.Generic.IEnumerator<C>", "System.Collections.Generic.IEnumerable<C>.GetEnumerator()"),
// For interface:
// (45,27): error CS0656: Missing compiler required member 'System.Collections.Generic.IEnumerator`1.get_Current'
// foreach (var c in e2) { } // Interface
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e2").WithArguments("System.Collections.Generic.IEnumerator`1", "get_Current"));
}
[WorkItem(798000, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/798000")]
[Fact]
public void MissingIEnumerableGetEnumerator()
{
var text = @"
namespace System
{
public class Object { }
public class ValueType {}
public struct Void { }
public struct Boolean { }
}
namespace System.Collections
{
public interface IEnumerable { }
}
public class Enumerable : System.Collections.IEnumerable { }
class C
{
void Goo(System.Collections.IEnumerable e1, Enumerable e2)
{
foreach (var c in e1) { } // Pattern
foreach (var c in e2) { } // Interface
}
}
";
var comp = CreateEmptyCompilation(text);
comp.VerifyDiagnostics(
// For pattern:
// (22,27): error CS1579: foreach statement cannot operate on variables of type 'System.Collections.IEnumerable' because 'System.Collections.IEnumerable' does not contain a public definition for 'GetEnumerator'
// foreach (var c in e1) { }
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "e1").WithArguments("System.Collections.IEnumerable", "GetEnumerator"),
// For interface:
// (23,27): error CS0656: Missing compiler required member 'System.Collections.IEnumerable.GetEnumerator'
// foreach (var c in e2) { } // Interface
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e2").WithArguments("System.Collections.IEnumerable", "GetEnumerator"),
// (23,27): error CS0656: Missing compiler required member 'System.Collections.IEnumerator.get_Current'
// foreach (var c in e2) { } // Interface
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e2").WithArguments("System.Collections.IEnumerator", "get_Current"),
// (23,27): error CS0656: Missing compiler required member 'System.Collections.IEnumerator.MoveNext'
// foreach (var c in e2) { } // Interface
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e2").WithArguments("System.Collections.IEnumerator", "MoveNext"));
}
[WorkItem(798000, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/798000")]
[Fact]
public void MissingIEnumeratorMoveNext()
{
var text = @"
namespace System
{
public class Object { }
public class ValueType {}
public struct Void { }
public struct Boolean { }
}
namespace System.Collections
{
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
object Current { get; }
}
}
public class Enumerable : System.Collections.IEnumerable
{
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return null;
}
}
class C
{
void Goo(System.Collections.IEnumerable e1, Enumerable e2)
{
foreach (var c in e1) { } // Pattern
foreach (var c in e2) { } // Interface
}
}
";
var comp = CreateEmptyCompilation(text);
comp.VerifyDiagnostics(
// For pattern:
// (37,27): error CS0117: 'System.Collections.IEnumerator' does not contain a definition for 'MoveNext'
// foreach (var c in e1) { } // Pattern
Diagnostic(ErrorCode.ERR_NoSuchMember, "e1").WithArguments("System.Collections.IEnumerator", "MoveNext"),
// For interface:
// (37,27): error CS0202: foreach requires that the return type 'System.Collections.IEnumerator' of 'System.Collections.IEnumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
// foreach (var c in e1) { } // Pattern
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "e1").WithArguments("System.Collections.IEnumerator", "System.Collections.IEnumerable.GetEnumerator()"),
// (38,27): error CS0656: Missing compiler required member 'System.Collections.IEnumerator.MoveNext'
// foreach (var c in e2) { } // Interface
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e2").WithArguments("System.Collections.IEnumerator", "MoveNext"));
}
[WorkItem(798000, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/798000")]
[Fact]
public void MissingIEnumeratorCurrent()
{
var text = @"
namespace System
{
public class Object { }
public class ValueType {}
public struct Void { }
public struct Boolean { }
}
namespace System.Collections
{
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
bool MoveNext();
//object Current { get; }
}
}
public class Enumerable : System.Collections.IEnumerable
{
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return null;
}
}
class C
{
void Goo(System.Collections.IEnumerable e1, Enumerable e2)
{
foreach (var c in e1) { } // Pattern
foreach (var c in e2) { } // Interface
}
}
";
var comp = CreateEmptyCompilation(text);
comp.VerifyDiagnostics(
// For pattern:
// (44,27): error CS0117: 'System.Collections.IEnumerator' does not contain a definition for 'Current'
// foreach (var c in e1) { } // Pattern
Diagnostic(ErrorCode.ERR_NoSuchMember, "e1").WithArguments("System.Collections.IEnumerator", "Current"),
// (44,27): error CS0202: foreach requires that the return type 'System.Collections.IEnumerator' of 'System.Collections.IEnumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
// foreach (var c in e1) { } // Pattern
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "e1").WithArguments("System.Collections.IEnumerator", "System.Collections.IEnumerable.GetEnumerator()"),
// For interface:
// (45,27): error CS0656: Missing compiler required member 'System.Collections.IEnumerator.get_Current'
// foreach (var c in e2) { } // Interface
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e2").WithArguments("System.Collections.IEnumerator", "get_Current"));
}
[WorkItem(798000, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/798000")]
[Fact]
public void MissingIEnumeratorCurrentGetter()
{
var text = @"
namespace System
{
public class Object { }
public class ValueType {}
public struct Void { }
public struct Boolean { }
}
namespace System.Collections
{
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
bool MoveNext();
object Current { /* get; */ set; }
}
}
public class Enumerable : System.Collections.IEnumerable
{
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return null;
}
}
class C
{
void Goo(System.Collections.IEnumerable e1, Enumerable e2)
{
foreach (var c in e1) { } // Pattern
foreach (var c in e2) { } // Interface
}
}
";
var comp = CreateEmptyCompilation(text);
comp.VerifyDiagnostics(
// For pattern:
// (44,27): error CS0202: foreach requires that the return type 'System.Collections.IEnumerator' of 'System.Collections.IEnumerable.GetEnumerator()' must have a suitable public MoveNext method and public Current property
// foreach (var c in e1) { } // Pattern
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "e1").WithArguments("System.Collections.IEnumerator", "System.Collections.IEnumerable.GetEnumerator()"),
// For interface:
// (45,27): error CS0656: Missing compiler required member 'System.Collections.IEnumerator.get_Current'
// foreach (var c in e2) { } // Interface
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e2").WithArguments("System.Collections.IEnumerator", "get_Current"));
}
[WorkItem(530381, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530381")]
[Fact]
public void BadCollectionCascadingErrors()
{
var source = @"
class Program
{
static void Main()
{
foreach(var x in Goo) { }
}
}
";
CreateCompilation(source).VerifyDiagnostics(
// (6,26): error CS0103: The name 'Goo' does not exist in the current context
// foreach(var x in Goo) { }
Diagnostic(ErrorCode.ERR_NameNotInContext, "Goo").WithArguments("Goo").WithLocation(6, 26));
}
[WorkItem(847507, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/847507")]
[Fact]
public void InferIterationVariableTypeWithErrors()
{
var source = @"
class Program
{
static void Main()
{
foreach(var x in new string[1]) { }
}
}
namespace System
{
public class Object { }
public class String : Object { }
}
";
var comp = CreateEmptyCompilation(source); // Lots of errors, since corlib is missing.
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var localSymbol = model.GetDeclaredSymbol(foreachSyntax);
// Code Path 1: SourceLocalSymbol.Type.
var localSymbolType = localSymbol.Type;
Assert.Equal(SpecialType.System_String, localSymbolType.SpecialType);
Assert.NotEqual(TypeKind.Error, localSymbolType.TypeKind);
// Code Path 2: SemanticModel.
var varSyntax = foreachSyntax.Type;
var info = model.GetSymbolInfo(varSyntax); // Used to assert.
Assert.Equal(localSymbolType, info.Symbol);
}
[WorkItem(667275, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/667275")]
[Fact]
public void Repro667275()
{
// This is not the simplest repro, but it is the one from the bug.
var source = @"
using System.Collections.Generic;
class myClass<T>
{
public static implicit operator T(myClass<T> m) { return default(T); }
}
class Test
{
static void Main()
{
var myObj = new myClass<List<string>>();
foreach (var x in myObj) { }
}
}
";
CreateCompilation(source).VerifyDiagnostics(
// (14,27): error CS1579: foreach statement cannot operate on variables of type 'myClass<System.Collections.Generic.List<string>>' because 'myClass<System.Collections.Generic.List<string>>' does not contain a public definition for 'GetEnumerator'
// foreach (var x in myObj) { }
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "myObj").WithArguments("myClass<System.Collections.Generic.List<string>>", "GetEnumerator").WithLocation(14, 27));
}
[WorkItem(667275, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/667275")]
[Fact]
public void Repro667275_Simplified()
{
// No generics, no foreach.
var source = @"
using System.Collections;
class Dummy
{
public static implicit operator MyEnumerable(Dummy d)
{
return null;
}
}
public class MyEnumerable : IEnumerable
{
IEnumerator IEnumerable.GetEnumerator()
{
throw new System.NotImplementedException();
}
}
class Test
{
static void Main()
{
Dummy d = new Dummy();
MyEnumerable m = d; // succeeds
IEnumerable i = d; // fails
}
}
";
CreateCompilation(source).VerifyDiagnostics(
// (27,25): error CS0266: Cannot implicitly convert type 'Dummy' to 'System.Collections.IEnumerable'. An explicit conversion exists (are you missing a cast?)
// IEnumerable i = d; // fails
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "d").WithArguments("Dummy", "System.Collections.IEnumerable").WithLocation(27, 25));
}
[WorkItem(963197, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/963197")]
[Fact]
public void Repro963197()
{
var source =
@"using System;
class Program
{
public static string B = ""B"";
public static string C = ""C"";
static void Main(string[] args)
{
foreach (var a in new { B, C })
{
Console.WriteLine(a);
}
}
}";
CreateCompilation(source).VerifyDiagnostics(
// (9,27): error CS1579: foreach statement cannot operate on variables of type '<anonymous type: string B, string C>' because '<anonymous type: string B, string C>' does not contain a public definition for 'GetEnumerator'
// foreach (var a in new { B, C })
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "new { B, C }").WithArguments("<anonymous type: string B, string C>", "GetEnumerator").WithLocation(9, 27)
);
}
[WorkItem(11387, "https://github.com/dotnet/roslyn/issues/11387")]
[Fact]
public void StringNotIEnumerable()
{
var source1 =
@"namespace System
{
public class Object { }
public struct Void { }
public class ValueType { }
public struct Boolean { }
public struct Int32 { }
public struct Char { }
public class String
{
public int Length => 2;
[System.Runtime.CompilerServices.IndexerName(""Chars"")]
public char this[int i] => 'a';
}
public interface IDisposable
{
void Dispose();
}
public abstract class Attribute
{
protected Attribute() { }
}
public class AttributeUsageAttribute : Attribute
{
public AttributeUsageAttribute(AttributeTargets t) { }
public bool AllowMultiple { get; set; }
public bool Inherited { get; set; }
}
public struct Enum { }
public enum AttributeTargets { }
}
namespace System.Runtime.CompilerServices
{
using System;
public sealed class IndexerNameAttribute: Attribute
{
public IndexerNameAttribute(String indexerName)
{}
}
}
namespace System.Reflection {
using System;
public sealed class DefaultMemberAttribute : Attribute
{
public DefaultMemberAttribute(String memberName)
{}
}
}
namespace System.Collections
{
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
}
}";
var compilation1 = CreateEmptyCompilation(source1, assemblyName: GetUniqueName());
var reference1 = MetadataReference.CreateFromStream(compilation1.EmitToStream());
var text =
@"class C
{
static void M(string s)
{
foreach (var c in s)
{
// comment
}
}
}";
var comp = CreateEmptyCompilation(text, new[] { reference1 });
CompileAndVerify(comp, verify: Verification.FailsPEVerify).
VerifyIL("C.M", @"
{
// Code size 28 (0x1c)
.maxstack 2
.locals init (string V_0,
int V_1)
IL_0000: ldarg.0
IL_0001: stloc.0
IL_0002: ldc.i4.0
IL_0003: stloc.1
IL_0004: br.s IL_0012
IL_0006: ldloc.0
IL_0007: ldloc.1
IL_0008: callvirt ""char string.this[int].get""
IL_000d: pop
IL_000e: ldloc.1
IL_000f: ldc.i4.1
IL_0010: add
IL_0011: stloc.1
IL_0012: ldloc.1
IL_0013: ldloc.0
IL_0014: callvirt ""int string.Length.get""
IL_0019: blt.s IL_0006
IL_001b: ret
}
");
var boundNode = GetBoundForEachStatement(text);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal(SpecialType.System_String, info.CollectionType.SpecialType);
Assert.Equal(SpecialType.System_Char, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("System.CharEnumerator System.String.GetEnumerator()", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Empty(info.GetEnumeratorInfo.Arguments);
Assert.Equal("System.Char System.CharEnumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean System.CharEnumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.True(info.NeedsDisposal);
Assert.Equal(ConversionKind.Identity, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal(SpecialType.System_Char, boundNode.IterationVariables.Single().Type.SpecialType);
}
[Fact]
public void TestPatternDisposeRefStruct()
{
var text = @"
class C
{
static void Main()
{
foreach (var x in new Enumerable1())
{
System.Console.WriteLine(x);
}
}
}
class Enumerable1
{
public DisposableEnumerator GetEnumerator() { return new DisposableEnumerator(); }
}
ref struct DisposableEnumerator
{
int x;
public int Current { get { return x; } }
public bool MoveNext() { return ++x < 4; }
public void Dispose() { }
}";
var boundNode = GetBoundForEachStatement(text);
var enumeratorInfo = boundNode.EnumeratorInfoOpt;
Assert.Equal("void DisposableEnumerator.Dispose()", enumeratorInfo.PatternDisposeInfo.Method.ToTestDisplayString());
Assert.Empty(enumeratorInfo.PatternDisposeInfo.Arguments);
}
[Fact]
public void TestPatternDisposeRefStruct_7_3()
{
var text = @"
class C
{
static void Main()
{
foreach (var x in new Enumerable1())
{
System.Console.WriteLine(x);
}
}
}
class Enumerable1
{
public DisposableEnumerator GetEnumerator() { return new DisposableEnumerator(); }
}
ref struct DisposableEnumerator
{
int x;
public int Current { get { return x; } }
public bool MoveNext() { return ++x < 4; }
public void Dispose() { }
}";
var boundNode = GetBoundForEachStatement(text, TestOptions.Regular7_3,
// (6,27): error CS8370: Feature 'pattern-based disposal' is not available in C# 7.3. Please use language version 8.0 or greater.
// foreach (var x in new Enumerable1())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "new Enumerable1()").WithArguments("pattern-based disposal", "8.0").WithLocation(6, 27)
);
var enumeratorInfo = boundNode.EnumeratorInfoOpt;
Assert.Equal("void DisposableEnumerator.Dispose()", enumeratorInfo.PatternDisposeInfo.Method.ToTestDisplayString());
Assert.Empty(enumeratorInfo.PatternDisposeInfo.Arguments);
}
[Fact]
public void TestExtensionGetEnumerator()
{
var text = @"
using System;
public class C
{
public static void Main()
{
foreach (var i in new C())
{
Console.Write(i);
}
}
public sealed class Enumerator
{
public int Current { get; private set; }
public bool MoveNext() => Current++ != 3;
}
}
public static class Extensions
{
public static C.Enumerator GetEnumerator(this C self) => new C.Enumerator();
}";
var boundNode = GetBoundForEachStatement(text, options: TestOptions.Regular9);
ForEachEnumeratorInfo info = boundNode.EnumeratorInfoOpt;
Assert.NotNull(info);
Assert.Equal("C", info.CollectionType.ToTestDisplayString());
Assert.Equal(SpecialType.System_Int32, info.ElementTypeWithAnnotations.SpecialType);
Assert.Equal("C.Enumerator Extensions.GetEnumerator(this C self)", info.GetEnumeratorInfo.Method.ToTestDisplayString());
Assert.Equal("C", info.GetEnumeratorInfo.Arguments.Single().Type.ToTestDisplayString());
Assert.Equal("System.Int32 C.Enumerator.Current.get", info.CurrentPropertyGetter.ToTestDisplayString());
Assert.Equal("System.Boolean C.Enumerator.MoveNext()", info.MoveNextInfo.Method.ToTestDisplayString());
Assert.Empty(info.MoveNextInfo.Arguments);
Assert.False(info.NeedsDisposal);
Assert.Equal(ConversionKind.Identity, GetCollectionConversion(boundNode).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(info.CurrentConversion, info.CurrentPlaceholder).Kind);
Assert.Equal(ConversionKind.Identity, BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder).Kind);
Assert.Equal("System.Int32 i", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("C", boundNode.Expression.Type.ToDisplayString());
}
private static BoundForEachStatement GetBoundForEachStatement(string text, CSharpParseOptions options = null, params DiagnosticDescription[] diagnostics)
{
var tree = Parse(text, options: options);
var comp = CreateCompilationWithMscorlib40AndSystemCore(new[] { tree });
comp.VerifyDiagnostics(diagnostics);
var syntaxNode =
(CommonForEachStatementSyntax)tree.FindNodeOrTokenByKind(SyntaxKind.ForEachStatement).AsNode() ??
(CommonForEachStatementSyntax)tree.FindNodeOrTokenByKind(SyntaxKind.ForEachVariableStatement).AsNode();
var treeModel = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree);
var memberModel = treeModel.GetMemberModel(syntaxNode);
BoundForEachStatement boundNode = (BoundForEachStatement)memberModel.GetUpperBoundNode(syntaxNode);
// Make sure that the bound node info is exposed correctly in the API
ForEachEnumeratorInfo enumeratorInfo = boundNode.EnumeratorInfoOpt;
ForEachStatementInfo statementInfo = treeModel.GetForEachStatementInfo(syntaxNode);
if (enumeratorInfo == null)
{
Assert.Equal(default(ForEachStatementInfo), statementInfo);
}
else
{
Assert.Equal(enumeratorInfo.GetEnumeratorInfo.Method.GetPublicSymbol(), statementInfo.GetEnumeratorMethod);
Assert.Equal(enumeratorInfo.CurrentPropertyGetter.GetPublicSymbol(), statementInfo.CurrentProperty.GetMethod);
Assert.Equal(enumeratorInfo.MoveNextInfo.Method.GetPublicSymbol(), statementInfo.MoveNextMethod);
if (enumeratorInfo.NeedsDisposal)
{
if (enumeratorInfo.PatternDisposeInfo is object)
{
Assert.Equal(enumeratorInfo.PatternDisposeInfo.Method.GetPublicSymbol(), statementInfo.DisposeMethod);
}
else if (enumeratorInfo.IsAsync)
{
Assert.Equal("System.ValueTask System.IAsyncDisposable.DisposeAsync()", statementInfo.DisposeMethod.ToTestDisplayString());
}
else
{
Assert.Equal("void System.IDisposable.Dispose()", statementInfo.DisposeMethod.ToTestDisplayString());
}
}
else
{
Assert.Null(statementInfo.DisposeMethod);
}
Assert.Equal(enumeratorInfo.ElementType.GetPublicSymbol(), statementInfo.ElementType);
Assert.Equal(BoundNode.GetConversion(boundNode.ElementConversion, boundNode.ElementPlaceholder), statementInfo.ElementConversion);
Assert.Equal(BoundNode.GetConversion(enumeratorInfo.CurrentConversion, enumeratorInfo.CurrentPlaceholder), statementInfo.CurrentConversion);
}
return boundNode;
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/70340")]
public void ForEachStatementInfo_PointerElementType_Array()
{
var comp = CreateCompilation("""
class C
{
unsafe void M()
{
foreach (var x in new int*[0])
{
}
}
}
""", options: TestOptions.UnsafeDebugDll).VerifyDiagnostics();
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var loop = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(loop);
Assert.Equal(default, info);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/70340")]
public void ForEachStatementInfo_PointerElementType_Custom()
{
var comp = CreateCompilation("""
internal class MyEnumerable
{
public Enumerator GetEnumerator() => new Enumerator();
}
internal unsafe class Enumerator
{
public int* Current { get; }
public bool MoveNext() => true;
}
class C
{
void M()
{
foreach (var x in new MyEnumerable())
{
}
}
}
""", options: TestOptions.UnsafeDebugDll).VerifyDiagnostics();
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var loop = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(loop);
Assert.Equal(Conversion.Identity, info.CurrentConversion);
Assert.Equal(Conversion.Identity, info.ElementConversion);
Assert.Equal("System.Int32*", info.ElementType.ToTestDisplayString());
}
[WorkItem(1100741, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1100741")]
[Fact]
public void Bug1100741()
{
var source = @"
namespace ImmutableObjectGraph
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using IdentityFieldType = System.UInt32;
public static class RecursiveTypeExtensions
{
/// <summary>Gets the recursive parent of the specified value, or <c>null</c> if none could be found.</summary>
internal ParentedRecursiveType<<#= templateType.RecursiveParent.TypeName #>, <#= templateType.RecursiveTypeFromFamily.TypeName #>> GetParentedNode(<#= templateType.RequiredIdentityField.TypeName #> identity) {
if (this.Identity == identity) {
return new ParentedRecursiveType<<#= templateType.RecursiveParent.TypeName #>, <#= templateType.RecursiveTypeFromFamily.TypeName #>>(this, null);
}
if (this.LookupTable != null) {
System.Collections.Generic.KeyValuePair<<#= templateType.RecursiveType.TypeName #>, <#= templateType.RequiredIdentityField.TypeName #>> lookupValue;
if (this.LookupTable.TryGetValue(identity, out lookupValue)) {
var parentIdentity = lookupValue.Value;
return new ParentedRecursiveType<<#= templateType.RecursiveParent.TypeName #>, <#= templateType.RecursiveTypeFromFamily.TypeName #>>(this.LookupTable[identity].Key, (<#= templateType.RecursiveParent.TypeName #>)this.Find(parentIdentity));
}
} else {
// No lookup table means we have to aggressively search each child.
foreach (var child in this.Children) {
if (child.Identity.Equals(identity)) {
return new ParentedRecursiveType<<#= templateType.RecursiveParent.TypeName #>, <#= templateType.RecursiveTypeFromFamily.TypeName #>>(child, this);
}
var recursiveChild = child as <#= templateType.RecursiveParent.TypeName #>;
if (recursiveChild != null) {
var childResult = recursiveChild.GetParentedNode(identity);
if (childResult.Value != null) {
return childResult;
}
}
}
}
return default(ParentedRecursiveType<<#= templateType.RecursiveParent.TypeName #>, <#= templateType.RecursiveTypeFromFamily.TypeName #>>);
}
}
}
";
var compilation = CreateCompilation(source);
var tree = compilation.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().Where(n => n.Kind() == SyntaxKind.ForEachStatement).OfType<ForEachStatementSyntax>().Single();
var model = compilation.GetSemanticModel(tree);
Assert.Null(model.GetDeclaredSymbol(node));
}
[Fact, WorkItem(1733, "https://github.com/dotnet/roslyn/issues/1733")]
public void MissingBaseType()
{
var source1 = @"
namespace System
{
public class Object { }
public class ValueType {}
public struct Void { }
public struct Boolean { }
public struct Int32 { }
}
";
var comp1 = CreateEmptyCompilation(source1, options: TestOptions.DebugDll, assemblyName: "MissingBaseType1");
comp1.VerifyDiagnostics();
var source2 = @"
public class Enumerable
{
public Enumerator GetEnumerator()
{
return default(Enumerator);
}
}
public struct Enumerator
{
public int Current
{
get
{
return 0;
}
}
public bool MoveNext()
{
return false;
}
}";
var comp2 = CreateEmptyCompilation(source2, new[] { comp1.ToMetadataReference() }, options: TestOptions.DebugDll);
comp2.VerifyDiagnostics();
var source3 = @"
namespace System
{
public class Object { }
public class ValueType {}
public struct Void { }
public struct Boolean { }
public struct Int32 { }
}
";
var comp3 = CreateEmptyCompilation(source3, options: TestOptions.DebugDll, assemblyName: "MissingBaseType2");
comp3.VerifyDiagnostics();
var source4 = @"
class Program
{
static void Main()
{
foreach (var x in new Enumerable())
{ }
}
}";
var comp4 = CreateEmptyCompilation(source4, new[] { comp2.ToMetadataReference(), comp3.ToMetadataReference() });
comp4.VerifyDiagnostics(
// (6,9): error CS0012: The type 'ValueType' is defined in an assembly that is not referenced. You must add a reference to assembly 'MissingBaseType1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
// foreach (var x in new Enumerable())
Diagnostic(ErrorCode.ERR_NoTypeDef, @"foreach (var x in new Enumerable())
{ }").WithArguments("System.ValueType", "MissingBaseType1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 9)
);
}
[Fact]
[WorkItem(26585, "https://github.com/dotnet/roslyn/issues/26585")]
public void ForEachIteratorWithCurrentRefKind_Async_Ref()
{
CreateCompilation(@"
using System.Threading.Tasks;
class E
{
public class Enumerator
{
public ref int Current => throw null;
public bool MoveNext() => throw null;
}
public Enumerator GetEnumerator() => new Enumerator();
}
class C
{
public async static Task Test()
{
await Task.CompletedTask;
foreach (ref int x in new E())
{
System.Console.Write(x);
}
}
}").VerifyEmitDiagnostics();
}
[Fact]
[WorkItem(26585, "https://github.com/dotnet/roslyn/issues/26585")]
public void ForEachIteratorWithCurrentRefKind_Async_RefReadonly()
{
CreateCompilation(@"
using System.Threading.Tasks;
class E
{
public class Enumerator
{
public ref readonly int Current => throw null;
public bool MoveNext() => throw null;
}
public Enumerator GetEnumerator() => new Enumerator();
}
class C
{
public async static Task Test()
{
await Task.CompletedTask;
foreach (ref readonly int x in new E())
{
System.Console.Write(x);
}
}
}").VerifyEmitDiagnostics();
}
[Fact]
[WorkItem(26585, "https://github.com/dotnet/roslyn/issues/26585")]
public void ForEachIteratorWithCurrentRefKind_Iterator_Ref()
{
CreateCompilation(@"
using System.Collections.Generic;
class E
{
public class Enumerator
{
public ref int Current => throw null;
public bool MoveNext() => throw null;
}
public Enumerator GetEnumerator() => new Enumerator();
}
class C
{
public static IEnumerable<int> Test()
{
foreach (ref int x in new E())
{
yield return x;
}
}
}").VerifyEmitDiagnostics();
}
[Fact]
[WorkItem(26585, "https://github.com/dotnet/roslyn/issues/26585")]
public void ForEachIteratorWithCurrentRefKind_Iterator_RefReadonly()
{
CreateCompilation(@"
using System.Collections.Generic;
class E
{
public class Enumerator
{
public ref readonly int Current => throw null;
public bool MoveNext() => throw null;
}
public Enumerator GetEnumerator() => new Enumerator();
}
class C
{
public static IEnumerable<int> Test()
{
foreach (ref readonly int x in new E())
{
yield return x;
}
}
}").VerifyEmitDiagnostics();
}
[Fact]
[WorkItem(30016, "https://github.com/dotnet/roslyn/issues/30016")]
public void ForEachIteratorWithCurrentRefKind_DontPassFieldByValue()
{
var source = @"
using System;
struct S1
{
public int A;
}
class C
{
public static void Main()
{
Span<S1> items = new Span<S1>(new S1[1]);
foreach (ref var t in items) t.A++;
Console.WriteLine(items[0].A);
}
}";
var comp = CreateCompilationWithMscorlibAndSpan(source, options: TestOptions.ReleaseDebugExe).VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "1");
}
[WorkItem(32334, "https://github.com/dotnet/roslyn/issues/32334")]
[Fact]
public void SuppressForEachMissingMemberErrorOnErrorType()
{
CreateCompilation(@"
using System;
using System.Collections.Generic;
class C
{
void Goo()
{
var nonsense = Nonsense;
//Should not have error
foreach(var item in nonsense) {}
var nonsense2 = new Nonsense();
//Should not have error
foreach(var item in nonsense2) {}
var lazyNonsense = default(Lazy<Nonsense>);
//Should have error
foreach(var item in lazyNonsense) {}
var listNonsense = new List<Nonsense>();
//Should not have error
foreach(var item in listNonsense) {}
var nonsenseArray = new Nonsense[0];
//Should not have error
foreach(var item in nonsenseArray) {}
var stringNonsense = new String.Nonsense();
//Should not have error
foreach(var item in stringNonsense) {}
var nonsenseString = new Nonsense.String();
//Should not have error
foreach(var item in nonsenseString) {}
Nonsense? nullableNonsense = default;
//Should have error
foreach(var item in nullableNonsense) {}
var nonsenseTuple = (new Nonsense(), 42);
//Should have error
foreach(var item in nonsenseTuple) {}
}
}
").VerifyDiagnostics(
// (9,24): error CS0103: The name 'Nonsense' does not exist in the current context
// var nonsense = Nonsense;
Diagnostic(ErrorCode.ERR_NameNotInContext, "Nonsense").WithArguments("Nonsense").WithLocation(9, 24),
// (13,29): error CS0246: The type or namespace name 'Nonsense' could not be found (are you missing a using directive or an assembly reference?)
// var nonsense2 = new Nonsense();
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Nonsense").WithArguments("Nonsense").WithLocation(13, 29),
// (17,41): error CS0246: The type or namespace name 'Nonsense' could not be found (are you missing a using directive or an assembly reference?)
// var lazyNonsense = default(Lazy<Nonsense>);
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Nonsense").WithArguments("Nonsense").WithLocation(17, 41),
// (19,29): error CS1579: foreach statement cannot operate on variables of type 'Lazy<Nonsense>' because 'Lazy<Nonsense>' does not contain a public instance or extension definition for 'GetEnumerator'
// foreach(var item in lazyNonsense) {}
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "lazyNonsense").WithArguments("System.Lazy<Nonsense>", "GetEnumerator").WithLocation(19, 29),
// (21,37): error CS0246: The type or namespace name 'Nonsense' could not be found (are you missing a using directive or an assembly reference?)
// var listNonsense = new List<Nonsense>();
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Nonsense").WithArguments("Nonsense").WithLocation(21, 37),
// (25,33): error CS0246: The type or namespace name 'Nonsense' could not be found (are you missing a using directive or an assembly reference?)
// var nonsenseArray = new Nonsense[0];
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Nonsense").WithArguments("Nonsense").WithLocation(25, 33),
// (29,41): error CS0426: The type name 'Nonsense' does not exist in the type 'string'
// var stringNonsense = new String.Nonsense();
Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInAgg, "Nonsense").WithArguments("Nonsense", "string").WithLocation(29, 41),
// (33,34): error CS0246: The type or namespace name 'Nonsense' could not be found (are you missing a using directive or an assembly reference?)
// var nonsenseString = new Nonsense.String();
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Nonsense").WithArguments("Nonsense").WithLocation(33, 34),
// (37,9): error CS0246: The type or namespace name 'Nonsense' could not be found (are you missing a using directive or an assembly reference?)
// Nonsense? nullableNonsense = default;
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Nonsense").WithArguments("Nonsense").WithLocation(37, 9),
// (39,29): error CS1579: foreach statement cannot operate on variables of type 'Nonsense?' because 'Nonsense?' does not contain a public instance or extension definition for 'GetEnumerator'
// foreach(var item in nullableNonsense) {}
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "nullableNonsense").WithArguments("Nonsense?", "GetEnumerator").WithLocation(39, 29),
// (41,34): error CS0246: The type or namespace name 'Nonsense' could not be found (are you missing a using directive or an assembly reference?)
// var nonsenseTuple = (new Nonsense(), 42);
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Nonsense").WithArguments("Nonsense").WithLocation(41, 34),
// (43,29): error CS1579: foreach statement cannot operate on variables of type '(Nonsense, int)' because '(Nonsense, int)' does not contain a public instance or extension definition for 'GetEnumerator'
// foreach(var item in nonsenseTuple) {}
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "nonsenseTuple").WithArguments("(Nonsense, int)", "GetEnumerator").WithLocation(43, 29));
}
[Fact]
[WorkItem(61238, "https://github.com/dotnet/roslyn/issues/61238")]
public void ForEachIterator_RefAssignmentWithoutIdentityConversion()
{
string source = @"
using System;
Span<C2> items = new Span<C2>(new C2[1]);
foreach (ref C t in items) {}
class C {}
class C2 : C {}
";
CreateCompilationWithMscorlibAndSpan(source).VerifyDiagnostics(
// (4,21): error CS8173: The expression must be of type 'C' because it is being assigned by reference
// foreach (ref C t in items) {}
Diagnostic(ErrorCode.ERR_RefAssignmentMustHaveIdentityConversion, "items").WithArguments("C").WithLocation(4, 21)
);
}
[Fact]
[WorkItem(61238, "https://github.com/dotnet/roslyn/issues/61238")]
public void ForEachIterator_RefReadonlyAssignmentWithoutIdentityConversion()
{
string source = @"
using System;
Span<C2> items = new Span<C2>(new C2[1]);
foreach (ref readonly C t in items) {}
class C {}
class C2 : C {}
";
CreateCompilationWithMscorlibAndSpan(source).VerifyDiagnostics(
// (4,21): error CS8173: The expression must be of type 'C' because it is being assigned by reference
// foreach (ref C t in items) {}
Diagnostic(ErrorCode.ERR_RefAssignmentMustHaveIdentityConversion, "items").WithArguments("C").WithLocation(4, 30)
);
}
[Fact]
[WorkItem(61238, "https://github.com/dotnet/roslyn/issues/61238")]
public void ForEachIterator_ReadonlySpan_RefReadonlyAssignmentWithoutIdentityConversion()
{
string source = @"
using System;
ReadOnlySpan<C2> items = new ReadOnlySpan<C2>(new C2[1]);
foreach (ref readonly C t in items) {}
class C {}
class C2 : C {}
";
CreateCompilationWithMscorlibAndSpan(source).VerifyDiagnostics(
// (4,30): error CS8173: The expression must be of type 'C' because it is being assigned by reference
// foreach (ref readonly C t in items) {}
Diagnostic(ErrorCode.ERR_RefAssignmentMustHaveIdentityConversion, "items").WithArguments("C").WithLocation(4, 30)
);
}
[Fact]
public void ForEachIterator_ReadonlySpan_RefAssignmentWithoutReadonly()
{
string source = @"
using System;
ReadOnlySpan<C2> items = new ReadOnlySpan<C2>(new C2[1]);
foreach (ref C t in items) {}
class C {}
class C2 : C {}
";
CreateCompilationWithMscorlibAndSpan(source).VerifyDiagnostics(
// (4,21): error CS8331: Cannot assign to method 'Current.get' or use it as the right hand side of a ref assignment because it is a readonly variable
// foreach (ref C t in items) {}
Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "items").WithArguments("method", "Current.get").WithLocation(4, 21)
);
}
}
}
|