未验证 提交 9f78ab63 编写于 作者: A AlekseyTs 提交者: GitHub

An initial prototype to build a flow graph (#24263)

上级 b83993a7
......@@ -297,7 +297,7 @@ internal static UnaryOperatorKind DeriveUnaryOperatorKind(CSharp.UnaryOperatorKi
internal static BinaryOperatorKind DeriveBinaryOperatorKind(CSharp.BinaryOperatorKind operatorKind)
{
switch (operatorKind & CSharp.BinaryOperatorKind.OpMask)
switch (operatorKind.OperatorWithLogical())
{
case CSharp.BinaryOperatorKind.Addition:
return BinaryOperatorKind.Add;
......@@ -346,6 +346,12 @@ internal static BinaryOperatorKind DeriveBinaryOperatorKind(CSharp.BinaryOperato
case CSharp.BinaryOperatorKind.GreaterThan:
return BinaryOperatorKind.GreaterThan;
case CSharp.BinaryOperatorKind.LogicalAnd:
return BinaryOperatorKind.ConditionalAnd;
case CSharp.BinaryOperatorKind.LogicalOr:
return BinaryOperatorKind.ConditionalOr;
}
return BinaryOperatorKind.None;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// 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 Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Test.Utilities;
......@@ -431,5 +431,195 @@ void M(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int
VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
}
[Fact]
public void LogicalOrFlow_01()
{
string source = @"
class P
{
void M(bool a, bool b)
/*<bind>*/{
GetArray()[0] = a || b;
}/*</bind>*/
static bool[] GetArray() => null;
}
";
string expectedGraph = @"
Block[0] - Entry
Statements (0)
Next Block[1]
Block[1] - Block
Predecessors (1)
[0]
Statements (1)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean, IsImplicit) (Syntax: 'GetArray()[0]')
Left:
IFlowCaptureOperation: 0 (IsInitialization: True) (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'GetArray()[0]')
Right:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: System.Boolean) (Syntax: 'GetArray()[0]')
Array reference:
IInvocationOperation (System.Boolean[] P.GetArray()) (OperationKind.Invocation, Type: System.Boolean[]) (Syntax: 'GetArray()')
Instance Receiver:
null
Arguments(0)
Indices(1):
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0')
Jump if True to Block[3]
IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'a')
Next Block[2]
Block[2] - Block
Predecessors (1)
[1]
Statements (1)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean, IsImplicit) (Syntax: 'b')
Left:
IFlowCaptureOperation: 1 (IsInitialization: True) (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'b')
Right:
IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b')
Next Block[4]
Block[3] - Block
Predecessors (1)
[1]
Statements (1)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean, Constant: True, IsImplicit) (Syntax: 'a')
Left:
IFlowCaptureOperation: 1 (IsInitialization: True) (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'a')
Right:
ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True, IsImplicit) (Syntax: 'a')
Next Block[4]
Block[4] - Block
Predecessors (2)
[2]
[3]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'GetArray()[0] = a || b;')
Expression:
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'GetArray()[0] = a || b')
Left:
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'GetArray()[0]')
Right:
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'a || b')
Next Block[5]
Block[5] - Exit
Predecessors (1)
[4]
Statements (0)
";
var expectedDiagnostics = DiagnosticDescription.None;
VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>(source, expectedGraph, expectedDiagnostics);
}
[Fact]
public void LogicalOrFlow_02()
{
string source = @"
class P
{
void M(bool a, bool b, bool c)
/*<bind>*/{
GetArray()[0] = c && (a || b);
}/*</bind>*/
static bool[] GetArray() => null;
}
";
string expectedGraph = @"
Block[0] - Entry
Statements (0)
Next Block[1]
Block[1] - Block
Predecessors (1)
[0]
Statements (1)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean, IsImplicit) (Syntax: 'GetArray()[0]')
Left:
IFlowCaptureOperation: 0 (IsInitialization: True) (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'GetArray()[0]')
Right:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: System.Boolean) (Syntax: 'GetArray()[0]')
Array reference:
IInvocationOperation (System.Boolean[] P.GetArray()) (OperationKind.Invocation, Type: System.Boolean[]) (Syntax: 'GetArray()')
Instance Receiver:
null
Arguments(0)
Indices(1):
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0')
Jump if False to Block[5]
IParameterReferenceOperation: c (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'c')
Next Block[2]
Block[2] - Block
Predecessors (1)
[1]
Statements (0)
Jump if True to Block[4]
IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'a')
Next Block[3]
Block[3] - Block
Predecessors (1)
[2]
Statements (1)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean, IsImplicit) (Syntax: 'b')
Left:
IFlowCaptureOperation: 1 (IsInitialization: True) (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'b')
Right:
IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b')
Next Block[6]
Block[4] - Block
Predecessors (1)
[2]
Statements (1)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean, Constant: True, IsImplicit) (Syntax: 'a')
Left:
IFlowCaptureOperation: 1 (IsInitialization: True) (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'a')
Right:
ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True, IsImplicit) (Syntax: 'a')
Next Block[6]
Block[5] - Block
Predecessors (1)
[1]
Statements (1)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean, Constant: False, IsImplicit) (Syntax: 'c')
Left:
IFlowCaptureOperation: 1 (IsInitialization: True) (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'c')
Right:
ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False, IsImplicit) (Syntax: 'c')
Next Block[6]
Block[6] - Block
Predecessors (3)
[3]
[4]
[5]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'GetArray()[ ... & (a || b);')
Expression:
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'GetArray()[ ... && (a || b)')
Left:
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'GetArray()[0]')
Right:
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'c && (a || b)')
Next Block[7]
Block[7] - Exit
Predecessors (1)
[6]
Statements (0)
";
var expectedDiagnostics = DiagnosticDescription.None;
VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>(source, expectedGraph, expectedDiagnostics);
}
}
}
......@@ -67,5 +67,99 @@ private void M()
VerifyOperationTreeAndDiagnosticsForTest<ConditionalExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
}
[Fact]
public void ConditionalExpressionFlow_01()
{
string source = @"
class P
{
void M(bool a, bool b)
/*<bind>*/{
GetArray()[0] = a && b ? 1 : 2;
}/*</bind>*/
static int[] GetArray() => null;
}
";
string expectedGraph = @"
Block[0] - Entry
Statements (0)
Next Block[1]
Block[1] - Block
Predecessors (1)
[0]
Statements (1)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsImplicit) (Syntax: 'GetArray()[0]')
Left:
IFlowCaptureOperation: 0 (IsInitialization: True) (OperationKind.FlowCapture, Type: System.Int32, IsImplicit) (Syntax: 'GetArray()[0]')
Right:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: System.Int32) (Syntax: 'GetArray()[0]')
Array reference:
IInvocationOperation (System.Int32[] P.GetArray()) (OperationKind.Invocation, Type: System.Int32[]) (Syntax: 'GetArray()')
Instance Receiver:
null
Arguments(0)
Indices(1):
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0')
Jump if False to Block[4]
IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'a')
Next Block[2]
Block[2] - Block
Predecessors (1)
[1]
Statements (0)
Jump if False to Block[4]
IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b')
Next Block[3]
Block[3] - Block
Predecessors (1)
[2]
Statements (1)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsImplicit) (Syntax: '1')
Left:
IFlowCaptureOperation: 1 (IsInitialization: True) (OperationKind.FlowCapture, Type: System.Int32, IsImplicit) (Syntax: 'a && b ? 1 : 2')
Right:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
Next Block[5]
Block[4] - Block
Predecessors (2)
[1]
[2]
Statements (1)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsImplicit) (Syntax: '2')
Left:
IFlowCaptureOperation: 1 (IsInitialization: True) (OperationKind.FlowCapture, Type: System.Int32, IsImplicit) (Syntax: 'a && b ? 1 : 2')
Right:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2')
Next Block[5]
Block[5] - Block
Predecessors (2)
[3]
[4]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'GetArray()[ ... b ? 1 : 2;')
Expression:
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'GetArray()[ ... & b ? 1 : 2')
Left:
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: System.Int32, IsImplicit) (Syntax: 'GetArray()[0]')
Right:
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: System.Int32, IsImplicit) (Syntax: 'a && b ? 1 : 2')
Next Block[6]
Block[6] - Exit
Predecessors (1)
[5]
Statements (0)
";
var expectedDiagnostics = DiagnosticDescription.None;
VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>(source, expectedGraph, expectedDiagnostics);
}
}
}
......@@ -324,7 +324,7 @@ private void M()
string expectedOperationTree = @"
IConditionalOperation (OperationKind.Conditional, Type: null) (Syntax: 'if (m >= n ... }')
Condition:
IBinaryOperation (BinaryOperatorKind.And) (OperationKind.BinaryOperator, Type: System.Boolean) (Syntax: 'm >= n && m >= p')
IBinaryOperation (BinaryOperatorKind.ConditionalAnd) (OperationKind.BinaryOperator, Type: System.Boolean) (Syntax: 'm >= n && m >= p')
Left:
IBinaryOperation (BinaryOperatorKind.GreaterThanOrEqual) (OperationKind.BinaryOperator, Type: System.Boolean) (Syntax: 'm >= n')
Left:
......@@ -1122,7 +1122,7 @@ class C
Condition:
IUnaryOperation (UnaryOperatorKind.True) (OperationKind.UnaryOperator, Type: System.Boolean, IsImplicit) (Syntax: 'd.GetType() ... ).Equals(x)')
Operand:
IBinaryOperation (BinaryOperatorKind.And) (OperationKind.BinaryOperator, Type: dynamic) (Syntax: 'd.GetType() ... ).Equals(x)')
IBinaryOperation (BinaryOperatorKind.ConditionalAnd) (OperationKind.BinaryOperator, Type: dynamic) (Syntax: 'd.GetType() ... ).Equals(x)')
Left:
IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.BinaryOperator, Type: dynamic) (Syntax: 'd.GetType() == t')
Left:
......@@ -1164,5 +1164,228 @@ Type Arguments(0)
VerifyOperationTreeAndDiagnosticsForTest<IfStatementSyntax>(source, expectedOperationTree, expectedDiagnostics);
}
[Fact]
public void IfFlow_01()
{
string source = @"
class P
{
void M()
/*<bind>*/{
bool condition = false;
if (true)
{
condition = true;
}
}/*</bind>*/
}
";
string expectedGraph = @"
Block[0] - Entry
Statements (0)
Next Block[1]
Block[1] - Block
Predecessors (1)
[0]
Statements (1)
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null) (Syntax: 'bool condition = false;')
IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'bool condition = false')
Declarators:
IVariableDeclaratorOperation (Symbol: System.Boolean condition) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'condition = false')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= false')
ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false')
Initializer:
null
Jump if False to Block[3]
ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true')
Next Block[2]
Block[2] - Block
Predecessors (1)
[1]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'condition = true;')
Expression:
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'condition = true')
Left:
ILocalReferenceOperation: condition (OperationKind.LocalReference, Type: System.Boolean) (Syntax: 'condition')
Right:
ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true')
Next Block[3]
Block[3] - Exit
Predecessors (2)
[1]
[2]
Statements (0)
";
var expectedDiagnostics = new DiagnosticDescription[] {
// CS0219: The variable 'condition' is assigned but its value is never used
// bool condition = false;
Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "condition").WithArguments("condition").WithLocation(6, 14)
};
VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>(source, expectedGraph, expectedDiagnostics);
}
[Fact]
public void IfFlow_02()
{
string source = @"
class P
{
void M()
/*<bind>*/{
bool condition = false;
if (true)
{
;
}
else
{
condition = true;
}
}/*</bind>*/
}
";
string expectedGraph = @"
Block[0] - Entry
Statements (0)
Next Block[1]
Block[1] - Block
Predecessors (1)
[0]
Statements (1)
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null) (Syntax: 'bool condition = false;')
IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'bool condition = false')
Declarators:
IVariableDeclaratorOperation (Symbol: System.Boolean condition) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'condition = false')
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= false')
ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false')
Initializer:
null
Jump if False to Block[3]
ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true')
Next Block[2]
Block[2] - Block
Predecessors (1)
[1]
Statements (1)
IEmptyOperation (OperationKind.Empty, Type: null) (Syntax: ';')
Next Block[4]
Block[3] - Block
Predecessors (1)
[1]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'condition = true;')
Expression:
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'condition = true')
Left:
ILocalReferenceOperation: condition (OperationKind.LocalReference, Type: System.Boolean) (Syntax: 'condition')
Right:
ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true')
Next Block[4]
Block[4] - Exit
Predecessors (2)
[2]
[3]
Statements (0)
";
var expectedDiagnostics = new DiagnosticDescription[] {
// file.cs(13,13): warning CS0162: Unreachable code detected
// condition = true;
Diagnostic(ErrorCode.WRN_UnreachableCode, "condition").WithLocation(13, 13),
// file.cs(6,14): warning CS0219: The variable 'condition' is assigned but its value is never used
// bool condition = false;
Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "condition").WithArguments("condition").WithLocation(6, 14)
};
VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>(source, expectedGraph, expectedDiagnostics);
}
[Fact]
public void IfFlow_03()
{
string source = @"
class P
{
void M(bool a, bool b)
/*<bind>*/{
if (a && b)
{
a = false;
}
else
{
b = true;
}
}/*</bind>*/
}
";
string expectedGraph = @"
Block[0] - Entry
Statements (0)
Next Block[1]
Block[1] - Block
Predecessors (1)
[0]
Statements (0)
Jump if False to Block[4]
IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'a')
Next Block[2]
Block[2] - Block
Predecessors (1)
[1]
Statements (0)
Jump if False to Block[4]
IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b')
Next Block[3]
Block[3] - Block
Predecessors (1)
[2]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'a = false;')
Expression:
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'a = false')
Left:
IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'a')
Right:
ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false')
Next Block[5]
Block[4] - Block
Predecessors (2)
[1]
[2]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;')
Expression:
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true')
Left:
IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b')
Right:
ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true')
Next Block[5]
Block[5] - Exit
Predecessors (2)
[3]
[4]
Statements (0)
";
var expectedDiagnostics = DiagnosticDescription.None;
VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>(source, expectedGraph, expectedDiagnostics);
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// 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 Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Test.Utilities;
......@@ -3419,5 +3419,92 @@ void F(C x)
VerifyOperationTreeForTest<PrefixUnaryExpressionSyntax>(source, expectedOperationTree);
}
[Fact]
public void LogicalNotFlow_01()
{
string source = @"
class P
{
void M(bool a, bool b)
/*<bind>*/{
GetArray()[0] = !(a || b);
}/*</bind>*/
static bool[] GetArray() => null;
}
";
string expectedGraph = @"
Block[0] - Entry
Statements (0)
Next Block[1]
Block[1] - Block
Predecessors (1)
[0]
Statements (1)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean, IsImplicit) (Syntax: 'GetArray()[0]')
Left:
IFlowCaptureOperation: 0 (IsInitialization: True) (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'GetArray()[0]')
Right:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: System.Boolean) (Syntax: 'GetArray()[0]')
Array reference:
IInvocationOperation (System.Boolean[] P.GetArray()) (OperationKind.Invocation, Type: System.Boolean[]) (Syntax: 'GetArray()')
Instance Receiver:
null
Arguments(0)
Indices(1):
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0')
Jump if True to Block[3]
IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'a')
Next Block[2]
Block[2] - Block
Predecessors (1)
[1]
Statements (1)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean, IsImplicit) (Syntax: 'b')
Left:
IFlowCaptureOperation: 1 (IsInitialization: True) (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'b')
Right:
IUnaryOperation (UnaryOperatorKind.Not) (OperationKind.UnaryOperator, Type: System.Boolean, IsImplicit) (Syntax: 'b')
Operand:
IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b')
Next Block[4]
Block[3] - Block
Predecessors (1)
[1]
Statements (1)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean, Constant: False, IsImplicit) (Syntax: 'a')
Left:
IFlowCaptureOperation: 1 (IsInitialization: True) (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'a')
Right:
ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False, IsImplicit) (Syntax: 'a')
Next Block[4]
Block[4] - Block
Predecessors (2)
[2]
[3]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'GetArray()[ ... !(a || b);')
Expression:
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'GetArray()[ ... !(a || b)')
Left:
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'GetArray()[0]')
Right:
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: System.Boolean, IsImplicit) (Syntax: 'a || b')
Next Block[5]
Block[5] - Exit
Predecessors (1)
[4]
Statements (0)
";
var expectedDiagnostics = DiagnosticDescription.None;
VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>(source, expectedGraph, expectedDiagnostics);
}
}
}
......@@ -86,6 +86,14 @@ public IOperation GetOperation(SyntaxNode node, CancellationToken cancellationTo
protected abstract IOperation GetOperationCore(SyntaxNode node, CancellationToken cancellationToken);
/// <summary>
/// PROTOTYPE(dataflow): Add documentation. Also, we should guard this API with a feature flag before we merge it to master.
/// </summary>
public ImmutableArray<Operations.BasicBlock> GetControlFlowGraph(Operations.IBlockOperation body)
{
return Operations.ControlFlowGraphBuilder.Create(body);
}
/// <summary>
/// Returns true if this is a SemanticModel that ignores accessibility rules when answering semantic questions.
/// </summary>
......
......@@ -988,7 +988,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -1288,7 +1288,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -1499,7 +1499,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -1525,7 +1525,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -1551,7 +1551,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -2507,7 +2507,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -3329,7 +3329,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -3362,7 +3362,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -3825,7 +3825,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -3919,7 +3919,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -4012,7 +4012,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -4554,7 +4554,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -4585,7 +4585,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -4611,7 +4611,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -4995,7 +4995,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -6068,7 +6068,7 @@ public override IEnumerable<IOperation> Children
{
get
{
yield break;
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
......@@ -6547,4 +6547,32 @@ internal sealed partial class LazyTranslatedQueryExpression : BaseTranslatedQuer
}
protected override IOperation ExpressionImpl => _lazyExpression.Value;
}
internal sealed partial class FlowCapture : Operation, IFlowCaptureOperation
{
public FlowCapture(int id, SyntaxNode syntax, bool isInitialization, ITypeSymbol type, Optional<object> constantValue) :
base(OperationKind.FlowCapture, semanticModel: null, syntax, type, constantValue, isImplicit: true)
{
Id = id;
IsInitialization = isInitialization;
}
public int Id { get; }
public bool IsInitialization { get; }
public override IEnumerable<IOperation> Children
{
get
{
return Array.Empty<IOperation>();
}
}
public override void Accept(OperationVisitor visitor)
{
visitor.VisitFlowCapture(this);
}
public override TResult Accept<TArgument, TResult>(OperationVisitor<TArgument, TResult> visitor, TArgument argument)
{
return visitor.VisitFlowCapture(this, argument);
}
}
}
// 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.Collections.Immutable;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis.Operations
{
/// <summary>
/// PROTOTYPE(dataflow): Add documentation
/// </summary>
public enum BasicBlockKind
{
Entry,
Exit,
Block
}
/// <summary>
/// PROTOTYPE(dataflow): Add documentation
/// PROTOTYPE(dataflow): We need to figure out how to split it into a builder and
/// a public immutable type.
/// </summary>
public sealed class BasicBlock
{
private readonly ImmutableArray<IOperation>.Builder _statements;
private readonly ImmutableHashSet<BasicBlock>.Builder _predecessors;
public BasicBlock(BasicBlockKind kind)
{
Kind = kind;
_statements = ImmutableArray.CreateBuilder<IOperation>();
_predecessors = ImmutableHashSet.CreateBuilder<BasicBlock>();
}
public BasicBlockKind Kind { get; private set; }
public ImmutableArray<IOperation> Statements => _statements.ToImmutable();
/// <summary>
/// PROTOTYPE(dataflow): Tuple is temporary return type, we probably should use special structure instead.
/// </summary>
public (IOperation Condition, bool JumpIfTrue, BasicBlock Destination) Conditional { get; internal set; }
/// <summary>
/// PROTOTYPE(dataflow): During CR there was a suggestion to use different name - "Successor".
/// </summary>
public BasicBlock Next { get; internal set; }
public ImmutableHashSet<BasicBlock> Predecessors => _predecessors.ToImmutable();
internal void AddStatement(IOperation statement)
{
_statements.Add(statement);
}
internal void AddPredecessor(BasicBlock block)
{
_predecessors.Add(block);
}
internal void RemovePredecessor(BasicBlock block)
{
_predecessors.Remove(block);
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Operations
{
/// <summary>
/// Represents that an intermediate result is being captured, or the result of capturing is used later on.
/// PROTOTYPE(dataflow): Finalize the design how capturing/referencing intermediate results is represented.
/// </summary>
public interface IFlowCaptureOperation : IOperation
{
/// <summary>
/// An id used to match references to the same intermediate result.
/// </summary>
int Id { get; }
/// <summary>
/// If this is a place where the intermediate result is captured (vs. used), then
/// this property returns true.
/// PROTOTYPE(dataflow): Remove?
/// </summary>
bool IsInitialization { get; }
}
}
......@@ -99,7 +99,8 @@ protected void SetParentOperation(IOperation parent)
var result = Interlocked.CompareExchange(ref _parentDoNotAccessDirectly, parent, null);
// tree must belong to same semantic model if parent is given
Debug.Assert(parent == null || ((Operation)parent).SemanticModel == SemanticModel);
Debug.Assert(parent == null || ((Operation)parent).SemanticModel == SemanticModel ||
((Operation)parent).SemanticModel == null || SemanticModel == null);
// make sure given parent and one we already have is same if we have one already
Debug.Assert(result == null || result == parent);
......
......@@ -6,7 +6,7 @@
namespace Microsoft.CodeAnalysis.Operations
{
internal sealed class OperationCloner : OperationVisitor<object, IOperation>
internal class OperationCloner : OperationVisitor<object, IOperation>
{
private static readonly OperationCloner s_instance = new OperationCloner();
......@@ -18,7 +18,7 @@ internal sealed class OperationCloner : OperationVisitor<object, IOperation>
return s_instance.Visit(operation);
}
private OperationCloner()
protected OperationCloner()
{
}
......@@ -517,5 +517,10 @@ public override IOperation VisitRaiseEvent(IRaiseEventOperation operation, objec
{
return new RaiseEventStatement(Visit(operation.EventReference), VisitArray(operation.Arguments), ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit);
}
public override IOperation VisitFlowCapture(IFlowCaptureOperation operation, object argument)
{
return new FlowCapture(operation.Id, operation.Syntax, operation.IsInitialization, operation.Type, operation.ConstantValue);
}
}
}
......@@ -182,6 +182,8 @@ public enum OperationKind
/// <summary>Indicates an <see cref="IDeclarationPatternOperation"/>.</summary>
DeclarationPattern = 0x56,
FlowCapture = 0x57,
// /// <summary>Indicates an <see cref="IFixedOperation"/>.</summary>
// https://github.com/dotnet/roslyn/issues/21281
//Fixed = <TBD>,
......
......@@ -494,6 +494,11 @@ public virtual void VisitRaiseEvent(IRaiseEventOperation operation)
{
DefaultVisit(operation);
}
public virtual void VisitFlowCapture(IFlowCaptureOperation operation)
{
DefaultVisit(operation);
}
}
/// <summary>
......@@ -994,5 +999,10 @@ public virtual TResult VisitRaiseEvent(IRaiseEventOperation operation, TArgument
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitFlowCapture(IFlowCaptureOperation operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
}
}
......@@ -92,6 +92,7 @@ Microsoft.CodeAnalysis.OperationKind.EventReference = 30 -> Microsoft.CodeAnalys
Microsoft.CodeAnalysis.OperationKind.ExpressionStatement = 15 -> Microsoft.CodeAnalysis.OperationKind
Microsoft.CodeAnalysis.OperationKind.FieldInitializer = 72 -> Microsoft.CodeAnalysis.OperationKind
Microsoft.CodeAnalysis.OperationKind.FieldReference = 26 -> Microsoft.CodeAnalysis.OperationKind
Microsoft.CodeAnalysis.OperationKind.FlowCapture = 87 -> Microsoft.CodeAnalysis.OperationKind
Microsoft.CodeAnalysis.OperationKind.Increment = 66 -> Microsoft.CodeAnalysis.OperationKind
Microsoft.CodeAnalysis.OperationKind.InstanceReference = 39 -> Microsoft.CodeAnalysis.OperationKind
Microsoft.CodeAnalysis.OperationKind.InterpolatedString = 48 -> Microsoft.CodeAnalysis.OperationKind
......@@ -145,6 +146,17 @@ Microsoft.CodeAnalysis.Operations.ArgumentKind.DefaultValue = 3 -> Microsoft.Cod
Microsoft.CodeAnalysis.Operations.ArgumentKind.Explicit = 1 -> Microsoft.CodeAnalysis.Operations.ArgumentKind
Microsoft.CodeAnalysis.Operations.ArgumentKind.None = 0 -> Microsoft.CodeAnalysis.Operations.ArgumentKind
Microsoft.CodeAnalysis.Operations.ArgumentKind.ParamArray = 2 -> Microsoft.CodeAnalysis.Operations.ArgumentKind
Microsoft.CodeAnalysis.Operations.BasicBlock
Microsoft.CodeAnalysis.Operations.BasicBlock.BasicBlock(Microsoft.CodeAnalysis.Operations.BasicBlockKind kind) -> void
Microsoft.CodeAnalysis.Operations.BasicBlock.Conditional.get -> (Microsoft.CodeAnalysis.IOperation Condition, bool JumpIfTrue, Microsoft.CodeAnalysis.Operations.BasicBlock Destination)
Microsoft.CodeAnalysis.Operations.BasicBlock.Kind.get -> Microsoft.CodeAnalysis.Operations.BasicBlockKind
Microsoft.CodeAnalysis.Operations.BasicBlock.Next.get -> Microsoft.CodeAnalysis.Operations.BasicBlock
Microsoft.CodeAnalysis.Operations.BasicBlock.Predecessors.get -> System.Collections.Immutable.ImmutableHashSet<Microsoft.CodeAnalysis.Operations.BasicBlock>
Microsoft.CodeAnalysis.Operations.BasicBlock.Statements.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.IOperation>
Microsoft.CodeAnalysis.Operations.BasicBlockKind
Microsoft.CodeAnalysis.Operations.BasicBlockKind.Block = 2 -> Microsoft.CodeAnalysis.Operations.BasicBlockKind
Microsoft.CodeAnalysis.Operations.BasicBlockKind.Entry = 0 -> Microsoft.CodeAnalysis.Operations.BasicBlockKind
Microsoft.CodeAnalysis.Operations.BasicBlockKind.Exit = 1 -> Microsoft.CodeAnalysis.Operations.BasicBlockKind
Microsoft.CodeAnalysis.Operations.BinaryOperatorKind
Microsoft.CodeAnalysis.Operations.BinaryOperatorKind.Add = 1 -> Microsoft.CodeAnalysis.Operations.BinaryOperatorKind
Microsoft.CodeAnalysis.Operations.BinaryOperatorKind.And = 10 -> Microsoft.CodeAnalysis.Operations.BinaryOperatorKind
......@@ -307,6 +319,9 @@ Microsoft.CodeAnalysis.Operations.IFieldInitializerOperation.InitializedFields.g
Microsoft.CodeAnalysis.Operations.IFieldReferenceOperation
Microsoft.CodeAnalysis.Operations.IFieldReferenceOperation.Field.get -> Microsoft.CodeAnalysis.IFieldSymbol
Microsoft.CodeAnalysis.Operations.IFieldReferenceOperation.IsDeclaration.get -> bool
Microsoft.CodeAnalysis.Operations.IFlowCaptureOperation
Microsoft.CodeAnalysis.Operations.IFlowCaptureOperation.Id.get -> int
Microsoft.CodeAnalysis.Operations.IFlowCaptureOperation.IsInitialization.get -> bool
Microsoft.CodeAnalysis.Operations.IForEachLoopOperation
Microsoft.CodeAnalysis.Operations.IForEachLoopOperation.Collection.get -> Microsoft.CodeAnalysis.IOperation
Microsoft.CodeAnalysis.Operations.IForEachLoopOperation.LoopControlVariable.get -> Microsoft.CodeAnalysis.IOperation
......@@ -489,6 +504,7 @@ Microsoft.CodeAnalysis.Operations.UnaryOperatorKind.Plus = 3 -> Microsoft.CodeAn
Microsoft.CodeAnalysis.Operations.UnaryOperatorKind.True = 5 -> Microsoft.CodeAnalysis.Operations.UnaryOperatorKind
Microsoft.CodeAnalysis.RefKind.In = 3 -> Microsoft.CodeAnalysis.RefKind
Microsoft.CodeAnalysis.RefKind.RefReadOnly = 3 -> Microsoft.CodeAnalysis.RefKind
Microsoft.CodeAnalysis.SemanticModel.GetControlFlowGraph(Microsoft.CodeAnalysis.Operations.IBlockOperation body) -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Operations.BasicBlock>
Microsoft.CodeAnalysis.SemanticModel.GetOperation(Microsoft.CodeAnalysis.SyntaxNode node, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.IOperation
Microsoft.CodeAnalysis.SyntaxList<TNode>.SyntaxList(System.Collections.Generic.IEnumerable<TNode> nodes) -> void
Microsoft.CodeAnalysis.SyntaxList<TNode>.SyntaxList(TNode node) -> void
......@@ -568,6 +584,7 @@ virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitEventReference(M
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitExpressionStatement(Microsoft.CodeAnalysis.Operations.IExpressionStatementOperation operation) -> void
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitFieldInitializer(Microsoft.CodeAnalysis.Operations.IFieldInitializerOperation operation) -> void
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitFieldReference(Microsoft.CodeAnalysis.Operations.IFieldReferenceOperation operation) -> void
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitFlowCapture(Microsoft.CodeAnalysis.Operations.IFlowCaptureOperation operation) -> void
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitForEachLoop(Microsoft.CodeAnalysis.Operations.IForEachLoopOperation operation) -> void
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitForLoop(Microsoft.CodeAnalysis.Operations.IForLoopOperation operation) -> void
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitForToLoop(Microsoft.CodeAnalysis.Operations.IForToLoopOperation operation) -> void
......@@ -659,6 +676,7 @@ virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.V
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitExpressionStatement(Microsoft.CodeAnalysis.Operations.IExpressionStatementOperation operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitFieldInitializer(Microsoft.CodeAnalysis.Operations.IFieldInitializerOperation operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitFieldReference(Microsoft.CodeAnalysis.Operations.IFieldReferenceOperation operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitFlowCapture(Microsoft.CodeAnalysis.Operations.IFlowCaptureOperation operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitForEachLoop(Microsoft.CodeAnalysis.Operations.IForEachLoopOperation operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitForLoop(Microsoft.CodeAnalysis.Operations.IForLoopOperation operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Operations.OperationVisitor<TArgument, TResult>.VisitForToLoop(Microsoft.CodeAnalysis.Operations.IForToLoopOperation operation, TArgument argument) -> TResult
......
......@@ -1125,6 +1125,64 @@ protected static void VerifyOperationTreeForTest<TSyntaxNode>(CSharpCompilation
additionalOperationTreeVerifier?.Invoke(actualOperation, compilation, syntaxNode);
}
protected static void VerifyFlowGraphForTest<TSyntaxNode>(CSharpCompilation compilation, string expectedFlowGraph)
where TSyntaxNode : SyntaxNode
{
var tree = compilation.SyntaxTrees[0];
var model = compilation.GetSemanticModel(tree);
SyntaxNode syntaxNode = GetSyntaxNodeOfTypeForBinding<TSyntaxNode>(GetSyntaxNodeList(tree));
ImmutableArray<Operations.BasicBlock> graph = model.GetControlFlowGraph((Operations.IBlockOperation)model.GetOperation(syntaxNode));
var map = new Dictionary<Operations.BasicBlock, int>();
for (int i = 0; i < graph.Length; i++)
{
map.Add(graph[i], i);
}
var stringBuilder = PooledObjects.PooledStringBuilder.GetInstance();
for (int i = 0; i < graph.Length; i++)
{
var block = graph[i];
stringBuilder.Builder.AppendLine($"Block[{i}] - {block.Kind}");
var predecessors = block.Predecessors;
if (!predecessors.IsEmpty)
{
stringBuilder.Builder.AppendLine($" Predecessors ({predecessors.Count})");
foreach (int j in predecessors.Select(b => map[b]).OrderBy(ii => ii))
{
stringBuilder.Builder.AppendLine($" [{j}]");
}
}
var statements = block.Statements;
stringBuilder.Builder.AppendLine($" Statements ({statements.Length})");
foreach (var statement in statements)
{
stringBuilder.Builder.AppendLine(OperationTreeVerifier.GetOperationTree(compilation, statement, initialIndent: 8));
}
if (block.Conditional.Condition != null)
{
Assert.True(map.TryGetValue(block.Conditional.Destination, out int index));
stringBuilder.Builder.AppendLine($" Jump if {block.Conditional.JumpIfTrue} to Block[{index}]");
stringBuilder.Builder.AppendLine(OperationTreeVerifier.GetOperationTree(compilation, block.Conditional.Condition, initialIndent: 8));
}
if (block.Next != null)
{
Assert.True(map.TryGetValue(block.Next, out var index));
stringBuilder.Builder.AppendLine($" Next Block[{index}]");
}
}
var actualFlowGraph = stringBuilder.ToStringAndFree();
OperationTreeVerifier.Verify(expectedFlowGraph, actualFlowGraph);
}
protected static void VerifyOperationTreeForTest<TSyntaxNode>(
string testSrc,
string expectedOperationTree,
......@@ -1149,6 +1207,17 @@ protected static void VerifyOperationTreeForTest<TSyntaxNode>(CSharpCompilation
VerifyOperationTreeForTest<TSyntaxNode>(compilation, expectedOperationTree, additionalOperationTreeVerifier);
}
protected static void VerifyFlowGraphAndDiagnosticsForTest<TSyntaxNode>(
CSharpCompilation compilation,
string expectedFlowGraph,
DiagnosticDescription[] expectedDiagnostics)
where TSyntaxNode : SyntaxNode
{
var actualDiagnostics = compilation.GetDiagnostics().Where(d => d.Severity != DiagnosticSeverity.Hidden);
actualDiagnostics.Verify(expectedDiagnostics);
VerifyFlowGraphForTest<TSyntaxNode>(compilation, expectedFlowGraph);
}
private static readonly MetadataReference[] s_defaultOperationReferences = new[] { SystemRef, SystemCoreRef, ValueTupleRef, SystemRuntimeFacadeRef };
private static readonly MetadataReference[] s_latestOperationReferences = new[] { SystemRef, SystemCoreRef, ValueTupleRef, SystemRuntimeFacadeRef, MscorlibRef_v46 };
......@@ -1169,6 +1238,22 @@ protected static void VerifyOperationTreeForTest<TSyntaxNode>(CSharpCompilation
VerifyOperationTreeAndDiagnosticsForTest<TSyntaxNode>(compilation, expectedOperationTree, expectedDiagnostics, additionalOperationTreeVerifier);
}
protected static void VerifyFlowGraphAndDiagnosticsForTest<TSyntaxNode>(
string testSrc,
string expectedFlowGraph,
DiagnosticDescription[] expectedDiagnostics,
CSharpCompilationOptions compilationOptions = null,
CSharpParseOptions parseOptions = null,
MetadataReference[] additionalReferences = null,
bool useLatestFrameworkReferences = false)
where TSyntaxNode : SyntaxNode
{
var defaultRefs = useLatestFrameworkReferences ? s_latestOperationReferences : s_defaultOperationReferences;
var references = additionalReferences == null ? defaultRefs : additionalReferences.Concat(defaultRefs);
var compilation = CreateStandardCompilation(testSrc, references, sourceFileName: "file.cs", options: compilationOptions ?? TestOptions.ReleaseDll, parseOptions: parseOptions);
VerifyFlowGraphAndDiagnosticsForTest<TSyntaxNode>(compilation, expectedFlowGraph, expectedDiagnostics);
}
protected static MetadataReference VerifyOperationTreeAndDiagnosticsForTestWithIL<TSyntaxNode>(string testSrc,
string ilSource,
string expectedOperationTree,
......
......@@ -778,6 +778,17 @@ public override void VisitLocalReference(ILocalReferenceOperation operation)
LogCommonPropertiesAndNewLine(operation);
}
public override void VisitFlowCapture(IFlowCaptureOperation operation)
{
LogString(nameof(IFlowCaptureOperation));
LogString($": {operation.Id}");
if (operation.IsInitialization)
{
LogString($" (IsInitialization: {operation.IsInitialization})");
}
LogCommonPropertiesAndNewLine(operation);
}
public override void VisitParameterReference(IParameterReferenceOperation operation)
{
LogString(nameof(IParameterReferenceOperation));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册