未验证 提交 c9110d1b 编写于 作者: F Fred Silberberg 提交者: GitHub

Merge pull request #28285 from wachulski/feat/28095/get-parent-loop-op-for-branch-op

Add GetCorrespondingLoop() (and Switch) branch operation extensions
// 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<ArgumentNullException>(() => 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<LabeledStatementSyntax, GotoStatementSyntax>(@"
class C
{
void F()
{
/*<bind>*/begin:
for (;;)
{
/*<bind>*/goto begin;/*</bind>*/
}/*</bind>*/
}
}");
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<ForStatementSyntax, BreakStatementSyntax>(@"
class C
{
void F()
{
/*<bind>*/for (;;)
{
/*<bind>*/break;/*</bind>*/
}/*</bind>*/
}
}");
}
[CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)]
[WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")]
[Fact]
public void GetCorrespondingOperation_LoopLookup_WhileLoopWithContinue()
{
AssertOuterIsCorrespondingLoopOfInner<WhileStatementSyntax, ContinueStatementSyntax>(@"
class C
{
void F()
{
/*<bind>*/while (true)
{
/*<bind>*/continue;/*</bind>*/
}/*</bind>*/
}
}");
}
[CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)]
[WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")]
[Fact]
public void GetCorrespondingOperation_LoopLookup_DoWhileLoopWithBreakAndContinue()
{
AssertOuterIsCorrespondingLoopOfInner<DoStatementSyntax, ContinueStatementSyntax>(@"
class C
{
void F()
{
/*<bind>*/do
{
if (true)
break;
else
/*<bind>*/continue;/*</bind>*/
} while (true)/*</bind>*/
}
}");
}
[CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)]
[WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")]
[Fact]
public void GetCorrespondingOperation_LoopLookup_ForEachLoopWithBreak()
{
AssertOuterIsCorrespondingLoopOfInner<ForEachStatementSyntax, BreakStatementSyntax>(@"
class C
{
void F()
{
/*<bind>*/foreach (var i in new [] {1,2,3})
{
if (i == 2)
/*<bind>*/break;/*</bind>*/
}/*</bind>*/
}
}");
}
[CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)]
[WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")]
[Fact]
public void GetCorrespondingOperation_LoopLookup_ForEachLoopWithBreakAndContinue()
{
AssertOuterIsCorrespondingLoopOfInner<ForEachStatementSyntax, BreakStatementSyntax>(@"
class C
{
void F()
{
/*<bind>*/foreach (var i in new [] {1,2,3})
{
if (i == 2)
/*<bind>*/break;/*</bind>*/
else
continue;
}/*</bind>*/
}
}");
}
[CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)]
[WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")]
[Fact]
public void GetCorrespondingOperation_LoopLookup_NestedLoops()
{
AssertOuterIsCorrespondingLoopOfInner<ForStatementSyntax, BreakStatementSyntax>(@"
class C
{
void F()
{
/*<bind>*/for (;;)
{
for (;;)
{
}
/*<bind>*/break;/*</bind>*/
}/*</bind>*/
}
}");
}
[CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)]
[WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")]
[Fact]
public void GetCorrespondingOperation_LoopLookup_NestedLoops2()
{
AssertOuterIsCorrespondingLoopOfInner<ForStatementSyntax, BreakStatementSyntax>(@"
class C
{
void F()
{
for (;;)
{
/*<bind>*/for (;;)
{
/*<bind>*/break;/*</bind>*/
}/*</bind>*/
}
}
}");
}
[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()
{
/*<bind>*/switch (1)
{
case 1:
/*<bind>*/break;/*</bind>*/
}/*</bind>*/
}
}");
}
[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()
{
/*<bind>*/switch (1)
{
case 1:
switch (2)
{
case 2:
break;
}
/*<bind>*/break;/*</bind>*/
}/*</bind>*/
}
}");
}
[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:
/*<bind>*/switch (2)
{
case 2:
/*<bind>*/break;/*</bind>*/
}/*</bind>*/
break;
}
}
}");
}
[CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)]
[WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")]
[Fact]
public void GetCorrespondingOperation_LoopLookup_LoopInSwitch()
{
AssertOuterIsCorrespondingLoopOfInner<ForStatementSyntax, BreakStatementSyntax>(@"
class C
{
void F()
{
switch (1)
{
case 1:
/*<bind>*/for (;;)
{
/*<bind>*/break;/*</bind>*/
}/*</bind>*/
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 (;;)
{
/*<bind>*/switch (1)
{
case 1:
/*<bind>*/break;/*</bind>*/
}/*</bind>*/
}
}
}");
}
[CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)]
[WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")]
[Fact]
public void GetCorrespondingOperation_LoopLookup_ContinueNestedInIntermediateSwitch()
{
AssertOuterIsCorrespondingLoopOfInner<ForStatementSyntax, ContinueStatementSyntax>(@"
class C
{
void F()
{
/*<bind>*/for (;;)
{
switch (1)
{
case 1:
/*<bind>*/continue;/*</bind>*/
break;
}
}/*</bind>*/
}
}");
}
[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<ForStatementSyntax, BreakStatementSyntax>(@"
class C
{
void F()
{
/*<bind>*/break;/*</bind>*/
}
}");
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<SwitchStatementSyntax, BreakStatementSyntax>(@"
class C
{
void F()
{
/*<bind>*/break;/*</bind>*/
}
}");
Assert.Null(expected);
Assert.Null(actual);
}
private void AssertOuterIsCorrespondingLoopOfInner<TOuterSyntax, TInnerSyntax>(string source)
where TOuterSyntax : SyntaxNode
where TInnerSyntax : SyntaxNode
{
var (expected, actual) = GetOuterOperationAndCorrespondingInnerOperation<TOuterSyntax, TInnerSyntax>(source);
Assert.Equal(expected.Syntax, actual.Syntax);
}
private void AssertOuterIsCorrespondingSwitchOfInner(string source)
{
var (expected, actual) = GetOuterOperationAndCorrespondingInnerOperation<SwitchStatementSyntax, BreakStatementSyntax>(source);
Assert.Equal(expected.Syntax, actual.Syntax);
}
private (IOperation outer, IOperation corresponding) GetOuterOperationAndCorrespondingInnerOperation<TOuterSyntax, TInnerSyntax>(string source)
where TOuterSyntax : SyntaxNode
where TInnerSyntax : SyntaxNode
{
var compilation = CreateCompilation(source);
var outer = GetOperationAndSyntaxForTest<TOuterSyntax>(compilation).operation;
var inner = GetOperationAndSyntaxForTest<TInnerSyntax>(compilation).operation as IBranchOperation;
var correspondingOfInner = inner?.GetCorrespondingOperation();
return (outer, correspondingOfInner);
}
}
}
......@@ -1063,6 +1063,15 @@ internal class CodeAnalysisResources {
}
}
/// <summary>
/// Looks up a localized string similar to The provided operation must not be part of a Control Flow Graph..
/// </summary>
internal static string OperationMustNotBeControlFlowGraphPart {
get {
return ResourceManager.GetString("OperationMustNotBeControlFlowGraphPart", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Output kind not supported..
/// </summary>
......
......@@ -624,4 +624,7 @@
<data name="InvalidOperationBlockForAnalysisContext" xml:space="preserve">
<value>Given operation block does not belong to the current analysis context.</value>
</data>
<data name="OperationMustNotBeControlFlowGraphPart" xml:space="preserve">
<value>The provided operation must not be part of a Control Flow Graph.</value>
</data>
</root>
\ No newline at end of file
......@@ -322,5 +322,49 @@ internal static IOperation GetRootOperation(this IOperation operation)
return operation;
}
/// <summary>
/// Gets either a loop or a switch operation that corresponds to the given branch operation.
/// </summary>
/// <param name="operation">The branch operation for which a corresponding operation is looked up</param>
/// <returns>The corresponding operation or <c>null</c> in case not found (e.g. no loop or switch syntax, or the branch is not a break or continue)</returns>
/// <exception cref="ArgumentNullException"><paramref name="operation"/> is null</exception>
/// <exception cref="InvalidOperationException">The operation is a part of Control Flow Graph</exception>
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;
}
}
}
......@@ -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
......
......@@ -24,6 +24,11 @@
<target state="new">Given operation has a null semantic model.</target>
<note />
</trans-unit>
<trans-unit id="OperationMustNotBeControlFlowGraphPart">
<source>The provided operation must not be part of a Control Flow Graph.</source>
<target state="new">The provided operation must not be part of a Control Flow Graph.</target>
<note />
</trans-unit>
<trans-unit id="OutputKindNotSupported">
<source>Output kind not supported.</source>
<target state="translated">Druh výstupu se nepodporuje.</target>
......
......@@ -24,6 +24,11 @@
<target state="new">Given operation has a null semantic model.</target>
<note />
</trans-unit>
<trans-unit id="OperationMustNotBeControlFlowGraphPart">
<source>The provided operation must not be part of a Control Flow Graph.</source>
<target state="new">The provided operation must not be part of a Control Flow Graph.</target>
<note />
</trans-unit>
<trans-unit id="OutputKindNotSupported">
<source>Output kind not supported.</source>
<target state="translated">Ausgabetyp wird nicht unterstützt.</target>
......
......@@ -24,6 +24,11 @@
<target state="new">Given operation has a null semantic model.</target>
<note />
</trans-unit>
<trans-unit id="OperationMustNotBeControlFlowGraphPart">
<source>The provided operation must not be part of a Control Flow Graph.</source>
<target state="new">The provided operation must not be part of a Control Flow Graph.</target>
<note />
</trans-unit>
<trans-unit id="OutputKindNotSupported">
<source>Output kind not supported.</source>
<target state="translated">Tipo de salida no admitida.</target>
......
......@@ -24,6 +24,11 @@
<target state="new">Given operation has a null semantic model.</target>
<note />
</trans-unit>
<trans-unit id="OperationMustNotBeControlFlowGraphPart">
<source>The provided operation must not be part of a Control Flow Graph.</source>
<target state="new">The provided operation must not be part of a Control Flow Graph.</target>
<note />
</trans-unit>
<trans-unit id="OutputKindNotSupported">
<source>Output kind not supported.</source>
<target state="translated">Type de sortie non pris en charge.</target>
......
......@@ -24,6 +24,11 @@
<target state="new">Given operation has a null semantic model.</target>
<note />
</trans-unit>
<trans-unit id="OperationMustNotBeControlFlowGraphPart">
<source>The provided operation must not be part of a Control Flow Graph.</source>
<target state="new">The provided operation must not be part of a Control Flow Graph.</target>
<note />
</trans-unit>
<trans-unit id="OutputKindNotSupported">
<source>Output kind not supported.</source>
<target state="translated">Il tipo di output non è supportato.</target>
......
......@@ -24,6 +24,11 @@
<target state="new">Given operation has a null semantic model.</target>
<note />
</trans-unit>
<trans-unit id="OperationMustNotBeControlFlowGraphPart">
<source>The provided operation must not be part of a Control Flow Graph.</source>
<target state="new">The provided operation must not be part of a Control Flow Graph.</target>
<note />
</trans-unit>
<trans-unit id="OutputKindNotSupported">
<source>Output kind not supported.</source>
<target state="translated">出力の種類がサポートされていません。</target>
......
......@@ -24,6 +24,11 @@
<target state="new">Given operation has a null semantic model.</target>
<note />
</trans-unit>
<trans-unit id="OperationMustNotBeControlFlowGraphPart">
<source>The provided operation must not be part of a Control Flow Graph.</source>
<target state="new">The provided operation must not be part of a Control Flow Graph.</target>
<note />
</trans-unit>
<trans-unit id="OutputKindNotSupported">
<source>Output kind not supported.</source>
<target state="translated">출력 종류가 지원되지 않습니다.</target>
......
......@@ -24,6 +24,11 @@
<target state="new">Given operation has a null semantic model.</target>
<note />
</trans-unit>
<trans-unit id="OperationMustNotBeControlFlowGraphPart">
<source>The provided operation must not be part of a Control Flow Graph.</source>
<target state="new">The provided operation must not be part of a Control Flow Graph.</target>
<note />
</trans-unit>
<trans-unit id="OutputKindNotSupported">
<source>Output kind not supported.</source>
<target state="translated">Rodzaj wyjścia nie jest obsługiwany.</target>
......
......@@ -24,6 +24,11 @@
<target state="new">Given operation has a null semantic model.</target>
<note />
</trans-unit>
<trans-unit id="OperationMustNotBeControlFlowGraphPart">
<source>The provided operation must not be part of a Control Flow Graph.</source>
<target state="new">The provided operation must not be part of a Control Flow Graph.</target>
<note />
</trans-unit>
<trans-unit id="OutputKindNotSupported">
<source>Output kind not supported.</source>
<target state="translated">Tipo de saída sem suporte.</target>
......
......@@ -24,6 +24,11 @@
<target state="new">Given operation has a null semantic model.</target>
<note />
</trans-unit>
<trans-unit id="OperationMustNotBeControlFlowGraphPart">
<source>The provided operation must not be part of a Control Flow Graph.</source>
<target state="new">The provided operation must not be part of a Control Flow Graph.</target>
<note />
</trans-unit>
<trans-unit id="OutputKindNotSupported">
<source>Output kind not supported.</source>
<target state="translated">Неподдерживаемый тип выходных данных.</target>
......
......@@ -24,6 +24,11 @@
<target state="new">Given operation has a null semantic model.</target>
<note />
</trans-unit>
<trans-unit id="OperationMustNotBeControlFlowGraphPart">
<source>The provided operation must not be part of a Control Flow Graph.</source>
<target state="new">The provided operation must not be part of a Control Flow Graph.</target>
<note />
</trans-unit>
<trans-unit id="OutputKindNotSupported">
<source>Output kind not supported.</source>
<target state="translated">Çıkış türü desteklenmiyor.</target>
......
......@@ -24,6 +24,11 @@
<target state="new">Given operation has a null semantic model.</target>
<note />
</trans-unit>
<trans-unit id="OperationMustNotBeControlFlowGraphPart">
<source>The provided operation must not be part of a Control Flow Graph.</source>
<target state="new">The provided operation must not be part of a Control Flow Graph.</target>
<note />
</trans-unit>
<trans-unit id="OutputKindNotSupported">
<source>Output kind not supported.</source>
<target state="translated">输出类型不受支持。</target>
......
......@@ -24,6 +24,11 @@
<target state="new">Given operation has a null semantic model.</target>
<note />
</trans-unit>
<trans-unit id="OperationMustNotBeControlFlowGraphPart">
<source>The provided operation must not be part of a Control Flow Graph.</source>
<target state="new">The provided operation must not be part of a Control Flow Graph.</target>
<note />
</trans-unit>
<trans-unit id="OutputKindNotSupported">
<source>Output kind not supported.</source>
<target state="translated">輸出種類不支援。</target>
......
' 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
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_ForNull_ThrowsArgumentNullException()
Assert.ThrowsAny(Of ArgumentNullException)(Function() OperationExtensions.GetCorrespondingOperation(Nothing))
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_ForGotoBranch_ReturnsNull()
Dim result = GetOuterOperationAndCorrespondingInnerOperation(Of LabelStatementSyntax, GoToStatementSyntax)(
<![CDATA[
Class C
Sub F
begin: 'BIND1:"begin:"
For i = 0 To 1
GoTo begin 'BIND2:"GoTo begin"
Next
End Sub
End Class
]]>.Value)
Assert.IsAssignableFrom(GetType(ILabeledOperation), result.outer)
Assert.Null(result.corresponding)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_LoopLookup_ForLoopWithExit()
AssertOuterIsCorrespondingLoopOfInner(Of ForBlockSyntax, ExitStatementSyntax)(
<![CDATA[
Class C
Sub F
For i = 0 To 1 'BIND1:"For i = 0 To 1"
Exit For 'BIND2:"Exit For"
Next
End Sub
End Class
]]>.Value)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_LoopLookup_WhileLoopWithContinue()
AssertOuterIsCorrespondingLoopOfInner(Of WhileBlockSyntax, ContinueStatementSyntax)(
<![CDATA[
Class C
Sub F
While True 'BIND1:"While True"
Continue While 'BIND2:"Continue While"
End While
End Sub
End Class
]]>.Value)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_LoopLookup_DoWhileLoopWithExitAndContinue()
AssertOuterIsCorrespondingLoopOfInner(Of DoLoopBlockSyntax, ContinueStatementSyntax)(
<![CDATA[
Class C
Sub F
Do 'BIND1:"Do"
If True
Exit Do
Else
Continue Do 'BIND2:"Continue Do"
End If
Loop While True
End Sub
End Class
]]>.Value)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_LoopLookup_ForEachLoopWithExit()
AssertOuterIsCorrespondingLoopOfInner(Of ForEachBlockSyntax, ExitStatementSyntax)(
<![CDATA[
Class C
Sub F
For Each i In {1,2,3} 'BIND1:"For Each i In {1,2,3}"
If i = 2
Exit For 'BIND2:"Exit For"
End If
Next
End Sub
End Class
]]>.Value)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_LoopLookup_ForEachLoopWithExitAndContinue()
AssertOuterIsCorrespondingLoopOfInner(Of ForEachBlockSyntax, ContinueStatementSyntax)(
<![CDATA[
Class C
Sub F
For Each i In {1,2,3} 'BIND1:"For Each i In {1,2,3}"
If i Mod 2 = 0
Exit For
Else
Continue For 'BIND2:"Continue For"
End If
Next
End Sub
End Class
]]>.Value)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_LoopLookup_NestedLoops()
AssertOuterIsCorrespondingLoopOfInner(Of ForBlockSyntax, ExitStatementSyntax)(
<![CDATA[
Class C
Sub F
For i = 0 To 1 'BIND1:"For i = 0 To 1"
For j = 0 To 1
Next
Exit For 'BIND2:"Exit For"
Next
End Sub
End Class
]]>.Value)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_LoopLookup_NestedLoops2()
AssertOuterIsCorrespondingLoopOfInner(Of ForBlockSyntax, ExitStatementSyntax)(
<![CDATA[
Class C
Sub F
For i = 0 To 1
For j = 0 To 1 'BIND1:"For j = 0 To 1"
Exit For 'BIND2:"Exit For"
Next
Next
End Sub
End Class
]]>.Value)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_SwitchLookup_ExitInCase()
AssertOuterIsCorrespondingSwitchOfInner(
<![CDATA[
Class C
Sub F
Select Case 1 'BIND1:"Select Case 1"
Case 1
Exit Select 'BIND2:"Exit Select"
End Select
End Sub
End Class
]]>.Value)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_SwitchLookup_NestedSelects()
AssertOuterIsCorrespondingSwitchOfInner(
<![CDATA[
Class C
Sub F
Select Case 1 'BIND1:"Select Case 1"
Case 1
Select Case 2
Case 2
End Select
Exit Select 'BIND2:"Exit Select"
End Select
End Sub
End Class
]]>.Value)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_SwitchLookup_NestedSelects2()
AssertOuterIsCorrespondingSwitchOfInner(
<![CDATA[
Class C
Sub F
Select Case 1
Case 1
Select Case 2 'BIND1:"Select Case 2"
Case 2
Exit Select 'BIND2:"Exit Select"
End Select
End Select
End Sub
End Class
]]>.Value)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_LoopLookup_LoopInSelect()
AssertOuterIsCorrespondingLoopOfInner(Of ForBlockSyntax, ExitStatementSyntax)(
<![CDATA[
Class C
Sub F
Select Case 1
Case 1
For i = 0 To 1 'BIND1:"For i = 0 To 1"
Exit For 'BIND2:"Exit For"
Next
End Select
End Sub
End Class
]]>.Value)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_SwitchLookup_SelectInLoop()
AssertOuterIsCorrespondingSwitchOfInner(
<![CDATA[
Class C
Sub F
For i = 0 To 1
Select Case 1 'BIND1:"Select Case 1"
Case 1
Exit Select 'BIND2:"Exit Select"
End Select
Next
End Sub
End Class
]]>.Value)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_LoopLookup_ContinueNestedInIntermediateSelect()
AssertOuterIsCorrespondingLoopOfInner(Of ForBlockSyntax, ContinueStatementSyntax)(
<![CDATA[
Class C
Sub F
For i = 0 To 1 'BIND1:"For i = 0 To 1"
Select Case 1
Case 1
Continue For 'BIND2:"Continue For"
End Select
Next
End Sub
End Class
]]>.Value)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_LoopLookup_ExitButNoLoop_ReturnsNull()
Dim result = GetOuterOperationAndCorrespondingInnerOperation(Of ForBlockSyntax, ExitStatementSyntax)(
<![CDATA[
Class C
Sub F
' the following loop is just for utilize the common testing method (which expects 2 bindings in source)
For i = 0 To 1 'BIND1:"For i = 0 To 1"
Next
Exit For 'BIND2:"Exit For"
End Sub
End Class
]]>.Value)
Assert.IsAssignableFrom(GetType(ILoopOperation), result.outer)
Assert.Null(result.corresponding)
End Sub
<CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)>
<WorkItem(28095, "https://github.com/dotnet/roslyn/issues/28095")>
<Fact>
Public Sub GetCorrespondingOperation_SwitchLookup_ExitButNoSwitch_ReturnsNull()
Dim result = GetOuterOperationAndCorrespondingInnerOperation(Of SelectBlockSyntax, ExitStatementSyntax)(
<![CDATA[
Class C
Sub F
' the following switch is just for utilize the common testing method (which expects 2 bindings in source)
Select Case 1 'BIND1:"Select Case 1"
Case 1
End Select
Exit Select 'BIND2:"Exit Select"
End Sub
End Class
]]>.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
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册