/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Semantic/Semantics/UsingDeclarationTests.cs
966 строк
28 KB
Julien Couvreur
Disallow modifiers in local_using_declaration (#72589)
22 мар 2024, 03:05
Не верифицирован
22 мар 2024, 03:05
7ecf61f
Код
Авторство
О чём код?
// 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.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.UnitTests; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.Semantics { /// <summary> /// Tests related to binding (but not lowering) using declarations (i.e. using var x = ...). /// </summary> public class UsingDeclarationTests : CompilingTestBase { [Fact] public void UsingVariableIsNotReportedAsUnused() { var source = @" using System; class C { static void Main() { using var x = (IDisposable)null; } } "; CreateCompilation(source).VerifyDiagnostics(); } [Fact] public void DisallowGoToForwardAcrossUsingDeclarations() { var source = @" using System; class C { static void Main() { goto label1; using var x = (IDisposable)null; label1: return; } } "; CreateCompilation(source).VerifyDiagnostics( // (7,9): error CS8641: A goto target within the same block can not cross a using declaration. // goto label1; Diagnostic(ErrorCode.ERR_GoToForwardJumpOverUsingVar, "goto label1;").WithLocation(7, 9), // (8,9): warning CS0162: Unreachable code detected // using var x = (IDisposable)null; Diagnostic(ErrorCode.WRN_UnreachableCode, "using").WithLocation(8, 9) ); } [Fact] public void DisallowGoToForwardAcrossUsingDeclarationsFromLowerBlock() { var source = @" using System; class C { static void Main() { { goto label1; } using var x = (IDisposable)null; label1: return; } } "; CreateCompilation(source).VerifyDiagnostics( // (7,9): error CS8641: A goto target within the same block can not cross a using declaration. // goto label1; Diagnostic(ErrorCode.ERR_GoToForwardJumpOverUsingVar, "goto label1;").WithLocation(8, 13), // (8,9): warning CS0162: Unreachable code detected // using var x = (IDisposable)null; Diagnostic(ErrorCode.WRN_UnreachableCode, "using").WithLocation(10, 9) ); } [Fact] public void DisallowGoToForwardAcrossMultipleUsingDeclarationsGivesOnlyOneDiagnostic() { var source = @" using System; class C { static void Main() { goto label1; using var x = (IDisposable)null; using var y = (IDisposable)null; label1: return; } } "; CreateCompilation(source).VerifyDiagnostics( // (7,9): error CS8641: A goto target within the same block can not cross a using declaration. // goto label1; Diagnostic(ErrorCode.ERR_GoToForwardJumpOverUsingVar, "goto label1;").WithLocation(7, 9), // (8,9): warning CS0162: Unreachable code detected // using var x = (IDisposable)null; Diagnostic(ErrorCode.WRN_UnreachableCode, "using").WithLocation(8, 9) ); } [Fact] public void DisallowMultipleGoToForwardAcrossMultipleUsingDeclarations() { var source = @" using System; class C { static void Main() { goto label1; using var x = (IDisposable)null; goto label2; using var y = (IDisposable)null; label1: label2: return; } } "; CreateCompilation(source).VerifyDiagnostics( // (7,9): error CS8641: A goto target within the same block can not cross a using declaration. // goto label1; Diagnostic(ErrorCode.ERR_GoToForwardJumpOverUsingVar, "goto label1;").WithLocation(7, 9), // (8,9): warning CS0162: Unreachable code detected // using var x = (IDisposable)null; Diagnostic(ErrorCode.WRN_UnreachableCode, "using").WithLocation(8, 9), // (9,9): error CS8641: A goto target can not be after any using declarations. // goto label2; Diagnostic(ErrorCode.ERR_GoToForwardJumpOverUsingVar, "goto label2;").WithLocation(9, 9) ); } [Fact] public void DisallowGoToBackwardsAcrossUsingDeclarationsWhenLabelIsInTheSameScope() { var source = @" using System; class C { static void Main() { label1: using var x = (IDisposable)null; goto label1; } } "; CreateCompilation(source).VerifyDiagnostics( // (10,9): error CS8641: A goto target within the same block can not cross a using declaration. // goto label1; Diagnostic(ErrorCode.ERR_GoToBackwardJumpOverUsingVar, "goto label1;").WithLocation(10, 9) ); } [Fact] public void DisallowGoToBackwardsAcrossUsingDeclarationsWithMultipleLabels() { var source = @" using System; class C { static void Main() { label1: label2: label3: using var x = (IDisposable)null; goto label3; // disallowed } } "; CreateCompilation(source).VerifyDiagnostics( // (7,9): warning CS0164: This label has not been referenced // label1: Diagnostic(ErrorCode.WRN_UnreferencedLabel, "label1").WithLocation(7, 9), // (8,9): warning CS0164: This label has not been referenced // label2: Diagnostic(ErrorCode.WRN_UnreferencedLabel, "label2").WithLocation(8, 9), // (10,9): error CS8641: A goto target within the same block can not cross a using declaration. // goto label3; // disallowed Diagnostic(ErrorCode.ERR_GoToBackwardJumpOverUsingVar, "goto label3;").WithLocation(12, 9) ); } [Fact] public void DisallowGoToAcrossUsingDeclarationsComplexTest() { var source = @" using System; #pragma warning disable 162 // disable unreachable code warnings class C { static void Main() { label1: { label2: using var a = (IDisposable)null; goto label1; // allowed goto label2; // disallowed 1 } label3: using var b = (IDisposable)null; { goto label3; // disallowed 2 } { goto label4; // allowed goto label5; // disallowed 3 label4: using var c = (IDisposable)null; label5: using var d = (IDisposable)null; } using var e = (IDisposable)null; label6: { { goto label6; //allowed label7: { label8: using var f = (IDisposable)null; goto label7; // allowed { using var g = (IDisposable)null; goto label7; //allowed goto label8; // disallowed 4 } } } } } } "; CreateCompilation(source).VerifyDiagnostics( // (14,13): error CS8642: A goto cannot jump to a location before a using declaration within the same block. // goto label2; // disallowed 1 Diagnostic(ErrorCode.ERR_GoToBackwardJumpOverUsingVar, "goto label2;").WithLocation(14, 13), // (20,13): error CS8642: A goto cannot jump to a location before a using declaration within the same block. // goto label3; // disallowed 2 Diagnostic(ErrorCode.ERR_GoToBackwardJumpOverUsingVar, "goto label3;").WithLocation(20, 13), // (25,13): error CS8641: A goto cannot jump to a location after a using declaration. // goto label5; // disallowed 3 Diagnostic(ErrorCode.ERR_GoToForwardJumpOverUsingVar, "goto label5;").WithLocation(25, 13), // (45,25): error CS8642: A goto cannot jump to a location before a using declaration within the same block. // goto label8; // disallowed 4 Diagnostic(ErrorCode.ERR_GoToBackwardJumpOverUsingVar, "goto label8;").WithLocation(45, 25) ); } [Fact] public void AllowGoToAroundUsingDeclarations() { var source = @" using System; class C { static void Main() { goto label1; label1: using var x = (IDisposable)null; goto label2; label2: return; } } "; CreateCompilation(source).VerifyDiagnostics(); } [Fact] public void AllowGoToBackwardsAcrossUsingDeclarationsWhenLabelIsInHigherScope() { var source = @" using System; class C { static void Main() { label1: { using var x = (IDisposable)null; goto label1; } } } "; CreateCompilation(source).VerifyDiagnostics(); } [Fact] public void AllowGoToForwardsAcrossUsingDeclarationsInALowerBlock() { var source = @" using System; class C { static void Main() { goto label1; { using var x = (IDisposable)null; } label1: ; } } "; CreateCompilation(source).VerifyDiagnostics( // (8,9): warning CS0162: Unreachable code detected // using var x = (IDisposable)null; Diagnostic(ErrorCode.WRN_UnreachableCode, "using").WithLocation(9, 13) ); } [Fact] public void AllowGoToBackwardsAcrossUsingDeclarationsInALowerBlock() { var source = @" using System; class C { static void Main() { label1: { using var x = (IDisposable)null; } goto label1; } } "; CreateCompilation(source).VerifyDiagnostics(); } [Fact] public void UsingVariableCanBeInitializedWithExistingDisposable() { var source = @" using System; class C2 : IDisposable { public void Dispose() { Console.Write(""Disposed; ""); } } class C { static void Main() { var x = new C2(); using var x2 = x; using var x3 = x2; x = null; } } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe).VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: "Disposed; Disposed; "); } [Fact] public void UsingVariableCanBeInitializedWithExistingDisposableInASingleStatement() { var source = @" using System; class C2 : IDisposable { public void Dispose() { Console.Write(""Disposed; ""); } } class C { static void Main() { using C2 x = new C2(), x2 = x; } } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe).VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: "Disposed; Disposed; "); } [Fact] public void UsingVariableCanBeInitializedWithExistingRefStructDisposable() { var source = @" using System; ref struct C2 { public void Dispose() { Console.Write(""Disposed; ""); } } class C { static void Main() { var x = new C2(); using var x2 = x; using var x3 = x2; } } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe).VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: "Disposed; Disposed; "); } [Fact] public void UsingVariableCannotBeReAssigned() { var source = @" using System; class C { static void Main() { using var x = (IDisposable)null; x = null; } } "; CreateCompilation(source).VerifyDiagnostics( // (8,9): error CS1656: Cannot assign to 'x' because it is a 'using variable' // x = null; Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "x").WithArguments("x", "using variable").WithLocation(8, 9) ); } [Fact] public void UsingVariableCannotBeUsedAsOutVariable() { var source = @" using System; class C { static void Consume(out IDisposable x) { x = null; } static void Main() { using var x = (IDisposable)null; Consume(out x); } } "; CreateCompilation(source).VerifyDiagnostics( // (13,21): error CS1657: Cannot use 'x' as a ref or out value because it is a 'using variable' // Consume(out x); Diagnostic(ErrorCode.ERR_RefReadonlyLocalCause, "x").WithArguments("x", "using variable").WithLocation(13, 21) ); } [Fact] public void UsingVariableMustHaveInitializer() { var source = @" using System; class C { static void Main() { using IDisposable x; } } "; CreateCompilation(source).VerifyDiagnostics( // (7,27): error CS0210: You must provide an initializer in a fixed or using statement declaration // using IDisposable x; Diagnostic(ErrorCode.ERR_FixedMustInit, "x").WithLocation(7, 27) ); } [Fact] public void UsingVariableFromExistingVariable() { var source = @" using System; class C { static void Main() { var x = (IDisposable)null; using var x2 = x; } } "; CreateCompilation(source).VerifyDiagnostics(); } [Fact] public void UsingVariableFromExpression() { var source = @" using System; class C { static IDisposable GetDisposable() { return null; } static void Main() { using IDisposable x = GetDisposable(); } } "; CreateCompilation(source).VerifyDiagnostics(); } [Fact] public void UsingVariableFromAwaitExpression() { var source = @" using System; using System.Threading.Tasks; class C { static Task<IDisposable> GetDisposable() { return Task.FromResult<IDisposable>(null); } static async Task Main() { using IDisposable x = await GetDisposable(); } } "; CreateCompilation(source).VerifyDiagnostics(); } [Fact] public void UsingVariableInSwitchCase_01() { var source = @" using System; class C1 : IDisposable { public void Dispose() { } } class C2 { public static void Main() { int x = 5; switch (x) { case 5: using C1 o1 = new C1(); break; } } }"; CreateCompilation(source).VerifyDiagnostics( // (15,21): error CS8389: A using variable cannot be used directly within a switch section (consider using braces). // using C1 o1 = new C1(); Diagnostic(ErrorCode.ERR_UsingVarInSwitchCase, "using C1 o1 = new C1();").WithLocation(15, 17) ); } [Fact, WorkItem(63570, "https://github.com/dotnet/roslyn/issues/63570")] public void UsingVariableInSwitchCase_02() { var source = """ using System; StringSplitOptions temp = StringSplitOptions.RemoveEmptyEntries; switch (temp) { case StringSplitOptions.None: Console.WriteLine("None"); break; case StringSplitOptions.RemoveEmptyEntries: { using var streamReader = new C1(); goto case StringSplitOptions.None; } default: break; } class C1 : IDisposable { public void Dispose() { Console.WriteLine("Disposed"); } } """; var verifier = CompileAndVerify(source, expectedOutput: """ Disposed None """); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 39 (0x27) .maxstack 2 .locals init (System.StringSplitOptions V_0, //temp C1 V_1) //streamReader IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: brfalse.s IL_000a IL_0005: ldloc.0 IL_0006: ldc.i4.1 IL_0007: beq.s IL_0015 IL_0009: ret IL_000a: ldstr "None" IL_000f: call "void System.Console.WriteLine(string)" IL_0014: ret IL_0015: newobj "C1..ctor()" IL_001a: stloc.1 .try { IL_001b: leave.s IL_000a } finally { IL_001d: ldloc.1 IL_001e: brfalse.s IL_0026 IL_0020: ldloc.1 IL_0021: callvirt "void System.IDisposable.Dispose()" IL_0026: endfinally } } """); } [Fact, WorkItem(63570, "https://github.com/dotnet/roslyn/issues/63570")] public void UsingVariableInSwitchCase_03() { var source = """ using System; StringSplitOptions temp = StringSplitOptions.RemoveEmptyEntries; switch (temp) { case StringSplitOptions.None: goto case StringSplitOptions.RemoveEmptyEntries; case StringSplitOptions.RemoveEmptyEntries: { using var streamReader = new C1(); goto case StringSplitOptions.None; } } class C1 : IDisposable { public void Dispose() { Console.WriteLine("Disposed"); } } """; var verifier = CompileAndVerify(source); verifier.VerifyDiagnostics(); } [Fact, WorkItem(63570, "https://github.com/dotnet/roslyn/issues/63570")] public void UsingVariableInSwitchCase_04() { var source = """ using System; StringSplitOptions temp = StringSplitOptions.RemoveEmptyEntries; switch (temp) { case StringSplitOptions.RemoveEmptyEntries: { using var streamReader = new C1(); goto default; } default: Console.WriteLine("Default"); break; } class C1 : IDisposable { public void Dispose() { Console.WriteLine("Disposed"); } } """; var verifier = CompileAndVerify(source, expectedOutput: """ Disposed Default """); verifier.VerifyDiagnostics(); } [Fact, WorkItem(63570, "https://github.com/dotnet/roslyn/issues/63570")] public void UsingVariableInSwitchCase_05() { var source = """ using System; StringSplitOptions temp = StringSplitOptions.RemoveEmptyEntries; switch (temp) { default: Console.WriteLine("Default"); break; case StringSplitOptions.RemoveEmptyEntries: { using var streamReader = new C1(); goto default; } } class C1 : IDisposable { public void Dispose() { Console.WriteLine("Disposed"); } } """; var verifier = CompileAndVerify(source, expectedOutput: """ Disposed Default """); verifier.VerifyDiagnostics(); } [Fact] public void UsingVariableDiagnosticsInDeclarationAreOnlyEmittedOnce() { var source = @" using System; class C1 : IDisposable { public void Dispose() { } } class C2 { public static void Main() { using var c1 = new C1(), c2 = new C2(); } }"; CreateCompilation(source).VerifyDiagnostics( // (11,15): error CS0819: Implicitly-typed variables cannot have multiple declarators // using var c1 = new C1(), c2 = new C2(); Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableMultipleDeclarator, "var c1 = new C1(), c2 = new C2()").WithLocation(11, 15) ); } [Fact] public void UsingDeclarationWithAwaitsInAsync() { var source = @" using System; using System.Threading.Tasks; class C2 : IDisposable { public string ID { get; set; } public void Dispose() { Console.Write($""Dispose {ID}; ""); } } class C { static async Task<IDisposable> GetDisposable(string id) { await Task.Yield(); return new C2(){ ID = id }; } static async Task Main() { using IDisposable x = await GetDisposable(""c1""); await Task.Yield(); Console.Write(""after c1; ""); using IDisposable y = await GetDisposable(""c2""); Console.Write(""after c2; ""); } } "; var compilation = CreateCompilationWithTasksExtensions(source, options: TestOptions.DebugExe).VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: "after c1; after c2; Dispose c2; Dispose c1; "); } [Fact] public void UsingDeclarationsWithLangVer7_3() { var source = @" using System; class C { static void Main() { using IDisposable x = null; } } "; var expected = new[] { // (7,9): error CS8652: The feature 'using declarations' is not available in C# 7.3. Please use language version 8.0 or greater. // using IDisposable x = null; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "using").WithArguments("using declarations", "8.0").WithLocation(7, 9) }; CreateCompilation(source, parseOptions: TestOptions.Regular7_3).VerifyDiagnostics(expected); CreateCompilation(source, parseOptions: TestOptions.Regular8).VerifyDiagnostics(); } [Fact] public void AwaitUsingDeclarationsWithLangVer7_3() { var source = @" using System; using System.Threading.Tasks; class C { static async Task Main() { await using IAsyncDisposable x = null; } } "; var expected = new[] { // 0.cs(8,9): error CS8370: Feature 'pattern-based disposal' is not available in C# 7.3. Please use language version 8.0 or greater. // await using IAsyncDisposable x = null; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "await using IAsyncDisposable x = null;").WithArguments("pattern-based disposal", "8.0").WithLocation(8, 9), // 0.cs(8,15): error CS8370: Feature 'using declarations' is not available in C# 7.3. Please use language version 8.0 or greater. // await using IAsyncDisposable x = null; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "using").WithArguments("using declarations", "8.0").WithLocation(8, 15) }; CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, parseOptions: TestOptions.Regular7_3).VerifyDiagnostics(expected); CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, parseOptions: TestOptions.Regular8).VerifyDiagnostics(); } [Fact] public void UsingDeclarationsWithObsoleteTypes() { var source = @" using System; [Obsolete] class C1 : IDisposable { [Obsolete] public void Dispose() { } } class C2 : IDisposable { [Obsolete] public void Dispose() { } } ref struct S3 { [Obsolete] public void Dispose() { } } class C4 { static void Main() { // c1 is obsolete using C1 c1 = new C1(); // no warning, we don't warn on dispose being obsolete because it comes through interface using C2 c2 = new C2(); // warning, we're calling the pattern based obsolete method for the ref struct using S3 S3 = new S3(); } } "; CreateCompilation(source).VerifyDiagnostics( // (34,15): warning CS0612: 'C1' is obsolete // using C1 c1 = new C1(); Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "C1").WithArguments("C1").WithLocation(34, 15), // (34,27): warning CS0612: 'C1' is obsolete // using C1 c1 = new C1(); Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "C1").WithArguments("C1").WithLocation(34, 27), // (40,15): warning CS0612: 'S3.Dispose()' is obsolete // using S3 S3 = new S3(); Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "using S3 S3 = new S3();").WithArguments("S3.Dispose()").WithLocation(40, 9) ); } [Fact] [WorkItem(36413, "https://github.com/dotnet/roslyn/issues/36413")] public void UsingDeclarationsWithInvalidModifiers() { var source = @" using System; class C { static void Main() { using public readonly var x = (IDisposable)null; } } "; CreateCompilation(source, parseOptions: TestOptions.Regular8).VerifyDiagnostics( // (7,15): error CS9229: Modifiers cannot be placed on using declarations // using public readonly var x = (IDisposable)null; Diagnostic(ErrorCode.ERR_NoModifiersOnUsing, "public").WithLocation(7, 15), // (7,22): error CS9229: Modifiers cannot be placed on using declarations // using public readonly var x = (IDisposable)null; Diagnostic(ErrorCode.ERR_NoModifiersOnUsing, "readonly").WithLocation(7, 22) ); } } }