未验证 提交 5a426c06 编写于 作者: N Neal Gafter 提交者: GitHub

Merge pull request #23921 from gafter/rpatterns5

recursive-patterns(5): Respond to code review comments in #23208 and #23209, #23228, and #23357
此差异已折叠。
......@@ -4,14 +4,22 @@
- [ ] The specification needs to be updated with the additional syntax forms being added. See https://github.com/dotnet/csharplang/issues/1054 for a summary of the proposed changes vs C# 7.
### Unimplemented parts
- [ ] Fallback to a recursive pattern when a parenthesized expression is used.
- [ ] Give an error when a wildcard is used but something named `_` is in scope.
- [ ] Implement the switch statement in the presence of recursive patterns
- [x] Parsing
- [x] Binding
- [ ] Lowering
- [ ] Code-gen
- [ ] Debug info
- [ ] Edit-and-continue
- [ ] Implement the match expression
- [ ] Parsing
- [ ] Binding
- [ ] Lowering
- [ ] Code-gen
- [ ] It is an error if the name `var` binds to a type in a var pattern. Implement and test.
### Test plan needed
- [ ] We need a test plan for these additions. Here is a high-level list of some tests that are needed
- [ ] Scoping mechanism works for pattern variables in recursive patterns
......
......@@ -256,10 +256,15 @@ private BoundExpression FixTupleLiteral(ArrayBuilder<DeconstructionVariable> che
}
else
{
ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders;
if (variables.Count < 2)
{
Error(diagnostics, ErrorCode.ERR_DeconstructTooFewElements, syntax);
return false;
}
var inputPlaceholder = new BoundDeconstructValuePlaceholder(syntax, this.LocalScopeDepth, type);
var deconstructInvocation = MakeDeconstructInvocationExpression(variables.Count,
inputPlaceholder, rightSyntax, diagnostics, out outPlaceholders, requireTwoOrMoreElements: true);
BoundExpression deconstructInvocation = MakeDeconstructInvocationExpression(variables.Count,
inputPlaceholder, rightSyntax, diagnostics, outPlaceholders: out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders);
if (deconstructInvocation.HasAnyErrors)
{
......@@ -588,19 +593,13 @@ private static string ExtractDeconstructResultElementName(BoundExpression expres
/// The overload resolution is similar to writing `receiver.Deconstruct(out var x1, out var x2, ...)`.
/// </summary>
private BoundExpression MakeDeconstructInvocationExpression(
int numCheckedVariables, BoundExpression receiver, SyntaxNode rightSyntax,
DiagnosticBag diagnostics, out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders,
bool requireTwoOrMoreElements)
int numCheckedVariables,
BoundExpression receiver,
SyntaxNode rightSyntax,
DiagnosticBag diagnostics,
out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders)
{
var receiverSyntax = (CSharpSyntaxNode)receiver.Syntax;
if (requireTwoOrMoreElements && numCheckedVariables < 2)
{
Error(diagnostics, ErrorCode.ERR_DeconstructTooFewElements, receiverSyntax);
outPlaceholders = default(ImmutableArray<BoundDeconstructValuePlaceholder>);
return BadExpression(receiverSyntax, receiver);
}
if (receiver.Type.IsDynamic())
{
Error(diagnostics, ErrorCode.ERR_CannotDeconstructDynamic, rightSyntax);
......
......@@ -585,7 +585,7 @@ private static BoundExpression BindDefaultLiteral(ExpressionSyntax node)
return new BoundDefaultExpression(node, constantValueOpt: null, type: null);
}
internal virtual BoundSwitchExpressionCase BindSwitchExpressionArm(SwitchExpressionArmSyntax node, DiagnosticBag diagnostics)
internal virtual BoundSwitchExpressionArm BindSwitchExpressionArm(SwitchExpressionArmSyntax node, DiagnosticBag diagnostics)
{
return this.Next.BindSwitchExpressionArm(node, diagnostics);
}
......
......@@ -267,7 +267,7 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy
DiagnosticBag diagnostics)
{
TypeSyntax typeSyntax = node.Type;
BoundTypeExpression boundDeclType = BindPatternType(typeSyntax, operandType, ref hasErrors, out bool isVar, diagnostics);
BoundTypeExpression boundDeclType = BindPatternType(typeSyntax, operandType, diagnostics, ref hasErrors, out bool isVar);
if (typeSyntax.IsVar && !isVar)
{
// PROTOTYPE(patterns2): For compatibility, we temporarily parse the var pattern with a simple designator as a declaration pattern.
......@@ -277,7 +277,8 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy
diagnostics.Add(ErrorCode.ERR_VarMayNotBindToType, typeSyntax.Location, (boundDeclType.AliasOpt ?? (Symbol)boundDeclType.Type).ToDisplayString());
}
boundDeclType = new BoundTypeExpression(typeSyntax, null, inferredType: true, type: operandType, hasErrors: true);
boundDeclType = new BoundTypeExpression(
syntax: typeSyntax, aliasOpt: null, inferredType: true, type: operandType, hasErrors: true);
}
TypeSymbol declType = boundDeclType.Type;
......@@ -290,9 +291,9 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy
private BoundTypeExpression BindPatternType(
TypeSyntax typeSyntax,
TypeSymbol operandType,
DiagnosticBag diagnostics,
ref bool hasErrors,
out bool isVar,
DiagnosticBag diagnostics)
out bool isVar)
{
Debug.Assert(operandType != (object)null);
......@@ -357,7 +358,8 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy
}
variableSymbol = localSymbol;
variableAccess = new BoundLocal(node, localSymbol, null, declType);
variableAccess = new BoundLocal(
syntax: node, localSymbol: localSymbol, constantValueOpt: null, type: declType);
return;
}
else
......@@ -371,7 +373,8 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy
BoundExpression receiver = SynthesizeReceiver(node, expressionVariableField, diagnostics);
variableSymbol = expressionVariableField;
variableAccess = new BoundFieldAccess(node, receiver, expressionVariableField, null, hasErrors);
variableAccess = new BoundFieldAccess(
syntax: node, receiver: receiver, fieldSymbol: expressionVariableField, constantValueOpt: null, hasErrors: hasErrors);
return;
}
case DiscardDesignationSyntax _:
......@@ -385,11 +388,11 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy
}
TypeSymbol BindRecursivePatternType(TypeSyntax typeSyntax, TypeSymbol operandType, ref bool hasErrors, out BoundTypeExpression boundDeclType, DiagnosticBag diagnostics)
TypeSymbol BindRecursivePatternType(TypeSyntax typeSyntax, TypeSymbol operandType, DiagnosticBag diagnostics, ref bool hasErrors, out BoundTypeExpression boundDeclType)
{
if (typeSyntax != null)
{
boundDeclType = BindPatternType(typeSyntax, operandType, ref hasErrors, out bool isVar, diagnostics);
boundDeclType = BindPatternType(typeSyntax, operandType, diagnostics, ref hasErrors, out bool isVar);
if (isVar)
{
// The type `var` is not permitted in recursive patterns. If you want the type inferred, just omit it.
......@@ -414,7 +417,7 @@ TypeSymbol BindRecursivePatternType(TypeSyntax typeSyntax, TypeSymbol operandTyp
private BoundPattern BindDeconstructionPattern(DeconstructionPatternSyntax node, TypeSymbol operandType, bool hasErrors, DiagnosticBag diagnostics)
{
TypeSyntax typeSyntax = node.Type;
TypeSymbol declType = BindRecursivePatternType(typeSyntax, operandType, ref hasErrors, out BoundTypeExpression boundDeclType, diagnostics);
TypeSymbol declType = BindRecursivePatternType(typeSyntax, operandType, diagnostics, ref hasErrors, out BoundTypeExpression boundDeclType);
var patterns = ArrayBuilder<BoundPattern>.GetInstance(node.SubPatterns.Count);
MethodSymbol deconstructMethod = null;
......@@ -442,7 +445,7 @@ private BoundPattern BindDeconstructionPattern(DeconstructionPatternSyntax node,
// It is not a tuple type. Seek an appropriate Deconstruct method.
var inputPlaceholder = new BoundImplicitReceiver(node, declType); // A fake receiver expression to permit us to reuse binding logic
BoundExpression deconstruct = MakeDeconstructInvocationExpression(
node.SubPatterns.Count, inputPlaceholder, node, diagnostics, out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders, requireTwoOrMoreElements: false);
node.SubPatterns.Count, inputPlaceholder, node, diagnostics, outPlaceholders: out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders);
deconstructMethod = deconstruct.ExpressionSymbol as MethodSymbol;
// PROTOTYPE(patterns2): Set and check the deconstructMethod
......@@ -475,7 +478,7 @@ private BoundPattern BindDeconstructionPattern(DeconstructionPatternSyntax node,
private BoundPattern BindVarPattern(VarPatternSyntax node, TypeSymbol operandType, bool hasErrors, DiagnosticBag diagnostics)
{
TypeSymbol declType = operandType;
Symbol foundType = BindVarType(node.VarKeyword, diagnostics, out bool isVar, null);
Symbol foundType = BindVarType(varToken: node.VarKeyword, diagnostics: diagnostics, isVar: out bool isVar, basesBeingResolved: null);
if (!isVar)
{
// Give an error if there is a bindable type "var" in scope
......@@ -495,13 +498,17 @@ private BoundPattern BindVarDesignation(VarPatternSyntax node, VariableDesignati
//return new BoundDiscardPattern(designation);
// PROTOTYPE(patterns2): this should bind as a discard pattern, but for now we'll bind it as a declaration
// pattern for compatibility with the later phases of the compiler that do not yet handle the discard pattern.
var boundOperandType = new BoundTypeExpression(node, null, operandType); // fake a type expression for the variable's type
return new BoundDeclarationPattern(designation, null, null, boundOperandType, isVar: true, hasErrors: hasErrors);
var boundOperandType = new BoundTypeExpression(
syntax: node, aliasOpt: null, type: operandType); // fake a type expression for the variable's type
return new BoundDeclarationPattern(
syntax: designation, variable: null, variableAccess: null, declaredType: boundOperandType, isVar: true, hasErrors: hasErrors);
}
case SyntaxKind.SingleVariableDesignation:
{
BindPatternDesignation(node, designation, operandType, null, diagnostics, ref hasErrors, out Symbol variableSymbol, out BoundExpression variableAccess);
var boundOperandType = new BoundTypeExpression(node, null, operandType); // fake a type expression for the variable's type
BindPatternDesignation(
node: node, designation: designation, declType: operandType, typeSyntax: null, diagnostics: diagnostics,
hasErrors: ref hasErrors, variableSymbol: out Symbol variableSymbol, variableAccess: out BoundExpression variableAccess);
var boundOperandType = new BoundTypeExpression(syntax: node, aliasOpt: null, type: operandType); // fake a type expression for the variable's type
return new BoundDeclarationPattern(designation, variableSymbol, variableAccess, boundOperandType, isVar: true, hasErrors: hasErrors);
}
case SyntaxKind.ParenthesizedVariableDesignation:
......@@ -515,7 +522,8 @@ private BoundPattern BindVarDesignation(VarPatternSyntax node, VariableDesignati
ImmutableArray<TypeSymbol> elementTypes = operandType.TupleElementTypes;
if (elementTypes.Length != tupleDesignation.Variables.Count && !hasErrors)
{
var location = new SourceLocation(node.SyntaxTree, new Text.TextSpan(tupleDesignation.OpenParenToken.SpanStart, tupleDesignation.CloseParenToken.Span.End - tupleDesignation.OpenParenToken.SpanStart));
var location = new SourceLocation(node.SyntaxTree,
new Text.TextSpan(tupleDesignation.OpenParenToken.SpanStart, tupleDesignation.CloseParenToken.Span.End - tupleDesignation.OpenParenToken.SpanStart));
diagnostics.Add(ErrorCode.ERR_WrongNumberOfSubpatterns, location, operandType.TupleElementTypes, elementTypes.Length, tupleDesignation.Variables.Count);
hasErrors = true;
}
......@@ -532,7 +540,7 @@ private BoundPattern BindVarDesignation(VarPatternSyntax node, VariableDesignati
// It is not a tuple type. Seek an appropriate Deconstruct method.
var inputPlaceholder = new BoundImplicitReceiver(node, operandType); // A fake receiver expression to permit us to reuse binding logic
BoundExpression deconstruct = MakeDeconstructInvocationExpression(
tupleDesignation.Variables.Count, inputPlaceholder, node, diagnostics, out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders, requireTwoOrMoreElements: false);
tupleDesignation.Variables.Count, inputPlaceholder, node, diagnostics, outPlaceholders: out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders);
deconstructMethod = deconstruct.ExpressionSymbol as MethodSymbol;
// PROTOTYPE(patterns2): Set and check the deconstructMethod
......@@ -561,7 +569,7 @@ private BoundPattern BindVarDesignation(VarPatternSyntax node, VariableDesignati
private BoundPattern BindPropertyPattern(PropertyPatternSyntax node, TypeSymbol operandType, bool hasErrors, DiagnosticBag diagnostics)
{
TypeSyntax typeSyntax = node.Type;
TypeSymbol declType = BindRecursivePatternType(typeSyntax, operandType, ref hasErrors, out BoundTypeExpression boundDeclType, diagnostics);
TypeSymbol declType = BindRecursivePatternType(typeSyntax, operandType, diagnostics, ref hasErrors, out BoundTypeExpression boundDeclType);
ImmutableArray<(Symbol property, BoundPattern pattern)> propertiesOpt = BindPropertySubpattern(node.PropertySubpattern, declType, diagnostics, ref hasErrors);
BindPatternDesignation(node, node.Designation, declType, typeSyntax, diagnostics, ref hasErrors, out Symbol variableSymbol, out BoundExpression variableAccess);
return new BoundRecursivePattern(
......@@ -594,7 +602,7 @@ private BoundPattern BindPropertyPattern(PropertyPatternSyntax node, TypeSymbol
}
else
{
member = LookupMemberForPropertyPattern(inputType, name, out memberType, ref hasErrors, diagnostics);
member = LookupMemberForPropertyPattern(inputType, name, diagnostics, ref hasErrors, out memberType);
}
BoundPattern boundPattern = BindPattern(pattern, memberType, hasErrors, diagnostics);
......@@ -604,7 +612,8 @@ private BoundPattern BindPropertyPattern(PropertyPatternSyntax node, TypeSymbol
return builder.ToImmutableAndFree();
}
private Symbol LookupMemberForPropertyPattern(TypeSymbol inputType, IdentifierNameSyntax name, out TypeSymbol memberType, ref bool hasErrors, DiagnosticBag diagnostics)
private Symbol LookupMemberForPropertyPattern(
TypeSymbol inputType, IdentifierNameSyntax name, DiagnosticBag diagnostics, ref bool hasErrors, out TypeSymbol memberType)
{
Symbol symbol = BindPropertyPatternMember(inputType, name, ref hasErrors, diagnostics);
......@@ -682,7 +691,8 @@ private Symbol LookupMemberForPropertyPattern(TypeSymbol inputType, IdentifierNa
return null;
}
if (hasErrors || !CheckValueKind(memberName.Parent, boundMember, BindValueKind.RValue, false, diagnostics))
if (hasErrors || !CheckValueKind(node: memberName.Parent, expr: boundMember, valueKind: BindValueKind.RValue,
checkingReceiver: false, diagnostics: diagnostics))
{
return null;
}
......
......@@ -176,7 +176,7 @@ internal override void BindPatternSwitchLabelForInference(CasePatternSwitchLabel
throw ExceptionUtilities.Unreachable;
}
internal override BoundSwitchExpressionCase BindSwitchExpressionArm(SwitchExpressionArmSyntax node, DiagnosticBag diagnostics)
internal override BoundSwitchExpressionArm BindSwitchExpressionArm(SwitchExpressionArmSyntax node, DiagnosticBag diagnostics)
{
// There's supposed to be an overrider of this method (e.g. SwitchExpressionArmBinder) for the arm in the chain.
throw ExceptionUtilities.Unreachable;
......
......@@ -26,7 +26,7 @@ internal DecisionDagBuilder(CSharpCompilation compilation)
}
/// <summary>
/// Used to translate the pattern of an is-pattern expression.
/// Used to translate the pattern of an is-pattern expression. Returns the BoundDagTemp used to represent the root (input).
/// </summary>
public BoundDagTemp TranslatePattern(
BoundExpression input,
......
......@@ -644,7 +644,7 @@ internal class ExpressionFieldFinder : ExpressionVariableFinder<Symbol>
protected override Symbol MakePatternVariable(TypeSyntax type, SingleVariableDesignationSyntax designation, SyntaxNode nodeToBind)
{
return GlobalExpressionVariable.Create(
return designation == null ? null : GlobalExpressionVariable.Create(
_containingType, _modifiers, type,
designation.Identifier.ValueText, designation, designation.GetLocation(),
_containingFieldOpt, nodeToBind);
......@@ -652,30 +652,12 @@ protected override Symbol MakePatternVariable(TypeSyntax type, SingleVariableDes
protected override Symbol MakePatternVariable(DeconstructionPatternSyntax node, SyntaxNode nodeToBind)
{
var designation = node.Designation as SingleVariableDesignationSyntax;
if (designation == null)
{
return null;
}
return GlobalExpressionVariable.Create(
_containingType, _modifiers, node.Type,
designation.Identifier.ValueText, designation, designation.GetLocation(),
_containingFieldOpt, nodeToBind);
return MakePatternVariable(node.Type, node.Designation as SingleVariableDesignationSyntax, nodeToBind);
}
protected override Symbol MakePatternVariable(PropertyPatternSyntax node, SyntaxNode nodeToBind)
{
var designation = node.Designation as SingleVariableDesignationSyntax;
if (designation == null)
{
return null;
}
return GlobalExpressionVariable.Create(
_containingType, _modifiers, node.Type,
designation.Identifier.ValueText, designation, designation.GetLocation(),
_containingFieldOpt, nodeToBind);
return MakePatternVariable(node.Type, node.Designation as SingleVariableDesignationSyntax, nodeToBind);
}
protected override Symbol MakeDeclarationExpressionVariable(DeclarationExpressionSyntax node, SingleVariableDesignationSyntax designation, BaseArgumentListSyntax argumentListSyntaxOpt, SyntaxNode nodeToBind)
......
......@@ -13,6 +13,10 @@
namespace Microsoft.CodeAnalysis.CSharp
{
/// <summary>
/// Binder for one of the arms of a switch expression. For example, in the one-armed switch expression
/// "e switch { p when c => v }", this could be the binder for the arm "p when c => v".
/// </summary>
internal class SwitchExpressionArmBinder : Binder
{
private readonly SwitchExpressionArmSyntax _arm;
......@@ -26,7 +30,7 @@ public SwitchExpressionArmBinder(SwitchExpressionArmSyntax arm, ExpressionVariab
this._switchExpressionBinder = switchExpressionBinder;
}
internal override BoundSwitchExpressionCase BindSwitchExpressionArm(SwitchExpressionArmSyntax node, DiagnosticBag diagnostics)
internal override BoundSwitchExpressionArm BindSwitchExpressionArm(SwitchExpressionArmSyntax node, DiagnosticBag diagnostics)
{
Debug.Assert(node == _arm);
var caseBinder = this.GetBinder(node);
......@@ -37,7 +41,7 @@ internal override BoundSwitchExpressionCase BindSwitchExpressionArm(SwitchExpres
? caseBinder.BindBooleanExpression(node.WhenClause.Condition, diagnostics)
: null;
var result = caseBinder.BindValue(node.Expression, diagnostics, BindValueKind.RValue);
return new BoundSwitchExpressionCase(node, locals, pattern, guard, result, hasErrors);
return new BoundSwitchExpressionArm(node, locals, pattern, guard, result, hasErrors);
}
}
}
......@@ -31,7 +31,7 @@ internal override BoundExpression BindSwitchExpressionCore(SwitchExpressionSynta
// Bind switch expression and set the switch governing type.
var boundSwitchGoverningExpression = SwitchGoverningExpression;
diagnostics.AddRange(SwitchGoverningDiagnostics);
ImmutableArray<BoundSwitchExpressionCase> switchCases = BindSwitchExpressionCases(node, originalBinder, diagnostics);
ImmutableArray<BoundSwitchExpressionArm> switchCases = BindSwitchExpressionCases(node, originalBinder, diagnostics);
bool hasErrors = false;
TypeSymbol resultType = InferResultType(switchCases, diagnostics);
switchCases = AddConversionsToCases(switchCases, resultType, diagnostics);
......@@ -43,7 +43,7 @@ internal override BoundExpression BindSwitchExpressionCore(SwitchExpressionSynta
/// <summary>
/// Infer the result type of the switch expression by looking for a common type.
/// </summary>
private TypeSymbol InferResultType(ImmutableArray<BoundSwitchExpressionCase> switchCases, DiagnosticBag diagnostics)
private TypeSymbol InferResultType(ImmutableArray<BoundSwitchExpressionArm> switchCases, DiagnosticBag diagnostics)
{
var seenTypes = PooledHashSet<TypeSymbol>.GetInstance();
var typesInOrder = ArrayBuilder<TypeSymbol>.GetInstance();
......@@ -72,25 +72,25 @@ private TypeSymbol InferResultType(ImmutableArray<BoundSwitchExpressionCase> swi
/// <summary>
/// Rewrite the expressions in the switch expression cases to add a conversion to the result (common) type.
/// </summary>
private ImmutableArray<BoundSwitchExpressionCase> AddConversionsToCases(ImmutableArray<BoundSwitchExpressionCase> switchCases, TypeSymbol resultType, DiagnosticBag diagnostics)
private ImmutableArray<BoundSwitchExpressionArm> AddConversionsToCases(ImmutableArray<BoundSwitchExpressionArm> switchCases, TypeSymbol resultType, DiagnosticBag diagnostics)
{
var builder = ArrayBuilder<BoundSwitchExpressionCase>.GetInstance();
var builder = ArrayBuilder<BoundSwitchExpressionArm>.GetInstance();
foreach (var oldCase in switchCases)
{
var oldValue = oldCase.Value;
var newValue = GenerateConversionForAssignment(resultType, oldValue, diagnostics);
var newCase = (oldValue == newValue) ? oldCase :
new BoundSwitchExpressionCase(oldCase.Syntax, oldCase.Locals, oldCase.Pattern, oldCase.Guard, newValue, oldCase.HasErrors);
new BoundSwitchExpressionArm(oldCase.Syntax, oldCase.Locals, oldCase.Pattern, oldCase.Guard, newValue, oldCase.HasErrors);
builder.Add(newCase);
}
return builder.ToImmutableAndFree();
}
private ImmutableArray<BoundSwitchExpressionCase> BindSwitchExpressionCases(SwitchExpressionSyntax node, Binder originalBinder, DiagnosticBag diagnostics)
private ImmutableArray<BoundSwitchExpressionArm> BindSwitchExpressionCases(SwitchExpressionSyntax node, Binder originalBinder, DiagnosticBag diagnostics)
{
bool hasErrors = SwitchGoverningExpression.HasErrors;
var builder = ArrayBuilder<BoundSwitchExpressionCase>.GetInstance();
var builder = ArrayBuilder<BoundSwitchExpressionArm>.GetInstance();
foreach (var arm in node.Arms)
{
var armBinder = originalBinder.GetBinder(arm);
......@@ -105,11 +105,7 @@ internal BoundExpression SwitchGoverningExpression
{
get
{
if (_switchGoverningExpression == null)
{
EnsureSwitchGoverningExpressionAndDiagnosticsBound();
}
EnsureSwitchGoverningExpressionAndDiagnosticsBound();
Debug.Assert(_switchGoverningExpression != null);
return _switchGoverningExpression;
}
......@@ -122,16 +118,20 @@ protected DiagnosticBag SwitchGoverningDiagnostics
get
{
EnsureSwitchGoverningExpressionAndDiagnosticsBound();
Debug.Assert(_switchGoverningDiagnostics != null);
return _switchGoverningDiagnostics;
}
}
private void EnsureSwitchGoverningExpressionAndDiagnosticsBound()
{
var switchGoverningDiagnostics = new DiagnosticBag();
var boundSwitchGoverningExpression = BindSwitchGoverningExpression(switchGoverningDiagnostics);
_switchGoverningDiagnostics = switchGoverningDiagnostics;
Interlocked.CompareExchange(ref _switchGoverningExpression, boundSwitchGoverningExpression, null);
if (_switchGoverningExpression == null)
{
var switchGoverningDiagnostics = new DiagnosticBag();
var boundSwitchGoverningExpression = BindSwitchGoverningExpression(switchGoverningDiagnostics);
_switchGoverningDiagnostics = switchGoverningDiagnostics;
Interlocked.CompareExchange(ref _switchGoverningExpression, boundSwitchGoverningExpression, null);
}
}
private BoundExpression BindSwitchGoverningExpression(DiagnosticBag diagnostics)
......
......@@ -823,10 +823,10 @@
<Node Name="BoundSwitchExpression" Base="BoundExpression">
<Field Name="GoverningExpression" Type="BoundExpression"/>
<Field Name="SwitchSections" Type="ImmutableArray&lt;BoundSwitchExpressionCase&gt;"/>
<Field Name="SwitchSections" Type="ImmutableArray&lt;BoundSwitchExpressionArm&gt;"/>
<!--<Field Name="DecisionDag" Type="BoundDecisionDag" Null="allow"/>-->
</Node>
<Node Name="BoundSwitchExpressionCase" Base="BoundNode">
<Node Name="BoundSwitchExpressionArm" Base="BoundNode">
<Field Name="Locals" Type="ImmutableArray&lt;LocalSymbol&gt;"/>
<Field Name="Pattern" Type="BoundPattern" Null="disallow"/>
<Field Name="Guard" Type="BoundExpression" Null="allow"/>
......
......@@ -8981,11 +8981,11 @@ internal class CSharpResources {
}
/// <summary>
/// Looks up a localized string similar to A single-element deconstruct pattern is ambiguous with a parenthesized pattern; add &apos;{}&apos; after the close paren to disambiguate..
/// Looks up a localized string similar to A single-element deconstruct pattern requires a type before the open parenthesis..
/// </summary>
internal static string ERR_SingleElementPositionalPattern {
internal static string ERR_SingleElementPositionalPatternRequiresType {
get {
return ResourceManager.GetString("ERR_SingleElementPositionalPattern", resourceCulture);
return ResourceManager.GetString("ERR_SingleElementPositionalPatternRequiresType", resourceCulture);
}
}
......
......@@ -5271,8 +5271,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_SwitchExpressionNoBestType" xml:space="preserve">
<value>No best type was found for the switch expression.</value>
</data>
<data name="ERR_SingleElementPositionalPattern" xml:space="preserve">
<value>A single-element deconstruct pattern is ambiguous with a parenthesized pattern; add '{}' after the close paren to disambiguate.</value>
<data name="ERR_SingleElementPositionalPatternRequiresType" xml:space="preserve">
<value>A single-element deconstruct pattern requires a type before the open parenthesis.</value>
</data>
<data name="ERR_VarMayNotBindToType" xml:space="preserve">
<value>The syntax 'var' for a pattern is not permitted to bind to a type, but it binds to '{0}' here.</value>
......
......@@ -1566,7 +1566,7 @@ internal enum ErrorCode
//ERR_FeatureIsUnimplemented = 8404,
ERR_DefaultPattern = 8405,
ERR_SwitchExpressionNoBestType = 8406,
ERR_SingleElementPositionalPattern = 8407,
ERR_SingleElementPositionalPatternRequiresType = 8407,
ERR_VarMayNotBindToType = 8408,
#endregion diagnostics introduced for recursive patterns
......
......@@ -91,7 +91,7 @@ internal enum BoundKind: byte
PatternSwitchStatement,
PatternSwitchStatement2,
SwitchExpression,
SwitchExpressionCase,
SwitchExpressionArm,
EvaluationPoint,
DecisionPoint,
WhereClause,
......@@ -2934,7 +2934,7 @@ public BoundPatternSwitchStatement2 Update(BoundExpression expression, Immutable
internal sealed partial class BoundSwitchExpression : BoundExpression
{
public BoundSwitchExpression(SyntaxNode syntax, BoundExpression governingExpression, ImmutableArray<BoundSwitchExpressionCase> switchSections, TypeSymbol type, bool hasErrors = false)
public BoundSwitchExpression(SyntaxNode syntax, BoundExpression governingExpression, ImmutableArray<BoundSwitchExpressionArm> switchSections, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.SwitchExpression, syntax, type, hasErrors || governingExpression.HasErrors() || switchSections.HasErrors())
{
......@@ -2948,14 +2948,14 @@ public BoundSwitchExpression(SyntaxNode syntax, BoundExpression governingExpress
public BoundExpression GoverningExpression { get; }
public ImmutableArray<BoundSwitchExpressionCase> SwitchSections { get; }
public ImmutableArray<BoundSwitchExpressionArm> SwitchSections { get; }
public override BoundNode Accept(BoundTreeVisitor visitor)
{
return visitor.VisitSwitchExpression(this);
}
public BoundSwitchExpression Update(BoundExpression governingExpression, ImmutableArray<BoundSwitchExpressionCase> switchSections, TypeSymbol type)
public BoundSwitchExpression Update(BoundExpression governingExpression, ImmutableArray<BoundSwitchExpressionArm> switchSections, TypeSymbol type)
{
if (governingExpression != this.GoverningExpression || switchSections != this.SwitchSections || type != this.Type)
{
......@@ -2967,10 +2967,10 @@ public BoundSwitchExpression Update(BoundExpression governingExpression, Immutab
}
}
internal sealed partial class BoundSwitchExpressionCase : BoundNode
internal sealed partial class BoundSwitchExpressionArm : BoundNode
{
public BoundSwitchExpressionCase(SyntaxNode syntax, ImmutableArray<LocalSymbol> locals, BoundPattern pattern, BoundExpression guard, BoundExpression value, bool hasErrors = false)
: base(BoundKind.SwitchExpressionCase, syntax, hasErrors || pattern.HasErrors() || guard.HasErrors() || value.HasErrors())
public BoundSwitchExpressionArm(SyntaxNode syntax, ImmutableArray<LocalSymbol> locals, BoundPattern pattern, BoundExpression guard, BoundExpression value, bool hasErrors = false)
: base(BoundKind.SwitchExpressionArm, syntax, hasErrors || pattern.HasErrors() || guard.HasErrors() || value.HasErrors())
{
Debug.Assert(!locals.IsDefault, "Field 'locals' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
......@@ -2994,14 +2994,14 @@ public BoundSwitchExpressionCase(SyntaxNode syntax, ImmutableArray<LocalSymbol>
public override BoundNode Accept(BoundTreeVisitor visitor)
{
return visitor.VisitSwitchExpressionCase(this);
return visitor.VisitSwitchExpressionArm(this);
}
public BoundSwitchExpressionCase Update(ImmutableArray<LocalSymbol> locals, BoundPattern pattern, BoundExpression guard, BoundExpression value)
public BoundSwitchExpressionArm Update(ImmutableArray<LocalSymbol> locals, BoundPattern pattern, BoundExpression guard, BoundExpression value)
{
if (locals != this.Locals || pattern != this.Pattern || guard != this.Guard || value != this.Value)
{
var result = new BoundSwitchExpressionCase(this.Syntax, locals, pattern, guard, value, this.HasErrors);
var result = new BoundSwitchExpressionArm(this.Syntax, locals, pattern, guard, value, this.HasErrors);
result.WasCompilerGenerated = this.WasCompilerGenerated;
return result;
}
......@@ -6939,8 +6939,8 @@ internal R VisitInternal(BoundNode node, A arg)
return VisitPatternSwitchStatement2(node as BoundPatternSwitchStatement2, arg);
case BoundKind.SwitchExpression:
return VisitSwitchExpression(node as BoundSwitchExpression, arg);
case BoundKind.SwitchExpressionCase:
return VisitSwitchExpressionCase(node as BoundSwitchExpressionCase, arg);
case BoundKind.SwitchExpressionArm:
return VisitSwitchExpressionArm(node as BoundSwitchExpressionArm, arg);
case BoundKind.EvaluationPoint:
return VisitEvaluationPoint(node as BoundEvaluationPoint, arg);
case BoundKind.DecisionPoint:
......@@ -7421,7 +7421,7 @@ public virtual R VisitSwitchExpression(BoundSwitchExpression node, A arg)
{
return this.DefaultVisit(node, arg);
}
public virtual R VisitSwitchExpressionCase(BoundSwitchExpressionCase node, A arg)
public virtual R VisitSwitchExpressionArm(BoundSwitchExpressionArm node, A arg)
{
return this.DefaultVisit(node, arg);
}
......@@ -8089,7 +8089,7 @@ public virtual BoundNode VisitSwitchExpression(BoundSwitchExpression node)
{
return this.DefaultVisit(node);
}
public virtual BoundNode VisitSwitchExpressionCase(BoundSwitchExpressionCase node)
public virtual BoundNode VisitSwitchExpressionArm(BoundSwitchExpressionArm node)
{
return this.DefaultVisit(node);
}
......@@ -8834,7 +8834,7 @@ public override BoundNode VisitSwitchExpression(BoundSwitchExpression node)
this.VisitList(node.SwitchSections);
return null;
}
public override BoundNode VisitSwitchExpressionCase(BoundSwitchExpressionCase node)
public override BoundNode VisitSwitchExpressionArm(BoundSwitchExpressionArm node)
{
this.Visit(node.Pattern);
this.Visit(node.Guard);
......@@ -9744,11 +9744,11 @@ public override BoundNode VisitPatternSwitchStatement2(BoundPatternSwitchStateme
public override BoundNode VisitSwitchExpression(BoundSwitchExpression node)
{
BoundExpression governingExpression = (BoundExpression)this.Visit(node.GoverningExpression);
ImmutableArray<BoundSwitchExpressionCase> switchSections = (ImmutableArray<BoundSwitchExpressionCase>)this.VisitList(node.SwitchSections);
ImmutableArray<BoundSwitchExpressionArm> switchSections = (ImmutableArray<BoundSwitchExpressionArm>)this.VisitList(node.SwitchSections);
TypeSymbol type = this.VisitType(node.Type);
return node.Update(governingExpression, switchSections, type);
}
public override BoundNode VisitSwitchExpressionCase(BoundSwitchExpressionCase node)
public override BoundNode VisitSwitchExpressionArm(BoundSwitchExpressionArm node)
{
BoundPattern pattern = (BoundPattern)this.Visit(node.Pattern);
BoundExpression guard = (BoundExpression)this.Visit(node.Guard);
......@@ -11039,9 +11039,9 @@ public override TreeDumperNode VisitSwitchExpression(BoundSwitchExpression node,
}
);
}
public override TreeDumperNode VisitSwitchExpressionCase(BoundSwitchExpressionCase node, object arg)
public override TreeDumperNode VisitSwitchExpressionArm(BoundSwitchExpressionArm node, object arg)
{
return new TreeDumperNode("switchExpressionCase", null, new TreeDumperNode[]
return new TreeDumperNode("switchExpressionArm", null, new TreeDumperNode[]
{
new TreeDumperNode("locals", node.Locals, null),
new TreeDumperNode("pattern", null, new TreeDumperNode[] { Visit(node.Pattern, null) }),
......
......@@ -62,20 +62,6 @@ public override BoundNode VisitIsOperator(BoundIsOperator node)
{
// operand is a reference type with bound identity or implicit conversion
// We can replace the "is" instruction with a null check
Debug.Assert((object)operandType != null);
if (operandType.TypeKind == TypeKind.TypeParameter)
{
// We need to box the type parameter even if it is a known
// reference type to ensure there are no verifier errors
rewrittenOperand = MakeConversionNode(
syntax: rewrittenOperand.Syntax,
rewrittenOperand: rewrittenOperand,
conversion: Conversion.Boxing,
rewrittenType: _compilation.GetSpecialType(SpecialType.System_Object),
@checked: false);
}
return MakeNullCheck(syntax, rewrittenOperand, BinaryOperatorKind.NotEqual);
}
}
......
......@@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp
{
internal sealed partial class LocalRewriter
{
private struct IsPatternExpressionLocalRewriter : IDisposable
private struct IsPatternExpressionLocalRewriter
{
private readonly LocalRewriter _localRewriter;
private readonly SyntheticBoundNodeFactory _factory;
......@@ -45,14 +45,14 @@ public IsPatternExpressionLocalRewriter(LocalRewriter localRewriter, BoundExpres
this._sideEffectBuilder = ArrayBuilder<BoundExpression>.GetInstance();
}
public void Dispose()
public void Free()
{
_conjunctBuilder.Free();
_sideEffectBuilder.Free();
_tempAllocator.Dispose();
_tempAllocator.Free();
}
public class DagTempAllocator : IDisposable
public class DagTempAllocator
{
private readonly SyntheticBoundNodeFactory _factory;
private readonly PooledDictionary<BoundDagTemp, BoundExpression> _map = PooledDictionary<BoundDagTemp, BoundExpression>.GetInstance();
......@@ -63,7 +63,7 @@ public DagTempAllocator(SyntheticBoundNodeFactory factory)
this._factory = factory;
}
public void Dispose()
public void Free()
{
_temps.Free();
_map.Free();
......@@ -289,15 +289,16 @@ public BoundExpression LowerIsPattern(BoundPattern pattern, CSharpCompilation co
result = (result == null) ? conjunct : _factory.LogicalAnd(result, conjunct);
}
var bindingsBuilder = ArrayBuilder<BoundExpression>.GetInstance();
var bindingsBuilder = ArrayBuilder<BoundExpression>.GetInstance(bindings.Length);
foreach ((BoundExpression left, BoundDagTemp right) in bindings)
{
bindingsBuilder.Add(_factory.AssignmentExpression(left, _tempAllocator.GetTemp(right)));
}
if (bindingsBuilder.Count > 0)
var bindingAssignments = bindingsBuilder.ToImmutableAndFree();
if (bindingAssignments.Length > 0)
{
BoundSequence c = _factory.Sequence(ImmutableArray<LocalSymbol>.Empty, bindingsBuilder.ToImmutableAndFree(), _factory.Literal(true));
BoundSequence c = _factory.Sequence(ImmutableArray<LocalSymbol>.Empty, bindingAssignments, _factory.Literal(true));
result = (result == null) ? c : (BoundExpression)_factory.LogicalAnd(result, c);
}
else if (result == null)
......@@ -374,10 +375,10 @@ public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node
{
BoundExpression loweredExpression = VisitExpression(node.Expression);
BoundPattern loweredPattern = LowerPattern(node.Pattern);
using (var x = new IsPatternExpressionLocalRewriter(this, loweredExpression))
{
return x.LowerIsPattern(loweredPattern, this._compilation);
}
var isPatternRewriter = new IsPatternExpressionLocalRewriter(this, loweredExpression);
BoundExpression result = isPatternRewriter.LowerIsPattern(loweredPattern, this._compilation);
isPatternRewriter.Free();
return result;
}
BoundPattern LowerPattern(BoundPattern pattern)
......
......@@ -424,7 +424,7 @@ bool looksLikeCast()
{
// PROTOTYPE(patterns2): we parse it as a declaration pattern when we have simple designation, for compatibility.
// PROTOTYPE(patterns2): can we change it to use a var pattern in all cases?
//return _syntaxFactory.VarPattern(varIdentifier, varDesignation);
// PROTOTYPE(patterns2): For example: return _syntaxFactory.VarPattern(varIdentifier, varDesignation);
return _syntaxFactory.DeclarationPattern(_syntaxFactory.IdentifierName(typeIdentifierToken), varDesignation);
}
}
......@@ -454,21 +454,18 @@ bool looksLikeCast()
{
// There is an ambiguity between a deconstruction pattern `(` pattern `)`
// and a constant expression pattern that happens to be parenthesized.
// We treat such syntax as a parenthesized expression always.
// Per 2017-11-20 LDM we treat such syntax as a parenthesized expression always.
return _syntaxFactory.ConstantPattern(_syntaxFactory.ParenthesizedExpression(openParenToken, cp.Expression, closeParenToken));
}
// 2017-11-20 LDM decision is to disallow a deconstruction pattern that contains just a
// single subpattern but for which the type is omitted. We'll look at other ways of disambiguating later,
// such as perhaps permitting `var` to infer the type, or a trailing comma. This also keeps the design
// space open for using parens for grouping patterns in the future, e.g. if we introduce `or` and
// `and` patterns.
var result = _syntaxFactory.DeconstructionPattern(type, openParenToken, subPatterns, closeParenToken, propertySubpattern0, designation0);
return this.AddError(result, ErrorCode.ERR_SingleElementPositionalPattern);
}
return _syntaxFactory.DeconstructionPattern(type, openParenToken, subPatterns, closeParenToken, propertySubpattern0, designation0);
var result = _syntaxFactory.DeconstructionPattern(type, openParenToken, subPatterns, closeParenToken, propertySubpattern0, designation0);
// 2017-11-20 LDM decision is to disallow a deconstruction pattern that contains just a
// single subpattern but for which the type is omitted.
// This keeps the design space open for using parentheses for grouping patterns in the future, e.g. if we introduce `or` and
// `and` patterns. We may add other ways to disambiguate later (e.g. a property subpattern or a trailing comma inside the parens).
return (type == null && subPatterns.Count == 1) ? this.AddError(result, ErrorCode.ERR_SingleElementPositionalPatternRequiresType) : result;
}
if (parsePropertySubpattern(out PropertySubpatternSyntax propertySubpattern))
......
......@@ -16,6 +16,11 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests
[CompilerTrait(CompilerFeature.Patterns)]
public class PatternMatchingTests2 : PatternMatchingTestBase
{
CSharpCompilation CreatePatternCompilation(string source)
{
return CreateStandardCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
}
[Fact]
public void Patterns2_00()
{
......@@ -30,7 +35,7 @@ public static void Main()
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
);
var comp = CompileAndVerify(compilation, expectedOutput: @"1");
......@@ -72,7 +77,7 @@ public void Deconstruct(out int X, out int Y)
public int Length => 5;
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
);
var comp = CompileAndVerify(compilation, expectedOutput: "");
......@@ -117,6 +122,7 @@ public static void Deconstruct(this Point p, out int X, out int Y)
}
}
";
// We use a compilation profile that provides System.Runtime.CompilerServices.ExtensionAttribute needed for this test
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
compilation.VerifyDiagnostics(
);
......@@ -163,7 +169,7 @@ public ValueTuple(T1 item1, T2 item2)
}
}
}";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
);
var comp = CompileAndVerify(compilation, expectedOutput: "");
......@@ -205,7 +211,7 @@ public void Deconstruct(out int X, out int Y)
public int Length => 5;
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
);
var comp = CompileAndVerify(compilation, expectedOutput: "");
......@@ -248,7 +254,7 @@ public ValueTuple(T1 item1, T2 item2)
void testErrorCase(string s1, string s2, string s3)
{
var source = string.Format(sourceTemplate, s1, s2, s3);
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
// (12,13): error CS8120: The switch case has already been handled by a previous case.
// case (_, _): // error - subsumed
......@@ -258,7 +264,7 @@ void testErrorCase(string s1, string s2, string s3)
void testGoodCase(string s1, string s2)
{
var source = string.Format(sourceTemplate, s1, s2, string.Empty);
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
);
}
......@@ -310,7 +316,7 @@ public void Deconstruct(out int X, out int Y)
public int Length => 5;
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
);
var comp = CompileAndVerify(compilation, expectedOutput: "True");
......@@ -346,7 +352,7 @@ public ValueTuple(T1 item1, T2 item2)
}
}
}";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
// (6,18): error CS8405: A default literal 'default' is not valid as a pattern. Use another literal (e.g. '0' or 'null') as appropriate. To match everything, use a discard pattern '_'.
// if (i is default) {} // error 1
......@@ -378,7 +384,7 @@ public static void Main()
var r = 1 switch { _ => 0 };
}
}";
CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe).VerifyDiagnostics(
CreateStandardCompilation(source, options: TestOptions.DebugExe).VerifyDiagnostics(
// (5,17): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable.
// var r = 1 switch ( _ => 0 );
Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "1 switch { _ => 0 }").WithArguments("recursive patterns", "patterns2").WithLocation(5, 17)
......@@ -413,7 +419,7 @@ public ValueTuple(T1 item1, T2 item2)
}
}
}";
CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns).VerifyDiagnostics(
CreatePatternCompilation(source).VerifyDiagnostics(
// (5,18): error CS8117: Invalid operand for pattern match; value required, but found '(int, <null>)'.
// var r1 = (1, null) switch ( _ => 0 );
Diagnostic(ErrorCode.ERR_BadPatternExpression, "(1, null)").WithArguments("(int, <null>)").WithLocation(5, 18),
......@@ -440,7 +446,7 @@ public static void Main()
}";
// PROTOTYPE(patterns2): This is admittedly poor syntax error recovery (for the line declaring r2),
// but this test demonstrates that it is a syntax error.
CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns).VerifyDiagnostics(
CreatePatternCompilation(source).VerifyDiagnostics(
// (6,34): error CS1003: Syntax error, '=>' expected
// var r1 = b switch { true ? true : true => true, false => false };
Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>", "?").WithLocation(6, 34),
......@@ -515,7 +521,7 @@ public ValueTuple(T1 item1, T2 item2)
}
}
}";
CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns).VerifyDiagnostics(
CreatePatternCompilation(source).VerifyDiagnostics(
);
}
......@@ -531,7 +537,7 @@ public static void Main()
var x = 1 switch { 1 => 1, _ => throw null };
}
}";
CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns).VerifyDiagnostics(
CreatePatternCompilation(source).VerifyDiagnostics(
);
}
......@@ -550,7 +556,7 @@ public static void Main()
public static void M() {}
public delegate void D();
}";
CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns).VerifyDiagnostics(
CreatePatternCompilation(source).VerifyDiagnostics(
);
}
......@@ -569,7 +575,7 @@ public static void Main()
System.Console.WriteLine(u);
}
}";
CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns).VerifyDiagnostics(
CreatePatternCompilation(source).VerifyDiagnostics(
);
}
......@@ -589,7 +595,7 @@ public static void Main()
}
static int M(int i) => i;
}";
CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns).VerifyDiagnostics(
CreatePatternCompilation(source).VerifyDiagnostics(
// (8,34): error CS0165: Use of unassigned local variable 'u'
// System.Console.WriteLine(u);
Diagnostic(ErrorCode.ERR_UseDefViolation, "u").WithArguments("u").WithLocation(8, 34)
......@@ -612,7 +618,7 @@ public static void Main()
}
static int M(int i) => i;
}";
CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns).VerifyDiagnostics(
CreatePatternCompilation(source).VerifyDiagnostics(
// (7,47): error CS0165: Use of unassigned local variable 'u'
// var x = q switch { 0 => u=0, 1 => u=M(u), _ => u=2 };
Diagnostic(ErrorCode.ERR_UseDefViolation, "u").WithArguments("u").WithLocation(7, 47)
......@@ -637,7 +643,7 @@ public static void Main()
}
static int M(int i) => i;
}";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
);
var names = new[] { "x1", "x2", "x3", "x4", "x5" };
......@@ -670,7 +676,7 @@ public static void Main()
if (a is _) { }
}
}";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
// (6,18): error CS0246: The type or namespace name '_' could not be found (are you missing a using directive or an assembly reference?)
// if (a is _) { }
......@@ -693,10 +699,10 @@ public static void Main()
if (t is (int x)) { } // error 1
switch (t) { case (_): break; } // error 2
var u = t switch { (int y) => y, _ => 2 }; // error 3
if (t is (int z1) _) { } // ok
if (t is (Item1: int z2)) { } // ok
if (t is (int z3) { }) { } // ok
if (t is ValueTuple<int>(int z4)) { } // ok
if (t is (int z1) _) { } // error 4
if (t is (Item1: int z2)) { } // error 5
if (t is (int z3) { }) { } // error 6
if (t is ValueTuple<int>(int z4)) { } // ok
}
private static bool Check<T>(T expected, T actual)
{
......@@ -715,18 +721,27 @@ public ValueTuple(T item1)
this.Item1 = item1;
}
}
}";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
}";;
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
// (8,18): error CS8407: A single-element deconstruct pattern is ambiguous with a parenthesized pattern; add '{}' after the close paren to disambiguate.
// (8,18): error CS8407: A single-element deconstruct pattern requires a type before the open parenthesis.
// if (t is (int x)) { } // error 1
Diagnostic(ErrorCode.ERR_SingleElementPositionalPattern, "(int x)").WithLocation(8, 18),
// (9,27): error CS8407: A single-element deconstruct pattern is ambiguous with a parenthesized pattern; add '{}' after the close paren to disambiguate.
Diagnostic(ErrorCode.ERR_SingleElementPositionalPatternRequiresType, "(int x)").WithLocation(8, 18),
// (9,27): error CS8407: A single-element deconstruct pattern requires a type before the open parenthesis.
// switch (t) { case (_): break; } // error 2
Diagnostic(ErrorCode.ERR_SingleElementPositionalPattern, "(_)").WithLocation(9, 27),
// (10,28): error CS8407: A single-element deconstruct pattern is ambiguous with a parenthesized pattern; add '{}' after the close paren to disambiguate.
Diagnostic(ErrorCode.ERR_SingleElementPositionalPatternRequiresType, "(_)").WithLocation(9, 27),
// (10,28): error CS8407: A single-element deconstruct pattern requires a type before the open parenthesis.
// var u = t switch { (int y) => y, _ => 2 }; // error 3
Diagnostic(ErrorCode.ERR_SingleElementPositionalPattern, "(int y)").WithLocation(10, 28)
Diagnostic(ErrorCode.ERR_SingleElementPositionalPatternRequiresType, "(int y)").WithLocation(10, 28),
// (11,18): error CS8407: A single-element deconstruct pattern requires a type before the open parenthesis.
// if (t is (int z1) _) { } // error 4
Diagnostic(ErrorCode.ERR_SingleElementPositionalPatternRequiresType, "(int z1) _").WithLocation(11, 18),
// (12,18): error CS8407: A single-element deconstruct pattern requires a type before the open parenthesis.
// if (t is (Item1: int z2)) { } // error 5
Diagnostic(ErrorCode.ERR_SingleElementPositionalPatternRequiresType, "(Item1: int z2)").WithLocation(12, 18),
// (13,18): error CS8407: A single-element deconstruct pattern requires a type before the open parenthesis.
// if (t is (int z3) { }) { } // error 6
Diagnostic(ErrorCode.ERR_SingleElementPositionalPatternRequiresType, "(int z3) { }").WithLocation(13, 18)
);
}
......@@ -766,7 +781,7 @@ public ValueTuple(T1 item1, T2 item2)
}
}
}";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
);
var comp = CompileAndVerify(compilation, expectedOutput: @"");
......@@ -811,7 +826,7 @@ public ValueTuple(T1 item1, T2 item2)
}
}
}";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
// (9,21): error CS0029: Cannot implicitly convert type '(int, int)' to 'N.var'
// var t = (1, 2);
......
......@@ -6715,10 +6715,6 @@ static int Main()
";
var semanticInfo = GetSemanticInfoForTest<InitializerExpressionSyntax>(sourceCode);
Assert.Null(semanticInfo.Type);
//Assert.Equal("int[]", semanticInfo.Type.ToString());
//Assert.Equal("int[]", semanticInfo.ConvertedType.ToString());
//Assert.Equal(ConversionKind.Identity, semanticInfo.ImplicitConversion.Kind);
//Assert.True(semanticInfo.IsCompileTimeConstant);
}
[Fact]
......@@ -8894,23 +8890,6 @@ public static int Main(string[] args)
// case /*<bind>*/()=>3/*</bind>*/:
Diagnostic(ErrorCode.ERR_MissingDeconstruct, "()").WithArguments("string", "0").WithLocation(12, 28)
);
// Due to language changes to support recursive patterns, the parser will no longer treat this syntax as a lambda.
//var semanticInfo = GetSemanticInfoForTest<ParenthesizedLambdaExpressionSyntax>(sourceCode);
//Assert.Null(semanticInfo.Type);
//Assert.Equal("System.String", semanticInfo.ConvertedType.ToTestDisplayString());
//Assert.Equal(TypeKind.Class, semanticInfo.ConvertedType.TypeKind);
//Assert.Equal(ConversionKind.NoConversion, semanticInfo.ImplicitConversion.Kind);
//Assert.Equal("lambda expression", semanticInfo.Symbol.ToTestDisplayString());
//Assert.Equal(SymbolKind.Method, semanticInfo.Symbol.Kind);
//Assert.Equal(0, semanticInfo.CandidateSymbols.Length);
//Assert.Equal(0, semanticInfo.MethodGroup.Length);
//Assert.False(semanticInfo.IsCompileTimeConstant);
}
[Fact]
......@@ -8952,22 +8931,6 @@ public static int Main(string[] args)
// case /*<bind>*/()=>/*</bind>*/:
Diagnostic(ErrorCode.ERR_MissingDeconstruct, "()").WithArguments("string", "0").WithLocation(13, 28)
);
// Due to language changes to support recursive patterns, the parser will no longer treat this syntax as a lambda.
//var semanticInfo = GetSemanticInfoForTest<ParenthesizedLambdaExpressionSyntax>(sourceCode);
//Assert.Null(semanticInfo.Type);
//Assert.Equal("System.String", semanticInfo.ConvertedType.ToTestDisplayString());
//Assert.Equal(TypeKind.Class, semanticInfo.ConvertedType.TypeKind);
//Assert.Equal(ConversionKind.NoConversion, semanticInfo.ImplicitConversion.Kind);
//Assert.Equal("lambda expression", semanticInfo.Symbol.ToTestDisplayString());
//Assert.Equal(SymbolKind.Method, semanticInfo.Symbol.Kind);
//Assert.Equal(0, semanticInfo.CandidateSymbols.Length);
//Assert.Equal(0, semanticInfo.MethodGroup.Length);
//Assert.False(semanticInfo.IsCompileTimeConstant);
}
[Fact]
......
......@@ -1832,6 +1832,9 @@ public void ParenthesizedExpression_02()
public void ParenthesizedExpression_03()
{
UsingStatement(@"switch (e) { case (x: ((3))): ; }",
// (1,19): error CS8407: A single-element deconstruct pattern requires a type before the open parenthesis.
// switch (e) { case (x: ((3))): ; }
Diagnostic(ErrorCode.ERR_SingleElementPositionalPatternRequiresType, "(x: ((3)))").WithLocation(1, 19),
// (1,19): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable.
// switch (e) { case (x: ((3))): ; }
Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(x: ((3)))").WithArguments("recursive patterns", "patterns2").WithLocation(1, 19)
......@@ -1900,15 +1903,18 @@ public void ParenthesizedExpression_03()
public void ParenthesizedExpression_04()
{
UsingStatement(@"switch (e) { case (((x: 3))): ; }",
// (1,19): error CS8407: A single-element deconstruct pattern is ambiguous with a parenthesized pattern; add '{}' after the close paren to disambiguate.
// (1,19): error CS8407: A single-element deconstruct pattern requires a type before the open parenthesis.
// switch (e) { case (((x: 3))): ; }
Diagnostic(ErrorCode.ERR_SingleElementPositionalPattern, "(((x: 3)))").WithLocation(1, 19),
Diagnostic(ErrorCode.ERR_SingleElementPositionalPatternRequiresType, "(((x: 3)))").WithLocation(1, 19),
// (1,19): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable.
// switch (e) { case (((x: 3))): ; }
Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(((x: 3)))").WithArguments("recursive patterns", "patterns2").WithLocation(1, 19),
// (1,20): error CS8407: A single-element deconstruct pattern is ambiguous with a parenthesized pattern; add '{}' after the close paren to disambiguate.
// (1,20): error CS8407: A single-element deconstruct pattern requires a type before the open parenthesis.
// switch (e) { case (((x: 3))): ; }
Diagnostic(ErrorCode.ERR_SingleElementPositionalPatternRequiresType, "((x: 3))").WithLocation(1, 20),
// (1,21): error CS8407: A single-element deconstruct pattern requires a type before the open parenthesis.
// switch (e) { case (((x: 3))): ; }
Diagnostic(ErrorCode.ERR_SingleElementPositionalPattern, "((x: 3))").WithLocation(1, 20)
Diagnostic(ErrorCode.ERR_SingleElementPositionalPatternRequiresType, "(x: 3)").WithLocation(1, 21)
);
N(SyntaxKind.SwitchStatement);
{
......@@ -2208,6 +2214,9 @@ public void BrokenPattern_08()
public void ParenthesizedExpression_05()
{
UsingStatement(@"switch (e) { case (x: ): ; }",
// (1,19): error CS8407: A single-element deconstruct pattern requires a type before the open parenthesis.
// switch (e) { case (x: ): ; }
Diagnostic(ErrorCode.ERR_SingleElementPositionalPatternRequiresType, "(x: )").WithLocation(1, 19),
// (1,19): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable.
// switch (e) { case (x: ): ; }
Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(x: )").WithArguments("recursive patterns", "patterns2").WithLocation(1, 19),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册