diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index af60a5d4a9a9a..6f086dff5993e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -581,6 +581,12 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic case SyntaxKind.IsPatternExpression: return BindIsPatternExpression((IsPatternExpressionSyntax)node, diagnostics); + case SyntaxKind.MatchExpression: + return BindMatchExpression((MatchExpressionSyntax)node, diagnostics); + + case SyntaxKind.ThrowExpression: + return BindThrowExpression((ThrowExpressionSyntax)node, diagnostics); + default: // NOTE: We could probably throw an exception here, but it's conceivable // that a non-parser syntax tree could reach this point with an unexpected diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 82171f39f207c..3a41a997dfb0e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -132,7 +132,11 @@ private BoundPattern BindConstantPattern(ConstantPatternSyntax node, BoundExpres private bool CheckValidPatternType(CSharpSyntaxNode typeSyntax, BoundExpression operand, TypeSymbol operandType, TypeSymbol patternType, bool isVar, DiagnosticBag diagnostics) { - if (patternType.IsNullableType() && !isVar) + if (operandType?.IsErrorType() == true || patternType?.IsErrorType() == true) + { + return false; + } + else if (patternType.IsNullableType() && !isVar) { // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type. Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType()); @@ -225,5 +229,121 @@ private bool CheckValidPatternType(CSharpSyntaxNode typeSyntax, BoundExpression DeclareLocalVariable(localSymbol, identifier, declType); return new BoundDeclarationPattern(node, localSymbol, boundDeclType, isVar, hasErrors); } + + private TypeSymbol BestType(MatchExpressionSyntax node, ArrayBuilder cases, DiagnosticBag diagnostics) + { + var types = ArrayBuilder.GetInstance(); + + int n = cases.Count; + for (int i = 0; i < n; i++) + { + var e = cases[i].Expression; + if (e.Type != null && !types.Contains(e.Type)) types.Add(e.Type); + } + + var allTypes = types.ToImmutableAndFree(); + + TypeSymbol bestType; + if (allTypes.IsDefaultOrEmpty) + { + diagnostics.Add(ErrorCode.ERR_AmbigMatch0, node.MatchToken.GetLocation()); + bestType = CreateErrorType(); + } + else if (allTypes.Length == 1) + { + bestType = allTypes[0]; + } + else + { + HashSet useSiteDiagnostics = null; + bestType = BestTypeInferrer.InferBestType( + allTypes, + Conversions, + ref useSiteDiagnostics); + diagnostics.Add(node, useSiteDiagnostics); + if ((object)bestType == null) + { + diagnostics.Add(ErrorCode.ERR_AmbigMatch1, node.MatchToken.GetLocation()); + bestType = CreateErrorType(); + } + } + + for (int i = 0; i < n; i++) + { + var c = cases[i]; + var e = c.Expression; + var converted = GenerateConversionForAssignment(bestType, e, diagnostics); + if (e != converted) + { + cases[i] = new BoundMatchCase(c.Syntax, c.Locals, c.Pattern, c.Guard, converted); + } + } + + return bestType; + } + + private BoundExpression BindMatchExpression(MatchExpressionSyntax node, DiagnosticBag diagnostics) + { + var expression = BindValue(node.Left, diagnostics, BindValueKind.RValue); + // TODO: any constraints on a switch expression must be enforced here. For example, + // it must have a type (not be target-typed, lambda, null, etc) + + var sectionBuilder = ArrayBuilder.GetInstance(); + foreach (var section in node.Sections) + { + var sectionBinder = new PatternVariableBinder(section, this); // each section has its own locals. + var pattern = sectionBinder.BindPattern(section.Pattern, expression, expression.Type, section.HasErrors, diagnostics); + var guard = (section.Condition != null) ? sectionBinder.BindBooleanExpression(section.Condition, diagnostics) : null; + var e = sectionBinder.BindExpression(section.Expression, diagnostics); + sectionBuilder.Add(new BoundMatchCase(section, sectionBinder.Locals, pattern, guard, e, section.HasErrors)); + } + + var resultType = BestType(node, sectionBuilder, diagnostics); + return new BoundMatchExpression(node, expression, sectionBuilder.ToImmutableAndFree(), resultType); + } + + private BoundExpression BindThrowExpression(ThrowExpressionSyntax node, DiagnosticBag diagnostics) + { + bool hasErrors = false; + if (node.Parent != null && !node.HasErrors) + { + switch (node.Parent.Kind()) + { + case SyntaxKind.ConditionalExpression: + { + var papa = (ConditionalExpressionSyntax)node.Parent; + if (node == papa.WhenTrue || node == papa.WhenFalse) goto syntaxOk; + break; + } + case SyntaxKind.CoalesceExpression: + { + var papa = (BinaryExpressionSyntax)node.Parent; + if (node == papa.Right) goto syntaxOk; + break; + } + case SyntaxKind.MatchSection: + { + var papa = (MatchSectionSyntax)node.Parent; + if (node == papa.Expression) goto syntaxOk; + break; + } + case SyntaxKind.ArrowExpressionClause: + { + var papa = (ArrowExpressionClauseSyntax)node.Parent; + if (node == papa.Expression) goto syntaxOk; + break; + } + default: + break; + } + + diagnostics.Add(ErrorCode.ERR_ThrowMisplaced, node.ThrowKeyword.GetLocation()); + hasErrors = true; + syntaxOk:; + } + + var thrownExpression = BindThrownExpression(node.Expression, diagnostics, ref hasErrors); + return new BoundThrowExpression(node, thrownExpression, null, hasErrors); + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 4f190cbd2c8d6..915287bdd90f0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -277,37 +277,44 @@ internal BoundStatement BindPossibleEmbeddedStatement(StatementSyntax node, Diag return BindStatement(node, diagnostics); } - private BoundThrowStatement BindThrow(ThrowStatementSyntax node, DiagnosticBag diagnostics) + private BoundExpression BindThrownExpression(ExpressionSyntax exprSyntax, DiagnosticBag diagnostics, ref bool hasErrors) { - BoundExpression boundExpr = null; - bool hasErrors = false; + var boundExpr = BindValue(exprSyntax, diagnostics, BindValueKind.RValue); - ExpressionSyntax exprSyntax = node.Expression; - if (exprSyntax != null) + // SPEC VIOLATION: The spec requires the thrown exception to have a type, and that the type + // be System.Exception or derived from System.Exception. (Or, if a type parameter, to have + // an effective base class that meets that criterion.) However, we allow the literal null + // to be thrown, even though it does not meet that criterion and will at runtime always + // produce a null reference exception. + + if (!boundExpr.IsLiteralNull()) { - boundExpr = BindValue(exprSyntax, diagnostics, BindValueKind.RValue); + var type = boundExpr.Type; - // SPEC VIOLATION: The spec requires the thrown exception to have a type, and that the type - // be System.Exception or derived from System.Exception. (Or, if a type parameter, to have - // an effective base class that meets that criterion.) However, we allow the literal null - // to be thrown, even though it does not meet that criterion and will at runtime always - // produce a null reference exception. + // If the expression is a lambda, anonymous method, or method group then it will + // have no compile-time type; give the same error as if the type was wrong. + HashSet useSiteDiagnostics = null; - if (!boundExpr.IsLiteralNull()) + if ((object)type == null || !type.IsErrorType() && !Compilation.IsExceptionType(type.EffectiveType(ref useSiteDiagnostics), ref useSiteDiagnostics)) { - var type = boundExpr.Type; + diagnostics.Add(ErrorCode.ERR_BadExceptionType, exprSyntax.Location); + hasErrors = true; + diagnostics.Add(exprSyntax, useSiteDiagnostics); + } + } - // If the expression is a lambda, anonymous method, or method group then it will - // have no compile-time type; give the same error as if the type was wrong. - HashSet useSiteDiagnostics = null; + return boundExpr; + } - if ((object)type == null || !type.IsErrorType() && !Compilation.IsExceptionType(type.EffectiveType(ref useSiteDiagnostics), ref useSiteDiagnostics)) - { - diagnostics.Add(ErrorCode.ERR_BadExceptionType, exprSyntax.Location); - hasErrors = true; - diagnostics.Add(exprSyntax, useSiteDiagnostics); - } - } + private BoundThrowStatement BindThrow(ThrowStatementSyntax node, DiagnosticBag diagnostics) + { + BoundExpression boundExpr = null; + bool hasErrors = false; + + ExpressionSyntax exprSyntax = node.Expression; + if (exprSyntax != null) + { + boundExpr = BindThrownExpression(exprSyntax, diagnostics, ref hasErrors); } else if (!this.Flags.Includes(BinderFlags.InCatchBlock)) { diff --git a/src/Compilers/CSharp/Portable/Binder/PatternVariableBinder.cs b/src/Compilers/CSharp/Portable/Binder/PatternVariableBinder.cs index dac12f3527bd0..ceda019570777 100644 --- a/src/Compilers/CSharp/Portable/Binder/PatternVariableBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/PatternVariableBinder.cs @@ -11,15 +11,15 @@ namespace Microsoft.CodeAnalysis.CSharp { internal sealed class PatternVariableBinder : LocalScopeBinder { - private readonly ExpressionSyntax expression; - private readonly ImmutableArray expressions; - private readonly ImmutableArray patterns; + private readonly ExpressionSyntax Expression; + private readonly ImmutableArray Expressions; + private readonly ImmutableArray Patterns; public readonly SyntaxNode Syntax; internal PatternVariableBinder(SyntaxNode syntax, ImmutableArray expressions, Binder next) : base(next) { this.Syntax = syntax; - this.expressions = expressions; + this.Expressions = expressions; } internal PatternVariableBinder(SyntaxNode syntax, IEnumerable declarations, Binder next) : base(next) @@ -31,7 +31,7 @@ internal PatternVariableBinder(SyntaxNode syntax, IEnumerable arguments, Binder next) : base(next) @@ -43,7 +43,7 @@ internal PatternVariableBinder(SyntaxNode syntax, IEnumerable ar var value = arg.Expression; if (value != null) expressions.Add(value); } - this.expressions = expressions.ToImmutableAndFree(); + this.Expressions = expressions.ToImmutableAndFree(); } internal PatternVariableBinder(SwitchSectionSyntax syntax, Binder next) : base(next) @@ -53,7 +53,7 @@ internal PatternVariableBinder(SwitchSectionSyntax syntax, Binder next) : base(n var patterns = ArrayBuilder.GetInstance(); foreach (var label in syntax.Labels) { - var match = label as CaseMatchLabelSyntax; + var match = label as CasePatternSwitchLabelSyntax; if (match != null) { patterns.Add(match.Pattern); @@ -64,8 +64,18 @@ internal PatternVariableBinder(SwitchSectionSyntax syntax, Binder next) : base(n } } - this.expressions = expressions.ToImmutableAndFree(); - this.patterns = patterns.ToImmutableAndFree(); + this.Expressions = expressions.ToImmutableAndFree(); + this.Patterns = patterns.ToImmutableAndFree(); + } + + internal PatternVariableBinder(MatchSectionSyntax syntax, Binder next) : base(next) + { + this.Syntax = syntax; + this.Patterns = ImmutableArray.Create(syntax.Pattern); + this.Expressions = syntax.Condition != null + ? ImmutableArray.Create(syntax.Expression, syntax.Condition) + : ImmutableArray.Create(syntax.Expression) + ; } internal PatternVariableBinder(ForStatementSyntax syntax, Binder next) : base(next) @@ -77,21 +87,22 @@ internal PatternVariableBinder(ForStatementSyntax syntax, Binder next) : base(ne var value = decl.Initializer?.Value; if (value != null) expressions.Add(value); } + if (syntax.Initializers != null) expressions.AddRange(syntax.Initializers); if (syntax.Condition != null) expressions.Add(syntax.Condition); if (syntax.Incrementors != null) expressions.AddRange(syntax.Incrementors); - this.expressions = expressions.ToImmutableAndFree(); + this.Expressions = expressions.ToImmutableAndFree(); } internal PatternVariableBinder(SyntaxNode syntax, ExpressionSyntax expression, Binder next) : base(next) { - this.expression = expression; + this.Expression = expression; this.Syntax = syntax; } protected override ImmutableArray BuildLocals() { - var patterns = PatternVariableFinder.FindPatternVariables(expression, expressions, this.patterns); + var patterns = PatternVariableFinder.FindPatternVariables(Expression, Expressions, this.Patterns); var builder = ArrayBuilder.GetInstance(); foreach (var pattern in patterns) { @@ -121,7 +132,7 @@ class PatternVariableFinder : CSharpSyntaxWalker finder.Visit(expression); if (!expressions.IsDefaultOrEmpty) foreach (var subExpression in expressions) { - finder.Visit(subExpression); + if(subExpression != null) finder.Visit(subExpression); } var result = finder.declarationPatterns; @@ -148,6 +159,10 @@ public override void VisitDeclarationPattern(DeclarationPatternSyntax node) public override void VisitSimpleLambdaExpression(SimpleLambdaExpressionSyntax node) { } public override void VisitAnonymousMethodExpression(AnonymousMethodExpressionSyntax node) { } public override void VisitQueryExpression(QueryExpressionSyntax node) { } + public override void VisitMatchExpression(MatchExpressionSyntax node) + { + Visit(node.Left); + } #region pool private static readonly ObjectPool s_poolInstance = CreatePool(); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs index c39686d7d68a4..646304ef4f57f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs @@ -22,6 +22,7 @@ public struct Conversion : IEquatable internal static readonly Conversion ImplicitNullable = new Conversion(ConversionKind.ImplicitNullable); internal static readonly Conversion ImplicitReference = new Conversion(ConversionKind.ImplicitReference); internal static readonly Conversion ImplicitEnumeration = new Conversion(ConversionKind.ImplicitEnumeration); + internal static readonly Conversion ImplicitThrow = new Conversion(ConversionKind.ImplicitThrow); internal static readonly Conversion AnonymousFunction = new Conversion(ConversionKind.AnonymousFunction); internal static readonly Conversion Boxing = new Conversion(ConversionKind.Boxing); internal static readonly Conversion NullLiteral = new Conversion(ConversionKind.NullLiteral); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs index 7c5736c978b91..fc42a6fcfcfd9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs @@ -12,6 +12,7 @@ internal enum ConversionKind : byte Identity, ImplicitNumeric, ImplicitEnumeration, + ImplicitThrow, ImplicitNullable, NullLiteral, ImplicitReference, diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs index a51a036c0e6a4..748516faf17cf 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs @@ -26,6 +26,7 @@ public static bool IsImplicitConversion(this ConversionKind conversionKind) case ConversionKind.Identity: case ConversionKind.ImplicitNumeric: case ConversionKind.ImplicitEnumeration: + case ConversionKind.ImplicitThrow: case ConversionKind.ImplicitNullable: case ConversionKind.NullLiteral: case ConversionKind.ImplicitReference: diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index 85500fead954d..17e61eb5b1b28 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -132,6 +132,11 @@ private Conversion ClassifyImplicitBuiltInConversionFromExpression(BoundExpressi return Conversion.ImplicitEnumeration; } + if (HasImplicitThrowConversion(sourceExpression, destination)) + { + return Conversion.ImplicitThrow; + } + var kind = ClassifyImplicitConstantExpressionConversion(sourceExpression, destination); if (kind != ConversionKind.NoConversion) { @@ -175,6 +180,11 @@ private Conversion ClassifyImplicitBuiltInConversionFromExpression(BoundExpressi return Conversion.NoConversion; } + private bool HasImplicitThrowConversion(BoundExpression sourceExpression, TypeSymbol destination) + { + return sourceExpression.Kind == BoundKind.ThrowExpression; + } + public Conversion ClassifyImplicitConversionFromExpression(BoundExpression sourceExpression, TypeSymbol destination, ref HashSet useSiteDiagnostics) { Debug.Assert(sourceExpression != null); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs index d54f45da86d24..ac9147e28e596 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs @@ -596,6 +596,9 @@ private static bool IsEncompassingImplicitConversionKind(ConversionKind kind) // Added to spec in Roslyn timeframe. case ConversionKind.NullLiteral: case ConversionKind.NullToPointer: + + // Added for C# 7. + case ConversionKind.ImplicitThrow: return true; default: diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs index c3c0a53454b49..4554433c29ad5 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs @@ -175,7 +175,7 @@ private void BuildSwitchLabels(SyntaxList labelsSyntax, ref A case SyntaxKind.DefaultSwitchLabel: break; - case SyntaxKind.CaseMatchLabel: + case SyntaxKind.CasePatternSwitchLabel: throw new NotImplementedException(); default: throw ExceptionUtilities.UnexpectedValue(labelSyntax.Kind()); @@ -535,7 +535,7 @@ private BoundSwitchLabel BindSwitchSectionLabel(SwitchLabelSyntax node, Diagnost // Fetch the matching switch case label symbols matchedLabelSymbols = FindMatchingSwitchCaseLabels(labelExpressionConstant, caseLabelSyntax); break; - case SyntaxKind.CaseMatchLabel: + case SyntaxKind.CasePatternSwitchLabel: // pattern matching in case is not yet implemented. if (!node.HasErrors) { diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchBinder_BindPatternSwitch.cs b/src/Compilers/CSharp/Portable/Binder/SwitchBinder_BindPatternSwitch.cs index 8f5cddff432d8..9fe467a933124 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchBinder_BindPatternSwitch.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchBinder_BindPatternSwitch.cs @@ -14,38 +14,38 @@ namespace Microsoft.CodeAnalysis.CSharp { internal partial class SwitchBinder { - private BoundMatchStatement BindPatternSwitch(SwitchStatementSyntax node, DiagnosticBag diagnostics) + private BoundPatternSwitchStatement BindPatternSwitch(SwitchStatementSyntax node, DiagnosticBag diagnostics) { var boundSwitchExpression = BindValue(node.Expression, diagnostics, BindValueKind.RValue); // TODO: any constraints on a switch expression must be enforced here. For example, // it must have a type (not be target-typed, lambda, null, etc) DefaultSwitchLabelSyntax defaultLabel = null; - ImmutableArray boundMatchSections = BindMatchSections(boundSwitchExpression, node.Sections, ref defaultLabel, diagnostics); + ImmutableArray boundPatternSwitchSections = BindPatternSwitchSections(boundSwitchExpression, node.Sections, ref defaultLabel, diagnostics); - return new BoundMatchStatement(node, boundSwitchExpression, Locals, LocalFunctions, boundMatchSections, this.BreakLabel); + return new BoundPatternSwitchStatement(node, boundSwitchExpression, Locals, LocalFunctions, boundPatternSwitchSections, this.BreakLabel); } - private ImmutableArray BindMatchSections(BoundExpression boundSwitchExpression, SyntaxList sections, ref DefaultSwitchLabelSyntax defaultLabel, DiagnosticBag diagnostics) + private ImmutableArray BindPatternSwitchSections(BoundExpression boundSwitchExpression, SyntaxList sections, ref DefaultSwitchLabelSyntax defaultLabel, DiagnosticBag diagnostics) { // Bind match sections - var boundMatchSectionsBuilder = ArrayBuilder.GetInstance(); + var boundPatternSwitchSectionsBuilder = ArrayBuilder.GetInstance(); foreach (var sectionSyntax in sections) { - boundMatchSectionsBuilder.Add(BindMatchSection(boundSwitchExpression, sectionSyntax, ref defaultLabel, diagnostics)); + boundPatternSwitchSectionsBuilder.Add(BindPatternSwitchSection(boundSwitchExpression, sectionSyntax, ref defaultLabel, diagnostics)); } - return boundMatchSectionsBuilder.ToImmutableAndFree(); + return boundPatternSwitchSectionsBuilder.ToImmutableAndFree(); } - private BoundMatchSection BindMatchSection(BoundExpression boundSwitchExpression, SwitchSectionSyntax node, ref DefaultSwitchLabelSyntax defaultLabel, DiagnosticBag diagnostics) + private BoundPatternSwitchSection BindPatternSwitchSection(BoundExpression boundSwitchExpression, SwitchSectionSyntax node, ref DefaultSwitchLabelSyntax defaultLabel, DiagnosticBag diagnostics) { // Bind match section labels - var boundLabelsBuilder = ArrayBuilder.GetInstance(); + var boundLabelsBuilder = ArrayBuilder.GetInstance(); var sectionBinder = (PatternVariableBinder)this.GetBinder(node); // this binder can bind pattern variables from the section. foreach (var labelSyntax in node.Labels) { - BoundMatchLabel boundLabel = BindMatchSectionLabel(sectionBinder, boundSwitchExpression, labelSyntax, ref defaultLabel, diagnostics); + BoundPatternSwitchLabel boundLabel = BindPatternSwitchSectionLabel(sectionBinder, boundSwitchExpression, labelSyntax, ref defaultLabel, diagnostics); boundLabelsBuilder.Add(boundLabel); } @@ -56,17 +56,17 @@ private BoundMatchSection BindMatchSection(BoundExpression boundSwitchExpression boundStatementsBuilder.Add(sectionBinder.BindStatement(statement, diagnostics)); } - return new BoundMatchSection(node, sectionBinder.Locals, boundLabelsBuilder.ToImmutableAndFree(), boundStatementsBuilder.ToImmutableAndFree()); + return new BoundPatternSwitchSection(node, sectionBinder.Locals, boundLabelsBuilder.ToImmutableAndFree(), boundStatementsBuilder.ToImmutableAndFree()); } - private static BoundMatchLabel BindMatchSectionLabel(Binder sectionBinder, BoundExpression boundSwitchExpression, SwitchLabelSyntax node, ref DefaultSwitchLabelSyntax defaultLabel, DiagnosticBag diagnostics) + private static BoundPatternSwitchLabel BindPatternSwitchSectionLabel(Binder sectionBinder, BoundExpression boundSwitchExpression, SwitchLabelSyntax node, ref DefaultSwitchLabelSyntax defaultLabel, DiagnosticBag diagnostics) { switch (node.Kind()) { - case SyntaxKind.CaseMatchLabel: + case SyntaxKind.CasePatternSwitchLabel: { - var matchLabelSyntax = (CaseMatchLabelSyntax)node; - return new BoundMatchLabel(node, + var matchLabelSyntax = (CasePatternSwitchLabelSyntax)node; + return new BoundPatternSwitchLabel(node, sectionBinder.BindPattern(matchLabelSyntax.Pattern, boundSwitchExpression, boundSwitchExpression.Type, node.HasErrors, diagnostics), matchLabelSyntax.Condition != null ? sectionBinder.BindBooleanExpression(matchLabelSyntax.Condition, diagnostics) : null, node.HasErrors); } @@ -83,7 +83,7 @@ private static BoundMatchLabel BindMatchSectionLabel(Binder sectionBinder, Bound boundLabelExpression = sectionBinder.CreateConversion(boundLabelExpression, sectionBinder.GetSpecialType(SpecialType.System_Object, diagnostics, node), diagnostics); } var pattern = new BoundConstantPattern(node, boundLabelExpression, node.HasErrors); - return new BoundMatchLabel(node, pattern, null, node.HasErrors); + return new BoundPatternSwitchLabel(node, pattern, null, node.HasErrors); } case SyntaxKind.DefaultSwitchLabel: @@ -95,7 +95,7 @@ private static BoundMatchLabel BindMatchSectionLabel(Binder sectionBinder, Bound diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, "default"); } defaultLabel = defaultLabelSyntax; - return new BoundMatchLabel(node, pattern, null, node.HasErrors); // note that this is semantically last! + return new BoundPatternSwitchLabel(node, pattern, null, node.HasErrors); // note that this is semantically last! } default: @@ -114,7 +114,7 @@ private bool IsPatternSwitch(SwitchStatementSyntax node) { foreach (var label in section.Labels) { - if (label.Kind() == SyntaxKind.CaseMatchLabel) + if (label.Kind() == SyntaxKind.CasePatternSwitchLabel) { _isPatternSwitch = true; return true; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 7819b9e374f54..17d824c93e9b8 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -677,22 +677,22 @@ - + - + - + - + - + @@ -1372,6 +1372,22 @@ + + + + + + + + + + + + + + + + diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index d487722cba763..433669faf13d0 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -362,6 +362,7 @@ Semantics.ConversionKind IConversionExpression.Conversion case CSharp.ConversionKind.ImplicitDynamic: case CSharp.ConversionKind.ExplicitEnumeration: case CSharp.ConversionKind.ImplicitEnumeration: + case CSharp.ConversionKind.ImplicitThrow: case CSharp.ConversionKind.ExplicitNullable: case CSharp.ConversionKind.ImplicitNullable: case CSharp.ConversionKind.ExplicitNumeric: diff --git a/src/Compilers/CSharp/Portable/BoundTree/Statement.cs b/src/Compilers/CSharp/Portable/BoundTree/Statement.cs index 9082474354db8..16764947c99ae 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Statement.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Statement.cs @@ -353,7 +353,7 @@ partial class BoundLocalFunctionStatement protected override OperationKind StatementKind => OperationKind.LocalFunctionStatement; } - partial class BoundMatchStatement + partial class BoundPatternSwitchStatement { // TODO: this may need its own OperationKind. protected override OperationKind StatementKind => OperationKind.SwitchStatement; diff --git a/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj b/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj index 6c4e326b50382..4a02314af11fe 100644 --- a/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj +++ b/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj @@ -485,6 +485,7 @@ + diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index f2ac89f9f256b..7ae95a8cd7793 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -340,6 +340,24 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to Type of match expression cannot be determined because no branch has a type. + /// + internal static string ERR_AmbigMatch0 { + get { + return ResourceManager.GetString("ERR_AmbigMatch0", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type of match expression cannot be determined because no branch has a most specific type. + /// + internal static string ERR_AmbigMatch1 { + get { + return ResourceManager.GetString("ERR_AmbigMatch1", resourceCulture); + } + } + /// /// Looks up a localized string similar to Ambiguity between '{0}' and '{1}'. /// @@ -8143,6 +8161,15 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to A throw expression is not allowed here.. + /// + internal static string ERR_ThrowMisplaced { + get { + return ResourceManager.GetString("ERR_ThrowMisplaced", resourceCulture); + } + } + /// /// Looks up a localized string similar to Catch clauses cannot follow the general catch clause of a try statement. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 1c35bb6a1921a..9a346bef189a2 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -4690,4 +4690,13 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Invalid operand for pattern match. - \ No newline at end of file + + Type of match expression cannot be determined because no branch has a type + + + Type of match expression cannot be determined because no branch has a most specific type + + + A throw expression is not allowed here. + + diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs index 0b2c558a1d787..889d98cdf33be 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs @@ -73,6 +73,7 @@ private void EmitConversion(BoundConversion conversion) case ConversionKind.MethodGroup: case ConversionKind.ImplicitDynamic: case ConversionKind.ExplicitDynamic: + case ConversionKind.ImplicitThrow: // None of these things should reach codegen (yet? maybe?) throw ExceptionUtilities.UnexpectedValue(conversion.ConversionKind); case ConversionKind.PointerToVoid: diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index 38ea770e7d301..495b54d0dc8eb 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -270,6 +270,10 @@ private void EmitExpressionCore(BoundExpression expression, bool used) EmitPseudoVariableValue((BoundPseudoVariable)expression, used); break; + case BoundKind.ThrowExpression: + EmitThrowExpression((BoundThrowExpression)expression, used); + break; + default: // Code gen should not be invoked if there are errors. Debug.Assert(expression.Kind != BoundKind.BadExpression); @@ -279,6 +283,21 @@ private void EmitExpressionCore(BoundExpression expression, bool used) } } + private void EmitThrowExpression(BoundThrowExpression node, bool used) + { + this.EmitExpression(node.Expression, true); + var thrownType = node.Expression.Type; + if (thrownType?.TypeKind == TypeKind.TypeParameter) + { + this.EmitBox(thrownType, node.Expression.Syntax); + } + + _builder.EmitThrow(isRethrow: false); + + // to satisfy invariants, we push a default value to pretend to adjust the stack height + EmitDefaultValue(node.Type, used, node.Syntax); + } + private void EmitComplexConditionalReceiver(BoundComplexConditionalReceiver expression, bool used) { Debug.Assert(!expression.Type.IsReferenceType); diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs index 8d02785270af7..8e83f90d08f3f 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs @@ -148,7 +148,7 @@ private void EmitThrowStatement(BoundThrowStatement node) var exprType = expr.Type; // Expression type will be null for "throw null;". - if (((object)exprType != null) && (exprType.TypeKind == TypeKind.TypeParameter)) + if (exprType?.TypeKind == TypeKind.TypeParameter) { this.EmitBox(exprType, expr.Syntax); } diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 2046fe01d5f3e..ceefb3cae918b 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1323,5 +1323,8 @@ internal enum ErrorCode ERR_CantInferVoid = 8104, ERR_PatternNullableType = 8105, ERR_BadIsPatternExpression = 8106, + ERR_AmbigMatch0 = 8107, + ERR_AmbigMatch1 = 8108, + ERR_ThrowMisplaced = 8109, } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/ControlFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/ControlFlowPass.cs index 352b1f512f96a..8281716d79ddf 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/ControlFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/ControlFlowPass.cs @@ -333,14 +333,14 @@ public override BoundNode VisitSwitchSection(BoundSwitchSection node, bool lastS return null; } - protected override void VisitMatchSection(BoundMatchSection node, bool isLastSection) + protected override void VisitPatternSwitchSection(BoundPatternSwitchSection node, bool isLastSection) { - base.VisitMatchSection(node, isLastSection); + base.VisitPatternSwitchSection(node, isLastSection); // Check for switch section fall through error if (this.State.Alive) { - var syntax = node.MatchLabels.Last().Pattern.Syntax; + var syntax = node.PatternSwitchLabels.Last().Pattern.Syntax; Diagnostics.Add(isLastSection ? ErrorCode.ERR_SwitchFallOut : ErrorCode.ERR_SwitchFallThrough, new SourceLocation(syntax), syntax.ToString()); this.State.Reported = true; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs index af853978f4f0c..2d12805fe0464 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs @@ -1356,20 +1356,20 @@ public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) return result; } - public override BoundNode VisitMatchStatement(BoundMatchStatement node) + public override BoundNode VisitPatternSwitchStatement(BoundPatternSwitchStatement node) { DeclareVariables(node.InnerLocals); - var result = base.VisitMatchStatement(node); + var result = base.VisitPatternSwitchStatement(node); ReportUnusedVariables(node.InnerLocals); ReportUnusedVariables(node.InnerLocalFunctions); return result; } - protected override void VisitMatchSection(BoundMatchSection node, bool isLastSection) + protected override void VisitPatternSwitchSection(BoundPatternSwitchSection node, bool isLastSection) { // TODO: this an probably depend more heavily on the base class implementation. DeclareVariables(node.Locals); - base.VisitMatchSection(node, isLastSection); + base.VisitPatternSwitchSection(node, isLastSection); } private void AssignPatternVariables(BoundPattern pattern) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass_Switch.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass_Switch.cs index d732a67dc1114..5bef8792617e6 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass_Switch.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass_Switch.cs @@ -127,22 +127,22 @@ protected virtual void VisitSwitchSectionLabel(LabelSymbol label, BoundSwitchSec // Below here is the implementation for the pattern-matching variation of the switch statement. // =========================== - public override BoundNode VisitMatchStatement(BoundMatchStatement node) + public override BoundNode VisitPatternSwitchStatement(BoundPatternSwitchStatement node) { // visit switch header - LocalState breakState = VisitMatchHeader(node); + LocalState breakState = VisitPatternSwitchHeader(node); // visit switch block - VisitMatchBlock(node); + VisitPatternSwitchBlock(node); IntersectWith(ref breakState, ref this.State); ResolveBreaks(breakState, node.BreakLabel); return null; } - private void VisitMatchBlock(BoundMatchStatement node) + private void VisitPatternSwitchBlock(BoundPatternSwitchStatement node) { var afterSwitchState = UnreachableState(); - var switchSections = node.MatchSections; + var switchSections = node.PatternSwitchSections; var iLastSection = (switchSections.Length - 1); var dispatchState = this.State.Clone(); @@ -150,7 +150,7 @@ private void VisitMatchBlock(BoundMatchStatement node) for (var iSection = 0; iSection <= iLastSection; iSection++) { SetState(dispatchState.Clone()); - VisitMatchSection(switchSections[iSection], iSection == iLastSection); + VisitPatternSwitchSection(switchSections[iSection], iSection == iLastSection); // Even though it is illegal for the end of a switch section to be reachable, in erroneous // code it may be reachable. We treat that as an implicit break (branch to afterSwitchState). IntersectWith(ref afterSwitchState, ref this.State); @@ -162,21 +162,21 @@ private void VisitMatchBlock(BoundMatchStatement node) /// /// Visit the switch expression, and return the initial break state. /// - private LocalState VisitMatchHeader(BoundMatchStatement node) + private LocalState VisitPatternSwitchHeader(BoundPatternSwitchStatement node) { // decide if the switch has the moral equivalent of a default label. bool hasDefaultLabel = false; - foreach (var section in node.MatchSections) + foreach (var section in node.PatternSwitchSections) { - foreach (var boundMatchLabel in section.MatchLabels) + foreach (var boundPatternSwitchLabel in section.PatternSwitchLabels) { - if (boundMatchLabel.Guard != null && !IsConstantTrue(boundMatchLabel.Guard)) + if (boundPatternSwitchLabel.Guard != null && !IsConstantTrue(boundPatternSwitchLabel.Guard)) { continue; } - if (boundMatchLabel.Pattern.Kind == BoundKind.WildcardPattern || - boundMatchLabel.Pattern.Kind == BoundKind.DeclarationPattern && ((BoundDeclarationPattern)boundMatchLabel.Pattern).IsVar) + if (boundPatternSwitchLabel.Pattern.Kind == BoundKind.WildcardPattern || + boundPatternSwitchLabel.Pattern.Kind == BoundKind.DeclarationPattern && ((BoundDeclarationPattern)boundPatternSwitchLabel.Pattern).IsVar) { hasDefaultLabel = true; goto foundDefaultLabel; @@ -199,12 +199,12 @@ private LocalState VisitMatchHeader(BoundMatchStatement node) } } - protected virtual void VisitMatchSection(BoundMatchSection node, bool isLastSection) + protected virtual void VisitPatternSwitchSection(BoundPatternSwitchSection node, bool isLastSection) { // visit switch section labels var initialState = this.State; var afterGuardState = UnreachableState(); - foreach (var label in node.MatchLabels) + foreach (var label in node.PatternSwitchLabels) { SetState(initialState.Clone()); VisitPattern(label.Pattern); @@ -224,5 +224,34 @@ protected virtual void VisitMatchSection(BoundMatchSection node, bool isLastSect public virtual void VisitPattern(BoundPattern pattern) { } + + public override BoundNode VisitThrowExpression(BoundThrowExpression node) + { + VisitRvalue(node.Expression); + SetUnreachable(); + return node; + } + + public override BoundNode VisitMatchExpression(BoundMatchExpression node) + { + VisitRvalue(node.Left); + var initialState = this.State; + var endState = UnreachableState(); + foreach (var c in node.Cases) + { + SetState(initialState.Clone()); + VisitPattern(c.Pattern); + if (c.Guard != null) + { + VisitCondition(c.Guard); + SetState(StateWhenTrue); + } + VisitRvalue(c.Expression); + IntersectWith(ref endState, ref this.State); + } + + SetState(endState); + return node; + } } } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index 2d6acdc9c3621..0c9def2f0c66a 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -323,6 +323,7 @@ private static bool IsSafeForReordering(BoundExpression expression, RefKind kind break; case ConversionKind.ExplicitUserDefined: case ConversionKind.ImplicitUserDefined: + case ConversionKind.ImplicitThrow: return false; default: // Unhandled conversion kind in reordering logic diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 8d5d10a9797e4..0a6753b5a5e8b 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -224,6 +224,13 @@ private static bool IsFloatPointExpressionOfUnknownPrecision(BoundExpression rew } break; + case ConversionKind.ImplicitThrow: + { + // the operand must be a bound throw expression + var operand = (BoundThrowExpression)rewrittenOperand; + return _factory.ThrowExpression(operand.Expression, rewrittenType); + } + case ConversionKind.ImplicitEnumeration: // A conversion from constant zero to nullable is actually classified as an // implicit enumeration conversion, not an implicit nullable conversion. diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_MatchStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_MatchStatement.cs index db63a51f830f0..7d58757ad9e0e 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_MatchStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_MatchStatement.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp internal partial class LocalRewriter { // Rewriting for pattern-matching switch statements. - public override BoundNode VisitMatchStatement(BoundMatchStatement node) + public override BoundNode VisitPatternSwitchStatement(BoundPatternSwitchStatement node) { var statements = ArrayBuilder.GetInstance(); @@ -24,11 +24,11 @@ public override BoundNode VisitMatchStatement(BoundMatchStatement node) // save the default label, if and when we find it. LabelSymbol defaultLabel = null; - foreach (var section in node.MatchSections) + foreach (var section in node.PatternSwitchSections) { BoundExpression sectionCondition = _factory.Literal(false); bool isDefaultSection = false; - foreach (var label in section.MatchLabels) + foreach (var label in section.PatternSwitchLabels) { if (label.Syntax.Kind() == SyntaxKind.DefaultSwitchLabel) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs index 07a39ff43aab0..827e13df1e874 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs @@ -148,5 +148,32 @@ BoundExpression DeclPattern(CSharpSyntaxNode syntax, BoundExpression input, Loca _factory.SpecialType(SpecialType.System_Boolean)); } } + + public override BoundNode VisitMatchExpression(BoundMatchExpression node) + { + // TODO: find a better way to preserve the scope of pattern variables in a match. + + // we translate a match expression into a sequence of conditionals. + // However, because we have no way to express the proper scope of the pattern + // variables, we lump them all together at the top. + var locals = ArrayBuilder.GetInstance(); + BoundAssignmentOperator initialStore; + var temp =_factory.StoreToTemp(node.Left, out initialStore); + locals.Add(temp.LocalSymbol); + int n = node.Cases.Length; + BoundExpression result = _factory.ThrowNullExpression(node.Type); + for (int i = n-1; i >= 0; i--) + { + var c = node.Cases[i]; + locals.AddRange(c.Locals); + var condition = TranslatePattern(temp, c.Pattern); + if (c.Guard != null) condition = _factory.LogicalAnd(condition, VisitExpression(c.Guard)); + var consequence = VisitExpression(c.Expression); + _factory.Syntax = c.Syntax; + result = _factory.Conditional(condition, consequence, result, node.Type); + } + + return _factory.Sequence(locals.ToImmutableAndFree(), initialStore, result); + } } } diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index 28b091e8a6bda..ed8d0789309c9 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -903,6 +903,16 @@ public BoundStatement ThrowNull() return Throw(Null(Compilation.GetWellKnownType(Microsoft.CodeAnalysis.WellKnownType.System_Exception))); } + public BoundExpression ThrowNullExpression(TypeSymbol type) + { + return new BoundThrowExpression(Syntax, Null(Compilation.GetWellKnownType(Microsoft.CodeAnalysis.WellKnownType.System_Exception)), type); + } + + public BoundExpression ThrowExpression(BoundExpression thrown, TypeSymbol type) + { + return new BoundThrowExpression(thrown.Syntax, thrown, type); + } + public BoundExpression Null(TypeSymbol type) { BoundExpression nullLiteral = new BoundLiteral(Syntax, ConstantValue.Null, type) { WasCompilerGenerated = true }; @@ -1023,6 +1033,11 @@ public BoundExpression Convert(TypeSymbol type, BoundExpression arg, Conversion arg = Convert(conversion.Method.Parameters[0].Type, arg); } + if (conversion.Kind == ConversionKind.ImplicitReference && arg.IsLiteralNull()) + { + return Null(type); + } + return new BoundConversion(Syntax, arg, conversion, isChecked, true, null, type) { WasCompilerGenerated = true }; } diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index bb6ec5f66b5eb..73190b5e3cc38 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -7796,15 +7796,15 @@ private SwitchSectionSyntax ParseSwitchSection() } if (node is PatternSyntax) { - SyntaxToken with = null; ; + SyntaxToken when = null; ; ExpressionSyntax condition = null; if (this.CurrentToken.ContextualKind == SyntaxKind.WhenKeyword) { - with = this.EatContextualToken(SyntaxKind.WhenKeyword); + when = this.EatContextualToken(SyntaxKind.WhenKeyword); condition = ParseSubExpression(Precedence.Expression); } colon = this.EatToken(SyntaxKind.ColonToken); - label = _syntaxFactory.CaseMatchLabel(specifier, (PatternSyntax)node, with, condition, colon); + label = _syntaxFactory.CasePatternSwitchLabel(specifier, (PatternSyntax)node, when, condition, colon); label = CheckFeatureAvailability(label, MessageID.IDS_FeaturePatternMatching); } else @@ -8349,6 +8349,7 @@ private bool IsPossibleExpression() case SyntaxKind.NewKeyword: case SyntaxKind.DelegateKeyword: case SyntaxKind.ColonColonToken: // bad aliased name + case SyntaxKind.ThrowKeyword: return true; case SyntaxKind.IdentifierToken: // Specifically allow the from contextual keyword, because it can always be the start of an @@ -8381,7 +8382,6 @@ private static bool IsInvalidSubExpression(SyntaxKind kind) case SyntaxKind.LockKeyword: case SyntaxKind.ReturnKeyword: case SyntaxKind.SwitchKeyword: - case SyntaxKind.ThrowKeyword: case SyntaxKind.TryKeyword: case SyntaxKind.UsingKeyword: case SyntaxKind.WhileKeyword: @@ -8475,6 +8475,7 @@ private static Precedence GetPrecedence(SyntaxKind op) case SyntaxKind.IsExpression: case SyntaxKind.AsExpression: case SyntaxKind.IsPatternExpression: + case SyntaxKind.MatchExpression: return Precedence.Relational; case SyntaxKind.LeftShiftExpression: case SyntaxKind.RightShiftExpression: @@ -8606,6 +8607,11 @@ private ExpressionSyntax ParseSubExpressionCore(Precedence precedence) return this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_InvalidExprTerm, SyntaxFacts.GetText(tk)); } + if (precedence <= Precedence.Coalescing && tk == SyntaxKind.ThrowKeyword) + { + return ParseThrowExpression(); + } + // No left operand, so we need to parse one -- possibly preceded by a // unary operator. if (IsExpectedPrefixUnaryOperator(tk)) @@ -8647,7 +8653,7 @@ private ExpressionSyntax ParseSubExpressionCore(Precedence precedence) while (true) { // We either have a binary or assignment operator here, or we're finished. - tk = this.CurrentToken.Kind; + tk = this.CurrentToken.ContextualKind; bool isAssignmentOperator = false; if (IsExpectedBinaryOperator(tk)) @@ -8659,6 +8665,10 @@ private ExpressionSyntax ParseSubExpressionCore(Precedence precedence) opKind = SyntaxFacts.GetAssignmentExpression(tk); isAssignmentOperator = true; } + else if (tk == SyntaxKind.MatchKeyword) + { + opKind = SyntaxKind.MatchExpression; + } else { break; @@ -8703,7 +8713,7 @@ private ExpressionSyntax ParseSubExpressionCore(Precedence precedence) } // Precedence is okay, so we'll "take" this operator. - var opToken = this.EatToken(); + var opToken = this.EatContextualToken(tk); if (doubleOp) { // combine tokens into a single token @@ -8721,6 +8731,11 @@ private ExpressionSyntax ParseSubExpressionCore(Precedence precedence) { leftOperand = ParseIsExpression(leftOperand, opToken); } + else if (opKind == SyntaxKind.MatchExpression) + { + leftOperand = ParseMatchExpression(leftOperand, opToken); + leftOperand = CheckFeatureAvailability(leftOperand, MessageID.IDS_FeaturePatternMatching); + } else { var rightOperand = this.ParseSubExpression(newPrecedence); @@ -8756,6 +8771,14 @@ private ExpressionSyntax ParseSubExpressionCore(Precedence precedence) return leftOperand; } + private ExpressionSyntax ParseThrowExpression() + { + var throwToken = this.EatToken(SyntaxKind.ThrowKeyword); + var thrown = this.ParseSubExpression(Precedence.Coalescing); + var result = _syntaxFactory.ThrowExpression(throwToken, thrown); + return CheckFeatureAvailability(result, MessageID.IDS_FeaturePatternMatching); + } + private ExpressionSyntax ParseIsExpression(ExpressionSyntax leftOperand, SyntaxToken opToken) { var node = this.ParseTypeOrPattern(); @@ -8771,377 +8794,6 @@ private ExpressionSyntax ParseIsExpression(ExpressionSyntax leftOperand, SyntaxT } } - // Priority is the TypeSyntax. It might return TypeSyntax which might be a constant pattern such as enum 'Days.Sunday' - // We handle such cases in the binder of is operator. - // It is used for parsing patterns in the is operators. - private CSharpSyntaxNode ParseTypeOrPattern() - { - var tk = this.CurrentToken.Kind; - CSharpSyntaxNode node = null; - - switch (tk) - { - case SyntaxKind.AsteriskToken: - var asteriskToken = this.EatToken(); - return _syntaxFactory.WildcardPattern(asteriskToken); - case SyntaxKind.CloseParenToken: - case SyntaxKind.CloseBracketToken: - case SyntaxKind.CloseBraceToken: - case SyntaxKind.SemicolonToken: - case SyntaxKind.CommaToken: - // HACK: for error recovery, we prefer a (missing) type. - return this.ParseTypeCore(parentIsParameter: false, isOrAs: true, expectSizes: false, isArrayCreation: false); - default: - // attempt to disambiguate. - break; - } - - // If it is a nameof, skip the 'if' and parse as a constant pattern. - if (SyntaxFacts.IsPredefinedType(tk) || - (tk == SyntaxKind.IdentifierToken && this.CurrentToken.ContextualKind != SyntaxKind.NameOfKeyword)) - { - var resetPoint = this.GetResetPoint(); - TypeSyntax type = this.ParseTypeCore(parentIsParameter: false, isOrAs: true, expectSizes: false, isArrayCreation: false); - - tk = this.CurrentToken.Kind; - if (!type.IsMissing) - { - // X.Y.Z ( ... ) : RecursivePattern - if (tk == SyntaxKind.OpenParenToken) - { - node = _syntaxFactory.RecursivePattern(type, this.ParseSubRecursivePatternList()); - } - // X.Y.Z { ... } : PropertyPattern - else if (tk == SyntaxKind.OpenBraceToken) - { - node = _syntaxFactory.PropertyPattern(type, this.ParseSubPropertyPatternList()); - } - // X.Y.Z id - else if (this.IsTrueIdentifier()) - { - var identifier = ParseIdentifierToken(); - node = _syntaxFactory.DeclarationPattern(type, identifier); - } - } - if (node == null) - { - Debug.Assert(Precedence.Shift == Precedence.Relational + 1); - if (IsExpectedBinaryOperator(tk) && GetPrecedence(SyntaxFacts.GetBinaryExpression(tk)) > Precedence.Relational) - { - this.Reset(ref resetPoint); - // We parse a shift-expression ONLY (nothing looser) - i.e. not a relational expression - // So x is y < z should be parsed as (x is y) < z - // But x is y << z should be parsed as x is (y << z) - node = _syntaxFactory.ConstantPattern(this.ParseExpression(Precedence.Shift)); - } - // it is a typical is operator! - else - { - // Note that we don't bother checking for primary expressions such as X[e], X(e), X++, and X-- - // as those are never semantically valid constant expressions for a pattern, and X(e) is - // syntactically a recursive pattern that we checked for earlier. - node = type; - } - } - this.Release(ref resetPoint); - } - else - { - // it still might be a pattern such as (operand is 3) or (operand is -3) - node = _syntaxFactory.ConstantPattern(this.ParseExpressionCore()); - } - return node; - } - - private SubRecursivePatternListSyntax ParseSubRecursivePatternList() - { - if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.SubRecursivePatternList) - { - return (SubRecursivePatternListSyntax)this.EatNode(); - } - - SyntaxToken openToken, closeToken; - SeparatedSyntaxList subPatterns; - ParseSubRecursivePatternList(out openToken, out subPatterns, out closeToken, SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken); - - return _syntaxFactory.SubRecursivePatternList(openToken, subPatterns, closeToken); - } - - private void ParseSubRecursivePatternList( - out SyntaxToken openToken, - out SeparatedSyntaxList subPatterns, - out SyntaxToken closeToken, - SyntaxKind openKind, - SyntaxKind closeKind) - { - var open = this.EatToken(openKind); - var saveTerm = _termState; - _termState |= TerminatorState.IsEndOfArgumentList; - - SeparatedSyntaxListBuilder list = default(SeparatedSyntaxListBuilder); - try - { - if (this.CurrentToken.Kind != closeKind && this.CurrentToken.Kind != SyntaxKind.SemicolonToken) - { - tryAgain: - if (list.IsNull) - { - list = _pool.AllocateSeparated(); - } - - if (this.IsPossibleArgumentExpression() || this.CurrentToken.Kind == SyntaxKind.CommaToken) - { - // first argument - list.Add(this.ParseSubRecursivePattern()); - - // additional arguments - while (true) - { - if (this.CurrentToken.Kind == closeKind || this.CurrentToken.Kind == SyntaxKind.SemicolonToken) - { - break; - } - else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleArgumentExpression()) - { - list.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); - list.Add(this.ParseSubRecursivePattern()); - continue; - } - else if (this.SkipBadSubPatternListTokens(ref open, list, SyntaxKind.CommaToken, closeKind) == PostSkipAction.Abort) - { - break; - } - } - } - else if (this.SkipBadSubPatternListTokens(ref open, list, SyntaxKind.IdentifierToken, closeKind) == PostSkipAction.Continue) - { - goto tryAgain; - } - } - - _termState = saveTerm; - - openToken = open; - closeToken = this.EatToken(closeKind); - subPatterns = list.ToList(); - } - finally - { - if (!list.IsNull) - { - _pool.Free(list); - } - } - } - - private SubRecursivePatternSyntax ParseSubRecursivePattern() - { - NameColonSyntax nameColon = null; - if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken && this.PeekToken(1).Kind == SyntaxKind.ColonToken) - { - var name = this.ParseIdentifierName(); - var colon = this.EatToken(SyntaxKind.ColonToken); - nameColon = _syntaxFactory.NameColon(name, colon); - } - - PatternSyntax pattern = this.CurrentToken.Kind == SyntaxKind.CommaToken ? - this.AddError(_syntaxFactory.ConstantPattern(this.CreateMissingIdentifierName()), ErrorCode.ERR_MissingArgument) : - ParsePattern(); - - return _syntaxFactory.SubRecursivePattern(nameColon, pattern); - } - - // This method is used when we always want a pattern as a result. - // For instance, it is used in parsing recursivepattern and propertypattern. - // SubPatterns in these (recursivepattern, propertypattern) must be a type of Pattern. - private PatternSyntax ParsePattern() - { - var node = this.ParseExpressionOrPattern(); - if (node is PatternSyntax) - { - return (PatternSyntax)node; - } - Debug.Assert(node is ExpressionSyntax); - return _syntaxFactory.ConstantPattern((ExpressionSyntax)node); - } - - // Priority is the ExpressionSyntax. It might return ExpressionSyntax which might be a constant pattern such as 'case 3:' - // All constant expressions are converted to the constant pattern in the switch binder if it is a match statement. - // It is used for parsing patterns in the switch cases. It never returns constant pattern! - private CSharpSyntaxNode ParseExpressionOrPattern() - { - var tk = this.CurrentToken.Kind; - CSharpSyntaxNode node = null; - - if (tk == SyntaxKind.AsteriskToken) - { - var asteriskToken = this.EatToken(); - return _syntaxFactory.WildcardPattern(asteriskToken); - } - - // If it is a nameof, skip the 'if' and parse as an expression. - if ((SyntaxFacts.IsPredefinedType(tk) || tk == SyntaxKind.IdentifierToken) && - this.CurrentToken.ContextualKind != SyntaxKind.NameOfKeyword) - { - var resetPoint = this.GetResetPoint(); - TypeSyntax type = this.ParseTypeCore(parentIsParameter: false, isOrAs: true, expectSizes: false, isArrayCreation: false); - - - tk = this.CurrentToken.Kind; - if (!type.IsMissing) - { - // X.Y.Z ( ... ) : RecursivePattern - if (tk == SyntaxKind.OpenParenToken) - { - node = _syntaxFactory.RecursivePattern(type, this.ParseSubRecursivePatternList()); - } - // X.Y.Z { ... } : PropertyPattern - else if (tk == SyntaxKind.OpenBraceToken) - { - node = _syntaxFactory.PropertyPattern(type, this.ParseSubPropertyPatternList()); - } - // X.Y.Z id - else if (this.IsTrueIdentifier()) - { - var identifier = ParseIdentifierToken(); - node = _syntaxFactory.DeclarationPattern(type, identifier); - } - } - if (node == null) - { - // it is an expression for typical switch case. - // This can be transformed to the constant pattern in the SwitchBinder if there is a CaseMatchLabel in the sections. - this.Reset(ref resetPoint); - node = this.ParseSubExpression(Precedence.Expression); - } - this.Release(ref resetPoint); - } - else - { - node = this.ParseSubExpression(Precedence.Expression); - } - return node; - } - - private SubPropertyPatternListSyntax ParseSubPropertyPatternList() - { - var openBrace = this.EatToken(SyntaxKind.OpenBraceToken); - - var subPatterns = _pool.AllocateSeparated(); - try - { - this.ParseSubPropertyPatternList(ref openBrace, subPatterns); - - var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken); - return _syntaxFactory.SubPropertyPatternList( - openBrace, - subPatterns, - closeBrace); - } - finally - { - _pool.Free(subPatterns); - } - } - - private void ParseSubPropertyPatternList(ref SyntaxToken startToken, SeparatedSyntaxListBuilder list) - { - if (this.CurrentToken.Kind != SyntaxKind.CloseBraceToken) - { - tryAgain: - if (IsPossibleSubPropertyPattern() || this.CurrentToken.Kind == SyntaxKind.CommaToken) - { - // first argument - list.Add(ParseSubPropertyPattern()); - - // additional arguments - while (true) - { - if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken) - { - break; - } - else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || IsPossibleSubPropertyPattern()) - { - list.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); - - // check for exit case after legal trailing comma - if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken) - { - break; - } - - list.Add(ParseSubPropertyPattern()); - continue; - } - else if (this.SkipBadSubPatternListTokens(ref startToken, list, SyntaxKind.CommaToken, SyntaxKind.CloseBraceToken) == PostSkipAction.Abort) - { - break; - } - } - } - else if (this.SkipBadSubPatternListTokens(ref startToken, list, SyntaxKind.IdentifierToken, SyntaxKind.CloseBraceToken) == PostSkipAction.Continue) - { - goto tryAgain; - } - } - } - - private bool IsPossibleSubPropertyPattern() - { - return (this.CurrentToken.Kind == SyntaxKind.IdentifierToken) && (this.PeekToken(1).Kind == SyntaxKind.IsKeyword); - } - - private SubPropertyPatternSyntax ParseSubPropertyPattern() - { - var name = this.ParseIdentifierName(); - var operandToken = this.EatToken(SyntaxKind.IsKeyword); - - PatternSyntax pattern = this.CurrentToken.Kind == SyntaxKind.CommaToken ? - this.AddError(_syntaxFactory.ConstantPattern(this.CreateMissingIdentifierName()), ErrorCode.ERR_MissingArgument) : - ParsePattern(); - - return _syntaxFactory.SubPropertyPattern(name, operandToken, pattern); - } - - private PostSkipAction SkipBadSubPatternListTokens(ref SyntaxToken open, SeparatedSyntaxListBuilder list, SyntaxKind expected, SyntaxKind closeKind) where TNode : CSharpSyntaxNode - { - return this.SkipBadSeparatedListTokensWithExpectedKind(ref open, list, - p => p.CurrentToken.Kind != SyntaxKind.CommaToken && !p.IsPossiblePattern(), - p => p.CurrentToken.Kind == closeKind || p.CurrentToken.Kind == SyntaxKind.SemicolonToken || p.IsTerminator(), - expected); - } - - // It should not be accurate. This method is just used in the SkipBadSubPatternListTokens. - // It is very general way of checking whether there is a possible pattern. - private bool IsPossiblePattern() - { - // TODO(@gafter): this doesn't accept many forms that should be accepted, such as (1+1). - var tk = this.CurrentToken.Kind; - switch (tk) - { - case SyntaxKind.AsteriskToken: - case SyntaxKind.StringLiteralToken: - case SyntaxKind.CharacterLiteralToken: - case SyntaxKind.NumericLiteralToken: - case SyntaxKind.NullKeyword: - case SyntaxKind.TrueKeyword: - case SyntaxKind.FalseKeyword: - case SyntaxKind.ArgListKeyword: - return true; - case SyntaxKind.IdentifierToken: - var next = this.PeekToken(1).Kind; - if (next == SyntaxKind.DotToken || next == SyntaxKind.OpenBraceToken || - next == SyntaxKind.OpenParenToken) - { - return true; - } - break; - } - - return false; - } - private ExpressionSyntax ParseTerm(Precedence precedence) { ExpressionSyntax expr = null; diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs new file mode 100644 index 0000000000000..141eb7500fbe7 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs @@ -0,0 +1,422 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax +{ + internal partial class LanguageParser : SyntaxParser + { + // Priority is the TypeSyntax. It might return TypeSyntax which might be a constant pattern such as enum 'Days.Sunday' + // We handle such cases in the binder of is operator. + // It is used for parsing patterns in the is operators. + private CSharpSyntaxNode ParseTypeOrPattern() + { + var tk = this.CurrentToken.Kind; + CSharpSyntaxNode node = null; + + switch (tk) + { + case SyntaxKind.AsteriskToken: + var asteriskToken = this.EatToken(); + return _syntaxFactory.WildcardPattern(asteriskToken); + case SyntaxKind.CloseParenToken: + case SyntaxKind.CloseBracketToken: + case SyntaxKind.CloseBraceToken: + case SyntaxKind.SemicolonToken: + case SyntaxKind.CommaToken: + // HACK: for error recovery, we prefer a (missing) type. + return this.ParseTypeCore(parentIsParameter: false, isOrAs: true, expectSizes: false, isArrayCreation: false); + default: + // attempt to disambiguate. + break; + } + + // If it is a nameof, skip the 'if' and parse as a constant pattern. + if (SyntaxFacts.IsPredefinedType(tk) || + (tk == SyntaxKind.IdentifierToken && this.CurrentToken.ContextualKind != SyntaxKind.NameOfKeyword)) + { + var resetPoint = this.GetResetPoint(); + try + { + TypeSyntax type = this.ParseTypeCore(parentIsParameter: false, isOrAs: true, expectSizes: false, isArrayCreation: false); + + tk = this.CurrentToken.ContextualKind; + if (!type.IsMissing) + { + // X.Y.Z ( ... ) : RecursivePattern + if (tk == SyntaxKind.OpenParenToken) + { + node = _syntaxFactory.RecursivePattern(type, this.ParseSubRecursivePatternList()); + } + // X.Y.Z { ... } : PropertyPattern + else if (tk == SyntaxKind.OpenBraceToken) + { + node = _syntaxFactory.PropertyPattern(type, this.ParseSubPropertyPatternList()); + } + // X.Y.Z id + else if (this.IsTrueIdentifier()) + { + var identifier = ParseIdentifierToken(); + node = _syntaxFactory.DeclarationPattern(type, identifier); + } + } + if (node == null) + { + Debug.Assert(Precedence.Shift == Precedence.Relational + 1); + if (IsExpectedBinaryOperator(tk) && GetPrecedence(SyntaxFacts.GetBinaryExpression(tk)) > Precedence.Relational) + { + this.Reset(ref resetPoint); + // We parse a shift-expression ONLY (nothing looser) - i.e. not a relational expression + // So x is y < z should be parsed as (x is y) < z + // But x is y << z should be parsed as x is (y << z) + node = _syntaxFactory.ConstantPattern(this.ParseExpression(Precedence.Shift)); + } + // it is a typical is operator! + else + { + // Note that we don't bother checking for primary expressions such as X[e], X(e), X++, and X-- + // as those are never semantically valid constant expressions for a pattern, and X(e) is + // syntactically a recursive pattern that we checked for earlier. + node = type; + } + } + } + finally + { + this.Release(ref resetPoint); + } + } + else + { + // it still might be a pattern such as (operand is 3) or (operand is -3) + node = _syntaxFactory.ConstantPattern(this.ParseExpressionCore()); + } + return node; + } + + private SubRecursivePatternListSyntax ParseSubRecursivePatternList() + { + if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.SubRecursivePatternList) + { + return (SubRecursivePatternListSyntax)this.EatNode(); + } + + SyntaxToken openToken, closeToken; + SeparatedSyntaxList subPatterns; + ParseSubRecursivePatternList(out openToken, out subPatterns, out closeToken, SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken); + + return _syntaxFactory.SubRecursivePatternList(openToken, subPatterns, closeToken); + } + + private void ParseSubRecursivePatternList( + out SyntaxToken openToken, + out SeparatedSyntaxList subPatterns, + out SyntaxToken closeToken, + SyntaxKind openKind, + SyntaxKind closeKind) + { + var open = this.EatToken(openKind); + var saveTerm = _termState; + _termState |= TerminatorState.IsEndOfArgumentList; + + SeparatedSyntaxListBuilder list = default(SeparatedSyntaxListBuilder); + try + { + if (this.CurrentToken.Kind != closeKind && this.CurrentToken.Kind != SyntaxKind.SemicolonToken) + { + tryAgain: + if (list.IsNull) + { + list = _pool.AllocateSeparated(); + } + + if (this.IsPossibleArgumentExpression() || this.CurrentToken.Kind == SyntaxKind.CommaToken) + { + // first argument + list.Add(this.ParseSubRecursivePattern()); + + // additional arguments + while (true) + { + if (this.CurrentToken.Kind == closeKind || this.CurrentToken.Kind == SyntaxKind.SemicolonToken) + { + break; + } + else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleArgumentExpression()) + { + list.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); + list.Add(this.ParseSubRecursivePattern()); + continue; + } + else if (this.SkipBadSubPatternListTokens(ref open, list, SyntaxKind.CommaToken, closeKind) == PostSkipAction.Abort) + { + break; + } + } + } + else if (this.SkipBadSubPatternListTokens(ref open, list, SyntaxKind.IdentifierToken, closeKind) == PostSkipAction.Continue) + { + goto tryAgain; + } + } + + _termState = saveTerm; + + openToken = open; + closeToken = this.EatToken(closeKind); + subPatterns = list.ToList(); + } + finally + { + if (!list.IsNull) + { + _pool.Free(list); + } + } + } + + private SubRecursivePatternSyntax ParseSubRecursivePattern() + { + NameColonSyntax nameColon = null; + if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken && this.PeekToken(1).Kind == SyntaxKind.ColonToken) + { + var name = this.ParseIdentifierName(); + var colon = this.EatToken(SyntaxKind.ColonToken); + nameColon = _syntaxFactory.NameColon(name, colon); + } + + PatternSyntax pattern = this.CurrentToken.Kind == SyntaxKind.CommaToken ? + this.AddError(_syntaxFactory.ConstantPattern(this.CreateMissingIdentifierName()), ErrorCode.ERR_MissingArgument) : + ParsePattern(); + + return _syntaxFactory.SubRecursivePattern(nameColon, pattern); + } + + // This method is used when we always want a pattern as a result. + // For instance, it is used in parsing recursivepattern and propertypattern. + // SubPatterns in these (recursivepattern, propertypattern) must be a type of Pattern. + private PatternSyntax ParsePattern() + { + var node = this.ParseExpressionOrPattern(); + if (node is PatternSyntax) + { + return (PatternSyntax)node; + } + Debug.Assert(node is ExpressionSyntax); + return _syntaxFactory.ConstantPattern((ExpressionSyntax)node); + } + + // Priority is the ExpressionSyntax. It might return ExpressionSyntax which might be a constant pattern such as 'case 3:' + // All constant expressions are converted to the constant pattern in the switch binder if it is a match statement. + // It is used for parsing patterns in the switch cases. It never returns constant pattern! + private CSharpSyntaxNode ParseExpressionOrPattern() + { + var tk = this.CurrentToken.Kind; + CSharpSyntaxNode node = null; + + if (tk == SyntaxKind.AsteriskToken) + { + var asteriskToken = this.EatToken(); + return _syntaxFactory.WildcardPattern(asteriskToken); + } + + // If it is a nameof, skip the 'if' and parse as an expression. + if ((SyntaxFacts.IsPredefinedType(tk) || tk == SyntaxKind.IdentifierToken) && + this.CurrentToken.ContextualKind != SyntaxKind.NameOfKeyword) + { + var resetPoint = this.GetResetPoint(); + try + { + TypeSyntax type = this.ParseTypeCore(parentIsParameter: false, isOrAs: true, expectSizes: false, isArrayCreation: false); + tk = this.CurrentToken.Kind; + if (!type.IsMissing) + { + // X.Y.Z ( ... ) : RecursivePattern + if (tk == SyntaxKind.OpenParenToken) + { + node = _syntaxFactory.RecursivePattern(type, this.ParseSubRecursivePatternList()); + } + // X.Y.Z { ... } : PropertyPattern + else if (tk == SyntaxKind.OpenBraceToken) + { + node = _syntaxFactory.PropertyPattern(type, this.ParseSubPropertyPatternList()); + } + // X.Y.Z id + else if (this.IsTrueIdentifier()) + { + var identifier = ParseIdentifierToken(); + node = _syntaxFactory.DeclarationPattern(type, identifier); + } + } + if (node == null) + { + // it is an expression for typical switch case. + // This can be transformed to the constant pattern in the SwitchBinder if there is a CaseMatchLabel in the sections. + this.Reset(ref resetPoint); + node = this.ParseSubExpression(Precedence.Expression); + } + } + finally + { + this.Release(ref resetPoint); + } + } + else + { + node = this.ParseSubExpression(Precedence.Expression); + } + return node; + } + + private SubPropertyPatternListSyntax ParseSubPropertyPatternList() + { + var openBrace = this.EatToken(SyntaxKind.OpenBraceToken); + + var subPatterns = _pool.AllocateSeparated(); + try + { + this.ParseSubPropertyPatternList(ref openBrace, subPatterns); + + var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken); + return _syntaxFactory.SubPropertyPatternList( + openBrace, + subPatterns, + closeBrace); + } + finally + { + _pool.Free(subPatterns); + } + } + + private void ParseSubPropertyPatternList(ref SyntaxToken startToken, SeparatedSyntaxListBuilder list) + { + if (this.CurrentToken.Kind != SyntaxKind.CloseBraceToken) + { + tryAgain: + if (IsPossibleSubPropertyPattern() || this.CurrentToken.Kind == SyntaxKind.CommaToken) + { + // first argument + list.Add(ParseSubPropertyPattern()); + + // additional arguments + while (true) + { + if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken) + { + break; + } + else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || IsPossibleSubPropertyPattern()) + { + list.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); + + // check for exit case after legal trailing comma + if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken) + { + break; + } + + list.Add(ParseSubPropertyPattern()); + continue; + } + else if (this.SkipBadSubPatternListTokens(ref startToken, list, SyntaxKind.CommaToken, SyntaxKind.CloseBraceToken) == PostSkipAction.Abort) + { + break; + } + } + } + else if (this.SkipBadSubPatternListTokens(ref startToken, list, SyntaxKind.IdentifierToken, SyntaxKind.CloseBraceToken) == PostSkipAction.Continue) + { + goto tryAgain; + } + } + } + + private bool IsPossibleSubPropertyPattern() + { + return (this.CurrentToken.Kind == SyntaxKind.IdentifierToken) && (this.PeekToken(1).Kind == SyntaxKind.IsKeyword); + } + + private SubPropertyPatternSyntax ParseSubPropertyPattern() + { + var name = this.ParseIdentifierName(); + var operandToken = this.EatToken(SyntaxKind.IsKeyword); + + PatternSyntax pattern = this.CurrentToken.Kind == SyntaxKind.CommaToken ? + this.AddError(_syntaxFactory.ConstantPattern(this.CreateMissingIdentifierName()), ErrorCode.ERR_MissingArgument) : + ParsePattern(); + + return _syntaxFactory.SubPropertyPattern(name, operandToken, pattern); + } + + private PostSkipAction SkipBadSubPatternListTokens(ref SyntaxToken open, SeparatedSyntaxListBuilder list, SyntaxKind expected, SyntaxKind closeKind) where TNode : CSharpSyntaxNode + { + return this.SkipBadSeparatedListTokensWithExpectedKind(ref open, list, + p => p.CurrentToken.Kind != SyntaxKind.CommaToken && !p.IsPossiblePattern(), + p => p.CurrentToken.Kind == closeKind || p.CurrentToken.Kind == SyntaxKind.SemicolonToken || p.IsTerminator(), + expected); + } + + // It should not be accurate. This method is just used in the SkipBadSubPatternListTokens. + // It is very general way of checking whether there is a possible pattern. + private bool IsPossiblePattern() + { + // TODO(@gafter): this doesn't accept many forms that should be accepted, such as (1+1). + var tk = this.CurrentToken.Kind; + switch (tk) + { + case SyntaxKind.AsteriskToken: + case SyntaxKind.StringLiteralToken: + case SyntaxKind.CharacterLiteralToken: + case SyntaxKind.NumericLiteralToken: + case SyntaxKind.NullKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + case SyntaxKind.ArgListKeyword: + return true; + case SyntaxKind.IdentifierToken: + var next = this.PeekToken(1).Kind; + if (next == SyntaxKind.DotToken || next == SyntaxKind.OpenBraceToken || + next == SyntaxKind.OpenParenToken) + { + return true; + } + break; + } + + return false; + } + + private ExpressionSyntax ParseMatchExpression(ExpressionSyntax leftOperand, SyntaxToken opToken) + { + var openParen = this.EatToken(SyntaxKind.OpenParenToken); + var sections = _pool.Allocate(); + while (this.CurrentToken.Kind == SyntaxKind.CaseKeyword) + { + var mcase = this.ParseMatchSection(); + sections.Add(mcase); + } + var closeParen = this.EatToken(SyntaxKind.CloseParenToken); + var result = _syntaxFactory.MatchExpression(leftOperand, opToken, openParen, sections, closeParen); + _pool.Free(sections); + return result; + } + + private MatchSectionSyntax ParseMatchSection() + { + var caseKeyword = this.EatToken(SyntaxKind.CaseKeyword); + var pattern = ParsePattern(); + SyntaxToken when = null; ; + ExpressionSyntax condition = null; + if (this.CurrentToken.ContextualKind == SyntaxKind.WhenKeyword) + { + when = this.EatContextualToken(SyntaxKind.WhenKeyword); + condition = ParseExpressionCore(); + } + var colon = this.EatToken(SyntaxKind.ColonToken); + var expression = ParseExpressionCore(); + return _syntaxFactory.MatchSection(caseKeyword, pattern, when, condition, colon, expression); + } + } +} diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index c7151f21cb699..024442b7b1d59 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -12,16 +12,16 @@ Microsoft.CodeAnalysis.CSharp.CSharpParseOptions.WithKind(Microsoft.CodeAnalysis Microsoft.CodeAnalysis.CSharp.CSharpScriptCompilationInfo Microsoft.CodeAnalysis.CSharp.CSharpScriptCompilationInfo.PreviousScriptCompilation.get -> Microsoft.CodeAnalysis.CSharp.CSharpCompilation Microsoft.CodeAnalysis.CSharp.CSharpScriptCompilationInfo.WithPreviousScriptCompilation(Microsoft.CodeAnalysis.CSharp.CSharpCompilation compilation) -> Microsoft.CodeAnalysis.CSharp.CSharpScriptCompilationInfo -Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax.Condition.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax.Pattern.get -> Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken keyword, Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern, Microsoft.CodeAnalysis.SyntaxToken whenKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax condition, Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax.WhenKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken -Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax.WithColonToken(Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax.WithCondition(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax condition) -> Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax.WithKeyword(Microsoft.CodeAnalysis.SyntaxToken keyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax.WithPattern(Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax.WithWhenKeyword(Microsoft.CodeAnalysis.SyntaxToken whenKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.Condition.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.Pattern.get -> Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken keyword, Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern, Microsoft.CodeAnalysis.SyntaxToken whenKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax condition, Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.WhenKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.WithColonToken(Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.WithCondition(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax condition) -> Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.WithKeyword(Microsoft.CodeAnalysis.SyntaxToken keyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.WithPattern(Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.WithWhenKeyword(Microsoft.CodeAnalysis.SyntaxToken whenKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax.GetLoadDirectives() -> System.Collections.Generic.IList Microsoft.CodeAnalysis.CSharp.Syntax.ConstantPatternSyntax Microsoft.CodeAnalysis.CSharp.Syntax.ConstantPatternSyntax.Expression.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax @@ -156,11 +156,17 @@ Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternSyntax.Pattern.get -> Mi Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternSyntax.Update(Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax nameColon, Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternSyntax Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternSyntax.WithNameColon(Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax nameColon) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternSyntax Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternSyntax.WithPattern(Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.Expression.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.ThrowKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken throwKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.WithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.WithThrowKeyword(Microsoft.CodeAnalysis.SyntaxToken throwKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax Microsoft.CodeAnalysis.CSharp.Syntax.WildcardPatternSyntax Microsoft.CodeAnalysis.CSharp.Syntax.WildcardPatternSyntax.AsteriskToken.get -> Microsoft.CodeAnalysis.SyntaxToken Microsoft.CodeAnalysis.CSharp.Syntax.WildcardPatternSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken asteriskToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.WildcardPatternSyntax Microsoft.CodeAnalysis.CSharp.Syntax.WildcardPatternSyntax.WithAsteriskToken(Microsoft.CodeAnalysis.SyntaxToken asteriskToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.WildcardPatternSyntax -Microsoft.CodeAnalysis.CSharp.SyntaxKind.CaseMatchLabel = 8557 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.CasePatternSwitchLabel = 8557 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.ConstantPattern = 8660 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.DeclarationPattern = 8658 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.IsPatternExpression = 8657 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind @@ -177,9 +183,10 @@ Microsoft.CodeAnalysis.CSharp.SyntaxKind.SubPropertyPattern = 8555 -> Microsoft. Microsoft.CodeAnalysis.CSharp.SyntaxKind.SubPropertyPatternList = 8556 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.SubRecursivePattern = 8662 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.SubRecursivePatternList = 8663 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.ThrowExpression = 8926 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.WildcardPattern = 8659 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind override Microsoft.CodeAnalysis.CSharp.CSharpParseOptions.CommonWithKind(Microsoft.CodeAnalysis.SourceCodeKind kind) -> Microsoft.CodeAnalysis.ParseOptions -override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitCaseMatchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode +override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitCasePatternSwitchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitConstantPattern(Microsoft.CodeAnalysis.CSharp.Syntax.ConstantPatternSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitDeclarationPattern(Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationPatternSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitIsPatternExpression(Microsoft.CodeAnalysis.CSharp.Syntax.IsPatternExpressionSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode @@ -194,11 +201,12 @@ override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitSubPropertyPatt override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitSubPropertyPatternList(Microsoft.CodeAnalysis.CSharp.Syntax.SubPropertyPatternListSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitSubRecursivePattern(Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitSubRecursivePatternList(Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternListSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode +override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitThrowExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWildcardPattern(Microsoft.CodeAnalysis.CSharp.Syntax.WildcardPatternSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode -override Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void -override Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult -override Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax.ColonToken.get -> Microsoft.CodeAnalysis.SyntaxToken -override Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax.Keyword.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void +override Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult +override Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.ColonToken.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax.Keyword.get -> Microsoft.CodeAnalysis.SyntaxToken override Microsoft.CodeAnalysis.CSharp.Syntax.ConstantPatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void override Microsoft.CodeAnalysis.CSharp.Syntax.ConstantPatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult override Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationPatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void @@ -233,6 +241,8 @@ override Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternListSyntax.Acce override Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternListSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult override Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void override Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult +override Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void +override Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult override Microsoft.CodeAnalysis.CSharp.Syntax.WildcardPatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void override Microsoft.CodeAnalysis.CSharp.Syntax.WildcardPatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult static Microsoft.CodeAnalysis.CSharp.CSharpCompilation.CreateScriptCompilation(string assemblyName, Microsoft.CodeAnalysis.SyntaxTree syntaxTree = null, System.Collections.Generic.IEnumerable references = null, Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions options = null, Microsoft.CodeAnalysis.CSharp.CSharpCompilation previousScriptCompilation = null, System.Type returnType = null, System.Type globalsType = null) -> Microsoft.CodeAnalysis.CSharp.CSharpCompilation @@ -240,9 +250,9 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxExtensions.NormalizeWhitespace(this M static Microsoft.CodeAnalysis.CSharp.SyntaxExtensions.NormalizeWhitespace(this Microsoft.CodeAnalysis.SyntaxToken token, string indentation, bool elasticTrivia) -> Microsoft.CodeAnalysis.SyntaxToken static Microsoft.CodeAnalysis.CSharp.SyntaxExtensions.NormalizeWhitespace(this Microsoft.CodeAnalysis.SyntaxTriviaList list, string indentation = " ", string eol = "\r\n", bool elasticTrivia = false) -> Microsoft.CodeAnalysis.SyntaxTriviaList static Microsoft.CodeAnalysis.CSharp.SyntaxExtensions.NormalizeWhitespace(this Microsoft.CodeAnalysis.SyntaxTriviaList list, string indentation, bool elasticTrivia) -> Microsoft.CodeAnalysis.SyntaxTriviaList -static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.CaseMatchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax condition, Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax -static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.CaseMatchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern, Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax -static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.CaseMatchLabel(Microsoft.CodeAnalysis.SyntaxToken keyword, Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern, Microsoft.CodeAnalysis.SyntaxToken whenKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax condition, Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.CasePatternSwitchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax condition, Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.CasePatternSwitchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern, Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.CasePatternSwitchLabel(Microsoft.CodeAnalysis.SyntaxToken keyword, Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern, Microsoft.CodeAnalysis.SyntaxToken whenKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax condition, Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ConstantPattern(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConstantPatternSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DeclarationPattern(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationPatternSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IsPatternExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.IsPatternExpressionSyntax @@ -274,9 +284,11 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SubRecursivePattern(Microsoft static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SubRecursivePattern(Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SubRecursivePatternList(Microsoft.CodeAnalysis.SeparatedSyntaxList subPatterns = default(Microsoft.CodeAnalysis.SeparatedSyntaxList)) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternListSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SubRecursivePatternList(Microsoft.CodeAnalysis.SyntaxToken openParenToken, Microsoft.CodeAnalysis.SeparatedSyntaxList subPatterns, Microsoft.CodeAnalysis.SyntaxToken closeParenToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternListSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ThrowExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ThrowExpression(Microsoft.CodeAnalysis.SyntaxToken throwKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WildcardPattern() -> Microsoft.CodeAnalysis.CSharp.Syntax.WildcardPatternSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WildcardPattern(Microsoft.CodeAnalysis.SyntaxToken asteriskToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.WildcardPatternSyntax -virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitCaseMatchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax node) -> void +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitCasePatternSwitchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitConstantPattern(Microsoft.CodeAnalysis.CSharp.Syntax.ConstantPatternSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitDeclarationPattern(Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationPatternSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIsPatternExpression(Microsoft.CodeAnalysis.CSharp.Syntax.IsPatternExpressionSyntax node) -> void @@ -291,8 +303,9 @@ virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitSubPropertyPatter virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitSubPropertyPatternList(Microsoft.CodeAnalysis.CSharp.Syntax.SubPropertyPatternListSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitSubRecursivePattern(Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitSubRecursivePatternList(Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternListSyntax node) -> void +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitThrowExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWildcardPattern(Microsoft.CodeAnalysis.CSharp.Syntax.WildcardPatternSyntax node) -> void -virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitCaseMatchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.CaseMatchLabelSyntax node) -> TResult +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitCasePatternSwitchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitConstantPattern(Microsoft.CodeAnalysis.CSharp.Syntax.ConstantPatternSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitDeclarationPattern(Microsoft.CodeAnalysis.CSharp.Syntax.DeclarationPatternSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIsPatternExpression(Microsoft.CodeAnalysis.CSharp.Syntax.IsPatternExpressionSyntax node) -> TResult @@ -307,4 +320,5 @@ virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitSubPrope virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitSubPropertyPatternList(Microsoft.CodeAnalysis.CSharp.Syntax.SubPropertyPatternListSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitSubRecursivePattern(Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitSubRecursivePatternList(Microsoft.CodeAnalysis.CSharp.Syntax.SubRecursivePatternListSyntax node) -> TResult +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitThrowExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ThrowExpressionSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWildcardPattern(Microsoft.CodeAnalysis.CSharp.Syntax.WildcardPatternSyntax node) -> TResult \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index c6f671dffd1c4..59524c0e62b13 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -1627,6 +1627,13 @@ + + + + + + + @@ -2289,8 +2296,8 @@ Represents a switch label within a switch statement. - - + + diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index df34dc588a0c5..76d5d502a9682 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -341,7 +341,7 @@ public enum SyntaxKind : ushort PropertyPattern = 8554, SubPropertyPattern = 8555, SubPropertyPatternList = 8556, - CaseMatchLabel = 8557, + CasePatternSwitchLabel = 8557, // binary expressions AddExpression = 8668, @@ -551,5 +551,6 @@ public enum SyntaxKind : ushort LoadDirectiveTrivia = 8923, MatchSection = 8924, MatchExpression = 8925, + ThrowExpression = 8926, } } diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs index 365b36b3494f7..6d71553fb4330 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs @@ -35,6 +35,7 @@ public void DiagnosticAnalyzerAllInOne() syntaxKindsPatterns.Add(SyntaxKind.SubRecursivePattern); syntaxKindsPatterns.Add(SyntaxKind.MatchSection); syntaxKindsPatterns.Add(SyntaxKind.MatchExpression); + syntaxKindsPatterns.Add(SyntaxKind.ThrowExpression); var analyzer = new CSharpTrackingDiagnosticAnalyzer(); CreateExperimentalCompilationWithMscorlib45(source).VerifyAnalyzerDiagnostics(new[] { analyzer }); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs index b17eeb9607ef4..29dbb64b79a6b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs @@ -506,7 +506,7 @@ public struct X public static void Main() { var oa = new object[] { 1, 10, 20L, 1.2, ""foo"", true, null, new X(), new Exception(""boo"") }; - foreach (var o in oa) + foreach (var o in oa) { switch (o) { @@ -588,5 +588,47 @@ public static void Main() Diagnostic(ErrorCode.ERR_UseDefViolation, "x4").WithArguments("x4").WithLocation(14, 35) ); } + + [Fact] + public void MatchExpression00() + { + var source = +@"using System; +public struct X +{ + public static void Main() + { + var oa = new object[] { 1, 10, 20L, 1.2, ""foo"", true, null, new X(), new Exception(""boo"") }; + foreach (var o in oa) + { + var s = o match ( + case 1 : ""one"" + case int i : $""int {i}"" + case long l : $""long {l}"" + case double d : $""double {d}"" + case null : $""null"" + case ValueType z : $""struct {z.GetType().Name} {z}"" + case object q : $""class {q.GetType().Name} {q}"" + ); + Console.WriteLine(s); + } + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: patternParseOptions); + compilation.VerifyDiagnostics(); + var expectedOutput = +@"one +int 10 +long 20 +double 1.2 +class String foo +struct Boolean True +null +struct X X +class Exception System.Exception: boo +"; + var comp = CompileAndVerify(compilation, expectedOutput: expectedOutput); + } } } diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs index 9832423f17905..5fa5861e82fe3 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs @@ -38,6 +38,7 @@ public void DiagnosticAnalyzerDriverAllInOne() syntaxKindsPatterns.Add(SyntaxKind.SubRecursivePattern); syntaxKindsPatterns.Add(SyntaxKind.MatchSection); syntaxKindsPatterns.Add(SyntaxKind.MatchExpression); + syntaxKindsPatterns.Add(SyntaxKind.ThrowExpression); var analyzer = new CSharpTrackingDiagnosticAnalyzer(); using (var workspace = CSharpWorkspaceFactory.CreateWorkspaceFromFile(source, TestOptions.Regular))