/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenUsingStatementTests.cs
3 276 строк
79 KB
Fred Silberberg
Simplify sequence point display (#81104)
10 ноя 2025, 20:11
Не верифицирован
10 ноя 2025, 20:11
0544ad5
Код
Авторство
О чём код?
// 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 Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { public class UsingStatementTests : EmitMetadataTestBase { #region "From UsingStatementTests" private const string DisposableClass = @" public class DisposableClass : System.IDisposable { private readonly string name; public DisposableClass(string name) { this.name = name; System.Console.WriteLine(""Creating "" + name); } public void Dispose() { System.Console.WriteLine(""Disposing "" + name); } } "; private const string DisposableStruct = @" public struct DisposableStruct : System.IDisposable { private readonly string name; public DisposableStruct(string name) { this.name = name; System.Console.WriteLine(""Creating "" + name); } public void Dispose() { System.Console.WriteLine(""Disposing "" + name); } } "; [Fact] public void UsingExpressionNull() { var text = @" using System; public class Test { static void Main(string[] args) { System.Console.WriteLine(""Before""); using (null) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } "; var verifier = CompileAndVerify(text, expectedOutput: @"Before In After"); verifier.VerifyIL("Test.Main", @" { // Code size 31 (0x1f) .maxstack 1 IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldstr ""In"" IL_000f: call ""void System.Console.WriteLine(string)"" IL_0014: ldstr ""After"" IL_0019: call ""void System.Console.WriteLine(string)"" IL_001e: ret }"); } [Fact] public void UsingExpressionNullConstant() { var text = @" using System; public class Test { static void Main(string[] args) { System.Console.WriteLine(""Before""); const IDisposable o = null; using (o) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } "; var verifier = CompileAndVerify(text, expectedOutput: @"Before In After"); verifier.VerifyIL("Test.Main", @" { // Code size 31 (0x1f) .maxstack 1 IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldstr ""In"" IL_000f: call ""void System.Console.WriteLine(string)"" IL_0014: ldstr ""After"" IL_0019: call ""void System.Console.WriteLine(string)"" IL_001e: ret }"); } [Fact] public void UsingExpressionNullable() { var text = @" using System; public class Test { static void Main(string[] args) { System.Console.WriteLine(""Before""); using (new DisposableClass(""A"")) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } " + DisposableClass; var verifier = CompileAndVerify(text, expectedOutput: @"Before Creating A In Disposing A After"); verifier.VerifyIL("Test.Main", @" { // Code size 54 (0x36) .maxstack 1 .locals init (DisposableClass V_0) IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldstr ""A"" IL_000f: newobj ""DisposableClass..ctor(string)"" IL_0014: stloc.0 .try { IL_0015: ldstr ""In"" IL_001a: call ""void System.Console.WriteLine(string)"" IL_001f: leave.s IL_002b } finally { IL_0021: ldloc.0 IL_0022: brfalse.s IL_002a IL_0024: ldloc.0 IL_0025: callvirt ""void System.IDisposable.Dispose()"" IL_002a: endfinally } IL_002b: ldstr ""After"" IL_0030: call ""void System.Console.WriteLine(string)"" IL_0035: ret }"); } [Fact] public void UsingExpressionNonNullable() { var text = @" using System; public class Test { static void Main(string[] args) { System.Console.WriteLine(""Before""); using (new DisposableStruct(""A"")) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } " + DisposableStruct; var verifier = CompileAndVerify(text, expectedOutput: @"Before Creating A In Disposing A After"); verifier.VerifyIL("Test.Main", @" { // Code size 59 (0x3b) .maxstack 2 .locals init (DisposableStruct V_0) IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldloca.s V_0 IL_000c: ldstr ""A"" IL_0011: call ""DisposableStruct..ctor(string)"" .try { IL_0016: ldstr ""In"" IL_001b: call ""void System.Console.WriteLine(string)"" IL_0020: leave.s IL_0030 } finally { IL_0022: ldloca.s V_0 IL_0024: constrained. ""DisposableStruct"" IL_002a: callvirt ""void System.IDisposable.Dispose()"" IL_002f: endfinally } IL_0030: ldstr ""After"" IL_0035: call ""void System.Console.WriteLine(string)"" IL_003a: ret }"); } [Fact] public void UsingExpressionUnconstrainedTypeParameter() { var text = @" using System; public class Test { static void Main(string[] args) { M<DisposableClass>(new DisposableClass(""A"")); M<DisposableClass>(null); M<DisposableStruct>(new DisposableStruct(""B"")); } static void M<T>(T t) where T : IDisposable { System.Console.WriteLine(""Before""); using (t) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } " + DisposableClass + DisposableStruct; string expected = @"Creating A Before In Disposing A After Before In After Creating B Before In Disposing B After"; var verifier = CompileAndVerify(text, expectedOutput: expected); verifier.VerifyIL("Test.M<T>", @" { // Code size 57 (0x39) .maxstack 1 .locals init (T V_0) IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldarg.0 IL_000b: stloc.0 .try { IL_000c: ldstr ""In"" IL_0011: call ""void System.Console.WriteLine(string)"" IL_0016: leave.s IL_002e } finally { IL_0018: ldloc.0 IL_0019: box ""T"" IL_001e: brfalse.s IL_002d IL_0020: ldloca.s V_0 IL_0022: constrained. ""T"" IL_0028: callvirt ""void System.IDisposable.Dispose()"" IL_002d: endfinally } IL_002e: ldstr ""After"" IL_0033: call ""void System.Console.WriteLine(string)"" IL_0038: ret }"); } [Fact] public void UsingExpressionNonNullableTypeParameter() { var text = @" using System; public class Test { static void Main(string[] args) { M(new DisposableStruct(""A"")); } static void M<T>(T t) where T : struct, IDisposable { System.Console.WriteLine(""Before""); using (t) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } " + DisposableStruct; var verifier = CompileAndVerify(text, expectedOutput: @"Creating A Before In Disposing A After"); verifier.VerifyIL("Test.M<T>", @" { // Code size 49 (0x31) .maxstack 1 .locals init (T V_0) IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldarg.0 IL_000b: stloc.0 .try { IL_000c: ldstr ""In"" IL_0011: call ""void System.Console.WriteLine(string)"" IL_0016: leave.s IL_0026 } finally { IL_0018: ldloca.s V_0 IL_001a: constrained. ""T"" IL_0020: callvirt ""void System.IDisposable.Dispose()"" IL_0025: endfinally } IL_0026: ldstr ""After"" IL_002b: call ""void System.Console.WriteLine(string)"" IL_0030: ret }"); } [Fact] public void UsingDeclarationNull() { var text = @" using System; public class Test { static void Main(string[] args) { System.Console.WriteLine(""Before""); using (IDisposable i = null) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } "; var verifier = CompileAndVerify(text, expectedOutput: @"Before In After"); verifier.VerifyIL("Test.Main", @" { // Code size 31 (0x1f) .maxstack 1 IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldstr ""In"" IL_000f: call ""void System.Console.WriteLine(string)"" IL_0014: ldstr ""After"" IL_0019: call ""void System.Console.WriteLine(string)"" IL_001e: ret }"); } [Fact] public void UsingDeclarationNullConstant() { var text = @" using System; public class Test { static void Main(string[] args) { System.Console.WriteLine(""Before""); const IDisposable o = null; using (IDisposable i = o) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } "; var verifier = CompileAndVerify(text, expectedOutput: @"Before In After"); verifier.VerifyIL("Test.Main", @" { // Code size 31 (0x1f) .maxstack 1 IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldstr ""In"" IL_000f: call ""void System.Console.WriteLine(string)"" IL_0014: ldstr ""After"" IL_0019: call ""void System.Console.WriteLine(string)"" IL_001e: ret }"); } [Fact] public void UsingDeclarationNullable() { var text = @" using System; public class Test { static void Main(string[] args) { System.Console.WriteLine(""Before""); using (DisposableClass d = new DisposableClass(""A"")) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } " + DisposableClass; var verifier = CompileAndVerify(text, expectedOutput: @"Before Creating A In Disposing A After"); verifier.VerifyIL("Test.Main", @" { // Code size 54 (0x36) .maxstack 1 .locals init (DisposableClass V_0) //d IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldstr ""A"" IL_000f: newobj ""DisposableClass..ctor(string)"" IL_0014: stloc.0 .try { IL_0015: ldstr ""In"" IL_001a: call ""void System.Console.WriteLine(string)"" IL_001f: leave.s IL_002b } finally { IL_0021: ldloc.0 IL_0022: brfalse.s IL_002a IL_0024: ldloc.0 IL_0025: callvirt ""void System.IDisposable.Dispose()"" IL_002a: endfinally } IL_002b: ldstr ""After"" IL_0030: call ""void System.Console.WriteLine(string)"" IL_0035: ret }"); } [Fact] public void UsingDeclarationNonNullable() { var text = @" using System; public class Test { static void Main(string[] args) { System.Console.WriteLine(""Before""); using (DisposableStruct d = new DisposableStruct(""A"")) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } " + DisposableStruct; var verifier = CompileAndVerify(text, expectedOutput: @"Before Creating A In Disposing A After"); verifier.VerifyIL("Test.Main", @" { // Code size 59 (0x3b) .maxstack 2 .locals init (DisposableStruct V_0) //d IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldloca.s V_0 IL_000c: ldstr ""A"" IL_0011: call ""DisposableStruct..ctor(string)"" .try { IL_0016: ldstr ""In"" IL_001b: call ""void System.Console.WriteLine(string)"" IL_0020: leave.s IL_0030 } finally { IL_0022: ldloca.s V_0 IL_0024: constrained. ""DisposableStruct"" IL_002a: callvirt ""void System.IDisposable.Dispose()"" IL_002f: endfinally } IL_0030: ldstr ""After"" IL_0035: call ""void System.Console.WriteLine(string)"" IL_003a: ret }"); } [Fact] public void UsingDeclarationNullableTypeParameter() { var text = @" using System; public class Test { static void Main(string[] args) { M<DisposableClass>(new DisposableClass(""A"")); M<DisposableClass>(null); M<DisposableStruct>(new DisposableStruct(""B"")); } static void M<T>(T t) where T : IDisposable { System.Console.WriteLine(""Before""); using (T d = t) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } " + DisposableClass + DisposableStruct; string expected = @"Creating A Before In Disposing A After Before In After Creating B Before In Disposing B After"; var verifier = CompileAndVerify(text, expectedOutput: expected); verifier.VerifyIL("Test.M<T>", @" { // Code size 57 (0x39) .maxstack 1 .locals init (T V_0) //d IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldarg.0 IL_000b: stloc.0 .try { IL_000c: ldstr ""In"" IL_0011: call ""void System.Console.WriteLine(string)"" IL_0016: leave.s IL_002e } finally { IL_0018: ldloc.0 IL_0019: box ""T"" IL_001e: brfalse.s IL_002d IL_0020: ldloca.s V_0 IL_0022: constrained. ""T"" IL_0028: callvirt ""void System.IDisposable.Dispose()"" IL_002d: endfinally } IL_002e: ldstr ""After"" IL_0033: call ""void System.Console.WriteLine(string)"" IL_0038: ret }"); } [Fact] public void UsingDeclarationNonNullableTypeParameter() { var text = @" using System; public class Test { static void Main(string[] args) { M(new DisposableStruct(""A"")); } static void M<T>(T t) where T : struct, IDisposable { System.Console.WriteLine(""Before""); using (T d = t) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } " + DisposableStruct; var verifier = CompileAndVerify(text, expectedOutput: @"Creating A Before In Disposing A After"); verifier.VerifyIL("Test.M<T>", @" { // Code size 49 (0x31) .maxstack 1 .locals init (T V_0) //d IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldarg.0 IL_000b: stloc.0 .try { IL_000c: ldstr ""In"" IL_0011: call ""void System.Console.WriteLine(string)"" IL_0016: leave.s IL_0026 } finally { IL_0018: ldloca.s V_0 IL_001a: constrained. ""T"" IL_0020: callvirt ""void System.IDisposable.Dispose()"" IL_0025: endfinally } IL_0026: ldstr ""After"" IL_002b: call ""void System.Console.WriteLine(string)"" IL_0030: ret }"); } [Fact] public void UsingDeclarationMultipleNullable() { var text = @" using System; public class Test { static void Main(string[] args) { System.Console.WriteLine(""Before""); using (DisposableClass d1 = new DisposableClass(""A""), d2 = new DisposableClass(""B""), d3 = new DisposableClass(""C"")) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } " + DisposableClass; var verifier = CompileAndVerify(text, expectedOutput: @"Before Creating A Creating B Creating C In Disposing C Disposing B Disposing A After"); verifier.VerifyIL("Test.Main", @" { // Code size 96 (0x60) .maxstack 1 .locals init (DisposableClass V_0, //d1 DisposableClass V_1, //d2 DisposableClass V_2) //d3 IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldstr ""A"" IL_000f: newobj ""DisposableClass..ctor(string)"" IL_0014: stloc.0 .try { IL_0015: ldstr ""B"" IL_001a: newobj ""DisposableClass..ctor(string)"" IL_001f: stloc.1 .try { IL_0020: ldstr ""C"" IL_0025: newobj ""DisposableClass..ctor(string)"" IL_002a: stloc.2 .try { IL_002b: ldstr ""In"" IL_0030: call ""void System.Console.WriteLine(string)"" IL_0035: leave.s IL_0055 } finally { IL_0037: ldloc.2 IL_0038: brfalse.s IL_0040 IL_003a: ldloc.2 IL_003b: callvirt ""void System.IDisposable.Dispose()"" IL_0040: endfinally } } finally { IL_0041: ldloc.1 IL_0042: brfalse.s IL_004a IL_0044: ldloc.1 IL_0045: callvirt ""void System.IDisposable.Dispose()"" IL_004a: endfinally } } finally { IL_004b: ldloc.0 IL_004c: brfalse.s IL_0054 IL_004e: ldloc.0 IL_004f: callvirt ""void System.IDisposable.Dispose()"" IL_0054: endfinally } IL_0055: ldstr ""After"" IL_005a: call ""void System.Console.WriteLine(string)"" IL_005f: ret }"); } [Fact] public void UsingDeclarationMultipleNonNullable() { var text = @" using System; public class Test { static void Main(string[] args) { System.Console.WriteLine(""Before""); using (DisposableStruct d1 = new DisposableStruct(""A""), d2 = new DisposableStruct(""B""), d3 = new DisposableStruct(""C"")) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } " + DisposableStruct; var verifier = CompileAndVerify(text, expectedOutput: @"Before Creating A Creating B Creating C In Disposing C Disposing B Disposing A After"); verifier.VerifyIL("Test.Main", @" { // Code size 109 (0x6d) .maxstack 2 .locals init (DisposableStruct V_0, //d1 DisposableStruct V_1, //d2 DisposableStruct V_2) //d3 IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldloca.s V_0 IL_000c: ldstr ""A"" IL_0011: call ""DisposableStruct..ctor(string)"" .try { IL_0016: ldstr ""B"" IL_001b: newobj ""DisposableStruct..ctor(string)"" IL_0020: stloc.1 .try { IL_0021: ldstr ""C"" IL_0026: newobj ""DisposableStruct..ctor(string)"" IL_002b: stloc.2 .try { IL_002c: ldstr ""In"" IL_0031: call ""void System.Console.WriteLine(string)"" IL_0036: leave.s IL_0062 } finally { IL_0038: ldloca.s V_2 IL_003a: constrained. ""DisposableStruct"" IL_0040: callvirt ""void System.IDisposable.Dispose()"" IL_0045: endfinally } } finally { IL_0046: ldloca.s V_1 IL_0048: constrained. ""DisposableStruct"" IL_004e: callvirt ""void System.IDisposable.Dispose()"" IL_0053: endfinally } } finally { IL_0054: ldloca.s V_0 IL_0056: constrained. ""DisposableStruct"" IL_005c: callvirt ""void System.IDisposable.Dispose()"" IL_0061: endfinally } IL_0062: ldstr ""After"" IL_0067: call ""void System.Console.WriteLine(string)"" IL_006c: ret }"); } [Fact] public void UsingDeclarationMultipleSomeNull() { var text = @" using System; public class Test { static void Main(string[] args) { System.Console.WriteLine(""Before""); using (DisposableClass d1 = new DisposableClass(""A""), d2 = null, d3 = new DisposableClass(""C"")) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } " + DisposableClass + DisposableStruct; var verifier = CompileAndVerify(text, expectedOutput: @"Before Creating A Creating C In Disposing C Disposing A After"); verifier.VerifyIL("Test.Main", @" { // Code size 75 (0x4b) .maxstack 1 .locals init (DisposableClass V_0, //d1 DisposableClass V_1) //d3 IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldstr ""A"" IL_000f: newobj ""DisposableClass..ctor(string)"" IL_0014: stloc.0 .try { IL_0015: ldstr ""C"" IL_001a: newobj ""DisposableClass..ctor(string)"" IL_001f: stloc.1 .try { IL_0020: ldstr ""In"" IL_0025: call ""void System.Console.WriteLine(string)"" IL_002a: leave.s IL_0040 } finally { IL_002c: ldloc.1 IL_002d: brfalse.s IL_0035 IL_002f: ldloc.1 IL_0030: callvirt ""void System.IDisposable.Dispose()"" IL_0035: endfinally } } finally { IL_0036: ldloc.0 IL_0037: brfalse.s IL_003f IL_0039: ldloc.0 IL_003a: callvirt ""void System.IDisposable.Dispose()"" IL_003f: endfinally } IL_0040: ldstr ""After"" IL_0045: call ""void System.Console.WriteLine(string)"" IL_004a: ret }"); } [Fact] public void UsingDeclarationMultipleAllNull() { var text = @" using System; public class Test { static void Main(string[] args) { System.Console.WriteLine(""Before""); using (DisposableClass d1 = null, d2 = null, d3 = null) { System.Console.WriteLine(""In""); } System.Console.WriteLine(""After""); } } " + DisposableClass + DisposableStruct; var verifier = CompileAndVerify(text, expectedOutput: @"Before In After"); verifier.VerifyIL("Test.Main", @" { // Code size 31 (0x1f) .maxstack 1 IL_0000: ldstr ""Before"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ldstr ""In"" IL_000f: call ""void System.Console.WriteLine(string)"" IL_0014: ldstr ""After"" IL_0019: call ""void System.Console.WriteLine(string)"" IL_001e: ret }"); } [Fact, WorkItem(543249, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543249")] public void UsingInCatchBlock() { var text = @" class Test { static void Main() { try { throw new System.Exception(); } catch { using (var d = new DisposeImpl()) { } } System.Console.Write(""2""); } class DisposeImpl : System.IDisposable { public void Dispose() { System.Console.Write(""1""); } } }"; CompileAndVerify(text, expectedOutput: "12"); } #endregion // The object could be created inside the "using" statement [ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] public void ObjectCreateInsideUsing() { var source = @" using System; class Program { static void Main(string[] args) { using (MyManagedClass mnObj = new MyManagedClass()) { mnObj.Use(); } } class MyManagedClass : System.IDisposable { public void Dispose() { } public void Use() { } } } "; CompileAndVerify(source).VerifyIL("Program.Main", @" { // Code size 25 (0x19) .maxstack 1 .locals init (Program.MyManagedClass V_0) //mnObj // sequence point: MyManagedClass mnObj = new MyManagedClass() IL_0000: newobj ""Program.MyManagedClass..ctor()"" IL_0005: stloc.0 .try { // sequence point: mnObj.Use(); IL_0006: ldloc.0 IL_0007: callvirt ""void Program.MyManagedClass.Use()"" // sequence point: } IL_000c: leave.s IL_0018 } finally { // sequence point: <hidden> IL_000e: ldloc.0 IL_000f: brfalse.s IL_0017 IL_0011: ldloc.0 IL_0012: callvirt ""void System.IDisposable.Dispose()"" // sequence point: <hidden> IL_0017: endfinally } // sequence point: } IL_0018: ret }", sequencePointDisplay: SequencePointDisplayMode.Enhanced); } [Fact] public void UsingPatternTest() { var source = @" using System; ref struct S1 { public void Dispose() { Console.WriteLine(""S1.Dispose()""); } } class C2 { static void Main() { using (S1 s = new S1()) { } } }"; CompileAndVerify(source, expectedOutput: "S1.Dispose()").VerifyIL("C2.Main()", @" { // Code size 19 (0x13) .maxstack 1 .locals init (S1 V_0) //s IL_0000: ldloca.s V_0 IL_0002: initobj ""S1"" .try { IL_0008: leave.s IL_0012 } finally { IL_000a: ldloca.s V_0 IL_000c: call ""void S1.Dispose()"" IL_0011: endfinally } IL_0012: ret } "); } [Fact] public void UsingPatternDiffParameterOverloadTest() { var source = @" using System; ref struct S1 { public void Dispose() { Console.WriteLine(""S1.Dispose()""); } public void Dispose(int x) { Console.WriteLine(""S1.Dispose(int)""); } } class C2 { static void Main() { using (S1 s = new S1()) { } } }"; CompileAndVerify(source, expectedOutput: "S1.Dispose()").VerifyIL("C2.Main()", @" { // Code size 19 (0x13) .maxstack 1 .locals init (S1 V_0) //s IL_0000: ldloca.s V_0 IL_0002: initobj ""S1"" .try { IL_0008: leave.s IL_0012 } finally { IL_000a: ldloca.s V_0 IL_000c: call ""void S1.Dispose()"" IL_0011: endfinally } IL_0012: ret } " ); } [Fact] public void UsingPatternExtensionMethodTest() { var source = @" ref struct S1 { } static class C2 { public static void Dispose(this S1 s1) { } } class C3 { static void Main() { using (S1 s = new S1()) { } } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (17,16): error CS1674: 'S1': type used in a using statement must implement 'System.IDisposable'. // using (S1 s = new S1()) Diagnostic(ErrorCode.ERR_NoConvToIDisp, "S1 s = new S1()").WithArguments("S1").WithLocation(17, 16) ); } [Fact] public void UsingPatternExtensionMethodResolutionTest() { var source = @" using System; ref struct S1 { public void Dispose() { Console.WriteLine(""S1.Dispose()""); } } static class C2 { public static void Dispose(this S1 s1) { Console.WriteLine(""C2.Dispose(S1)""); } } class C3 { static void Main() { using (S1 s = new S1()) { } } }"; CompileAndVerify(source, expectedOutput: "S1.Dispose()").VerifyIL("C3.Main()", @" { // Code size 19 (0x13) .maxstack 1 .locals init (S1 V_0) //s IL_0000: ldloca.s V_0 IL_0002: initobj ""S1"" .try { IL_0008: leave.s IL_0012 } finally { IL_000a: ldloca.s V_0 IL_000c: call ""void S1.Dispose()"" IL_0011: endfinally } IL_0012: ret } "); } [Fact] public void UsingPatternExtensionMethodWithDefaultArguments() { var source = @" ref struct S1 { } static class C2 { internal static void Dispose(this S1 s1, int a = 1) { } } class C3 { static void Main() { using (S1 s = new S1()) { } } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (15,15): error CS1674: 'S1': type used in a using statement must implement 'System.IDisposable'. // using (S1 s = new S1()) Diagnostic(ErrorCode.ERR_NoConvToIDisp, "S1 s = new S1()").WithArguments("S1").WithLocation(15, 15) ); } [Fact] public void UsingPatternExtensionMethodWithParams() { var source = @" ref struct S1 { } static class C2 { internal static void Dispose(this S1 s1, params int[] args) { } } class C3 { static void Main() { using (S1 s = new S1()) { } } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (15,15): error CS1674: 'S1': type used in a using statement must implement 'System.IDisposable'. // using (S1 s = new S1()) Diagnostic(ErrorCode.ERR_NoConvToIDisp, "S1 s = new S1()").WithArguments("S1").WithLocation(15, 15) ); } // The object could be created outside the "using" statement [Fact] public void ObjectCreateOutsideUsing() { var source = @" using System; class Program { static void Main(string[] args) { MyManagedClass mnObj1 = new MyManagedClass(); using (mnObj1) { mnObj1.Use(); } } class MyManagedClass : System.IDisposable { public void Dispose() { } public void Use() { } } } "; CompileAndVerify(source).VerifyIL("Program.Main", @" { // Code size 27 (0x1b) .maxstack 1 .locals init (Program.MyManagedClass V_0, //mnObj1 Program.MyManagedClass V_1) IL_0000: newobj ""Program.MyManagedClass..ctor()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: stloc.1 .try { IL_0008: ldloc.0 IL_0009: callvirt ""void Program.MyManagedClass.Use()"" IL_000e: leave.s IL_001a } finally { IL_0010: ldloc.1 IL_0011: brfalse.s IL_0019 IL_0013: ldloc.1 IL_0014: callvirt ""void System.IDisposable.Dispose()"" IL_0019: endfinally } IL_001a: ret }"); } // The object could both be created outside the "using" statement [Fact] public void NoVarNameInsideUsing() { var source = @" using System; class Program { static void Main(string[] args) { MyManagedClass mnObj1 = new MyManagedClass(); using (mnObj1) { mnObj1.Use(); } } class MyManagedClass : System.IDisposable { public void Dispose() { } public void Use() { } } } "; CompileAndVerify(source).VerifyIL("Program.Main", @" { // Code size 27 (0x1b) .maxstack 1 .locals init (Program.MyManagedClass V_0, //mnObj1 Program.MyManagedClass V_1) IL_0000: newobj ""Program.MyManagedClass..ctor()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: stloc.1 .try { IL_0008: ldloc.0 IL_0009: callvirt ""void Program.MyManagedClass.Use()"" IL_000e: leave.s IL_001a } finally { IL_0010: ldloc.1 IL_0011: brfalse.s IL_0019 IL_0013: ldloc.1 IL_0014: callvirt ""void System.IDisposable.Dispose()"" IL_0019: endfinally } IL_001a: ret }"); } // Take the type parameter as object [Fact] public void TypeParameterAsUsingResource() { var source = @" class Gen<T> { public static void TestUsing(T obj) { using (obj) { } } } "; CreateCompilation(source).VerifyDiagnostics(Diagnostic(ErrorCode.ERR_NoConvToIDisp, "obj").WithArguments("T")); } [Fact] public void TypeParameterAsUsingResource_2() { var source = @" using System; using System.Collections.Generic; class Test<T> { public static IEnumerator<T> M<U>(IEnumerable<T> items) where U : IDisposable, new() { T val = default(T); U u = default(U); using (u = new U()) { } using (val) { } return null; } } class Gen<T> where T : new() { public static void TestUsing() { using (T disp = new T()) // Invalid { } } } "; CreateCompilation(source).VerifyDiagnostics( // (13,16): error CS1674: 'T': type used in a using statement must implement 'System.IDisposable'. // using (val) Diagnostic(ErrorCode.ERR_NoConvToIDisp, "val").WithArguments("T"), // (24,16): error CS1674: 'T': type used in a using statement must implement 'System.IDisposable'. // using (T disp = new T()) // Invalid Diagnostic(ErrorCode.ERR_NoConvToIDisp, "T disp = new T()").WithArguments("T")); } // Take the out parameter as object [Fact] public void OutParameterAsUsingResource() { var source = @" class Program { static void goo(ref MyManagedClass x, out MyManagedClass y) { using (x) { } using (y) // Invalid { } y = new MyManagedClass(); } class MyManagedClass : System.IDisposable { public void Dispose() { } public void Use() { } } } "; CreateCompilation(source).VerifyDiagnostics(Diagnostic(ErrorCode.ERR_UseDefViolationOut, "y").WithArguments("y")); } // Doesn't implement IDisposable, but has a Dispose() function [Fact] public void NotImplementIDispose() { var source = @" class Program { static void goo() { MyManagedClass res = new MyManagedClass(); using (res) // Invalid { } } class MyManagedClass { public void Dispose() { } } } "; CreateCompilation(source).VerifyDiagnostics( // (7,16): error CS1674: 'Program.MyManagedClass': type used in a using statement must implement 'System.IDisposable'. // using (res) // Invalid Diagnostic(ErrorCode.ERR_NoConvToIDisp, "res").WithArguments("Program.MyManagedClass").WithLocation(7, 16) ); } [Fact] public void NotImplementIDispose_Struct() { var source = @" class Program { static void goo() { MyManagedClass res = new MyManagedClass(); using (res) // Invalid { } } struct MyManagedClass { public void Dispose() { } } } "; CreateCompilation(source).VerifyDiagnostics( // (7,16): error CS1674: 'Program.MyManagedClass': type used in a using statement must implement 'System.IDisposable'. // using (res) // Invalid Diagnostic(ErrorCode.ERR_NoConvToIDisp, "res").WithArguments("Program.MyManagedClass").WithLocation(7, 16) ); } // Implicit implement IDisposable [Fact] public void ImplicitImplementIDispose() { var source = @" using System; class Program { static void goo() { MyManagedClass res = new MyManagedClass(); using (res) { } } class MyManagedClass :IDisposable { public void Dispose() { } } } "; CompileAndVerify(source).VerifyIL("Program.goo", @" { // Code size 19 (0x13) .maxstack 1 .locals init (Program.MyManagedClass V_0) IL_0000: newobj ""Program.MyManagedClass..ctor()"" IL_0005: stloc.0 .try { IL_0006: leave.s IL_0012 } finally { IL_0008: ldloc.0 IL_0009: brfalse.s IL_0011 IL_000b: ldloc.0 IL_000c: callvirt ""void System.IDisposable.Dispose()"" IL_0011: endfinally } IL_0012: ret }"); } [Fact] public void ImplicitImplementIDispose_Struct() { var source = @" using System; class Program { static void Main() { MyManagedClass res = new MyManagedClass(); using (res) { } } struct MyManagedClass :IDisposable { public void Dispose() { } } } "; CompileAndVerify(source).VerifyIL("Program.Main", @" { // Code size 27 (0x1b) .maxstack 1 .locals init (Program.MyManagedClass V_0) IL_0000: ldloca.s V_0 IL_0002: initobj ""Program.MyManagedClass"" IL_0008: ldloc.0 IL_0009: stloc.0 .try { IL_000a: leave.s IL_001a } finally { IL_000c: ldloca.s V_0 IL_000e: constrained. ""Program.MyManagedClass"" IL_0014: callvirt ""void System.IDisposable.Dispose()"" IL_0019: endfinally } IL_001a: ret }"); } // Explicit implement IDisposable [Fact] public void ExplicitImplementIDispose() { var source = @" using System; class Program { static void goo() { MyManagedClass res = new MyManagedClass(); using (res) { } } class MyManagedClass :IDisposable { public void Dispose() { } } } "; CompileAndVerify(source).VerifyIL("Program.goo", @" { // Code size 19 (0x13) .maxstack 1 .locals init (Program.MyManagedClass V_0) IL_0000: newobj ""Program.MyManagedClass..ctor()"" IL_0005: stloc.0 .try { IL_0006: leave.s IL_0012 } finally { IL_0008: ldloc.0 IL_0009: brfalse.s IL_0011 IL_000b: ldloc.0 IL_000c: callvirt ""void System.IDisposable.Dispose()"" IL_0011: endfinally } IL_0012: ret }"); } [Fact] public void ExplicitImplementIDispose_Struct() { var source = @" using System; class Program { static void Main() { MyManagedClass res = new MyManagedClass(); using (res) { } } struct MyManagedClass :IDisposable { public void Dispose() { } } } "; CompileAndVerify(source).VerifyIL("Program.Main", @" { // Code size 27 (0x1b) .maxstack 1 .locals init (Program.MyManagedClass V_0) IL_0000: ldloca.s V_0 IL_0002: initobj ""Program.MyManagedClass"" IL_0008: ldloc.0 IL_0009: stloc.0 .try { IL_000a: leave.s IL_001a } finally { IL_000c: ldloca.s V_0 IL_000e: constrained. ""Program.MyManagedClass"" IL_0014: callvirt ""void System.IDisposable.Dispose()"" IL_0019: endfinally } IL_001a: ret }"); } // The variable declared out of using is not read-only [Fact] public void VarDeclaredOutsideIsNotReadOnly() { var source = @" using System; class Program { static Res x; static void Main(string[] args) { using (x) { x = new Res(); // OK } } } class Res : IDisposable { void IDisposable.Dispose() { } } "; CompileAndVerify(source).VerifyIL("Program.Main", @" { // Code size 29 (0x1d) .maxstack 1 .locals init (Res V_0) IL_0000: ldsfld ""Res Program.x"" IL_0005: stloc.0 .try { IL_0006: newobj ""Res..ctor()"" IL_000b: stsfld ""Res Program.x"" IL_0010: leave.s IL_001c } finally { IL_0012: ldloc.0 IL_0013: brfalse.s IL_001b IL_0015: ldloc.0 IL_0016: callvirt ""void System.IDisposable.Dispose()"" IL_001b: endfinally } IL_001c: ret }"); } // The used variable is nulled-out in the using block [Fact] public void NulledVarDeclaredInUsing() { var source = @" using System; class Program { static MyManagedClass res1 = new MyManagedClass(); static void Main(string[] args) { using (res1) { res1.Func(); res1 = null; } } } class MyManagedClass : IDisposable { void IDisposable.Dispose() { System.Console.WriteLine(""Dispose""); } public void Func() { System.Console.WriteLine(""Func""); } } "; CompileAndVerify(source).VerifyIL("Program.Main", @" { // Code size 35 (0x23) .maxstack 1 .locals init (MyManagedClass V_0) IL_0000: ldsfld ""MyManagedClass Program.res1"" IL_0005: stloc.0 .try { IL_0006: ldsfld ""MyManagedClass Program.res1"" IL_000b: callvirt ""void MyManagedClass.Func()"" IL_0010: ldnull IL_0011: stsfld ""MyManagedClass Program.res1"" IL_0016: leave.s IL_0022 } finally { IL_0018: ldloc.0 IL_0019: brfalse.s IL_0021 IL_001b: ldloc.0 IL_001c: callvirt ""void System.IDisposable.Dispose()"" IL_0021: endfinally } IL_0022: ret }"); } // Dispose() not called if the object(Reference type) not created [Fact] public void ResourceIsNull_ReferenceType() { var source = @" using System; class Program { public static MyManagedClass MyProperty; static void Main(string[] args) { using (MyProperty) { System.Console.WriteLine(""InUsing""); } } } class MyManagedClass : IDisposable { void IDisposable.Dispose() { System.Console.WriteLine(""Dispose""); } public void Func() { System.Console.WriteLine(""Func""); } } "; CompileAndVerify(source, expectedOutput: "InUsing").VerifyIL("Program.Main", @" { // Code size 29 (0x1d) .maxstack 1 .locals init (MyManagedClass V_0) IL_0000: ldsfld ""MyManagedClass Program.MyProperty"" IL_0005: stloc.0 .try { IL_0006: ldstr ""InUsing"" IL_000b: call ""void System.Console.WriteLine(string)"" IL_0010: leave.s IL_001c } finally { IL_0012: ldloc.0 IL_0013: brfalse.s IL_001b IL_0015: ldloc.0 IL_0016: callvirt ""void System.IDisposable.Dispose()"" IL_001b: endfinally } IL_001c: ret }"); } [Fact] public void ResourceIsNull_ReferenceType_2() { var source = @" using System; class Program { public static MyManagedClass MyProperty; static void Main(string[] args) { using (MyProperty) { MyProperty = new MyManagedClass(); } } } class MyManagedClass : IDisposable { void IDisposable.Dispose() { System.Console.WriteLine(""Dispose""); } public void Func() { System.Console.WriteLine(""Func""); } } "; CompileAndVerify(source, expectedOutput: "").VerifyIL("Program.Main", @" { // Code size 29 (0x1d) .maxstack 1 .locals init (MyManagedClass V_0) IL_0000: ldsfld ""MyManagedClass Program.MyProperty"" IL_0005: stloc.0 .try { IL_0006: newobj ""MyManagedClass..ctor()"" IL_000b: stsfld ""MyManagedClass Program.MyProperty"" IL_0010: leave.s IL_001c } finally { IL_0012: ldloc.0 IL_0013: brfalse.s IL_001b IL_0015: ldloc.0 IL_0016: callvirt ""void System.IDisposable.Dispose()"" IL_001b: endfinally } IL_001c: ret }"); } // Dispose() called if the object(Value type) not created [Fact] public void ResourceIsNull_ValueType() { var source = @" using System; class Program { public static MyManagedClass MyProperty; static void Main(string[] args) { using (MyProperty) { System.Console.WriteLine(""InUsing""); } } } struct MyManagedClass : IDisposable { void IDisposable.Dispose() { System.Console.WriteLine(""Dispose""); } public void Func() { System.Console.WriteLine(""Func""); } } "; CompileAndVerify(source, expectedOutput: @"InUsing Dispose").VerifyIL("Program.Main", @" { // Code size 33 (0x21) .maxstack 1 .locals init (MyManagedClass V_0) IL_0000: ldsfld ""MyManagedClass Program.MyProperty"" IL_0005: stloc.0 .try { IL_0006: ldstr ""InUsing"" IL_000b: call ""void System.Console.WriteLine(string)"" IL_0010: leave.s IL_0020 } finally { IL_0012: ldloca.s V_0 IL_0014: constrained. ""MyManagedClass"" IL_001a: callvirt ""void System.IDisposable.Dispose()"" IL_001f: endfinally } IL_0020: ret }"); } [Fact] public void ResourceIsNull_ValueType_2() { var source = @" using System; class Program { public static MyManagedClass MyProperty; static void Main(string[] args) { using (MyProperty) { MyProperty = new MyManagedClass(); } } } struct MyManagedClass : IDisposable { void IDisposable.Dispose() { System.Console.WriteLine(""Dispose""); } public void Func() { System.Console.WriteLine(""Func""); } } "; CompileAndVerify(source, expectedOutput: "Dispose").VerifyIL("Program.Main", @" { // Code size 34 (0x22) .maxstack 1 .locals init (MyManagedClass V_0) IL_0000: ldsfld ""MyManagedClass Program.MyProperty"" IL_0005: stloc.0 .try { IL_0006: ldsflda ""MyManagedClass Program.MyProperty"" IL_000b: initobj ""MyManagedClass"" IL_0011: leave.s IL_0021 } finally { IL_0013: ldloca.s V_0 IL_0015: constrained. ""MyManagedClass"" IL_001b: callvirt ""void System.IDisposable.Dispose()"" IL_0020: endfinally } IL_0021: ret }"); } // Dispose() Not called if the object(Nullable type) not created [Fact] public void ResourceIsNull_NullableType() { var source = @" using System; class Program { public static MyManagedClass? MyProperty; static void Main(string[] args) { using (MyProperty) { System.Console.WriteLine(""InUsing""); } } } struct MyManagedClass : IDisposable { void IDisposable.Dispose() { System.Console.WriteLine(""Dispose""); } public void Func() { System.Console.WriteLine(""Func""); } } "; // This is not the same IL as is generated by the native compiler; the native compiler // generates the finally block with both a HasValue check *and* a boxing conversion: // // if (temp.HasValue) {((IDisposable) temp).Dispose(); } // // this is correct but slightly odd; if you're going to box then why not generate // // IDisposable temp2 = temp as IDisposable; if(temp2 != null) temp2.Dispose(); // // and if you're going to call HasValue then why not generate // // if (temp.HasValue) temp.GetValueOrDefault().Dispose(); // // without boxing? // // Roslyn does the latter. var comp = CompileAndVerify(source, expectedOutput: @"InUsing"); comp.VerifyIL("Program.Main", @" { // Code size 50 (0x32) .maxstack 1 .locals init (MyManagedClass? V_0, MyManagedClass V_1) IL_0000: ldsfld ""MyManagedClass? Program.MyProperty"" IL_0005: stloc.0 .try { IL_0006: ldstr ""InUsing"" IL_000b: call ""void System.Console.WriteLine(string)"" IL_0010: leave.s IL_0031 } finally { IL_0012: ldloca.s V_0 IL_0014: call ""bool MyManagedClass?.HasValue.get"" IL_0019: brfalse.s IL_0030 IL_001b: ldloca.s V_0 IL_001d: call ""MyManagedClass MyManagedClass?.GetValueOrDefault()"" IL_0022: stloc.1 IL_0023: ldloca.s V_1 IL_0025: constrained. ""MyManagedClass"" IL_002b: callvirt ""void System.IDisposable.Dispose()"" IL_0030: endfinally } IL_0031: ret }"); } [Fact] public void ResourceIsNull_NullableType_2() { var source = @" using System; class Program { public static MyManagedClass? MyProperty; static void Main(string[] args) { using (MyProperty) { MyProperty = new MyManagedClass(); } } } struct MyManagedClass : IDisposable { void IDisposable.Dispose() { System.Console.WriteLine(""Dispose""); } public void Func() { System.Console.WriteLine(""Func""); } } "; var comp = CompileAndVerify(source, expectedOutput: ""); // Note that changing the value of the expression does not cause the // Dispose to be called; we dispose the original value, not the new value. // // See comments in previous test regarding this codegen. comp.VerifyIL("Program.Main", @" { // Code size 59 (0x3b) .maxstack 1 .locals init (MyManagedClass? V_0, MyManagedClass V_1) IL_0000: ldsfld ""MyManagedClass? Program.MyProperty"" IL_0005: stloc.0 .try { IL_0006: ldloca.s V_1 IL_0008: initobj ""MyManagedClass"" IL_000e: ldloc.1 IL_000f: newobj ""MyManagedClass?..ctor(MyManagedClass)"" IL_0014: stsfld ""MyManagedClass? Program.MyProperty"" IL_0019: leave.s IL_003a } finally { IL_001b: ldloca.s V_0 IL_001d: call ""bool MyManagedClass?.HasValue.get"" IL_0022: brfalse.s IL_0039 IL_0024: ldloca.s V_0 IL_0026: call ""MyManagedClass MyManagedClass?.GetValueOrDefault()"" IL_002b: stloc.1 IL_002c: ldloca.s V_1 IL_002e: constrained. ""MyManagedClass"" IL_0034: callvirt ""void System.IDisposable.Dispose()"" IL_0039: endfinally } IL_003a: ret }"); } [Fact] public void ResourceIsNull_NullableType_3() { var source = @" using System; class Program { public static MyManagedClass? MyProperty = new MyManagedClass(); static void Main(string[] args) { using (MyManagedClass? c = MyProperty) { c.Value.Func(); } } } struct MyManagedClass : IDisposable { void IDisposable.Dispose() { System.Console.WriteLine(""Dispose""); } public void Func() { System.Console.WriteLine(""Func""); } } "; string expected = @"Func Dispose"; var comp = CompileAndVerify(source, expectedOutput: expected); // Note that changing the value of the expression does not cause the // Dispose to be called; we dispose the original value, not the new value. // // See comments in previous test regarding this codegen. comp.VerifyIL("Program.Main", @" { // Code size 55 (0x37) .maxstack 1 .locals init (MyManagedClass? V_0, //c MyManagedClass V_1) IL_0000: ldsfld ""MyManagedClass? Program.MyProperty"" IL_0005: stloc.0 .try { IL_0006: ldloca.s V_0 IL_0008: call ""MyManagedClass MyManagedClass?.Value.get"" IL_000d: stloc.1 IL_000e: ldloca.s V_1 IL_0010: call ""void MyManagedClass.Func()"" IL_0015: leave.s IL_0036 } finally { IL_0017: ldloca.s V_0 IL_0019: call ""bool MyManagedClass?.HasValue.get"" IL_001e: brfalse.s IL_0035 IL_0020: ldloca.s V_0 IL_0022: call ""MyManagedClass MyManagedClass?.GetValueOrDefault()"" IL_0027: stloc.1 IL_0028: ldloca.s V_1 IL_002a: constrained. ""MyManagedClass"" IL_0030: callvirt ""void System.IDisposable.Dispose()"" IL_0035: endfinally } IL_0036: ret }"); } // Dispose() called for nested using [Fact, WorkItem(528943, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528943")] public void DisposeCalled_NestedUsing() { var source = @" using System; class Program { static void Main(string[] args) { MyManagedClass1 res1 = new MyManagedClass1(1); MyManagedClass1 res2 = new MyManagedClass1(2); using (res1) { using (res1) { } } } } struct MyManagedClass1 : IDisposable { int Count; public MyManagedClass1(int x) { this.Count = x; } void IDisposable.Dispose() { System.Console.WriteLine(string.Format (""{0}:Dispose"",Count )); } public void Throw() { throw new Exception(); } } "; CompileAndVerify(source, expectedOutput: @"1:Dispose 1:Dispose").VerifyIL("Program.Main", @" { // Code size 50 (0x32) .maxstack 2 .locals init (MyManagedClass1 V_0, //res1 MyManagedClass1 V_1, MyManagedClass1 V_2) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.1 IL_0003: call ""MyManagedClass1..ctor(int)"" IL_0008: ldc.i4.2 IL_0009: newobj ""MyManagedClass1..ctor(int)"" IL_000e: pop IL_000f: ldloc.0 IL_0010: stloc.1 .try { IL_0011: ldloc.0 IL_0012: stloc.2 .try { IL_0013: leave.s IL_0031 } finally { IL_0015: ldloca.s V_2 IL_0017: constrained. ""MyManagedClass1"" IL_001d: callvirt ""void System.IDisposable.Dispose()"" IL_0022: endfinally } } finally { IL_0023: ldloca.s V_1 IL_0025: constrained. ""MyManagedClass1"" IL_002b: callvirt ""void System.IDisposable.Dispose()"" IL_0030: endfinally } IL_0031: ret }"); } // Dispose() called after throw [Fact] public void DisposeCalledAfterThrow_ReferenceType() { var source = @" using System; class Program { public static MyManagedClass MyProperty= new MyManagedClass (); static void Main(string[] args) { using (MyProperty) { MyProperty.Throw(); } } } class MyManagedClass : IDisposable { void IDisposable.Dispose() { System.Console.WriteLine(""Dispose""); } public void Throw() { throw (new Exception(""Res1"")); } } "; CompileAndVerify(source).VerifyIL("Program.Main", @" { // Code size 29 (0x1d) .maxstack 1 .locals init (MyManagedClass V_0) IL_0000: ldsfld ""MyManagedClass Program.MyProperty"" IL_0005: stloc.0 .try { IL_0006: ldsfld ""MyManagedClass Program.MyProperty"" IL_000b: callvirt ""void MyManagedClass.Throw()"" IL_0010: leave.s IL_001c } finally { IL_0012: ldloc.0 IL_0013: brfalse.s IL_001b IL_0015: ldloc.0 IL_0016: callvirt ""void System.IDisposable.Dispose()"" IL_001b: endfinally } IL_001c: ret }"); } // Dispose() called after throw [Fact] public void DisposeCalledAfterThrow_ValueType() { var source = @" using System; class Program { public static MyManagedClass MyProperty= new MyManagedClass (); static void Main(string[] args) { using (MyProperty) { MyProperty.Throw(); } } } struct MyManagedClass : IDisposable { void IDisposable.Dispose() { System.Console.WriteLine(""Dispose""); } public void Throw() { throw (new Exception(""Res1"")); } } "; CompileAndVerify(source).VerifyIL("Program.Main", @" { // Code size 33 (0x21) .maxstack 1 .locals init (MyManagedClass V_0) IL_0000: ldsfld ""MyManagedClass Program.MyProperty"" IL_0005: stloc.0 .try { IL_0006: ldsflda ""MyManagedClass Program.MyProperty"" IL_000b: call ""void MyManagedClass.Throw()"" IL_0010: leave.s IL_0020 } finally { IL_0012: ldloca.s V_0 IL_0014: constrained. ""MyManagedClass"" IL_001a: callvirt ""void System.IDisposable.Dispose()"" IL_001f: endfinally } IL_0020: ret }"); } // Dispose() called for first objects with exception thrown after second block [Fact, WorkItem(542982, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542982")] public void DisposeCalledAfterThrow_NestedUsing() { var source = @" using System; class Program { static void Main(string[] args) { using (MyManagedClass res1 = new MyManagedClass()) { res1.Func(); using (MyManagedClass res2 = new MyManagedClass()) { res2.Func(); } res1.Throw(); } } } struct MyManagedClass : IDisposable { void IDisposable.Dispose() { System.Console.WriteLine(""Dispose""); } public void Func() { System.Console.WriteLine(""Func""); } public void Throw() { throw new Exception(); } } "; CompileAndVerify(source).VerifyIL("Program.Main", @" { // Code size 70 (0x46) .maxstack 1 .locals init (MyManagedClass V_0, //res1 MyManagedClass V_1) //res2 IL_0000: ldloca.s V_0 IL_0002: initobj ""MyManagedClass"" .try { IL_0008: ldloca.s V_0 IL_000a: call ""void MyManagedClass.Func()"" IL_000f: ldloca.s V_1 IL_0011: initobj ""MyManagedClass"" .try { IL_0017: ldloca.s V_1 IL_0019: call ""void MyManagedClass.Func()"" IL_001e: leave.s IL_002e } finally { IL_0020: ldloca.s V_1 IL_0022: constrained. ""MyManagedClass"" IL_0028: callvirt ""void System.IDisposable.Dispose()"" IL_002d: endfinally } IL_002e: ldloca.s V_0 IL_0030: call ""void MyManagedClass.Throw()"" IL_0035: leave.s IL_0045 } finally { IL_0037: ldloca.s V_0 IL_0039: constrained. ""MyManagedClass"" IL_003f: callvirt ""void System.IDisposable.Dispose()"" IL_0044: endfinally } IL_0045: ret }"); } [Fact, WorkItem(542982, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542982")] public void DisposeCalledAfterThrow_NestedUsing_2() { var source = @" using System; class Program { static void Main(string[] args) { using (MyManagedClass res1 = new MyManagedClass()) { using (MyManagedClass res2 = new MyManagedClass()) { res1.Throw(); } } } } struct MyManagedClass : IDisposable { void IDisposable.Dispose() { System.Console.WriteLine(""Dispose""); } public void Throw() { throw new Exception(); } } "; CompileAndVerify(source).VerifyIL("Program.Main", @" { // Code size 54 (0x36) .maxstack 1 .locals init (MyManagedClass V_0, //res1 MyManagedClass V_1) //res2 IL_0000: ldloca.s V_0 IL_0002: initobj ""MyManagedClass"" .try { IL_0008: ldloca.s V_1 IL_000a: initobj ""MyManagedClass"" .try { IL_0010: ldloca.s V_0 IL_0012: call ""void MyManagedClass.Throw()"" IL_0017: leave.s IL_0035 } finally { IL_0019: ldloca.s V_1 IL_001b: constrained. ""MyManagedClass"" IL_0021: callvirt ""void System.IDisposable.Dispose()"" IL_0026: endfinally } } finally { IL_0027: ldloca.s V_0 IL_0029: constrained. ""MyManagedClass"" IL_002f: callvirt ""void System.IDisposable.Dispose()"" IL_0034: endfinally } IL_0035: ret }"); } // Multiple objects can be used in with a using statement, but they must be declared inside the using statement [Fact] public void MultipleResourceInUsing() { var source = @" using System; class Program { static void Main(string[] args) { MyManagedClass r1, r2; using (r1;r2) // Invalid { } } } struct MyManagedClass : IDisposable { void IDisposable.Dispose() { } } "; CreateCompilation(source).VerifyDiagnostics( // (8,18): error CS1026: ) expected // using (r1;r2) // Invalid Diagnostic(ErrorCode.ERR_CloseParenExpected, ";"), // (8,18): warning CS0642: Possible mistaken empty statement // using (r1;r2) // Invalid Diagnostic(ErrorCode.WRN_PossibleMistakenNullStatement, ";"), // (8,21): error CS1002: ; expected // using (r1;r2) // Invalid Diagnostic(ErrorCode.ERR_SemicolonExpected, ")"), // (8,21): error CS1513: } expected // using (r1;r2) // Invalid Diagnostic(ErrorCode.ERR_RbraceExpected, ")")); } // Multiple objects can be used in with a using statement, but they must be declared inside the using statement [Fact, WorkItem(542982, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542982")] public void MultipleResourceInUsing_2() { var source = @" using System; class Program { static void Main(string[] args) { using (MyManagedClass1 res1 = new MyManagedClass1(), res2)// Invalid { } } } struct MyManagedClass1 : IDisposable { void IDisposable.Dispose() { } } "; CreateCompilation(source).VerifyDiagnostics(Diagnostic(ErrorCode.ERR_FixedMustInit, "res2")); } // Dispose() called for both objects when exception thrown in compound case [Fact, WorkItem(528943, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528943")] public void DisposeCalledForMultiResources() { var source = @" using System; class Program { static void Main(string[] args) { using (MyManagedClass1 res1 = new MyManagedClass1(1), res2 = new MyManagedClass1(2)) { res1.Throw(); } } } struct MyManagedClass1 : IDisposable { int Count; public MyManagedClass1(int x) { this.Count = x; } void IDisposable.Dispose() { System.Console.WriteLine(string.Format (""{0}:Dispose"",Count )); } public void Throw() { throw new Exception(); } } "; CompileAndVerify(source).VerifyIL("Program.Main", @" { // Code size 53 (0x35) .maxstack 2 .locals init (MyManagedClass1 V_0, //res1 MyManagedClass1 V_1) //res2 IL_0000: ldloca.s V_0 IL_0002: ldc.i4.1 IL_0003: call ""MyManagedClass1..ctor(int)"" .try { IL_0008: ldc.i4.2 IL_0009: newobj ""MyManagedClass1..ctor(int)"" IL_000e: stloc.1 .try { IL_000f: ldloca.s V_0 IL_0011: call ""void MyManagedClass1.Throw()"" IL_0016: leave.s IL_0034 } finally { IL_0018: ldloca.s V_1 IL_001a: constrained. ""MyManagedClass1"" IL_0020: callvirt ""void System.IDisposable.Dispose()"" IL_0025: endfinally } } finally { IL_0026: ldloca.s V_0 IL_0028: constrained. ""MyManagedClass1"" IL_002e: callvirt ""void System.IDisposable.Dispose()"" IL_0033: endfinally } IL_0034: ret }"); } // Dangling using keyword [WorkItem(528933, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528933")] [Fact] public void DanglingUsing() { var source = @" class A { void B() { using } } "; CreateCompilation(source).VerifyDiagnostics( // (6,8): error CS1031: Type expected // using Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(6, 8), // (6,8): error CS1001: Identifier expected // using Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(6, 8), // (6,8): error CS1002: ; expected // using Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(6, 8), // (7,1): error CS0210: You must provide an initializer in a fixed or using statement declaration // } Diagnostic(ErrorCode.ERR_FixedMustInit, "").WithLocation(7, 1)); } // If statement directly following a using () [Fact] public void IfFollowingUsing() { var source = @" using System; class Program { static void Main(string[] args) { int n = 0; using (new MyManagedClass1()) if (true) n++; } } class MyManagedClass1 : IDisposable { void IDisposable.Dispose() { } } "; CreateCompilation(source).VerifyDiagnostics(); } // Query expression in using statement [Fact] public void QueryAsUsing() { var source = @" using System.Linq; class Program { static void Main(string[] args) { using (from x in new int[] { 1 } select x) { } } } "; CreateCompilationWithMscorlib40AndSystemCore(source).VerifyDiagnostics(Diagnostic(ErrorCode.ERR_NoConvToIDisp, "from x in new int[] { 1 } select x").WithArguments("System.Collections.Generic.IEnumerable<int>")); } // Error when using a lambda in a using() [Fact] public void LambdaAsUsing() { var source = @" class Program { static void Main(string[] args) { using (x => x) // err { } using (() => { }) // err { } using ((int @int) => { return @int; }) // err { } } } "; CreateCompilation(source).VerifyDiagnostics( // (6,16): error CS1674: 'lambda expression': type used in a using statement must implement 'System.IDisposable'. // using (x => x) // err Diagnostic(ErrorCode.ERR_NoConvToIDisp, "x => x").WithArguments("lambda expression"), // (9,16): error CS1674: 'lambda expression': type used in a using statement must implement 'System.IDisposable'. // using (() => { }) // err Diagnostic(ErrorCode.ERR_NoConvToIDisp, "() => { }").WithArguments("lambda expression"), // (12,16): error CS1674: 'lambda expression': type used in a using statement must implement 'System.IDisposable'. // using ((int @int) => { return @int; }) // err Diagnostic(ErrorCode.ERR_NoConvToIDisp, "(int @int) => { return @int; }").WithArguments("lambda expression")); } // Anonymous types cannot appear in using [Fact] public void AnonymousAsUsing() { var source = @" class Program { static void Main(string[] args) { using (var a = new { }) { } using (var b = new { p1 = 10 }) { } using (var c = new { p1 = 10.0, p2 = 'a' }) { } using (new { }) { ; } using (new { f1 = ""12345"", f2 = 'S' }) { } } } "; CreateCompilation(source).VerifyDiagnostics( // (6,16): error CS1674: '<empty anonymous type>': type used in a using statement must implement 'System.IDisposable'. // using (var a = new { }) Diagnostic(ErrorCode.ERR_NoConvToIDisp, "var a = new { }").WithArguments("<empty anonymous type>"), // (9,16): error CS1674: '<anonymous type: int p1>': type used in a using statement must implement 'System.IDisposable'. // using (var b = new { p1 = 10 }) Diagnostic(ErrorCode.ERR_NoConvToIDisp, "var b = new { p1 = 10 }").WithArguments("<anonymous type: int p1>"), // (12,16): error CS1674: '<anonymous type: double p1, char p2>': type used in a using statement must implement 'System.IDisposable'. // using (var c = new { p1 = 10.0, p2 = 'a' }) Diagnostic(ErrorCode.ERR_NoConvToIDisp, "var c = new { p1 = 10.0, p2 = 'a' }").WithArguments("<anonymous type: double p1, char p2>"), // (15,16): error CS1674: '<empty anonymous type>': type used in a using statement must implement 'System.IDisposable'. // using (new { }) Diagnostic(ErrorCode.ERR_NoConvToIDisp, "new { }").WithArguments("<empty anonymous type>"), // (19,16): error CS1674: '<anonymous type: string f1, char f2>': type used in a using statement must implement 'System.IDisposable'. // using (new { f1 = "12345", f2 = 'S' }) Diagnostic(ErrorCode.ERR_NoConvToIDisp, @"new { f1 = ""12345"", f2 = 'S' }").WithArguments("<anonymous type: string f1, char f2>") ); } // User-defined Conversions [Fact] public void UserDefinedConversions() { var source = @" using System; struct MyManagedClass1 : IDisposable { public int Number; public void Dispose() { Number = -1; } public static implicit operator MyManagedClass1(int i) { MyManagedClass1 temp = new MyManagedClass1(); temp.Number = i; return temp; } } class Program { static void Main() { using (MyManagedClass1? str = (int?)1) { } using (MyManagedClass1? str = (int?)null) { } using (MyManagedClass1? str = 2) { } using (MyManagedClass1 str = (MyManagedClass1)(int?)3) { } } }"; var comp = CompileAndVerify(source, expectedOutput: @""); } // Anonymous Delegate in using block [Fact] public void AnonymousDelegateInUsing() { var source = @" using System; delegate T D1<T>(T t); class A1 { static void Goo<T>(T t) where T : IDisposable { T local = t; using (T t1 = ((D1<T>)delegate(T tt) { return t; })(t)) { } } static void Main() { } } "; CreateCompilationWithMscorlib40AndSystemCore(source).VerifyDiagnostics(); } // Put the using around the try [Fact, WorkItem(528943, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528943")] public void UsingAroundTry() { var source = @" using System; class Program { public static void Main() { MyManagedClass1 x = new MyManagedClass1(); using (x) { try { System.Console.WriteLine(""Try""); } finally { x = null;// Warning CS0728 System.Console.WriteLine(""Catch""); } } } } public class MyManagedClass1 : IDisposable { public void Dispose() { System.Console.WriteLine(""Dispose()""); } } "; CompileAndVerify(source, expectedOutput: @"Try Catch Dispose()").VerifyIL("Program.Main", @" { // Code size 40 (0x28) .maxstack 1 .locals init (MyManagedClass1 V_0) IL_0000: newobj ""MyManagedClass1..ctor()"" IL_0005: stloc.0 .try { .try { IL_0006: ldstr ""Try"" IL_000b: call ""void System.Console.WriteLine(string)"" IL_0010: leave.s IL_0027 } finally { IL_0012: ldstr ""Catch"" IL_0017: call ""void System.Console.WriteLine(string)"" IL_001c: endfinally } } finally { IL_001d: ldloc.0 IL_001e: brfalse.s IL_0026 IL_0020: ldloc.0 IL_0021: callvirt ""void System.IDisposable.Dispose()"" IL_0026: endfinally } IL_0027: ret }"); } // Put the using in the try [Fact] public void UsingInTry() { var source = @" using System; class Program { public static void Main() { MyManagedClass1 x = new MyManagedClass1(); try { System.Console.WriteLine(""Try""); using (x) { throw new Exception(); } } catch { System.Console.WriteLine(""Catch""); x = null; } } } public class MyManagedClass1 : IDisposable { public void Dispose() { System.Console.WriteLine(""Dispose()""); } } "; CompileAndVerify(source, expectedOutput: @"Try Dispose() Catch").VerifyIL("Program.Main", @" { // Code size 50 (0x32) .maxstack 1 .locals init (MyManagedClass1 V_0, //x MyManagedClass1 V_1) IL_0000: newobj ""MyManagedClass1..ctor()"" IL_0005: stloc.0 .try { IL_0006: ldstr ""Try"" IL_000b: call ""void System.Console.WriteLine(string)"" IL_0010: ldloc.0 IL_0011: stloc.1 .try { IL_0012: newobj ""System.Exception..ctor()"" IL_0017: throw } finally { IL_0018: ldloc.1 IL_0019: brfalse.s IL_0021 IL_001b: ldloc.1 IL_001c: callvirt ""void System.IDisposable.Dispose()"" IL_0021: endfinally } } catch object { IL_0022: pop IL_0023: ldstr ""Catch"" IL_0028: call ""void System.Console.WriteLine(string)"" IL_002d: ldnull IL_002e: stloc.0 IL_002f: leave.s IL_0031 } IL_0031: ret }"); } [Fact] public void TestValueTypeUsingVariableCanBeMutatedByInstanceMethods() { const string source = @" struct A : System.IDisposable { int field; void Set(A a) { this = a; } public void Dispose() { } static void Main() { using (var a = new A()) { a.Set(new A { field = 5 }); System.Console.Write(a.field); } } }"; CompileAndVerify(source, expectedOutput: "5"); } [Fact, WorkItem(1077204, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077204")] public void TestValueTypeUsingVariableFieldsAreReadonly() { const string source = @" struct B { public int field; public void SetField(int value) { field = value; } } struct A : System.IDisposable { B b; public void Dispose() { } static void Main() { using (var a = new A()) { a.b.SetField(5); System.Console.Write(a.b.field); } } }"; CompileAndVerify(source, expectedOutput: "0"); } [Fact, WorkItem(1077204, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077204")] public void TestValueTypeUsingVariableFieldsAreReadonly2() { const string source = @" struct C { public int field; public void SetField(int value) { field = value; } } struct B { public C c; } struct A : System.IDisposable { B b; public void Dispose() { } static void Main() { using (var a = new A()) { a.b.c.SetField(5); System.Console.Write(a.b.c.field); } } }"; CompileAndVerify(source, expectedOutput: "0"); } [Theory, CombinatorialData] public void OptionalParameter([CombinatorialValues("ref", "in", "ref readonly", "")] string modifier) { var source = $$""" using System; using (var s = new S()) { Console.Write("1"); } ref struct S { public void Dispose({{modifier}} int x = 2) => Console.Write(x); } """; if (modifier == "ref") { CreateCompilation(source).VerifyDiagnostics( // (2,8): error CS1674: 'S': type used in a using statement must implement 'System.IDisposable'. // using (var s = new S()) Diagnostic(ErrorCode.ERR_NoConvToIDisp, "var s = new S()").WithArguments("S").WithLocation(2, 8), // (8,25): error CS1741: A ref or out parameter cannot have a default value // public void Dispose(ref int x = 2) => Console.Write(x); Diagnostic(ErrorCode.ERR_RefOutDefaultValue, "ref").WithLocation(8, 25)); } else { var verifier = CompileAndVerify(source, expectedOutput: "12"); if (modifier == "ref readonly") { verifier.VerifyDiagnostics( // (8,46): warning CS9200: A default value is specified for 'ref readonly' parameter 'x', but 'ref readonly' should be used only for references. Consider declaring the parameter as 'in'. // public void Dispose(ref readonly int x = 2) => Console.Write(x); Diagnostic(ErrorCode.WRN_RefReadonlyParameterDefaultValue, "2").WithArguments("x").WithLocation(8, 46)); } else { verifier.VerifyDiagnostics(); } } } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/73741")] public void MutatingThroughRefFields_01( [CombinatorialValues("readonly", "")] string vReadonly) { var source = $$""" using System; V v = default; using (var d = new R(ref v)) { d.V.F++; } Console.Write(v.F); ref struct R(ref V v) { public {{vReadonly}} ref V V = ref v; public void Dispose() { } } struct V { public int F; } """; CompileAndVerify(source, targetFramework: TargetFramework.Net70, verify: Verification.FailsPEVerify, expectedOutput: ExecutionConditionUtil.IsDesktop ? null : "1").VerifyDiagnostics(); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/73741")] public void MutatingThroughRefFields_02( [CombinatorialValues("readonly", "")] string vReadonly) { var source = $$""" using System; V v = default; using (var d = new R(ref v)) { d.V.F += 2; } Console.Write(v.F); ref struct R(ref V v) { public {{vReadonly}} ref V V = ref v; public void Dispose() { } } struct V { public int F; } """; CompileAndVerify(source, targetFramework: TargetFramework.Net70, verify: Verification.FailsPEVerify, expectedOutput: ExecutionConditionUtil.IsDesktop ? null : "2").VerifyDiagnostics(); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/73741")] public void MutatingThroughRefFields_03( [CombinatorialValues("readonly", "")] string vReadonly) { var source = $$""" using System; V v = default; using (var d = new R(ref v)) { d.V.S.Inc(); } Console.Write(v.S.F); ref struct R(ref V v) { public {{vReadonly}} ref V V = ref v; public void Dispose() { } } struct V { public S S; } struct S { public int F; public void Inc() => F++; } """; CompileAndVerify(source, targetFramework: TargetFramework.Net70, verify: Verification.FailsPEVerify, expectedOutput: ExecutionConditionUtil.IsDesktop ? null : "1").VerifyDiagnostics(); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/73741")] public void MutatingThroughRefFields_04( [CombinatorialValues("readonly", "")] string vReadonly) { var source = $$""" using System; V v = default; using (var d = new R(ref v)) { d.V.F++; } Console.Write(v.F); ref struct R(ref V v) { public {{vReadonly}} ref readonly V V = ref v; public void Dispose() { } } struct V { public int F; } """; CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( // (7,5): error CS8332: Cannot assign to a member of field 'V' or use it as the right hand side of a ref assignment because it is a readonly variable // d.V.F++; Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "d.V.F").WithArguments("field", "V").WithLocation(7, 5)); } } }