diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 6fdf3f6d9cc72e2759bddc5c485dffe39f0bcda7..81ae9fd81d5a18a578d9feb8c9f94ff92f810aa0 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -1277,6 +1277,11 @@ internal Symbol GetSpecialTypeMember(SpecialMember specialMember) return Assembly.GetSpecialTypeMember(specialMember); } + internal override ISymbol CommonGetSpecialTypeMember(SpecialMember specialMember) + { + return GetSpecialTypeMember(specialMember); + } + internal TypeSymbol GetTypeByReflectionType(Type type, DiagnosticBag diagnostics) { var result = Assembly.GetTypeByReflectionType(type, includeReferences: true); diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ILockStatement.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ILockStatement.cs index 07bded68413a0b96f0af4fa2c8331166799b0ade..ee0378ff8f27db642dea8b2ae1e502c583dd0a30 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ILockStatement.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ILockStatement.cs @@ -1,4 +1,4 @@ -// 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; @@ -373,5 +373,428 @@ public void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void LockFlow_01() + { + string source = @" +class P +{ + void M(object source1, object source2, object source3) +/**/{ + lock (source1 ?? source2) + source3?.ToString(); + }/**/ +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] +Block[B1] - Block + Predecessors: [B0] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'source1') + Value: + IParameterReferenceOperation: source1 (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'source1') + + Jump if True (Regular) to Block[B3] + IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'source1') + Operand: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'source1') + + Next (Regular) Block[B2] +Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'source1') + Value: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'source1') + + Next (Regular) Block[B4] +Block[B3] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'source2') + Value: + IParameterReferenceOperation: source2 (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'source2') + + Next (Regular) Block[B4] +Block[B4] - Block + Predecessors: [B2] [B3] + Statements (1) + IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'source1 ?? source2') + Value: + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'source1 ?? source2') + + Next (Regular) Block[B5] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B5] - Block + Predecessors: [B4] + Statements (2) + IInvocationOperation (void System.Threading.Monitor.Enter(System.Object obj, ref System.Boolean lockTaken)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'source1 ?? source2') + Instance Receiver: + null + Arguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: obj) (OperationKind.Argument, Type: null, Constant: null, IsImplicit) (Syntax: 'source1 ?? source2') + IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'source1 ?? source2') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: lockTaken) (OperationKind.Argument, Type: null, Constant: null, IsImplicit) (Syntax: 'source1 ?? source2') + IFlowCaptureReferenceOperation: 3 (OperationKind.FlowCaptureReference, Type: System.Boolean, IsImplicit) (Syntax: 'source1 ?? source2') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + + IFlowCaptureOperation: 4 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'source3') + Value: + IParameterReferenceOperation: source3 (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'source3') + + Jump if True (Regular) to Block[B10] + IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'source3') + Operand: + IFlowCaptureReferenceOperation: 4 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'source3') + Finalizing: {R3} + Leaving: {R2} {R1} + + Next (Regular) Block[B6] + Block[B6] - Block + Predecessors: [B5] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'source3?.ToString();') + Expression: + IInvocationOperation (virtual System.String System.Object.ToString()) (OperationKind.Invocation, Type: System.String) (Syntax: '.ToString()') + Instance Receiver: + IFlowCaptureReferenceOperation: 4 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'source3') + Arguments(0) + + Next (Regular) Block[B10] + Finalizing: {R3} + Leaving: {R2} {R1} +} +.finally {R3} +{ + Block[B7] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B9] + IFlowCaptureReferenceOperation: 3 (OperationKind.FlowCaptureReference, Type: System.Boolean, IsImplicit) (Syntax: 'source1 ?? source2') + + Next (Regular) Block[B8] + Block[B8] - Block + Predecessors: [B7] + Statements (1) + IInvocationOperation (void System.Threading.Monitor.Exit(System.Object obj)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'source1 ?? source2') + Instance Receiver: + null + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: obj) (OperationKind.Argument, Type: null, Constant: null, IsImplicit) (Syntax: 'source1 ?? source2') + IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'source1 ?? source2') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + + Next (Regular) Block[B9] + Block[B9] - Block + Predecessors: [B7] [B8] + Statements (0) + Next (StructuredExceptionHandling) Block[null] +} + +Block[B10] - Exit + Predecessors: [B5] [B6] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void LockFlow_02() + { + string source = @" +class P +{ + void M(string input1, bool input2) +/**/{ + lock (input1) + input2 = true; + }/**/ +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] +Block[B1] - Block + Predecessors: [B0] + Statements (2) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'input1') + Value: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'input1') + Conversion: CommonConversion (Exists: True) + Operand: + IParameterReferenceOperation: input1 (OperationKind.ParameterReference, Type: System.String) (Syntax: 'input1') + + IInvocationOperation (void System.Threading.Monitor.Enter(System.Object obj)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'input1') + Instance Receiver: + null + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: obj) (OperationKind.Argument, Type: null, Constant: null, IsImplicit) (Syntax: 'input1') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'input1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + + Next (Regular) Block[B2] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'input2 = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'input2 = true') + Left: + IParameterReferenceOperation: input2 (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'input2') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B4] + Finalizing: {R3} + Leaving: {R2} {R1} +} +.finally {R3} +{ + Block[B3] - Block + Predecessors (0) + Statements (1) + IInvocationOperation (void System.Threading.Monitor.Exit(System.Object obj)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'input1') + Instance Receiver: + null + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: obj) (OperationKind.Argument, Type: null, Constant: null, IsImplicit) (Syntax: 'input1') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'input1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + + Next (StructuredExceptionHandling) Block[null] +} + +Block[B4] - Exit + Predecessors: [B2] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics, + targetFramework: Roslyn.Test.Utilities.TargetFramework.Empty, + references: new[] { MscorlibRef_v20}); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void LockFlow_03() + { + string source = @" +class P +{ + void M(int input1, bool input2) +/**/{ + lock (input1) + input2 = true; + }/**/ +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] +Block[B1] - Block + Predecessors: [B0] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'input1') + Value: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsInvalid, IsImplicit) (Syntax: 'input1') + Conversion: CommonConversion (Exists: True) + Operand: + IParameterReferenceOperation: input1 (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'input1') + + Next (Regular) Block[B2] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B2] - Block + Predecessors: [B1] + Statements (2) + IInvocationOperation (void System.Threading.Monitor.Enter(System.Object obj, ref System.Boolean lockTaken)) (OperationKind.Invocation, Type: System.Void, IsInvalid, IsImplicit) (Syntax: 'input1') + Instance Receiver: + null + Arguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: obj) (OperationKind.Argument, Type: null, Constant: null, IsInvalid, IsImplicit) (Syntax: 'input1') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Object, IsInvalid, IsImplicit) (Syntax: 'input1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: lockTaken) (OperationKind.Argument, Type: null, Constant: null, IsInvalid, IsImplicit) (Syntax: 'input1') + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Boolean, IsInvalid, IsImplicit) (Syntax: 'input1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'input2 = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'input2 = true') + Left: + IParameterReferenceOperation: input2 (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'input2') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B6] + Finalizing: {R3} + Leaving: {R2} {R1} +} +.finally {R3} +{ + Block[B3] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B5] + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Boolean, IsInvalid, IsImplicit) (Syntax: 'input1') + + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B3] + Statements (1) + IInvocationOperation (void System.Threading.Monitor.Exit(System.Object obj)) (OperationKind.Invocation, Type: System.Void, IsInvalid, IsImplicit) (Syntax: 'input1') + Instance Receiver: + null + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: obj) (OperationKind.Argument, Type: null, Constant: null, IsInvalid, IsImplicit) (Syntax: 'input1') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Object, IsInvalid, IsImplicit) (Syntax: 'input1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + + Next (Regular) Block[B5] + Block[B5] - Block + Predecessors: [B3] [B4] + Statements (0) + Next (StructuredExceptionHandling) Block[null] +} + +Block[B6] - Exit + Predecessors: [B2] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(6,15): error CS0185: 'int' is not a reference type as required by the lock statement + // lock (input1) + Diagnostic(ErrorCode.ERR_LockNeedsReference, "input1").WithArguments("int").WithLocation(6, 15) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void LockFlow_04() + { + string source = @" +class P +{ + void M(bool input2) +/**/{ + lock (null) + input2 = true; + }/**/ +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] +Block[B1] - Block + Predecessors: [B0] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'null') + Value: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + + Next (Regular) Block[B2] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B2] - Block + Predecessors: [B1] + Statements (2) + IInvocationOperation (void System.Threading.Monitor.Enter(System.Object obj, ref System.Boolean lockTaken)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'null') + Instance Receiver: + null + Arguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: obj) (OperationKind.Argument, Type: null, Constant: null, IsImplicit) (Syntax: 'null') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: lockTaken) (OperationKind.Argument, Type: null, Constant: null, IsImplicit) (Syntax: 'null') + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Boolean, IsImplicit) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'input2 = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'input2 = true') + Left: + IParameterReferenceOperation: input2 (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'input2') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B6] + Finalizing: {R3} + Leaving: {R2} {R1} +} +.finally {R3} +{ + Block[B3] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B5] + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Boolean, IsImplicit) (Syntax: 'null') + + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B3] + Statements (1) + IInvocationOperation (void System.Threading.Monitor.Exit(System.Object obj)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'null') + Instance Receiver: + null + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: obj) (OperationKind.Argument, Type: null, Constant: null, IsImplicit) (Syntax: 'null') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + + Next (Regular) Block[B5] + Block[B5] - Block + Predecessors: [B3] [B4] + Statements (0) + Next (StructuredExceptionHandling) Block[null] +} + +Block[B6] - Exit + Predecessors: [B2] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IUsingStatement.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IUsingStatement.cs index 242ee66e261b412904f7a67dcc7ca234ee0e2e11..bdac3e218102f1da634b91943cc5bdad64b45ad6 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IUsingStatement.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IUsingStatement.cs @@ -2372,5 +2372,101 @@ void M(dynamic input1, dynamic input2, bool b) VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void UsingFlow_13() + { + string source = @" +class P +{ + void M(System.IDisposable input, object o) +/**/{ + using (input) + { + o?.ToString(); + } + }/**/ +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] +Block[B1] - Block + Predecessors: [B0] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'input') + Value: + IParameterReferenceOperation: input (OperationKind.ParameterReference, Type: System.IDisposable) (Syntax: 'input') + + Next (Regular) Block[B2] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'o') + Value: + IParameterReferenceOperation: o (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'o') + + Jump if True (Regular) to Block[B7] + IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'o') + Operand: + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'o') + Finalizing: {R3} + Leaving: {R2} {R1} + + Next (Regular) Block[B3] + Block[B3] - Block + Predecessors: [B2] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'o?.ToString();') + Expression: + IInvocationOperation (virtual System.String System.Object.ToString()) (OperationKind.Invocation, Type: System.String) (Syntax: '.ToString()') + Instance Receiver: + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'o') + Arguments(0) + + Next (Regular) Block[B7] + Finalizing: {R3} + Leaving: {R2} {R1} +} +.finally {R3} +{ + Block[B4] - Block + Predecessors (0) + Statements (0) + Jump if True (Regular) to Block[B6] + IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'input') + Operand: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.IDisposable, IsImplicit) (Syntax: 'input') + + Next (Regular) Block[B5] + Block[B5] - Block + Predecessors: [B4] + Statements (1) + IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'input') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.IDisposable, IsImplicit) (Syntax: 'input') + Arguments(0) + + Next (Regular) Block[B6] + Block[B6] - Block + Predecessors: [B4] [B5] + Statements (0) + Next (StructuredExceptionHandling) Block[null] +} + +Block[B7] - Exit + Predecessors: [B2] [B3] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } } } diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index 2d792a5441350e8ca4753b12ce80cf698708a780..de269ebbba778c4e1364ad3b5b3fc814a4ff524c 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -791,6 +791,11 @@ public INamedTypeSymbol GetSpecialType(SpecialType specialType) return CommonGetSpecialType(specialType); } + /// + /// Get the symbol for the predefined type member from the COR Library referenced by this compilation. + /// + internal abstract ISymbol CommonGetSpecialTypeMember(SpecialMember specialMember); + /// /// Returns true if the type is System.Type. /// @@ -798,6 +803,9 @@ public INamedTypeSymbol GetSpecialType(SpecialType specialType) protected abstract INamedTypeSymbol CommonGetSpecialType(SpecialType specialType); + /// + /// Lookup member declaration in well known type used by this Compilation. + /// internal abstract ISymbol CommonGetWellKnownTypeMember(WellKnownMember member); /// diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 188a921589ce1a6709382c94b869c9fb377dfa7f..8e79746c52416551f9b65b4fc7989d4c51e4e894 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -1612,9 +1612,10 @@ public override IOperation VisitCoalesce(ICoalesceOperation operation, int? capt var whenNull = new BasicBlock(BasicBlockKind.Block); Optional constantValue = operation.Value.ConstantValue; + Compilation compilation = ((Operation)operation).SemanticModel.Compilation; LinkBlocks(CurrentBasicBlock, - (Operation.SetParentOperation(MakeIsNullOperation(((Operation)operation).SemanticModel, - new FlowCaptureReference(testExpressionCaptureId, valueSyntax, valueTypeOpt, constantValue)), + (Operation.SetParentOperation(MakeIsNullOperation(new FlowCaptureReference(testExpressionCaptureId, valueSyntax, valueTypeOpt, constantValue), + compilation), null), true, RegularBranch(whenNull))); @@ -1631,7 +1632,7 @@ public override IOperation VisitCoalesce(ICoalesceOperation operation, int? capt if (ITypeSymbolHelpers.IsNullableType(valueTypeOpt) && (!testConversion.IsIdentity || !ITypeSymbolHelpers.IsNullableType(operation.Type))) { - possiblyUnwrappedValue = TryUnwrapNullableValue(capturedValue); + possiblyUnwrappedValue = TryUnwrapNullableValue(capturedValue, compilation); // PROTOTYPE(dataflow): The scenario with missing GetValueOrDefault is not covered by unit-tests. } else @@ -1688,28 +1689,29 @@ private static IOperation MakeInvalidOperation(ITypeSymbol type, IOperation chil constantValue: default, isImplicit: true); } - private static IsNullOperation MakeIsNullOperation(SemanticModel semanticModel, IOperation operand) + private static IsNullOperation MakeIsNullOperation(IOperation operand, Compilation compilation) { Optional constantValue = operand.ConstantValue; return new IsNullOperation(operand.Syntax, operand, - semanticModel.Compilation.GetSpecialType(SpecialType.System_Boolean), + compilation.GetSpecialType(SpecialType.System_Boolean), constantValue.HasValue ? new Optional(constantValue.Value == null) : default); } - private static IOperation TryUnwrapNullableValue(IOperation value) + private static IOperation TryUnwrapNullableValue(IOperation value, Compilation compilation) { ITypeSymbol valueType = value.Type; Debug.Assert(ITypeSymbolHelpers.IsNullableType(valueType)); - foreach (ISymbol candidate in valueType.GetMembers("GetValueOrDefault")) + var method = (IMethodSymbol)compilation.CommonGetSpecialTypeMember(SpecialMember.System_Nullable_T_GetValueOrDefault); + + if (method != null) { - if (candidate.Kind == SymbolKind.Method && !candidate.IsStatic && candidate.DeclaredAccessibility == Accessibility.Public) + foreach (ISymbol candidate in valueType.GetMembers(method.Name)) { - var method = (IMethodSymbol)candidate; - if (method.Parameters.Length == 0 && method.RefKind == RefKind.None && - method.OriginalDefinition.ReturnType.Equals(((INamedTypeSymbol)valueType).OriginalDefinition.TypeParameters[0])) + if (candidate.OriginalDefinition.Equals(method)) { + method = (IMethodSymbol)candidate; return new InvocationExpression(method, value, isVirtual: false, ImmutableArray.Empty, semanticModel: null, value.Syntax, method.ReturnType, constantValue: default, isImplicit: true); @@ -1733,6 +1735,7 @@ public override IOperation VisitConditionalAccess(IConditionalAccessOperation op var whenNull = new BasicBlock(BasicBlockKind.Block); + Compilation compilation = ((Operation)operation).SemanticModel.Compilation; IConditionalAccessOperation currentConditionalAccess = operation; IOperation testExpression; @@ -1746,8 +1749,8 @@ public override IOperation VisitConditionalAccess(IConditionalAccessOperation op Optional constantValue = testExpression.ConstantValue; LinkBlocks(CurrentBasicBlock, - (Operation.SetParentOperation(MakeIsNullOperation(((Operation)operation).SemanticModel, - new FlowCaptureReference(testExpressionCaptureId, testExpressionSyntax, testExpressionType, constantValue)), + (Operation.SetParentOperation(MakeIsNullOperation(new FlowCaptureReference(testExpressionCaptureId, testExpressionSyntax, testExpressionType, constantValue), + compilation), null), true, RegularBranch(whenNull))); @@ -1757,7 +1760,7 @@ public override IOperation VisitConditionalAccess(IConditionalAccessOperation op if (ITypeSymbolHelpers.IsNullableType(testExpressionType)) { - receiver = TryUnwrapNullableValue(receiver) ?? + receiver = TryUnwrapNullableValue(receiver, compilation) ?? // PROTOTYPE(dataflow): The scenario with missing GetValueOrDefault is not covered by unit-tests. MakeInvalidOperation(((INamedTypeSymbol)testExpressionType).TypeArguments[0], receiver); } @@ -1773,68 +1776,97 @@ public override IOperation VisitConditionalAccess(IConditionalAccessOperation op currentConditionalAccess = (IConditionalAccessOperation)currentConditionalAccess.WhenNotNull; } - int resultCaptureId = captureIdForResult ?? _availableCaptureId++; - - if (ITypeSymbolHelpers.IsNullableType(operation.Type) && !ITypeSymbolHelpers.IsNullableType(currentConditionalAccess.WhenNotNull.Type)) + // Avoid creation of default values and FlowCapture for conditional access on a statement level. + if (_currentStatement == operation || + (_currentStatement == operation.Parent && _currentStatement?.Kind == OperationKind.ExpressionStatement)) { - IOperation access = Visit(currentConditionalAccess.WhenNotNull); - AddStatement(new FlowCapture(resultCaptureId, currentConditionalAccess.WhenNotNull.Syntax, - TryMakeNullableValue((INamedTypeSymbol)operation.Type, access) ?? - // PROTOTYPE(dataflow): The scenario with missing constructor is not covered by unit-tests. - MakeInvalidOperation(operation.Type, access))); + Debug.Assert(captureIdForResult == null); + + IOperation result = Visit(currentConditionalAccess.WhenNotNull); + Debug.Assert(_currentConditionalAccessInstance == null); + + if (_currentStatement != operation) + { + var expressionStatement = (IExpressionStatementOperation)_currentStatement; + result = new ExpressionStatement(result, semanticModel: null, expressionStatement.Syntax, + expressionStatement.Type, expressionStatement.ConstantValue, + expressionStatement.IsImplicit); + } + + AddStatement(result); + AppendNewBlock(whenNull); + return null; } else { - VisitAndCapture(currentConditionalAccess.WhenNotNull, resultCaptureId); - } + int resultCaptureId = captureIdForResult ?? _availableCaptureId++; + + if (ITypeSymbolHelpers.IsNullableType(operation.Type) && !ITypeSymbolHelpers.IsNullableType(currentConditionalAccess.WhenNotNull.Type)) + { + IOperation access = Visit(currentConditionalAccess.WhenNotNull); + AddStatement(new FlowCapture(resultCaptureId, currentConditionalAccess.WhenNotNull.Syntax, + TryMakeNullableValue((INamedTypeSymbol)operation.Type, access, compilation) ?? + // PROTOTYPE(dataflow): The scenario with missing constructor is not covered by unit-tests. + MakeInvalidOperation(operation.Type, access))); + } + else + { + VisitAndCapture(currentConditionalAccess.WhenNotNull, resultCaptureId); + } - Debug.Assert(_currentConditionalAccessInstance == null); + Debug.Assert(_currentConditionalAccessInstance == null); - var afterAccess = new BasicBlock(BasicBlockKind.Block); - LinkBlocks(CurrentBasicBlock, afterAccess); - _currentBasicBlock = null; + var afterAccess = new BasicBlock(BasicBlockKind.Block); + LinkBlocks(CurrentBasicBlock, afterAccess); + _currentBasicBlock = null; - AppendNewBlock(whenNull); + AppendNewBlock(whenNull); - SyntaxNode defaultValueSyntax = (operation.Operation == testExpression ? testExpression : operation).Syntax; + SyntaxNode defaultValueSyntax = (operation.Operation == testExpression ? testExpression : operation).Syntax; - AddStatement(new FlowCapture(resultCaptureId, - defaultValueSyntax, - new DefaultValueExpression(semanticModel: null, defaultValueSyntax, operation.Type, - (operation.Type.IsReferenceType && !ITypeSymbolHelpers.IsNullableType(operation.Type)) ? - new Optional(null) : default, - isImplicit: true))); + AddStatement(new FlowCapture(resultCaptureId, + defaultValueSyntax, + new DefaultValueExpression(semanticModel: null, defaultValueSyntax, operation.Type, + (operation.Type.IsReferenceType && !ITypeSymbolHelpers.IsNullableType(operation.Type)) ? + new Optional(null) : default, + isImplicit: true))); - AppendNewBlock(afterAccess); + AppendNewBlock(afterAccess); - return new FlowCaptureReference(resultCaptureId, operation.Syntax, operation.Type, operation.ConstantValue); + return new FlowCaptureReference(resultCaptureId, operation.Syntax, operation.Type, operation.ConstantValue); + } } - private static IOperation TryMakeNullableValue(INamedTypeSymbol type, IOperation underlyingValue) + private static IOperation TryMakeNullableValue(INamedTypeSymbol type, IOperation underlyingValue, Compilation compilation) { Debug.Assert(ITypeSymbolHelpers.IsNullableType(type)); - foreach (IMethodSymbol method in type.InstanceConstructors) - { - if (method.DeclaredAccessibility == Accessibility.Public && method.Parameters.Length == 1 && - method.OriginalDefinition.Parameters[0].Type.Equals(type.OriginalDefinition.TypeParameters[0])) - { - return new ObjectCreationExpression(method, initializer: null, - ImmutableArray.Create( - new ArgumentOperation(underlyingValue, - ArgumentKind.Explicit, - method.Parameters[0], - inConversionOpt: null, - outConversionOpt: null, - semanticModel: null, - underlyingValue.Syntax, - constantValue: null, - isImplicit: true)), - semanticModel: null, - underlyingValue.Syntax, - type, - constantValue: null, - isImplicit: true); + var method = (IMethodSymbol)compilation.CommonGetSpecialTypeMember(SpecialMember.System_Nullable_T__ctor); + + if (method != null) + { + foreach (ISymbol candidate in type.InstanceConstructors) + { + if (candidate.OriginalDefinition.Equals(method)) + { + method = (IMethodSymbol)candidate; + return new ObjectCreationExpression(method, initializer: null, + ImmutableArray.Create( + new ArgumentOperation(underlyingValue, + ArgumentKind.Explicit, + method.Parameters[0], + inConversionOpt: null, + outConversionOpt: null, + semanticModel: null, + underlyingValue.Syntax, + constantValue: null, + isImplicit: true)), + semanticModel: null, + underlyingValue.Syntax, + type, + constantValue: null, + isImplicit: true); + } } } @@ -1849,6 +1881,21 @@ public override IOperation VisitConditionalAccessInstance(IConditionalAccessInst return result; } + public override IOperation VisitExpressionStatement(IExpressionStatementOperation operation, int? captureIdForResult) + { + Debug.Assert(_currentStatement == operation); + + IOperation underlying = Visit(operation.Operation); + + if (underlying == null) + { + Debug.Assert(operation.Operation.Kind == OperationKind.ConditionalAccess); + return null; + } + + return new ExpressionStatement(underlying, semanticModel: null, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); + } + public override IOperation VisitWhileLoop(IWhileLoopOperation operation, int? captureIdForResult) { Debug.Assert(_currentStatement == operation); @@ -2197,8 +2244,8 @@ public override IOperation VisitUsing(IUsingOperation operation, int? captureIdF { Debug.Assert(operation == _currentStatement); - SemanticModel semanticModel = ((Operation)operation).SemanticModel; - ITypeSymbol iDisposable = semanticModel.Compilation.GetSpecialType(SpecialType.System_IDisposable); + Compilation compilation = ((Operation)operation).SemanticModel.Compilation; + ITypeSymbol iDisposable = compilation.GetSpecialType(SpecialType.System_IDisposable); if (operation.Resources.Kind == OperationKind.VariableDeclarationGroup) { @@ -2325,7 +2372,7 @@ void processResource(IOperation resource, ArrayBuilder<(IVariableDeclarationOper if (!(resource.Type?.IsValueType == true && !ITypeSymbolHelpers.IsNullableType(resource.Type))) { - IOperation condition = MakeIsNullOperation(semanticModel, OperationCloner.CloneOperation(resource)); + IOperation condition = MakeIsNullOperation(OperationCloner.CloneOperation(resource), compilation); condition = Operation.SetParentOperation(condition, null); LinkBlocks(CurrentBasicBlock, (condition, JumpIfTrue: true, RegularBranch(endOfFinally))); _currentBasicBlock = null; @@ -2359,24 +2406,193 @@ IOperation tryDispose(IOperation value) { Debug.Assert(value.Type == iDisposable); - foreach (ISymbol candidate in iDisposable.GetMembers("Dispose")) + var method = (IMethodSymbol)compilation.CommonGetSpecialTypeMember(SpecialMember.System_IDisposable__Dispose); + if (method != null) { - if (candidate.Kind == SymbolKind.Method && !candidate.IsStatic && candidate.DeclaredAccessibility == Accessibility.Public) - { - var method = (IMethodSymbol)candidate; - if (method.Parameters.Length == 0 && method.RefKind == RefKind.None && method.ReturnsVoid) - { - return new InvocationExpression(method, value, isVirtual: true, - ImmutableArray.Empty, semanticModel: null, value.Syntax, - method.ReturnType, constantValue: default, isImplicit: true); - } - } + return new InvocationExpression(method, value, isVirtual: true, + ImmutableArray.Empty, semanticModel: null, value.Syntax, + method.ReturnType, constantValue: default, isImplicit: true); } return null; } } + public override IOperation VisitLock(ILockOperation operation, int? captureIdForResult) + { + Debug.Assert(operation == _currentStatement); + + SemanticModel semanticModel = ((Operation)operation).SemanticModel; + ITypeSymbol objectType = semanticModel.Compilation.GetSpecialType(SpecialType.System_Object); + + // If Monitor.Enter(object, ref bool) is available: + // + // L $lock = `LockedValue`; + // bool $lockTaken = false; + // try + // { + // Monitor.Enter($lock, ref $lockTaken); + // `body` + // } + // finally + // { + // if ($lockTaken) Monitor.Exit($lock); + // } + + // If Monitor.Enter(object, ref bool) is not available: + // + // L $lock = `LockedValue`; + // Monitor.Enter($lock); // NB: before try-finally so we don't Exit if an exception prevents us from acquiring the lock. + // try + // { + // `body` + // } + // finally + // { + // Monitor.Exit($lock); + // } + + // If original type of the LockedValue object is System.Object, VB calls runtime helper (if one is available) + // Microsoft.VisualBasic.CompilerServices.ObjectFlowControl.CheckForSyncLockOnValueType to ensure no value type is + // used. + // For simplicity, we will not synthesize this call because its presence is unlikely to affect graph analysis. + + IOperation lockedValue = Visit(operation.LockedValue); + + if (!objectType.Equals(lockedValue.Type)) + { + lockedValue = new ConversionOperation(lockedValue, ConvertibleConversion.Instance, isTryCast: false, isChecked: false, + semanticModel: null, lockedValue.Syntax, objectType, constantValue: default, isImplicit: true); + } + + int captureId = _availableCaptureId++; + AddStatement(new FlowCapture(captureId, lockedValue.Syntax, lockedValue)); + lockedValue = new FlowCaptureReference(captureId, lockedValue.Syntax, lockedValue.Type, constantValue: default); + + var enterMethod = (IMethodSymbol)semanticModel.Compilation.CommonGetWellKnownTypeMember(WellKnownMember.System_Threading_Monitor__Enter2); + bool legacyMode = (enterMethod == null); + + if (legacyMode) + { + enterMethod = (IMethodSymbol)semanticModel.Compilation.CommonGetWellKnownTypeMember(WellKnownMember.System_Threading_Monitor__Enter); + + // Monitor.Enter($lock); + if (enterMethod == null) + { + // PROTOTYPE(dataflow): The scenario with missing Enter is not covered by unit-tests. + AddStatement(MakeInvalidOperation(type: null, lockedValue)); + } + else + { + AddStatement(new InvocationExpression(enterMethod, instance: null, isVirtual: false, + ImmutableArray.Create( + new ArgumentOperation(lockedValue, + ArgumentKind.Explicit, + enterMethod.Parameters[0], + inConversionOpt: null, + outConversionOpt: null, + semanticModel: null, + lockedValue.Syntax, + constantValue: null, + isImplicit: true)), + semanticModel: null, lockedValue.Syntax, + enterMethod.ReturnType, constantValue: default, isImplicit: true)); + } + } + + var afterTryFinally = new BasicBlock(BasicBlockKind.Block); + + EnterRegion(new RegionBuilder(ControlFlowGraph.RegionKind.TryAndFinally)); + EnterRegion(new RegionBuilder(ControlFlowGraph.RegionKind.Try)); + + IOperation lockTaken = null; + if (!legacyMode) + { + // Monitor.Enter($lock, ref $lockTaken); + lockTaken = new FlowCaptureReference(_availableCaptureId++, lockedValue.Syntax, semanticModel.Compilation.GetSpecialType(SpecialType.System_Boolean), constantValue: default); + AddStatement(new InvocationExpression(enterMethod, instance: null, isVirtual: false, + ImmutableArray.Create( + new ArgumentOperation(lockedValue, + ArgumentKind.Explicit, + enterMethod.Parameters[0], + inConversionOpt: null, + outConversionOpt: null, + semanticModel: null, + lockedValue.Syntax, + constantValue: null, + isImplicit: true), + new ArgumentOperation(lockTaken, + ArgumentKind.Explicit, + enterMethod.Parameters[1], + inConversionOpt: null, + outConversionOpt: null, + semanticModel: null, + lockedValue.Syntax, + constantValue: null, + isImplicit: true)), + semanticModel: null, lockedValue.Syntax, + enterMethod.ReturnType, constantValue: default, isImplicit: true)); + } + + VisitStatement(operation.Body); + + LinkBlocks(CurrentBasicBlock, afterTryFinally); + + Debug.Assert(_currentRegion.Kind == ControlFlowGraph.RegionKind.Try); + LeaveRegion(); + + var endOfFinally = new BasicBlock(BasicBlockKind.Block); + endOfFinally.InternalNext.Branch.Kind = BasicBlock.BranchKind.StructuredExceptionHandling; + + EnterRegion(new RegionBuilder(ControlFlowGraph.RegionKind.Finally)); + AppendNewBlock(new BasicBlock(BasicBlockKind.Block)); + + if (!legacyMode) + { + // if ($lockTaken) + IOperation condition = OperationCloner.CloneOperation(lockTaken); + condition = Operation.SetParentOperation(condition, null); + LinkBlocks(CurrentBasicBlock, (condition, JumpIfTrue: false, RegularBranch(endOfFinally))); + _currentBasicBlock = null; + } + + // Monitor.Exit($lock); + var exitMethod = (IMethodSymbol)semanticModel.Compilation.CommonGetWellKnownTypeMember(WellKnownMember.System_Threading_Monitor__Exit); + lockedValue = OperationCloner.CloneOperation(lockedValue); + + if (exitMethod == null) + { + // PROTOTYPE(dataflow): The scenario with missing Exit is not covered by unit-tests. + AddStatement(MakeInvalidOperation(type: null, lockedValue)); + } + else + { + AddStatement(new InvocationExpression(exitMethod, instance: null, isVirtual: false, + ImmutableArray.Create( + new ArgumentOperation(lockedValue, + ArgumentKind.Explicit, + exitMethod.Parameters[0], + inConversionOpt: null, + outConversionOpt: null, + semanticModel: null, + lockedValue.Syntax, + constantValue: null, + isImplicit: true)), + semanticModel: null, lockedValue.Syntax, + exitMethod.ReturnType, constantValue: default, isImplicit: true)); + } + + AppendNewBlock(endOfFinally); + + LeaveRegion(); + Debug.Assert(_currentRegion.Kind == ControlFlowGraph.RegionKind.TryAndFinally); + LeaveRegion(); + + AppendNewBlock(afterTryFinally, linkToPrevious: false); + + return null; + } + public override IOperation VisitEnd(IEndOperation operation, int? captureIdForResult) { Debug.Assert(_currentStatement == operation); @@ -2682,21 +2898,11 @@ public override IOperation VisitForEachLoop(IForEachLoopOperation operation, int return new ForEachLoopStatement(operation.Locals, operation.ContinueLabel, operation.ExitLabel, Visit(operation.LoopControlVariable), Visit(operation.Collection), VisitArray(operation.NextVariables), Visit(operation.Body), semanticModel: null, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); } - public override IOperation VisitLock(ILockOperation operation, int? captureIdForResult) - { - return new LockStatement(Visit(operation.LockedValue), Visit(operation.Body), semanticModel: null, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); - } - internal override IOperation VisitFixed(IFixedOperation operation, int? captureIdForResult) { return new FixedStatement(Visit(operation.Variables), Visit(operation.Body), semanticModel: null, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); } - public override IOperation VisitExpressionStatement(IExpressionStatementOperation operation, int? captureIdForResult) - { - return new ExpressionStatement(Visit(operation.Operation), semanticModel: null, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); - } - internal override IOperation VisitWith(IWithOperation operation, int? captureIdForResult) { return new WithStatement(Visit(operation.Body), Visit(operation.Value), semanticModel: null, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index f11516fe5fd0132868378faf2b732512f9c697b4..432cd5041055ff37f2433b76a88cb51bae6097f2 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -1093,13 +1093,33 @@ protected static void VerifyFlowGraphForTest(CSharpCompilation comp MetadataReference[] references = null, bool useLatestFrameworkReferences = false) where TSyntaxNode : SyntaxNode + { + VerifyFlowGraphAndDiagnosticsForTest( + testSrc, + expectedFlowGraph, + expectedDiagnostics, + targetFramework: useLatestFrameworkReferences ? TargetFramework.Mscorlib46Extended : TargetFramework.Standard, + compilationOptions, + parseOptions, + references); + } + + protected static void VerifyFlowGraphAndDiagnosticsForTest( + string testSrc, + string expectedFlowGraph, + DiagnosticDescription[] expectedDiagnostics, + TargetFramework targetFramework, + CSharpCompilationOptions compilationOptions = null, + CSharpParseOptions parseOptions = null, + MetadataReference[] references = null) + where TSyntaxNode : SyntaxNode { parseOptions = parseOptions?.WithFlowAnalysisFeature() ?? TestOptions.RegularWithFlowAnalysisFeature; var compilation = CreateCompilation( new[] { Parse(testSrc, filename: "file.cs", options: parseOptions) }, references, options: compilationOptions ?? TestOptions.ReleaseDll, - targetFramework: useLatestFrameworkReferences ? TargetFramework.Mscorlib46Extended : TargetFramework.Standard); + targetFramework: targetFramework); VerifyFlowGraphAndDiagnosticsForTest(compilation, expectedFlowGraph, expectedDiagnostics); } diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index 8647d2272e9a02f429f8b0da89352e9968615d86..99bb5dd0b9b962de5936cb7855258117f7b9b4f5 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -1798,6 +1798,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return Assembly.GetSpecialTypeMember(memberId) End Function + Friend Overrides Function CommonGetSpecialTypeMember(specialMember As SpecialMember) As ISymbol + Return GetSpecialTypeMember(specialMember) + End Function + Friend Function GetTypeByReflectionType(type As Type, diagnostics As DiagnosticBag) As TypeSymbol ' TODO: See CSharpCompilation.GetTypeByReflectionType Return GetSpecialType(SpecialType.System_Object) diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ILockStatement.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ILockStatement.vb index 468c7577b5953f132acad835866ed0df7f2ce92c..6984e51cdb82504a187f919c4cf5e2023a51d802 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ILockStatement.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ILockStatement.vb @@ -359,5 +359,8 @@ ILockOperation (OperationKind.Lock, Type: null) (Syntax: 'SyncLock o' ... nd Syn VerifyOperationTreeAndDiagnosticsForTest(Of SyncLockBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) End Sub + + ' PROTOTYPE(dataflow): Port applicable LockFlow_** test scenarios from C#. + End Class End Namespace diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IUsingStatement.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IUsingStatement.vb index 8efd9e92e1af756470acb005a4bddfaeb2e9f503..a924490a69ec66966e12ec375c5903916c78d3fe 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IUsingStatement.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IUsingStatement.vb @@ -982,11 +982,11 @@ IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (S VerifyOperationTreeAndDiagnosticsForTest(Of ExpressionStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) End Sub - ' PROTOTYPE(dataflow): Port applicable UsingFlow_01 - UsingFlow_12 test scenarios from C#. + ' PROTOTYPE(dataflow): Port applicable UsingFlow_01 - UsingFlow_13 test scenarios from C#. - Public Sub UsingFlow_13() + Public Sub UsingFlow_14() Dim source =