未验证 提交 9e05ee88 编写于 作者: C Charles Stoner 提交者: GitHub

More static local function tests (#48642)

上级 f3a38e3a
......@@ -10,6 +10,7 @@
using Microsoft.CodeAnalysis.Test.Extensions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using System;
using System.Collections.Immutable;
using System.Linq;
......@@ -1718,7 +1719,6 @@ public void M()
// void Local<[A, B, CLSCompliant, D]T>() { }
Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "CLSCompliant").WithArguments("isCompliant", "System.CLSCompliantAttribute.CLSCompliantAttribute(bool)").WithLocation(7, 27));
var model = comp.GetSemanticModel(tree);
var x = tree.GetRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>().Where(v => v.Identifier.ValueText == "x").Single();
......@@ -1925,7 +1925,6 @@ public void M()
// void Local<[A, B, CLSCompliant, D]T>() { }
Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "CLSCompliant").WithArguments("isCompliant", "System.CLSCompliantAttribute.CLSCompliantAttribute(bool)").WithLocation(7, 27));
var localDecl = tree.FindNodeOrTokenByKind(SyntaxKind.LocalFunctionStatement);
var model = comp.GetSemanticModel(tree);
var localSymbol = Assert.IsType<LocalFunctionSymbol>(model.GetDeclaredSymbol(localDecl.AsNode()).GetSymbol());
......@@ -2482,7 +2481,6 @@ public void BadLocal2()
// (13,17): warning CS8321: The local function 'BadLocal2' is declared but never used
// public void BadLocal2()
Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "BadLocal2").WithArguments("BadLocal2").WithLocation(13, 17));
}
[Fact]
......@@ -2729,7 +2727,7 @@ static void Main()
[WorkItem(3923, "https://github.com/dotnet/roslyn/issues/3923")]
[Fact]
public void ExpressionTreeLocalFunctionUsage()
public void ExpressionTreeLocalFunctionUsage_01()
{
var source = @"
using System;
......@@ -2754,16 +2752,54 @@ Expression<Func<T>> Local<T>(Expression<Func<T>> f)
}
";
VerifyDiagnostics(source,
// (16,35): error CS8096: An expression tree may not contain a reference to a local function
// Console.Write(Local(() => Id(2)));
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsLocalFunction, "Id(2)").WithLocation(16, 35),
// (17,51): error CS8096: An expression tree may not contain a reference to a local function
// Console.Write(Local<Func<int, int>>(() => Id));
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsLocalFunction, "Id").WithLocation(17, 51),
// (18,35): error CS8096: An expression tree may not contain a reference to a local function
// Console.Write(Local(() => new Func<int, int>(Id)));
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsLocalFunction, "Id").WithLocation(18, 54)
);
// (16,35): error CS8096: An expression tree may not contain a reference to a local function
// Console.Write(Local(() => Id(2)));
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsLocalFunction, "Id(2)").WithLocation(16, 35),
// (17,51): error CS8096: An expression tree may not contain a reference to a local function
// Console.Write(Local<Func<int, int>>(() => Id));
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsLocalFunction, "Id").WithLocation(17, 51),
// (18,35): error CS8096: An expression tree may not contain a reference to a local function
// Console.Write(Local(() => new Func<int, int>(Id)));
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsLocalFunction, "Id").WithLocation(18, 54)
);
}
[Fact]
public void ExpressionTreeLocalFunctionUsage_02()
{
var source = @"
using System;
using System.Linq.Expressions;
class Program
{
static void Main()
{
static T Id<T>(T x)
{
return x;
}
static Expression<Func<T>> Local<T>(Expression<Func<T>> f)
{
return f;
}
Console.Write(Local(() => Id(2)));
Console.Write(Local<Func<int, int>>(() => Id));
Console.Write(Local(() => new Func<int, int>(Id)));
Console.Write(Local(() => nameof(Id)));
}
}
";
VerifyDiagnostics(source,
// (16,35): error CS8096: An expression tree may not contain a reference to a local function
// Console.Write(Local(() => Id(2)));
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsLocalFunction, "Id(2)").WithLocation(16, 35),
// (17,51): error CS8096: An expression tree may not contain a reference to a local function
// Console.Write(Local<Func<int, int>>(() => Id));
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsLocalFunction, "Id").WithLocation(17, 51),
// (18,35): error CS8096: An expression tree may not contain a reference to a local function
// Console.Write(Local(() => new Func<int, int>(Id)));
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsLocalFunction, "Id").WithLocation(18, 54)
);
}
[Fact]
......@@ -2798,6 +2834,7 @@ static void Main()
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsLocalFunction, "Local(x)").WithLocation(11, 20)
);
}
[Fact]
public void BadScoping()
{
......@@ -5660,6 +5697,54 @@ static void F1(int y)
Diagnostic(ErrorCode.ERR_StaticLocalFunctionCannotCaptureVariable, "x").WithArguments("x").WithLocation(8, 25));
}
/// <summary>
/// Can reference type parameters from enclosing scope.
/// </summary>
[Fact]
public void StaticWithTypeParameterReferences()
{
var source =
@"using static System.Console;
class A<T>
{
internal string F1()
{
static string L1() => typeof(T).FullName;
return L1();
}
}
class B
{
internal string F2<T>()
{
static string L2() => typeof(T).FullName;
return L2();
}
internal static string F3()
{
static string L3<T>()
{
static string L4() => typeof(T).FullName;
return L4();
}
return L3<byte>();
}
}
class Program
{
static void Main()
{
WriteLine(new A<int>().F1());
WriteLine(new B().F2<string>());
WriteLine(B.F3());
}
}";
CompileAndVerify(source, expectedOutput:
@"System.Int32
System.String
System.Byte");
}
[Fact]
public void Conditional_ThisReferenceInStatic()
{
......@@ -6645,15 +6730,302 @@ public void EmittedAsStatic_02()
{
static void M<T>()
{
static void local(T t) { }
static void local(T t) { System.Console.Write(t.GetType().FullName); }
System.Action<T> action = local;
action(default(T));
}
static void Main()
{
M<int>();
}
}";
CompileAndVerify(source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: m =>
CompileAndVerify(source, options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), expectedOutput: "System.Int32", symbolValidator: m =>
{
var method = (MethodSymbol)m.GlobalNamespace.GetMember("Program.<M>g__local|0_0");
Assert.True(method.IsStatic);
Assert.True(method.IsGenericMethod);
Assert.Equal("void Program.<M>g__local|0_0<T>(T t)", method.ToTestDisplayString());
});
}
/// <summary>
/// Local function in generic method is emitted as a generic
/// method even if no references to type parameters.
/// </summary>
[Fact]
[WorkItem(38143, "https://github.com/dotnet/roslyn/issues/38143")]
public void EmittedAsStatic_03()
{
var source =
@"class Program
{
static void M<T>() where T : new()
{
static void local(object o) { System.Console.Write(o.GetType().FullName); }
local(new T());
}
static void Main()
{
M<int>();
}
}";
CompileAndVerify(source, options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), expectedOutput: "System.Int32", symbolValidator: m =>
{
var method = (MethodSymbol)m.GlobalNamespace.GetMember("Program.<M>g__local|0_0");
Assert.True(method.IsStatic);
Assert.True(method.IsGenericMethod);
Assert.Equal("void Program.<M>g__local|0_0<T>(System.Object o)", method.ToTestDisplayString());
});
}
/// <summary>
/// Emit 'call' rather than 'callvirt' for local functions regardless of whether
/// the local function is static.
/// </summary>
[Fact]
public void EmitCallInstruction()
{
var source =
@"using static System.Console;
class Program
{
static void Main()
{
int i;
void L1() => WriteLine(i++);
static void L2(int i) => WriteLine(i);
i = 1;
L1();
L2(i);
}
}";
var verifier = CompileAndVerify(source, expectedOutput:
@"1
2");
verifier.VerifyIL("Program.Main",
@"{
// Code size 27 (0x1b)
.maxstack 2
.locals init (Program.<>c__DisplayClass0_0 V_0) //CS$<>8__locals0
IL_0000: ldloca.s V_0
IL_0002: ldc.i4.1
IL_0003: stfld ""int Program.<>c__DisplayClass0_0.i""
IL_0008: ldloca.s V_0
IL_000a: call ""void Program.<Main>g__L1|0_0(ref Program.<>c__DisplayClass0_0)""
IL_000f: ldloc.0
IL_0010: ldfld ""int Program.<>c__DisplayClass0_0.i""
IL_0015: call ""void Program.<Main>g__L2|0_1(int)""
IL_001a: ret
}");
}
/// <summary>
/// '_' should bind to '_' symbol in outer scope even in static local function.
/// </summary>
[Fact]
public void UnderscoreInOuterScope()
{
var source =
@"#pragma warning disable 8321
class C1
{
object _;
void F1()
{
void A1(object x) => _ = x;
static void B1(object y) => _ = y;
}
}
class C2
{
static void F2()
{
object _;
void A2(object x) => _ = x;
static void B2(object y) => _ = y;
}
static void F3()
{
void A3(object x) => _ = x;
static void B3(object y) => _ = y;
}
}";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (8,37): error CS8422: A static local function cannot contain a reference to 'this' or 'base'.
// static void B1(object y) => _ = y;
Diagnostic(ErrorCode.ERR_StaticLocalFunctionCannotCaptureThis, "_").WithLocation(8, 37),
// (17,37): error CS8421: A static local function cannot contain a reference to '_'.
// static void B2(object y) => _ = y;
Diagnostic(ErrorCode.ERR_StaticLocalFunctionCannotCaptureVariable, "_").WithArguments("_").WithLocation(17, 37));
var tree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(tree);
var nodes = tree.GetRoot().DescendantNodes().OfType<AssignmentExpressionSyntax>();
var actualSymbols = nodes.Select(n => model.GetSymbolInfo(n.Left).Symbol).Select(s => $"{s.Kind}: {s.ToTestDisplayString()}").ToArray();
var expectedSymbols = new[]
{
"Field: System.Object C1._",
"Field: System.Object C1._",
"Local: System.Object _",
"Local: System.Object _",
"Discard: System.Object _",
"Discard: System.Object _",
};
AssertEx.Equal(expectedSymbols, actualSymbols);
}
/// <summary>
/// 'var' should bind to 'var' symbol in outer scope even in static local function.
/// </summary>
[Fact]
public void VarInOuterScope()
{
var source =
@"#pragma warning disable 8321
class C1
{
class var { }
static void F1()
{
void A1(object x) { var y = x; }
static void B1(object x) { var y = x; }
}
}
namespace N
{
using var = System.String;
class C2
{
static void F2()
{
void A2(object x) { var y = x; }
static void B3(object x) { var y = x; }
}
}
}";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (7,37): error CS0266: Cannot implicitly convert type 'object' to 'C1.var'. An explicit conversion exists (are you missing a cast?)
// void A1(object x) { var y = x; }
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "x").WithArguments("object", "C1.var").WithLocation(7, 37),
// (8,44): error CS0266: Cannot implicitly convert type 'object' to 'C1.var'. An explicit conversion exists (are you missing a cast?)
// static void B1(object x) { var y = x; }
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "x").WithArguments("object", "C1.var").WithLocation(8, 44),
// (18,41): error CS0266: Cannot implicitly convert type 'object' to 'string'. An explicit conversion exists (are you missing a cast?)
// void A2(object x) { var y = x; }
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "x").WithArguments("object", "string").WithLocation(18, 41),
// (19,48): error CS0266: Cannot implicitly convert type 'object' to 'string'. An explicit conversion exists (are you missing a cast?)
// static void B3(object x) { var y = x; }
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "x").WithArguments("object", "string").WithLocation(19, 48));
var tree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(tree);
var nodes = tree.GetRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>();
var actualSymbols = nodes.Select(n => model.GetDeclaredSymbol(n)).ToTestDisplayStrings();
var expectedSymbols = new[]
{
"C1.var y",
"C1.var y",
"System.String y",
"System.String y",
};
AssertEx.Equal(expectedSymbols, actualSymbols);
}
[Fact]
public void AwaitWithinAsyncOuterScope_01()
{
var source =
@"#pragma warning disable 1998
#pragma warning disable 8321
using System.Threading.Tasks;
class Program
{
void F1()
{
void A1() { await Task.Yield(); }
static void B1() { await Task.Yield(); }
}
void F2()
{
async void A2() { await Task.Yield(); }
async static void B2() { await Task.Yield(); }
}
async void F3()
{
void A3() { await Task.Yield(); }
static void B3() { await Task.Yield(); }
}
async void F4()
{
async void A4() { await Task.Yield(); }
async static void B4() { await Task.Yield(); }
}
}";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (8,21): error CS4033: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
// void A1() { await Task.Yield(); }
Diagnostic(ErrorCode.ERR_BadAwaitWithoutVoidAsyncMethod, "await Task.Yield()").WithLocation(8, 21),
// (9,28): error CS4033: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
// static void B1() { await Task.Yield(); }
Diagnostic(ErrorCode.ERR_BadAwaitWithoutVoidAsyncMethod, "await Task.Yield()").WithLocation(9, 28),
// (18,21): error CS4033: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
// void A3() { await Task.Yield(); }
Diagnostic(ErrorCode.ERR_BadAwaitWithoutVoidAsyncMethod, "await Task.Yield()").WithLocation(18, 21),
// (19,28): error CS4033: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
// static void B3() { await Task.Yield(); }
Diagnostic(ErrorCode.ERR_BadAwaitWithoutVoidAsyncMethod, "await Task.Yield()").WithLocation(19, 28));
}
/// <summary>
/// 'await' should be a contextual keyword in the same way,
/// regardless of whether local function is static.
/// </summary>
[Fact]
public void AwaitWithinAsyncOuterScope_02()
{
var source =
@"#pragma warning disable 1998
#pragma warning disable 8321
class Program
{
void F1()
{
void A1<await>() { }
static void B1<await>() { }
}
void F2()
{
async void A2<await>() { }
async static void B2<await>() { }
}
async void F3()
{
void A3<await>() { }
static void B3<await>() { }
}
async void F4()
{
async void A4<await>() { }
async static void B4<await>() { }
}
}";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (12,23): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression
// async void A2<await>() { }
Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(12, 23),
// (13,30): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression
// async static void B2<await>() { }
Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(13, 30),
// (22,23): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression
// async void A4<await>() { }
Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(22, 23),
// (23,30): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression
// async static void B4<await>() { }
Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(23, 30));
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册