diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IBranchOperation.Extensions.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IBranchOperation.Extensions.cs new file mode 100644 index 0000000000000000000000000000000000000000..cca844ad6c9c491405cce821dc672c1f80491c81 --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IBranchOperation.Extensions.cs @@ -0,0 +1,382 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public partial class IOperationTests : SemanticModelTestBase + { + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_ForNull_ThrowsArgumentNullException() + { + Assert.ThrowsAny(() => OperationExtensions.GetCorrespondingOperation(null)); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_ForGotoBranch_ReturnsNull() + { + var result = GetOuterOperationAndCorrespondingInnerOperation(@" +class C +{ + void F() + { +/**/begin: + for (;;) + { + /**/goto begin;/**/ + }/**/ + } +}"); + Assert.IsAssignableFrom(typeof(ILabeledOperation), result.outer); + Assert.Null(result.corresponding); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_LoopLookup_ForLoopWithBreak() + { + AssertOuterIsCorrespondingLoopOfInner(@" +class C +{ + void F() + { + /**/for (;;) + { + /**/break;/**/ + }/**/ + } +}"); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_LoopLookup_WhileLoopWithContinue() + { + AssertOuterIsCorrespondingLoopOfInner(@" +class C +{ + void F() + { + /**/while (true) + { + /**/continue;/**/ + }/**/ + } +}"); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_LoopLookup_DoWhileLoopWithBreakAndContinue() + { + AssertOuterIsCorrespondingLoopOfInner(@" +class C +{ + void F() + { + /**/do + { + if (true) + break; + else + /**/continue;/**/ + } while (true)/**/ + } +}"); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_LoopLookup_ForEachLoopWithBreak() + { + AssertOuterIsCorrespondingLoopOfInner(@" +class C +{ + void F() + { + /**/foreach (var i in new [] {1,2,3}) + { + if (i == 2) + /**/break;/**/ + }/**/ + } +}"); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_LoopLookup_ForEachLoopWithBreakAndContinue() + { + AssertOuterIsCorrespondingLoopOfInner(@" +class C +{ + void F() + { + /**/foreach (var i in new [] {1,2,3}) + { + if (i == 2) + /**/break;/**/ + else + continue; + }/**/ + } +}"); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_LoopLookup_NestedLoops() + { + AssertOuterIsCorrespondingLoopOfInner(@" +class C +{ + void F() + { + /**/for (;;) + { + for (;;) + { + } + /**/break;/**/ + }/**/ + } +}"); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_LoopLookup_NestedLoops2() + { + AssertOuterIsCorrespondingLoopOfInner(@" +class C +{ + void F() + { + for (;;) + { + /**/for (;;) + { + /**/break;/**/ + }/**/ + } + } +}"); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_SwitchLookup_BreakInCase() + { + AssertOuterIsCorrespondingSwitchOfInner(@" +class C +{ + void F() + { + /**/switch (1) + { + case 1: + /**/break;/**/ + }/**/ + } +}"); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_SwitchLookup_NestedSwitches() + { + AssertOuterIsCorrespondingSwitchOfInner(@" +class C +{ + void F() + { + /**/switch (1) + { + case 1: + switch (2) + { + case 2: + break; + } + /**/break;/**/ + }/**/ + } +}"); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_SwitchLookup_NestedSwitches2() + { + AssertOuterIsCorrespondingSwitchOfInner(@" +class C +{ + void F() + { + switch (1) + { + case 1: + /**/switch (2) + { + case 2: + /**/break;/**/ + }/**/ + break; + } + } +}"); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_LoopLookup_LoopInSwitch() + { + AssertOuterIsCorrespondingLoopOfInner(@" +class C +{ + void F() + { + switch (1) + { + case 1: + /**/for (;;) + { + /**/break;/**/ + }/**/ + break; + } + } +}"); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_SwitchLookup_SwitchInLoop() + { + AssertOuterIsCorrespondingSwitchOfInner(@" +class C +{ + void F() + { + for (;;) + { + /**/switch (1) + { + case 1: + /**/break;/**/ + }/**/ + } + } +}"); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_LoopLookup_ContinueNestedInIntermediateSwitch() + { + AssertOuterIsCorrespondingLoopOfInner(@" +class C +{ + void F() + { + /**/for (;;) + { + switch (1) + { + case 1: + /**/continue;/**/ + break; + } + }/**/ + } +}"); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_LoopLookup_BreakButNoLoop_ReturnsNull() + { + var (expected, actual) = GetOuterOperationAndCorrespondingInnerOperation(@" +class C +{ + void F() + { + /**/break;/**/ + } +}"); + + Assert.Null(expected); + Assert.Null(actual); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")] + [Fact] + public void GetCorrespondingOperation_SwitchLookup_BreakButNoSwitch_ReturnsNull() + { + var (expected, actual) = GetOuterOperationAndCorrespondingInnerOperation(@" +class C +{ + void F() + { + /**/break;/**/ + } +}"); + + Assert.Null(expected); + Assert.Null(actual); + } + + private void AssertOuterIsCorrespondingLoopOfInner(string source) + where TOuterSyntax : SyntaxNode + where TInnerSyntax : SyntaxNode + { + var (expected, actual) = GetOuterOperationAndCorrespondingInnerOperation(source); + + Assert.Equal(expected.Syntax, actual.Syntax); + } + + private void AssertOuterIsCorrespondingSwitchOfInner(string source) + { + var (expected, actual) = GetOuterOperationAndCorrespondingInnerOperation(source); + + Assert.Equal(expected.Syntax, actual.Syntax); + } + + private (IOperation outer, IOperation corresponding) GetOuterOperationAndCorrespondingInnerOperation(string source) + where TOuterSyntax : SyntaxNode + where TInnerSyntax : SyntaxNode + { + var compilation = CreateCompilation(source); + + var outer = GetOperationAndSyntaxForTest(compilation).operation; + var inner = GetOperationAndSyntaxForTest(compilation).operation as IBranchOperation; + var correspondingOfInner = inner?.GetCorrespondingOperation(); + + return (outer, correspondingOfInner); + } + } +} diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs index 5b3a66f3e44af7d51aae7f122306bb9c8bb33cb0..ea6c23fc473e7e023ed96561f2930e24f8bd4e43 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs @@ -1063,6 +1063,15 @@ internal class CodeAnalysisResources { } } + /// + /// Looks up a localized string similar to The provided operation must not be part of a Control Flow Graph.. + /// + internal static string OperationMustNotBeControlFlowGraphPart { + get { + return ResourceManager.GetString("OperationMustNotBeControlFlowGraphPart", resourceCulture); + } + } + /// /// Looks up a localized string similar to Output kind not supported.. /// diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx index 22e854c757d06bd2605f49ca1e93fefeee8c37ca..5e74c149e5acc1662c5ab572a8eb7b817bda1cf1 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx @@ -624,4 +624,7 @@ Given operation block does not belong to the current analysis context. + + The provided operation must not be part of a Control Flow Graph. + \ No newline at end of file diff --git a/src/Compilers/Core/Portable/Operations/OperationExtensions.cs b/src/Compilers/Core/Portable/Operations/OperationExtensions.cs index e635275dc7d4aa383788843cc9daa56e4af74cad..5e3c9cfab566ca11981d2cb65c752cba03a99518 100644 --- a/src/Compilers/Core/Portable/Operations/OperationExtensions.cs +++ b/src/Compilers/Core/Portable/Operations/OperationExtensions.cs @@ -322,5 +322,49 @@ internal static IOperation GetRootOperation(this IOperation operation) return operation; } + + /// + /// Gets either a loop or a switch operation that corresponds to the given branch operation. + /// + /// The branch operation for which a corresponding operation is looked up + /// The corresponding operation or null in case not found (e.g. no loop or switch syntax, or the branch is not a break or continue) + /// is null + /// The operation is a part of Control Flow Graph + public static IOperation GetCorrespondingOperation(this IBranchOperation operation) + { + if (operation == null) + { + throw new ArgumentNullException(nameof(operation)); + } + + if (operation.SemanticModel == null) + { + throw new InvalidOperationException(CodeAnalysisResources.OperationMustNotBeControlFlowGraphPart); + } + + if (operation.BranchKind != BranchKind.Break && operation.BranchKind != BranchKind.Continue) + { + return null; + } + + if (operation.Target == null) + { + return null; + } + + for (IOperation current = operation; current.Parent != null; current = current.Parent) + { + switch (current) + { + case ILoopOperation correspondingLoop when operation.Target.Equals(correspondingLoop.ExitLabel) || + operation.Target.Equals(correspondingLoop.ContinueLabel): + return correspondingLoop; + case ISwitchOperation correspondingSwitch when operation.Target.Equals(correspondingSwitch.ExitLabel): + return correspondingSwitch; + } + } + + return default; + } } } diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 27641979304ed92c05dcf33a50f1e6a5db9884c0..788c4217d97e649425d473b762894f91b4f2f3c2 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -119,6 +119,7 @@ static Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph.Create(Microsoft.Cod static Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph.Create(Microsoft.CodeAnalysis.Operations.IParameterInitializerOperation initializer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph static Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph.Create(Microsoft.CodeAnalysis.Operations.IPropertyInitializerOperation initializer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph static Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph.Create(Microsoft.CodeAnalysis.SyntaxNode node, Microsoft.CodeAnalysis.SemanticModel semanticModel, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph +static Microsoft.CodeAnalysis.Operations.OperationExtensions.GetCorrespondingOperation(this Microsoft.CodeAnalysis.Operations.IBranchOperation operation) -> Microsoft.CodeAnalysis.IOperation virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCaughtException(Microsoft.CodeAnalysis.FlowAnalysis.ICaughtExceptionOperation operation) -> void virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitFlowCapture(Microsoft.CodeAnalysis.FlowAnalysis.IFlowCaptureOperation operation) -> void virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitFlowCaptureReference(Microsoft.CodeAnalysis.FlowAnalysis.IFlowCaptureReferenceOperation operation) -> void diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf index 8e8d78794a9bd3377191b9870f7e55dd9e1b7491..7e067ff78a31f967886ec207a2a88f66aac8f3f2 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf @@ -24,6 +24,11 @@ Given operation has a null semantic model. + + The provided operation must not be part of a Control Flow Graph. + The provided operation must not be part of a Control Flow Graph. + + Output kind not supported. Druh výstupu se nepodporuje. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf index 97c3cc24d0e54c66b19d4d099a6ab5699037190e..0ca1599a89a12776b87fa33b8a9299271de1e2b8 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf @@ -24,6 +24,11 @@ Given operation has a null semantic model. + + The provided operation must not be part of a Control Flow Graph. + The provided operation must not be part of a Control Flow Graph. + + Output kind not supported. Ausgabetyp wird nicht unterstützt. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf index e149f5e542f2b5765957cf01e606f95d87b0d3c7..a260cb4393095231627765614bdf74706d3d6766 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf @@ -24,6 +24,11 @@ Given operation has a null semantic model. + + The provided operation must not be part of a Control Flow Graph. + The provided operation must not be part of a Control Flow Graph. + + Output kind not supported. Tipo de salida no admitida. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf index 2e3aaacd745cf7aec40ba4bbb91f532701588da8..32e62809f6d0b49aecad821089ddf73d39cc18fe 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf @@ -24,6 +24,11 @@ Given operation has a null semantic model. + + The provided operation must not be part of a Control Flow Graph. + The provided operation must not be part of a Control Flow Graph. + + Output kind not supported. Type de sortie non pris en charge. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf index 2223e98b5a8797dd09310edd565b52d13d98a46e..59ecffecb301063cb0a8edb22f2ddcbb4e934c85 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf @@ -24,6 +24,11 @@ Given operation has a null semantic model. + + The provided operation must not be part of a Control Flow Graph. + The provided operation must not be part of a Control Flow Graph. + + Output kind not supported. Il tipo di output non è supportato. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf index 1b12cc20f0ad7cd5cdf9b1cc861fd8a87a49eb1b..ca20f67afa09c4b07ce22606cff1d107e5058395 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf @@ -24,6 +24,11 @@ Given operation has a null semantic model. + + The provided operation must not be part of a Control Flow Graph. + The provided operation must not be part of a Control Flow Graph. + + Output kind not supported. 出力の種類がサポートされていません。 diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf index b623680d761b84153e8aa2509c3880740e217f5b..193cc79b9c4dc31d873388a3f6728556ec63baa7 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf @@ -24,6 +24,11 @@ Given operation has a null semantic model. + + The provided operation must not be part of a Control Flow Graph. + The provided operation must not be part of a Control Flow Graph. + + Output kind not supported. 출력 종류가 지원되지 않습니다. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf index e4ebba266f6639388f13f8bd088c6e10a7835c66..d5323f39f4cab9410ec56c5c8dd396aa6bba327c 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf @@ -24,6 +24,11 @@ Given operation has a null semantic model. + + The provided operation must not be part of a Control Flow Graph. + The provided operation must not be part of a Control Flow Graph. + + Output kind not supported. Rodzaj wyjścia nie jest obsługiwany. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf index f151b14fc23e82b534025b3b86700d18f893806e..9c0b1fa90530738376b24f009f91f55cc748268d 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf @@ -24,6 +24,11 @@ Given operation has a null semantic model. + + The provided operation must not be part of a Control Flow Graph. + The provided operation must not be part of a Control Flow Graph. + + Output kind not supported. Tipo de saída sem suporte. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf index 68eace6515a794c138984f1b6a8e5f9f922f7b71..43be04d5541b84802a71c5fc3773af19a57ac1f7 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf @@ -24,6 +24,11 @@ Given operation has a null semantic model. + + The provided operation must not be part of a Control Flow Graph. + The provided operation must not be part of a Control Flow Graph. + + Output kind not supported. Неподдерживаемый тип выходных данных. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf index 1514b81a15814035f6d7d0f01625c047d93c255f..ffb34ffbc0e4e52133d4cb5502379a01a5b9eb19 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf @@ -24,6 +24,11 @@ Given operation has a null semantic model. + + The provided operation must not be part of a Control Flow Graph. + The provided operation must not be part of a Control Flow Graph. + + Output kind not supported. Çıkış türü desteklenmiyor. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf index d67baa960612c52a34bdff661822d9fc8e489bfe..80e5dbffc000363a5de0efc55d3801adec0b6192 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf @@ -24,6 +24,11 @@ Given operation has a null semantic model. + + The provided operation must not be part of a Control Flow Graph. + The provided operation must not be part of a Control Flow Graph. + + Output kind not supported. 输出类型不受支持。 diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf index a2f9cbdb1072e9216d3689fb4366f989a08f0209..11411cc61d898f2b7a7fcc43b91dada65ec650c0 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf @@ -24,6 +24,11 @@ Given operation has a null semantic model. + + The provided operation must not be part of a Control Flow Graph. + The provided operation must not be part of a Control Flow Graph. + + Output kind not supported. 輸出種類不支援。 diff --git a/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IBranchOperation.Extensions.vb b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IBranchOperation.Extensions.vb new file mode 100644 index 0000000000000000000000000000000000000000..86924795bd146433213d1f7e58eaaf3a89f55a02 --- /dev/null +++ b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IBranchOperation.Extensions.vb @@ -0,0 +1,354 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports Microsoft.CodeAnalysis.Operations +Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Roslyn.Test.Utilities + +Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests + + Partial Public Class IOperationTests + Inherits BasicTestBase + + + + + Public Sub GetCorrespondingOperation_ForNull_ThrowsArgumentNullException() + Assert.ThrowsAny(Of ArgumentNullException)(Function() OperationExtensions.GetCorrespondingOperation(Nothing)) + End Sub + + + + + Public Sub GetCorrespondingOperation_ForGotoBranch_ReturnsNull() + Dim result = GetOuterOperationAndCorrespondingInnerOperation(Of LabelStatementSyntax, GoToStatementSyntax)( + .Value) + + Assert.IsAssignableFrom(GetType(ILabeledOperation), result.outer) + Assert.Null(result.corresponding) + End Sub + + + + + Public Sub GetCorrespondingOperation_LoopLookup_ForLoopWithExit() + AssertOuterIsCorrespondingLoopOfInner(Of ForBlockSyntax, ExitStatementSyntax)( + .Value) + End Sub + + + + + Public Sub GetCorrespondingOperation_LoopLookup_WhileLoopWithContinue() + AssertOuterIsCorrespondingLoopOfInner(Of WhileBlockSyntax, ContinueStatementSyntax)( + .Value) + End Sub + + + + + Public Sub GetCorrespondingOperation_LoopLookup_DoWhileLoopWithExitAndContinue() + AssertOuterIsCorrespondingLoopOfInner(Of DoLoopBlockSyntax, ContinueStatementSyntax)( + .Value) + End Sub + + + + + Public Sub GetCorrespondingOperation_LoopLookup_ForEachLoopWithExit() + AssertOuterIsCorrespondingLoopOfInner(Of ForEachBlockSyntax, ExitStatementSyntax)( + .Value) + End Sub + + + + + Public Sub GetCorrespondingOperation_LoopLookup_ForEachLoopWithExitAndContinue() + AssertOuterIsCorrespondingLoopOfInner(Of ForEachBlockSyntax, ContinueStatementSyntax)( + .Value) + End Sub + + + + + Public Sub GetCorrespondingOperation_LoopLookup_NestedLoops() + AssertOuterIsCorrespondingLoopOfInner(Of ForBlockSyntax, ExitStatementSyntax)( + .Value) + End Sub + + + + + Public Sub GetCorrespondingOperation_LoopLookup_NestedLoops2() + AssertOuterIsCorrespondingLoopOfInner(Of ForBlockSyntax, ExitStatementSyntax)( + .Value) + End Sub + + + + + Public Sub GetCorrespondingOperation_SwitchLookup_ExitInCase() + AssertOuterIsCorrespondingSwitchOfInner( + .Value) + End Sub + + + + + Public Sub GetCorrespondingOperation_SwitchLookup_NestedSelects() + AssertOuterIsCorrespondingSwitchOfInner( + .Value) + End Sub + + + + + Public Sub GetCorrespondingOperation_SwitchLookup_NestedSelects2() + AssertOuterIsCorrespondingSwitchOfInner( + .Value) + End Sub + + + + + Public Sub GetCorrespondingOperation_LoopLookup_LoopInSelect() + AssertOuterIsCorrespondingLoopOfInner(Of ForBlockSyntax, ExitStatementSyntax)( + .Value) + End Sub + + + + + Public Sub GetCorrespondingOperation_SwitchLookup_SelectInLoop() + AssertOuterIsCorrespondingSwitchOfInner( + .Value) + End Sub + + + + + Public Sub GetCorrespondingOperation_LoopLookup_ContinueNestedInIntermediateSelect() + AssertOuterIsCorrespondingLoopOfInner(Of ForBlockSyntax, ContinueStatementSyntax)( + .Value) + End Sub + + + + + Public Sub GetCorrespondingOperation_LoopLookup_ExitButNoLoop_ReturnsNull() + Dim result = GetOuterOperationAndCorrespondingInnerOperation(Of ForBlockSyntax, ExitStatementSyntax)( + .Value) + + Assert.IsAssignableFrom(GetType(ILoopOperation), result.outer) + Assert.Null(result.corresponding) + End Sub + + + + + Public Sub GetCorrespondingOperation_SwitchLookup_ExitButNoSwitch_ReturnsNull() + Dim result = GetOuterOperationAndCorrespondingInnerOperation(Of SelectBlockSyntax, ExitStatementSyntax)( + .Value) + + Assert.IsAssignableFrom(GetType(ISwitchOperation), result.outer) + Assert.Null(result.corresponding) + End Sub + + Private Sub AssertOuterIsCorrespondingLoopOfInner(Of TOuterSyntax As SyntaxNode, TInnerSyntax As SyntaxNode)(source As string) + Dim result As (expected As IOperation, actual As IOperation) + result = GetOuterOperationAndCorrespondingInnerOperation(Of TOuterSyntax, TInnerSyntax)(source) + + Assert.Equal(result.expected.Syntax, result.actual.Syntax) + End Sub + + Private Sub AssertOuterIsCorrespondingSwitchOfInner(source As string) + Dim result As (expected As IOperation, actual As IOperation) + result = GetOuterOperationAndCorrespondingInnerOperation(Of SelectBlockSyntax, ExitStatementSyntax)(source) + + Assert.Equal(result.expected.Syntax, result.actual.Syntax) + End Sub + + Private Function GetOuterOperationAndCorrespondingInnerOperation(Of TOuterSyntax As SyntaxNode, TInnerSyntax As SyntaxNode)( + source As string) As (outer As IOperation, corresponding As IOperation) + + Dim fileName = "a.vb" + Dim syntaxTree = Parse(source, fileName) + Dim compilation = CreateEmptyCompilation({syntaxTree}) + + Dim outer = GetOperationAndSyntaxForTest(Of TOuterSyntax)(compilation, fileName, 1).operation + Dim inner = TryCast(GetOperationAndSyntaxForTest(Of TInnerSyntax)(compilation, fileName, 2).operation, IBranchOperation) + Dim correspondingOfInner = inner?.GetCorrespondingOperation() + + Return (outer, correspondingOfInner) + + End Function + + End Class + +End Namespace