diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 8d63d3bd4aa62c68373f2be896742ecc1cbed3b0..fb3d2053e9296eada65edc5665d083889e11fbe4 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -304,7 +304,7 @@ private IInvocationExpression CreateBoundCallOperation(BoundCall boundCall) return new LazyInvocationExpression(targetMethod, instance, isVirtual, argumentsInEvaluationOrder, _semanticModel, syntax, type, constantValue, isImplicit); } - private ILocalReferenceExpression CreateBoundLocalOperation(BoundLocal boundLocal) + private IOperation CreateBoundLocalOperation(BoundLocal boundLocal) { ILocalSymbol local = boundLocal.LocalSymbol; bool isDeclaration = boundLocal.IsDeclaration; @@ -1112,8 +1112,7 @@ private IBlockStatement CreateBoundBlockOperation(BoundBlock boundBlock) // Filter out all OperationKind.None except fixed statements for now. // https://github.com/dotnet/roslyn/issues/21776 .Where(s => s.operation.Kind != OperationKind.None || - s.bound.Kind == BoundKind.FixedStatement || - s.bound.Kind == BoundKind.TryStatement) + s.bound.Kind == BoundKind.FixedStatement) .Select(s => s.operation).ToImmutableArray()); ImmutableArray locals = boundBlock.Locals.As(); @@ -1300,15 +1299,17 @@ private ITryStatement CreateBoundTryStatementOperation(BoundTryStatement boundTr private ICatchClause CreateBoundCatchBlockOperation(BoundCatchBlock boundCatchBlock) { - Lazy handler = new Lazy(() => (IBlockStatement)Create(boundCatchBlock.Body)); - ITypeSymbol caughtType = boundCatchBlock.ExceptionTypeOpt; + var exceptionSourceOpt = (BoundLocal)boundCatchBlock.ExceptionSourceOpt; + Lazy expressionDeclarationOrExpression = new Lazy(() => exceptionSourceOpt != null ? CreateVariableDeclaration(exceptionSourceOpt) : null); + ITypeSymbol exceptionType = boundCatchBlock.ExceptionTypeOpt; + ImmutableArray locals = boundCatchBlock.Locals.As(); Lazy filter = new Lazy(() => Create(boundCatchBlock.ExceptionFilterOpt)); - ILocalSymbol exceptionLocal = (boundCatchBlock.Locals.FirstOrDefault()?.DeclarationKind == CSharp.Symbols.LocalDeclarationKind.CatchVariable) ? boundCatchBlock.Locals.FirstOrDefault() : null; + Lazy handler = new Lazy(() => (IBlockStatement)Create(boundCatchBlock.Body)); SyntaxNode syntax = boundCatchBlock.Syntax; ITypeSymbol type = null; Optional constantValue = default(Optional); bool isImplicit = boundCatchBlock.WasCompilerGenerated; - return new LazyCatchClause(handler, caughtType, filter, exceptionLocal, _semanticModel, syntax, type, constantValue, isImplicit); + return new LazyCatchClause(expressionDeclarationOrExpression, exceptionType, locals, filter, handler, _semanticModel, syntax, type, constantValue, isImplicit); } private IFixedStatement CreateBoundFixedStatementOperation(BoundFixedStatement boundFixedStatement) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs index cc3894e54544f1926a1d6e35adea28e5f74288d0..9c8b8198f62548fa1dbbce2e6c0e918dd9eff4d3 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs @@ -56,6 +56,11 @@ private IVariableDeclaration CreateVariableDeclaration(BoundLocalDeclaration bou return OperationFactory.CreateVariableDeclaration(boundLocalDeclaration.LocalSymbol, Create(boundLocalDeclaration.InitializerOpt), _semanticModel, syntax); } + private IVariableDeclaration CreateVariableDeclaration(BoundLocal boundLocal) + { + return OperationFactory.CreateVariableDeclaration(boundLocal.LocalSymbol, initialValue: null, semanticModel: _semanticModel, syntax: boundLocal.Syntax); + } + private IOperation CreateBoundCallInstanceOperation(BoundCall boundCall) { if (boundCall.Method == null || boundCall.Method.IsStatic) diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IWhileUntilLoopStatement.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IWhileUntilLoopStatement.cs index 8da450539a99ee7b8cc49dc39f63e3315fef9b07..13a3a7c6ea88097d3f6e912b80545796accab872 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IWhileUntilLoopStatement.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IWhileUntilLoopStatement.cs @@ -896,7 +896,7 @@ public void TryMethod() Target: ILocalReferenceExpression: x (OperationKind.LocalReferenceExpression, Type: System.SByte) (Syntax: 'x') Right: ILiteralExpression (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') Body: IBlockStatement (1 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') - ITryStatement (OperationKind.None) (Syntax: 'try ... }') + ITryStatement (OperationKind.TryStatement) (Syntax: 'try ... }') Body: IBlockStatement (1 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') IExpressionStatement (OperationKind.ExpressionStatement) (Syntax: 'y = (sbyte)(x / 2);') Expression: ISimpleAssignmentExpression (OperationKind.SimpleAssignmentExpression, Type: System.SByte) (Syntax: 'y = (sbyte)(x / 2)') diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_TryCatch.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_TryCatch.cs index 2b38b358253db93c0b22196b25de6aa1ea2f6797..4270b675e4e39bcf6356d06a561f860ab23a31cd 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_TryCatch.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_TryCatch.cs @@ -1,9 +1,7 @@ // 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.Semantics; using Microsoft.CodeAnalysis.Test.Utilities; -using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests @@ -37,14 +35,18 @@ void M(int i) } "; string expectedOperationTree = @" -ITryStatement (OperationKind.None) (Syntax: 'try ... }') +ITryStatement (OperationKind.TryStatement) (Syntax: 'try ... }') Body: IBlockStatement (1 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') IExpressionStatement (OperationKind.ExpressionStatement) (Syntax: 'i = 0;') Expression: ISimpleAssignmentExpression (OperationKind.SimpleAssignmentExpression, Type: System.Int32) (Syntax: 'i = 0') Left: IParameterReferenceExpression: i (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'i') Right: ILiteralExpression (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') Catch clauses(1): - ICatchClause (Exception type: System.Exception, Exception local: System.Exception ex) (OperationKind.None) (Syntax: 'catch (Exce ... }') + ICatchClause (Exception type: System.Exception) (OperationKind.CatchClause) (Syntax: 'catch (Exce ... }') + Locals: Local_1: System.Exception ex + ExceptionDeclarationOrExpression: IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(Exception ex)') + Variables: Local_1: System.Exception ex + Initializer: null Filter: IBinaryOperatorExpression (BinaryOperatorKind.GreaterThan) (OperationKind.BinaryOperatorExpression, Type: System.Boolean) (Syntax: 'i > 0') Left: IParameterReferenceExpression: i (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'i') Right: ILiteralExpression (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') @@ -91,14 +93,18 @@ void M(int i) "; string expectedOperationTree = @" IBlockStatement (1 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') - ITryStatement (OperationKind.None) (Syntax: 'try ... }') + ITryStatement (OperationKind.TryStatement) (Syntax: 'try ... }') Body: IBlockStatement (1 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') IExpressionStatement (OperationKind.ExpressionStatement) (Syntax: 'i = 0;') Expression: ISimpleAssignmentExpression (OperationKind.SimpleAssignmentExpression, Type: System.Int32) (Syntax: 'i = 0') Left: IParameterReferenceExpression: i (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'i') Right: ILiteralExpression (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') Catch clauses(1): - ICatchClause (Exception type: System.Exception, Exception local: System.Exception ex) (OperationKind.None) (Syntax: 'catch (Exce ... }') + ICatchClause (Exception type: System.Exception) (OperationKind.CatchClause) (Syntax: 'catch (Exce ... }') + Locals: Local_1: System.Exception ex + ExceptionDeclarationOrExpression: IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(Exception ex)') + Variables: Local_1: System.Exception ex + Initializer: null Filter: IBinaryOperatorExpression (BinaryOperatorKind.GreaterThan) (OperationKind.BinaryOperatorExpression, Type: System.Boolean) (Syntax: 'i > 0') Left: IParameterReferenceExpression: i (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'i') Right: ILiteralExpression (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') @@ -116,5 +122,704 @@ void M(int i) VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_SingleCatchClause() + { + string source = @" +class C +{ + static void Main() + { + /**/try + { + } + catch (System.IO.IOException e) + { + }/**/ + } +} +"; + string expectedOperationTree = @" +ITryStatement (OperationKind.TryStatement) (Syntax: 'try ... }') + Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Catch clauses(1): + ICatchClause (Exception type: System.IO.IOException) (OperationKind.CatchClause) (Syntax: 'catch (Syst ... }') + Locals: Local_1: System.IO.IOException e + ExceptionDeclarationOrExpression: IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(System.IO. ... xception e)') + Variables: Local_1: System.IO.IOException e + Initializer: null + Filter: null + Handler: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Finally: null +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS0168: The variable 'e' is declared but never used + // catch (System.IO.IOException e) + Diagnostic(ErrorCode.WRN_UnreferencedVar, "e").WithArguments("e").WithLocation(9, 38) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_SingleCatchClauseAndFilter() + { + string source = @" +class C +{ + static void Main() + { + /**/try + { + } + catch (System.IO.IOException e) when (e.Message != null) + { + }/**/ + } +} +"; + string expectedOperationTree = @" +ITryStatement (OperationKind.TryStatement) (Syntax: 'try ... }') + Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Catch clauses(1): + ICatchClause (Exception type: System.IO.IOException) (OperationKind.CatchClause) (Syntax: 'catch (Syst ... }') + Locals: Local_1: System.IO.IOException e + ExceptionDeclarationOrExpression: IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(System.IO. ... xception e)') + Variables: Local_1: System.IO.IOException e + Initializer: null + Filter: IBinaryOperatorExpression (BinaryOperatorKind.NotEquals) (OperationKind.BinaryOperatorExpression, Type: System.Boolean) (Syntax: 'e.Message != null') + Left: IPropertyReferenceExpression: System.String System.Exception.Message { get; } (OperationKind.PropertyReferenceExpression, Type: System.String) (Syntax: 'e.Message') + Instance Receiver: ILocalReferenceExpression: e (OperationKind.LocalReferenceExpression, Type: System.IO.IOException) (Syntax: 'e') + Right: IConversionExpression (Implicit, TryCast: False, Unchecked) (OperationKind.ConversionExpression, Type: System.String, Constant: null) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: ILiteralExpression (OperationKind.LiteralExpression, Type: null, Constant: null) (Syntax: 'null') + Handler: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Finally: null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_MultipleCatchClausesWithDifferentCaughtTypes() + { + string source = @" +class C +{ + static void Main() + { + /**/try + { + } + catch (System.IO.IOException e) + { + } + catch (System.Exception e) when (e.Message != null) + { + }/**/ + } +} +"; + string expectedOperationTree = @" +ITryStatement (OperationKind.TryStatement) (Syntax: 'try ... }') + Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Catch clauses(2): + ICatchClause (Exception type: System.IO.IOException) (OperationKind.CatchClause) (Syntax: 'catch (Syst ... }') + Locals: Local_1: System.IO.IOException e + ExceptionDeclarationOrExpression: IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(System.IO. ... xception e)') + Variables: Local_1: System.IO.IOException e + Initializer: null + Filter: null + Handler: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + ICatchClause (Exception type: System.Exception) (OperationKind.CatchClause) (Syntax: 'catch (Syst ... }') + Locals: Local_1: System.Exception e + ExceptionDeclarationOrExpression: IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(System.Exception e)') + Variables: Local_1: System.Exception e + Initializer: null + Filter: IBinaryOperatorExpression (BinaryOperatorKind.NotEquals) (OperationKind.BinaryOperatorExpression, Type: System.Boolean) (Syntax: 'e.Message != null') + Left: IPropertyReferenceExpression: System.String System.Exception.Message { get; } (OperationKind.PropertyReferenceExpression, Type: System.String) (Syntax: 'e.Message') + Instance Receiver: ILocalReferenceExpression: e (OperationKind.LocalReferenceExpression, Type: System.Exception) (Syntax: 'e') + Right: IConversionExpression (Implicit, TryCast: False, Unchecked) (OperationKind.ConversionExpression, Type: System.String, Constant: null) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: ILiteralExpression (OperationKind.LiteralExpression, Type: null, Constant: null) (Syntax: 'null') + Handler: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Finally: null +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS0168: The variable 'e' is declared but never used + // catch (System.IO.IOException e) + Diagnostic(ErrorCode.WRN_UnreferencedVar, "e").WithArguments("e").WithLocation(9, 38) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_MultipleCatchClausesWithDuplicateCaughtTypes() + { + string source = @" +class C +{ + static void Main() + { + /**/try + { + } + catch (System.IO.IOException e) + { + } + catch (System.IO.IOException e) when (e.Message != null) + { + }/**/ + } +} +"; + string expectedOperationTree = @" +ITryStatement (OperationKind.TryStatement, IsInvalid) (Syntax: 'try ... }') + Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Catch clauses(2): + ICatchClause (Exception type: System.IO.IOException) (OperationKind.CatchClause) (Syntax: 'catch (Syst ... }') + Locals: Local_1: System.IO.IOException e + ExceptionDeclarationOrExpression: IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(System.IO. ... xception e)') + Variables: Local_1: System.IO.IOException e + Initializer: null + Filter: null + Handler: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + ICatchClause (Exception type: System.IO.IOException) (OperationKind.CatchClause, IsInvalid) (Syntax: 'catch (Syst ... }') + Locals: Local_1: System.IO.IOException e + ExceptionDeclarationOrExpression: IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: '(System.IO. ... xception e)') + Variables: Local_1: System.IO.IOException e + Initializer: null + Filter: IBinaryOperatorExpression (BinaryOperatorKind.NotEquals) (OperationKind.BinaryOperatorExpression, Type: System.Boolean) (Syntax: 'e.Message != null') + Left: IPropertyReferenceExpression: System.String System.Exception.Message { get; } (OperationKind.PropertyReferenceExpression, Type: System.String) (Syntax: 'e.Message') + Instance Receiver: ILocalReferenceExpression: e (OperationKind.LocalReferenceExpression, Type: System.IO.IOException) (Syntax: 'e') + Right: IConversionExpression (Implicit, TryCast: False, Unchecked) (OperationKind.ConversionExpression, Type: System.String, Constant: null) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: ILiteralExpression (OperationKind.LiteralExpression, Type: null, Constant: null) (Syntax: 'null') + Handler: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Finally: null +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS0160: A previous catch clause already catches all exceptions of this or of a super type ('IOException') + // catch (System.IO.IOException e) when (e.Message != null) + Diagnostic(ErrorCode.ERR_UnreachableCatch, "System.IO.IOException").WithArguments("System.IO.IOException").WithLocation(12, 16), + // CS0168: The variable 'e' is declared but never used + // catch (System.IO.IOException e) + Diagnostic(ErrorCode.WRN_UnreferencedVar, "e").WithArguments("e").WithLocation(9, 38) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_CatchClauseWithoutExceptionLocal() + { + string source = @" +using System; + +class C +{ + static void M(string s) + { + /**/try + { + } + catch (Exception) + { + }/**/ + } +} +"; + string expectedOperationTree = @" +ITryStatement (OperationKind.TryStatement) (Syntax: 'try ... }') + Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Catch clauses(1): + ICatchClause (Exception type: System.Exception) (OperationKind.CatchClause) (Syntax: 'catch (Exce ... }') + ExceptionDeclarationOrExpression: null + Filter: null + Handler: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Finally: null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_CatchClauseWithoutCaughtTypeOrExceptionLocal() + { + string source = @" +class C +{ + static void M(object o) + { + /**/try + { + } + catch + { + }/**/ + } +} +"; + string expectedOperationTree = @" +ITryStatement (OperationKind.TryStatement) (Syntax: 'try ... }') + Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Catch clauses(1): + ICatchClause (Exception type: null) (OperationKind.CatchClause) (Syntax: 'catch ... }') + ExceptionDeclarationOrExpression: null + Filter: null + Handler: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Finally: null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_FinallyWithoutCatchClause() + { + string source = @" +using System; + +class C +{ + static void M(string s) + { + /**/try + { + } + finally + { + Console.WriteLine(s); + }/**/ + } +} +"; + string expectedOperationTree = @" +ITryStatement (OperationKind.TryStatement) (Syntax: 'try ... }') + Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Catch clauses(0) + Finally: IBlockStatement (1 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + IExpressionStatement (OperationKind.ExpressionStatement) (Syntax: 'Console.WriteLine(s);') + Expression: IInvocationExpression (void System.Console.WriteLine(System.String value)) (OperationKind.InvocationExpression, Type: System.Void) (Syntax: 'Console.WriteLine(s)') + Instance Receiver: null + Arguments(1): + IArgument (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument) (Syntax: 's') + IParameterReferenceExpression: s (OperationKind.ParameterReferenceExpression, Type: System.String) (Syntax: 's') + 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) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_TryBlockWithLocalDeclaration() + { + string source = @" +using System; + +class C +{ + static void M(string s) + { + /**/try + { + int i = 0; + } + catch (Exception) + { + }/**/ + } +} +"; + string expectedOperationTree = @" +ITryStatement (OperationKind.TryStatement) (Syntax: 'try ... }') + Body: IBlockStatement (1 statements, 1 locals) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Locals: Local_1: System.Int32 i + IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'int i = 0;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'i = 0') + Variables: Local_1: System.Int32 i + Initializer: ILiteralExpression (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') + Catch clauses(1): + ICatchClause (Exception type: System.Exception) (OperationKind.CatchClause) (Syntax: 'catch (Exce ... }') + ExceptionDeclarationOrExpression: null + Filter: null + Handler: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Finally: null +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS0219: The variable 'i' is assigned but its value is never used + // int i = 0; + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "i").WithArguments("i").WithLocation(10, 17) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_CatchClauseWithLocalDeclaration() + { + string source = @" +using System; + +class C +{ + static void M(string s) + { + /**/try + { + } + catch (Exception) + { + int i = 0; + }/**/ + } +} +"; + string expectedOperationTree = @" +ITryStatement (OperationKind.TryStatement) (Syntax: 'try ... }') + Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Catch clauses(1): + ICatchClause (Exception type: System.Exception) (OperationKind.CatchClause) (Syntax: 'catch (Exce ... }') + ExceptionDeclarationOrExpression: null + Filter: null + Handler: IBlockStatement (1 statements, 1 locals) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Locals: Local_1: System.Int32 i + IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'int i = 0;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'i = 0') + Variables: Local_1: System.Int32 i + Initializer: ILiteralExpression (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') + Finally: null +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS0219: The variable 'i' is assigned but its value is never used + // int i = 0; + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "i").WithArguments("i").WithLocation(13, 17) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_CatchFilterWithLocalDeclaration() + { + string source = @" +using System; + +class C +{ + static void M(object o) + { + /**/try + { + } + catch (Exception) when (o is string s) + { + }/**/ + } +} +"; + string expectedOperationTree = @" +ITryStatement (OperationKind.TryStatement) (Syntax: 'try ... }') + Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Catch clauses(1): + ICatchClause (Exception type: System.Exception) (OperationKind.CatchClause) (Syntax: 'catch (Exce ... }') + Locals: Local_1: System.String s + ExceptionDeclarationOrExpression: null + Filter: IIsPatternExpression (OperationKind.IsPatternExpression, Type: System.Boolean) (Syntax: 'o is string s') + Expression: IParameterReferenceExpression: o (OperationKind.ParameterReferenceExpression, Type: System.Object) (Syntax: 'o') + Pattern: IDeclarationPattern (Declared Symbol: System.String s) (OperationKind.DeclarationPattern) (Syntax: 'string s') + Handler: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Finally: null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_CatchFilterAndSourceWithLocalDeclaration() + { + string source = @" +using System; + +class C +{ + static void M(object o) + { + /**/try + { + } + catch (Exception e) when (o is string s) + { + }/**/ + } +} +"; + string expectedOperationTree = @" +ITryStatement (OperationKind.TryStatement) (Syntax: 'try ... }') + Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Catch clauses(1): + ICatchClause (Exception type: System.Exception) (OperationKind.CatchClause) (Syntax: 'catch (Exce ... }') + Locals: Local_1: System.Exception e + Local_2: System.String s + ExceptionDeclarationOrExpression: IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(Exception e)') + Variables: Local_1: System.Exception e + Initializer: null + Filter: IIsPatternExpression (OperationKind.IsPatternExpression, Type: System.Boolean) (Syntax: 'o is string s') + Expression: IParameterReferenceExpression: o (OperationKind.ParameterReferenceExpression, Type: System.Object) (Syntax: 'o') + Pattern: IDeclarationPattern (Declared Symbol: System.String s) (OperationKind.DeclarationPattern) (Syntax: 'string s') + Handler: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Finally: null +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // file.cs(11,26): warning CS0168: The variable 'e' is declared but never used + // catch (Exception e) when (o is string s) + Diagnostic(ErrorCode.WRN_UnreferencedVar, "e").WithArguments("e").WithLocation(11, 26) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_FinallyWithLocalDeclaration() + { + string source = @" +class C +{ + static void Main() + { + /**/try + { + } + finally + { + int i = 0; + }/**/ + } +} +"; + string expectedOperationTree = @" +ITryStatement (OperationKind.TryStatement) (Syntax: 'try ... }') + Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Catch clauses(0) + Finally: IBlockStatement (1 statements, 1 locals) (OperationKind.BlockStatement) (Syntax: '{ ... }') + Locals: Local_1: System.Int32 i + IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'int i = 0;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'i = 0') + Variables: Local_1: System.Int32 i + Initializer: ILiteralExpression (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS0219: The variable 'i' is assigned but its value is never used + // int i = 0; + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "i").WithArguments("i").WithLocation(11, 17) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_InvalidCaughtType() + { + string source = @" +class C +{ + static void Main() + { + try + { + } + /**/catch (int e) + { + }/**/ + } +} +"; + string expectedOperationTree = @" +ICatchClause (Exception type: System.Int32) (OperationKind.CatchClause, IsInvalid) (Syntax: 'catch (int ... }') + Locals: Local_1: System.Int32 e + ExceptionDeclarationOrExpression: IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: '(int e)') + Variables: Local_1: System.Int32 e + Initializer: null + Filter: null + Handler: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS0155: The type caught or thrown must be derived from System.Exception + // /**/catch (int e) + Diagnostic(ErrorCode.ERR_BadExceptionType, "int").WithLocation(9, 26), + // CS0168: The variable 'e' is declared but never used + // /**/catch (int e) + Diagnostic(ErrorCode.WRN_UnreferencedVar, "e").WithArguments("e").WithLocation(9, 30) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_GetOperationForCatchClause() + { + string source = @" +class C +{ + static void Main() + { + try + { + } + /**/catch (System.IO.IOException e) when (e.Message != null) + { + }/**/ + } +} +"; + string expectedOperationTree = @" +ICatchClause (Exception type: System.IO.IOException) (OperationKind.CatchClause) (Syntax: 'catch (Syst ... }') + Locals: Local_1: System.IO.IOException e + ExceptionDeclarationOrExpression: IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(System.IO. ... xception e)') + Variables: Local_1: System.IO.IOException e + Initializer: null + Filter: IBinaryOperatorExpression (BinaryOperatorKind.NotEquals) (OperationKind.BinaryOperatorExpression, Type: System.Boolean) (Syntax: 'e.Message != null') + Left: IPropertyReferenceExpression: System.String System.Exception.Message { get; } (OperationKind.PropertyReferenceExpression, Type: System.String) (Syntax: 'e.Message') + Instance Receiver: ILocalReferenceExpression: e (OperationKind.LocalReferenceExpression, Type: System.IO.IOException) (Syntax: 'e') + Right: IConversionExpression (Implicit, TryCast: False, Unchecked) (OperationKind.ConversionExpression, Type: System.String, Constant: null) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: ILiteralExpression (OperationKind.LiteralExpression, Type: null, Constant: null) (Syntax: 'null') + Handler: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_GetOperationForCatchDeclaration() + { + string source = @" +class C +{ + static void Main() + { + try + { + } + catch /**/(System.IO.IOException e)/**/ when (e.Message != null) + { + } + } +} +"; + string expectedOperationTree = @" +IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: '(System.IO. ... xception e)') + Variables: Local_1: System.IO.IOException e + Initializer: null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_GetOperationForCatchFilterClause() + { + string source = @" +using System; + +class C +{ + static void M(string s) + { + try + { + } + catch (Exception) /**/when (s != null)/**/ + { + } + } +} +"; + // GetOperation returns null for CatchFilterClauseSyntax + Assert.Null(GetOperationTreeForTest(source)); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_GetOperationForCatchFilterClauseExpression() + { + string source = @" +using System; + +class C +{ + static void M(string s) + { + try + { + } + catch (Exception) when (/**/s != null/**/) + { + } + } +} +"; + string expectedOperationTree = @" +IBinaryOperatorExpression (BinaryOperatorKind.NotEquals) (OperationKind.BinaryOperatorExpression, Type: System.Boolean) (Syntax: 's != null') + Left: IParameterReferenceExpression: s (OperationKind.ParameterReferenceExpression, Type: System.String) (Syntax: 's') + Right: IConversionExpression (Implicit, TryCast: False, Unchecked) (OperationKind.ConversionExpression, Type: System.String, Constant: null) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: ILiteralExpression (OperationKind.LiteralExpression, Type: null, Constant: null) (Syntax: 'null') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TryCatch_GetOperationForFinallyClause() + { + string source = @" +using System; + +class C +{ + static void M(string s) + { + try + { + } + /**/finally + { + Console.WriteLine(s); + }/**/ + } +} +"; + // GetOperation returns null for FinallyClauseSyntax + Assert.Null(GetOperationTreeForTest(source)); + } } } diff --git a/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs index 47afb3d4913ed080f4c2343edf56337e3442ab06..c1622a7fe8e919407bcda6c2bb3ad1257045a07f 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs @@ -821,41 +821,49 @@ internal abstract partial class CaseClause : Operation, ICaseClause /// internal abstract partial class BaseCatchClause : Operation, ICatchClause { - protected BaseCatchClause(ITypeSymbol caughtType, ILocalSymbol exceptionLocal, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue, bool isImplicit) : - // https://github.com/dotnet/roslyn/issues/22008 - // base(OperationKind.CatchClause, semanticModel, syntax, type, constantValue, isImplicit) - base(OperationKind.None, semanticModel, syntax, type, constantValue, isImplicit) + protected BaseCatchClause(ITypeSymbol exceptionType, ImmutableArray locals, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue, bool isImplicit) : + base(OperationKind.CatchClause, semanticModel, syntax, type, constantValue, isImplicit) { - CaughtType = caughtType; - ExceptionLocal = exceptionLocal; + ExceptionType = exceptionType; + Locals = locals; } - - protected abstract IBlockStatement HandlerImpl { get; } /// - /// Type of exception to be handled. + /// Type of the exception handled by the catch clause. /// - public ITypeSymbol CaughtType { get; } - protected abstract IOperation FilterImpl { get; } + public ITypeSymbol ExceptionType { get; } /// - /// Symbol for the local catch variable bound to the caught exception. + /// Locals declared by the and/or clause. /// - public ILocalSymbol ExceptionLocal { get; } + public ImmutableArray Locals { get; } + protected abstract IOperation ExceptionDeclarationOrExpressionImpl { get; } + protected abstract IOperation FilterImpl { get; } + protected abstract IBlockStatement HandlerImpl { get; } public override IEnumerable Children { get { + yield return ExceptionDeclarationOrExpression; yield return Filter; yield return Handler; } } /// - /// Body of the exception handler. + /// Optional source for exception. This could be any of the following operation: + /// 1. Declaration for the local catch variable bound to the caught exception (C# and VB) OR + /// 2. Type expression for the caught expression type (C#) OR + /// 3. Null, indicating no expression (C#) + /// 4. Reference to an existing local or parameter (VB) OR + /// 5. An error expression (VB) /// - public IBlockStatement Handler => Operation.SetParentOperation(HandlerImpl, this); + public IOperation ExceptionDeclarationOrExpression => Operation.SetParentOperation(ExceptionDeclarationOrExpressionImpl, this); /// /// Filter expression to be executed to determine whether to handle the exception. /// public IOperation Filter => Operation.SetParentOperation(FilterImpl, this); + /// + /// Body of the exception handler. + /// + public IBlockStatement Handler => Operation.SetParentOperation(HandlerImpl, this); public override void Accept(OperationVisitor visitor) { visitor.VisitCatchClause(this); @@ -871,15 +879,17 @@ public override void Accept(OperationVisitor visitor) /// internal sealed partial class CatchClause : BaseCatchClause, ICatchClause { - public CatchClause(IBlockStatement handler, ITypeSymbol caughtType, IOperation filter, ILocalSymbol exceptionLocal, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue, bool isImplicit) : - base(caughtType, exceptionLocal, semanticModel, syntax, type, constantValue, isImplicit) + public CatchClause(IOperation exceptionDeclarationOrExpression, ITypeSymbol exceptionType, ImmutableArray locals, IOperation filter, IBlockStatement handler, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue, bool isImplicit) : + base(exceptionType, locals, semanticModel, syntax, type, constantValue, isImplicit) { - HandlerImpl = handler; + ExceptionDeclarationOrExpressionImpl = exceptionDeclarationOrExpression; FilterImpl = filter; + HandlerImpl = handler; } protected override IBlockStatement HandlerImpl { get; } protected override IOperation FilterImpl { get; } + protected override IOperation ExceptionDeclarationOrExpressionImpl { get; } } /// @@ -887,18 +897,21 @@ internal sealed partial class CatchClause : BaseCatchClause, ICatchClause /// internal sealed partial class LazyCatchClause : BaseCatchClause, ICatchClause { - private readonly Lazy _lazyHandler; + private readonly Lazy _lazyExceptionDeclarationOrExpression; private readonly Lazy _lazyFilter; + private readonly Lazy _lazyHandler; - public LazyCatchClause(Lazy handler, ITypeSymbol caughtType, Lazy filter, ILocalSymbol exceptionLocal, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue, bool isImplicit) : base(caughtType, exceptionLocal, semanticModel, syntax, type, constantValue, isImplicit) + public LazyCatchClause(Lazy exceptionDeclarationOrExpression, ITypeSymbol exceptionType, ImmutableArray locals, Lazy filter, Lazy handler, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue, bool isImplicit) + : base(exceptionType, locals, semanticModel, syntax, type, constantValue, isImplicit) { - _lazyHandler = handler ?? throw new System.ArgumentNullException(nameof(handler)); + _lazyExceptionDeclarationOrExpression = exceptionDeclarationOrExpression ?? throw new System.ArgumentNullException(nameof(exceptionDeclarationOrExpression)); _lazyFilter = filter ?? throw new System.ArgumentNullException(nameof(filter)); + _lazyHandler = handler ?? throw new System.ArgumentNullException(nameof(handler)); } - protected override IBlockStatement HandlerImpl => _lazyHandler.Value; - + protected override IOperation ExceptionDeclarationOrExpressionImpl => _lazyExceptionDeclarationOrExpression.Value; protected override IOperation FilterImpl => _lazyFilter.Value; + protected override IBlockStatement HandlerImpl => _lazyHandler.Value; } /// @@ -4328,15 +4341,13 @@ internal abstract partial class SymbolInitializer : Operation, ISymbolInitialize internal abstract partial class BaseTryStatement : Operation, ITryStatement { protected BaseTryStatement(SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue, bool isImplicit) : - // https://github.com/dotnet/roslyn/issues/22008 - // base(OperationKind.TryStatement, semanticModel, syntax, type, constantValue, isImplicit) - base(OperationKind.None, semanticModel, syntax, type, constantValue, isImplicit) + base(OperationKind.TryStatement, semanticModel, syntax, type, constantValue, isImplicit) { } protected abstract IBlockStatement BodyImpl { get; } protected abstract ImmutableArray CatchesImpl { get; } - protected abstract IBlockStatement FinallyHandlerImpl { get; } + protected abstract IBlockStatement FinallyImpl { get; } public override IEnumerable Children { get @@ -4346,7 +4357,7 @@ public override IEnumerable Children { yield return catche; } - yield return FinallyHandler; + yield return Finally; } } /// @@ -4360,7 +4371,7 @@ public override IEnumerable Children /// /// Finally handler of the try. /// - public IBlockStatement FinallyHandler => Operation.SetParentOperation(FinallyHandlerImpl, this); + public IBlockStatement Finally => Operation.SetParentOperation(FinallyImpl, this); public override void Accept(OperationVisitor visitor) { visitor.VisitTryStatement(this); @@ -4381,12 +4392,12 @@ internal sealed partial class TryStatement : BaseTryStatement, ITryStatement { BodyImpl = body; CatchesImpl = catches; - FinallyHandlerImpl = finallyHandler; + FinallyImpl = finallyHandler; } protected override IBlockStatement BodyImpl { get; } protected override ImmutableArray CatchesImpl { get; } - protected override IBlockStatement FinallyHandlerImpl { get; } + protected override IBlockStatement FinallyImpl { get; } } /// @@ -4409,7 +4420,7 @@ public LazyTryStatement(Lazy body, Lazy CatchesImpl => _lazyCatches.Value; - protected override IBlockStatement FinallyHandlerImpl => _lazyFinallyHandler.Value; + protected override IBlockStatement FinallyImpl => _lazyFinallyHandler.Value; } /// diff --git a/src/Compilers/Core/Portable/Operations/ICatchClause.cs b/src/Compilers/Core/Portable/Operations/ICatchClause.cs index aba85581d9fe73f38432690a4799c8d3d24effb6..4254f834fd3903b55d171edf1462ae2c705c3c73 100644 --- a/src/Compilers/Core/Portable/Operations/ICatchClause.cs +++ b/src/Compilers/Core/Portable/Operations/ICatchClause.cs @@ -11,24 +11,36 @@ namespace Microsoft.CodeAnalysis.Semantics /// This interface is reserved for implementation by its associated APIs. We reserve the right to /// change it in the future. /// - internal interface ICatchClause : IOperation // https://github.com/dotnet/roslyn/issues/22008 + public interface ICatchClause : IOperation { /// /// Body of the exception handler. /// IBlockStatement Handler { get; } + /// - /// Type of exception to be handled. + /// Locals declared by the and/or clause. /// - ITypeSymbol CaughtType { get; } + ImmutableArray Locals { get; } + /// - /// Filter expression to be executed to determine whether to handle the exception. + /// Type of the exception handled by the catch clause. /// - IOperation Filter { get; } + ITypeSymbol ExceptionType { get; } + /// - /// Symbol for the local catch variable bound to the caught exception. + /// Optional source for exception. This could be any of the following operation: + /// 1. Declaration for the local catch variable bound to the caught exception (C# and VB) OR + /// 2. Null, indicating no declaration or expression (C# and VB) + /// 3. Reference to an existing local or parameter (VB) OR + /// 4. Other expression for error scenarios (VB) /// - ILocalSymbol ExceptionLocal { get; } + IOperation ExceptionDeclarationOrExpression { get; } + + /// + /// Filter expression to be executed to determine whether to handle the exception. + /// + IOperation Filter { get; } } } diff --git a/src/Compilers/Core/Portable/Operations/ITryStatement.cs b/src/Compilers/Core/Portable/Operations/ITryStatement.cs index 4396a2cd73a8658f77de86a41f3aad538b5e0e9f..4aa94a05f2a3348c75cc59e247bb920219059983 100644 --- a/src/Compilers/Core/Portable/Operations/ITryStatement.cs +++ b/src/Compilers/Core/Portable/Operations/ITryStatement.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Semantics /// This interface is reserved for implementation by its associated APIs. We reserve the right to /// change it in the future. /// - internal interface ITryStatement : IOperation // https://github.com/dotnet/roslyn/issues/22008 + public interface ITryStatement : IOperation { /// /// Body of the try, over which the handlers are active. @@ -24,7 +24,7 @@ internal interface ITryStatement : IOperation // https://github.com/dotnet/rosly /// /// Finally handler of the try. /// - IBlockStatement FinallyHandler { get; } + IBlockStatement Finally { get; } } } diff --git a/src/Compilers/Core/Portable/Operations/OperationCloner.cs b/src/Compilers/Core/Portable/Operations/OperationCloner.cs index 1d7ce40caaf582b4ecb8457be51ea97ceed94782..8adcd1fdaabaece35b7b04b4b96617bfe76e59d5 100644 --- a/src/Compilers/Core/Portable/Operations/OperationCloner.cs +++ b/src/Compilers/Core/Portable/Operations/OperationCloner.cs @@ -139,14 +139,14 @@ public override IOperation VisitLockStatement(ILockStatement operation, object a return new LockStatement(Visit(operation.Expression), Visit(operation.Body), ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); } - internal override IOperation VisitTryStatement(ITryStatement operation, object argument) + public override IOperation VisitTryStatement(ITryStatement operation, object argument) { - return new TryStatement(Visit(operation.Body), VisitArray(operation.Catches), Visit(operation.FinallyHandler), ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); + return new TryStatement(Visit(operation.Body), VisitArray(operation.Catches), Visit(operation.Finally), ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); } - internal override IOperation VisitCatchClause(ICatchClause operation, object argument) + public override IOperation VisitCatchClause(ICatchClause operation, object argument) { - return new CatchClause(Visit(operation.Handler), operation.CaughtType, Visit(operation.Filter), operation.ExceptionLocal, ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); + return new CatchClause(Visit(operation.ExceptionDeclarationOrExpression), operation.ExceptionType, operation.Locals, Visit(operation.Filter), Visit(operation.Handler), ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); } public override IOperation VisitUsingStatement(IUsingStatement operation, object argument) diff --git a/src/Compilers/Core/Portable/Operations/OperationKind.cs b/src/Compilers/Core/Portable/Operations/OperationKind.cs index a5c668583672b321f0c007230e747665347d5aba..d2df4e8df7e35d524ce90240f85a96120be91300 100644 --- a/src/Compilers/Core/Portable/Operations/OperationKind.cs +++ b/src/Compilers/Core/Portable/Operations/OperationKind.cs @@ -38,9 +38,8 @@ public enum OperationKind YieldBreakStatement = 0xc, /// Indicates an . LockStatement = 0xd, - // https://github.com/dotnet/roslyn/issues/22008 - // /// Indicates an . - // TryStatement = 0xe, + /// Indicates an . + TryStatement = 0xe, /// Indicates an . UsingStatement = 0xf, /// Indicates an . @@ -205,9 +204,8 @@ public enum OperationKind /// Indicates an . Argument = 0x407, - // https://github.com/dotnet/roslyn/issues/22008 - // /// Indicates an . - // CatchClause = 0x408, + /// Indicates an . + CatchClause = 0x408, /// Indicates an . SwitchCase = 0x409, diff --git a/src/Compilers/Core/Portable/Operations/OperationVisitor.cs b/src/Compilers/Core/Portable/Operations/OperationVisitor.cs index 9159cc8b0d357b92700046c4365393c9b676fd06..9a076780c3eb9316b6ae6e12bd3973eef6c07b65 100644 --- a/src/Compilers/Core/Portable/Operations/OperationVisitor.cs +++ b/src/Compilers/Core/Portable/Operations/OperationVisitor.cs @@ -130,14 +130,12 @@ public virtual void VisitLockStatement(ILockStatement operation) DefaultVisit(operation); } - // https://github.com/dotnet/roslyn/issues/22008 - internal virtual void VisitTryStatement(ITryStatement operation) + public virtual void VisitTryStatement(ITryStatement operation) { DefaultVisit(operation); } - // https://github.com/dotnet/roslyn/issues/22008 - internal virtual void VisitCatchClause(ICatchClause operation) + public virtual void VisitCatchClause(ICatchClause operation) { DefaultVisit(operation); } @@ -623,14 +621,12 @@ public virtual TResult VisitLockStatement(ILockStatement operation, TArgument ar return DefaultVisit(operation, argument); } - // https://github.com/dotnet/roslyn/issues/22008 - internal virtual TResult VisitTryStatement(ITryStatement operation, TArgument argument) + public virtual TResult VisitTryStatement(ITryStatement operation, TArgument argument) { return DefaultVisit(operation, argument); } - // https://github.com/dotnet/roslyn/issues/22008 - internal virtual TResult VisitCatchClause(ICatchClause operation, TArgument argument) + public virtual TResult VisitCatchClause(ICatchClause operation, TArgument argument) { return DefaultVisit(operation, argument); } diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 3733e1d66b87479746d414a330893b66ece30541..b5872c3045bf3deaa7ccfeabd63ab65e6db99c8e 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -63,6 +63,7 @@ Microsoft.CodeAnalysis.OperationKind.BinaryOperatorExpression = 270 -> Microsoft Microsoft.CodeAnalysis.OperationKind.BlockStatement = 2 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.BranchStatement = 8 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.CaseClause = 1034 -> Microsoft.CodeAnalysis.OperationKind +Microsoft.CodeAnalysis.OperationKind.CatchClause = 1032 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.CoalesceExpression = 272 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.CollectionElementInitializerExpression = 290 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.CompoundAssignmentExpression = 281 -> Microsoft.CodeAnalysis.OperationKind @@ -122,6 +123,7 @@ Microsoft.CodeAnalysis.OperationKind.SwitchCase = 1033 -> Microsoft.CodeAnalysis Microsoft.CodeAnalysis.OperationKind.SwitchStatement = 4 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.ThrowExpression = 519 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.TranslatedQueryExpression = 297 -> Microsoft.CodeAnalysis.OperationKind +Microsoft.CodeAnalysis.OperationKind.TryStatement = 14 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.TupleExpression = 292 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.TypeOfExpression = 513 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.TypeParameterObjectCreationExpression = 275 -> Microsoft.CodeAnalysis.OperationKind @@ -230,6 +232,12 @@ Microsoft.CodeAnalysis.Semantics.IBranchStatement.BranchKind.get -> Microsoft.Co Microsoft.CodeAnalysis.Semantics.IBranchStatement.Target.get -> Microsoft.CodeAnalysis.ILabelSymbol Microsoft.CodeAnalysis.Semantics.ICaseClause Microsoft.CodeAnalysis.Semantics.ICaseClause.CaseKind.get -> Microsoft.CodeAnalysis.Semantics.CaseKind +Microsoft.CodeAnalysis.Semantics.ICatchClause +Microsoft.CodeAnalysis.Semantics.ICatchClause.ExceptionDeclarationOrExpression.get -> Microsoft.CodeAnalysis.IOperation +Microsoft.CodeAnalysis.Semantics.ICatchClause.ExceptionType.get -> Microsoft.CodeAnalysis.ITypeSymbol +Microsoft.CodeAnalysis.Semantics.ICatchClause.Filter.get -> Microsoft.CodeAnalysis.IOperation +Microsoft.CodeAnalysis.Semantics.ICatchClause.Handler.get -> Microsoft.CodeAnalysis.Semantics.IBlockStatement +Microsoft.CodeAnalysis.Semantics.ICatchClause.Locals.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Semantics.ICoalesceExpression Microsoft.CodeAnalysis.Semantics.ICoalesceExpression.Expression.get -> Microsoft.CodeAnalysis.IOperation Microsoft.CodeAnalysis.Semantics.ICoalesceExpression.WhenNull.get -> Microsoft.CodeAnalysis.IOperation @@ -420,6 +428,10 @@ Microsoft.CodeAnalysis.Semantics.IThrowExpression Microsoft.CodeAnalysis.Semantics.IThrowExpression.Expression.get -> Microsoft.CodeAnalysis.IOperation Microsoft.CodeAnalysis.Semantics.ITranslatedQueryExpression Microsoft.CodeAnalysis.Semantics.ITranslatedQueryExpression.Expression.get -> Microsoft.CodeAnalysis.IOperation +Microsoft.CodeAnalysis.Semantics.ITryStatement +Microsoft.CodeAnalysis.Semantics.ITryStatement.Body.get -> Microsoft.CodeAnalysis.Semantics.IBlockStatement +Microsoft.CodeAnalysis.Semantics.ITryStatement.Catches.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.Semantics.ITryStatement.Finally.get -> Microsoft.CodeAnalysis.Semantics.IBlockStatement Microsoft.CodeAnalysis.Semantics.ITupleExpression Microsoft.CodeAnalysis.Semantics.ITupleExpression.Elements.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Semantics.ITypeOfExpression @@ -510,6 +522,7 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitAwaitExpression(M virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitBinaryOperatorExpression(Microsoft.CodeAnalysis.Semantics.IBinaryOperatorExpression operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitBlockStatement(Microsoft.CodeAnalysis.Semantics.IBlockStatement operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitBranchStatement(Microsoft.CodeAnalysis.Semantics.IBranchStatement operation) -> void +virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitCatchClause(Microsoft.CodeAnalysis.Semantics.ICatchClause operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitCoalesceExpression(Microsoft.CodeAnalysis.Semantics.ICoalesceExpression operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitCollectionElementInitializerExpression(Microsoft.CodeAnalysis.Semantics.ICollectionElementInitializerExpression operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitCompoundAssignmentExpression(Microsoft.CodeAnalysis.Semantics.ICompoundAssignmentExpression operation) -> void @@ -575,6 +588,7 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitSwitchCase(Micros virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitSwitchStatement(Microsoft.CodeAnalysis.Semantics.ISwitchStatement operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitThrowExpression(Microsoft.CodeAnalysis.Semantics.IThrowExpression operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTranslatedQueryExpression(Microsoft.CodeAnalysis.Semantics.ITranslatedQueryExpression operation) -> void +virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTryStatement(Microsoft.CodeAnalysis.Semantics.ITryStatement operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTupleExpression(Microsoft.CodeAnalysis.Semantics.ITupleExpression operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTypeOfExpression(Microsoft.CodeAnalysis.Semantics.ITypeOfExpression operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTypeParameterObjectCreationExpression(Microsoft.CodeAnalysis.Semantics.ITypeParameterObjectCreationExpression operation) -> void @@ -596,6 +610,7 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.Vi virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitBinaryOperatorExpression(Microsoft.CodeAnalysis.Semantics.IBinaryOperatorExpression operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitBlockStatement(Microsoft.CodeAnalysis.Semantics.IBlockStatement operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitBranchStatement(Microsoft.CodeAnalysis.Semantics.IBranchStatement operation, TArgument argument) -> TResult +virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitCatchClause(Microsoft.CodeAnalysis.Semantics.ICatchClause operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitCoalesceExpression(Microsoft.CodeAnalysis.Semantics.ICoalesceExpression operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitCollectionElementInitializerExpression(Microsoft.CodeAnalysis.Semantics.ICollectionElementInitializerExpression operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitCompoundAssignmentExpression(Microsoft.CodeAnalysis.Semantics.ICompoundAssignmentExpression operation, TArgument argument) -> TResult @@ -661,6 +676,7 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.Vi virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitSwitchStatement(Microsoft.CodeAnalysis.Semantics.ISwitchStatement operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitThrowExpression(Microsoft.CodeAnalysis.Semantics.IThrowExpression operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTranslatedQueryExpression(Microsoft.CodeAnalysis.Semantics.ITranslatedQueryExpression operation, TArgument argument) -> TResult +virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTryStatement(Microsoft.CodeAnalysis.Semantics.ITryStatement operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTupleExpression(Microsoft.CodeAnalysis.Semantics.ITupleExpression operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTypeOfExpression(Microsoft.CodeAnalysis.Semantics.ITypeOfExpression operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTypeParameterObjectCreationExpression(Microsoft.CodeAnalysis.Semantics.ITypeParameterObjectCreationExpression operation, TArgument argument) -> TResult diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 8d3c03dd1dbb2295ca9908d19db48ba5258ff802..15b22e95950e17bbff083e4265ac9afabfb9819c 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -1165,7 +1165,6 @@ protected static string GetOperationTreeForTest(CSharpCompilation compilation, I protected static string GetOperationTreeForTest( string testSrc, - string expectedOperationTree, CSharpCompilationOptions compilationOptions = null, CSharpParseOptions parseOptions = null, bool useLatestFrameworkReferences = false) @@ -1193,7 +1192,7 @@ protected static void VerifyOperationTreeForTest(CSharpCompilation bool useLatestFrameworkReferences = false) where TSyntaxNode : SyntaxNode { - var actualOperationTree = GetOperationTreeForTest(testSrc, expectedOperationTree, compilationOptions, parseOptions, useLatestFrameworkReferences); + var actualOperationTree = GetOperationTreeForTest(testSrc, compilationOptions, parseOptions, useLatestFrameworkReferences); OperationTreeVerifier.Verify(expectedOperationTree, actualOperationTree); } diff --git a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb index 77791edf4ab5cf11defa2d65140951be978f2860..21aef0cc25ef78bbecbfc9901d4b57e8d5e6f6ab 100644 --- a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb +++ b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb @@ -818,7 +818,7 @@ Namespace Microsoft.CodeAnalysis.Semantics Return New ParameterReferenceExpression(parameter, _semanticModel, syntax, type, constantValue, isImplicit) End Function - Private Function CreateBoundLocalOperation(boundLocal As BoundLocal) As ILocalReferenceExpression + Private Function CreateBoundLocalOperation(boundLocal As BoundLocal) As IOperation Dim local As ILocalSymbol = boundLocal.LocalSymbol Dim isDeclaration As Boolean = False Dim syntax As SyntaxNode = boundLocal.Syntax @@ -1096,15 +1096,27 @@ Namespace Microsoft.CodeAnalysis.Semantics End Function Private Function CreateBoundCatchBlockOperation(boundCatchBlock As BoundCatchBlock) As ICatchClause - Dim handler As Lazy(Of IBlockStatement) = New Lazy(Of IBlockStatement)(Function() DirectCast(Create(boundCatchBlock.Body), IBlockStatement)) - Dim caughtType As ITypeSymbol = boundCatchBlock.ExceptionSourceOpt?.Type + Dim exceptionDeclarationOrExpression As Lazy(Of IOperation) = New Lazy(Of IOperation)( + Function() + If boundCatchBlock.LocalOpt IsNot Nothing AndAlso + boundCatchBlock.ExceptionSourceOpt?.Kind = BoundKind.Local AndAlso + boundCatchBlock.LocalOpt Is DirectCast(boundCatchBlock.ExceptionSourceOpt, BoundLocal).LocalSymbol Then + Return OperationFactory.CreateVariableDeclaration(boundCatchBlock.LocalOpt, initialValue:=Nothing, semanticModel:=_semanticModel, syntax:=boundCatchBlock.ExceptionSourceOpt.Syntax) + Else + Return Create(boundCatchBlock.ExceptionSourceOpt) + End If + End Function) + Dim exceptionType As ITypeSymbol = If(boundCatchBlock.ExceptionSourceOpt?.Type, DirectCast(_semanticModel.Compilation, VisualBasicCompilation).GetWellKnownType(WellKnownType.System_Exception)) + Dim locals As ImmutableArray(Of ILocalSymbol) = If(boundCatchBlock.LocalOpt IsNot Nothing, + ImmutableArray.Create(Of ILocalSymbol)(boundCatchBlock.LocalOpt), + ImmutableArray(Of ILocalSymbol).Empty) Dim filter As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() Create(boundCatchBlock.ExceptionFilterOpt)) - Dim exceptionLocal As ILocalSymbol = boundCatchBlock.LocalOpt + Dim handler As Lazy(Of IBlockStatement) = New Lazy(Of IBlockStatement)(Function() DirectCast(Create(boundCatchBlock.Body), IBlockStatement)) Dim syntax As SyntaxNode = boundCatchBlock.Syntax Dim type As ITypeSymbol = Nothing Dim constantValue As [Optional](Of Object) = New [Optional](Of Object)() Dim isImplicit As Boolean = boundCatchBlock.WasCompilerGenerated - Return New LazyCatchClause(handler, caughtType, filter, exceptionLocal, _semanticModel, syntax, type, constantValue, isImplicit) + Return New LazyCatchClause(exceptionDeclarationOrExpression, exceptionType, locals, filter, handler, _semanticModel, syntax, type, constantValue, isImplicit) End Function Private Function CreateBoundBlockOperation(boundBlock As BoundBlock) As IBlockStatement @@ -1114,7 +1126,7 @@ Namespace Microsoft.CodeAnalysis.Semantics ' https://github.com/dotnet/roslyn/issues/21776 Return boundBlock.Statements.Select(Function(n) (s:=Create(n), bound:=n)).Where( Function(tuple) - Return tuple.s.Kind <> OperationKind.None OrElse tuple.bound.Kind = BoundKind.TryStatement OrElse + Return tuple.s.Kind <> OperationKind.None OrElse tuple.bound.Kind = BoundKind.WithStatement OrElse tuple.bound.Kind = BoundKind.StopStatement OrElse tuple.bound.Kind = BoundKind.EndStatement End Function).Select(Function(tuple) tuple.s).ToImmutableArray() diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests.vb index bf45123d10bbc742f4ca51428744b26ab3a4c8f4..5ab6d2505deee5b88373b3729f6f54b4c572d665 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests.vb @@ -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. Imports Microsoft.CodeAnalysis.Semantics Imports Microsoft.CodeAnalysis.Test.Utilities @@ -625,7 +625,11 @@ Module Program End Module]]>.Value Dim expectedOperationTree = .Value Dim expectedOperationTree = 0') Left: IParameterReferenceExpression: i (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'i') Right: ILiteralExpression (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') @@ -74,14 +78,18 @@ End Class]]>.Value Dim expectedOperationTree = 0') Left: IParameterReferenceExpression: i (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'i') Right: ILiteralExpression (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0') @@ -104,5 +112,907 @@ IBlockStatement (3 statements) (OperationKind.BlockStatement) (Syntax: 'Private VerifyOperationTreeAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) End Sub + + + + Public Sub TryCatch_SingleCatchClause() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_SingleCatchClauseAndFilter() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_MultipleCatchClausesWithDifferentCaughtTypes() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_MultipleCatchClausesWithDuplicateCaughtTypes() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_CatchClauseWithTypeExpression() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_CatchClauseWithLocalReferenceExpression() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_CatchClauseWithParameterReferenceExpression() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_CatchClauseWithFieldReferenceExpression() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_CatchClauseWithErrorExpression() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_CatchClauseWithInvalidExpression() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_CatchClauseWithoutCaughtTypeOrExceptionLocal() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_FinallyWithoutCatchClause() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_TryBlockWithLocalDeclaration() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_CatchBlockWithLocalDeclaration() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_FinallyWithLocalDeclaration() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_InvalidCaughtType() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of TryBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_GetOperationForCatchBlock() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of CatchBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_GetOperationForFinallyBlock() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of FinallyBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_GetOperationForCatchExceptionIdentifier() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of IdentifierNameSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_GetOperationForCatchExceptionDeclaration() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of IdentifierNameSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_GetOperationForCatchFilterClause() + Dim source = .Value + + Dim expectedOperationTree = .Value + + ' GetOperation return Nothing for CatchFilterClauseSyntax + Assert.Null(GetOperationTreeForTest(Of CatchFilterClauseSyntax)(source).operation) + End Sub + + + + Public Sub TryCatch_GetOperationForCatchFilterClauseExpression() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of BinaryExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_GetOperationForCatchStatement() + Dim source = .Value + + ' GetOperation returns Nothing for CatchStatementSyntax + Assert.Null(GetOperationTreeForTest(Of CatchStatementSyntax)(source).operation) + End Sub + + + + Public Sub TryCatch_GetOperationForTryStatement() + Dim source = .Value + + ' GetOperation returns Nothing for TryStatementSyntax + Assert.Null(GetOperationTreeForTest(Of TryStatementSyntax)(source).operation) + End Sub + + + + Public Sub TryCatch_GetOperationForEndTryStatement() + Dim source = .Value + + ' GetOperation returns Nothing for End Try statement + Assert.Null(GetOperationTreeForTest(Of EndBlockStatementSyntax)(source).operation) + End Sub + + + + Public Sub TryCatch_GetOperationForFinallyStatement() + Dim source = .Value + + ' GetOperation returns Nothing for FinallyStatementSyntax + Assert.Null(GetOperationTreeForTest(Of FinallyStatementSyntax)(source).operation) + End Sub + + + + Public Sub TryCatch_GetOperationForStatementInTryBlock() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of ExpressionStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_GetOperationForStatementInCatchBlock() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of ExpressionStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TryCatch_GetOperationForStatementInFinallyBlock() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of ExpressionStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + End Class End Namespace diff --git a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs index 7506f61821db24a433b69edf66c611264544753c..747dfd83054008530be38488094661425feed67e 100644 --- a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs +++ b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs @@ -557,22 +557,25 @@ public override void VisitLockStatement(ILockStatement operation) Visit(operation.Body, "Body"); } - internal override void VisitTryStatement(ITryStatement operation) + public override void VisitTryStatement(ITryStatement operation) { LogString(nameof(ITryStatement)); LogCommonPropertiesAndNewLine(operation); Visit(operation.Body, "Body"); VisitArray(operation.Catches, "Catch clauses", logElementCount: true); - Visit(operation.FinallyHandler, "Finally"); + Visit(operation.Finally, "Finally"); } - internal override void VisitCatchClause(ICatchClause operation) + public override void VisitCatchClause(ICatchClause operation) { LogString(nameof(ICatchClause)); - LogString($" (Exception type: {operation.CaughtType?.ToTestDisplayString()}, Exception local: {operation.ExceptionLocal?.ToTestDisplayString()})"); + var exceptionTypeStr = operation.ExceptionType != null ? operation.ExceptionType.ToTestDisplayString() : "null"; + LogString($" (Exception type: {exceptionTypeStr})"); LogCommonPropertiesAndNewLine(operation); + LogLocals(operation.Locals); + Visit(operation.ExceptionDeclarationOrExpression, "ExceptionDeclarationOrExpression"); Visit(operation.Filter, "Filter"); Visit(operation.Handler, "Handler"); } diff --git a/src/Test/Utilities/Portable/Compilation/TestOperationWalker.cs b/src/Test/Utilities/Portable/Compilation/TestOperationWalker.cs index dfa6971cb30ceb226b879e280021d97fa11e15de..95d95dccb3ccef7ea4693184448d875b6e0077f7 100644 --- a/src/Test/Utilities/Portable/Compilation/TestOperationWalker.cs +++ b/src/Test/Utilities/Portable/Compilation/TestOperationWalker.cs @@ -180,15 +180,15 @@ public override void VisitLockStatement(ILockStatement operation) base.VisitLockStatement(operation); } - internal override void VisitTryStatement(ITryStatement operation) + public override void VisitTryStatement(ITryStatement operation) { base.VisitTryStatement(operation); } - internal override void VisitCatchClause(ICatchClause operation) + public override void VisitCatchClause(ICatchClause operation) { - var caughtType = operation.CaughtType; - var exceptionLocal = operation.ExceptionLocal; + var exceptionType = operation.ExceptionType; + var locals = operation.Locals; base.VisitCatchClause(operation); }