提交 818dc464 编写于 作者: N Neal Gafter

Distinguish irrefutable and refutable patterns in the `is` expression

Both refutable and irrefutable is-pattern matches affect definite assignment:
- an irrefutable match leaves pattern variables definitely assigned
- a refutable match is an error
Fixes #25890

Also, we check to see if a constant input makes the result refutable or irrefutable:
- If a constant cannot match the pattern, we issue a warning
- If a constant always matches a constant pattern, we issue a warning
The latter two are subject to compat council approval.
Fixes #16099
上级 1f683c4c
......@@ -15,3 +15,13 @@ Each entry should include a short description of the break, followed by either a
if (o is _) // warning: The name '_' refers to the type '_', not the discard pattern. Use '@_' for the type, or 'var _' to discard.
```
3. In an *is-pattern-expression*, a warning is now issued when a constant expression does not match the provided pattern because of its value. Such code was previously accepted but gave no warning. For example
``` c#
if (3 is 4) // warning: the given expression never matches the provided pattern.
```
We also issue a warning when a constant expression *always* matches a constant pattern in an *is-pattern-expression*. For example
``` c#
if (3 is 3) // warning: the given expression always matches the provided constant.
```
Other cases of the pattern always matching (e.g. `e is var t`) do not trigger a warning, even when they are known by the compiler to produce an invariant result.
......@@ -2641,9 +2641,9 @@ private bool IsOperandErrors(CSharpSyntaxNode node, ref BoundExpression operand,
if (!operand.HasAnyErrors)
{
Error(diagnostics, ErrorCode.ERR_LambdaInIsAs, node);
operand = BadExpression(node, operand).MakeCompilerGenerated();
}
operand = BadExpression(node, operand).MakeCompilerGenerated();
return true;
default:
......@@ -2655,6 +2655,7 @@ private bool IsOperandErrors(CSharpSyntaxNode node, ref BoundExpression operand,
Error(diagnostics, ErrorCode.ERR_BadUnaryOp, node, SyntaxFacts.GetText(SyntaxKind.IsKeyword), operand.Display);
}
operand = BadExpression(node, operand).MakeCompilerGenerated();
return true;
}
......@@ -2724,7 +2725,7 @@ private BoundExpression BindIsOperator(BinaryExpressionSyntax node, DiagnosticBa
{
isTypeDiagnostics.Free();
diagnostics.AddRangeAndFree(isPatternDiagnostics);
return new BoundIsPatternExpression(node, operand, boundConstantPattern, resultType, operandHasErrors);
return MakeIsPatternExpression(node, operand, boundConstantPattern, resultType, operandHasErrors, diagnostics);
}
isPatternDiagnostics.Free();
......
......@@ -20,23 +20,53 @@ private BoundExpression BindIsPatternExpression(IsPatternExpressionSyntax node,
TypeSymbol expressionType = expression.Type;
if ((object)expressionType == null || expressionType.SpecialType == SpecialType.System_Void)
{
expressionType = CreateErrorType();
if (!hasErrors)
{
// value expected
diagnostics.Add(ErrorCode.ERR_BadPatternExpression, node.Expression.Location, expression.Display);
hasErrors = true;
}
expression = BadExpression(expression.Syntax, expression);
}
BoundPattern pattern = BindPattern(node.Pattern, expression.Type, hasErrors, diagnostics);
hasErrors |= pattern.HasErrors;
return MakeIsPatternExpression(
node, expression, pattern, GetSpecialType(SpecialType.System_Boolean, diagnostics, node),
hasErrors, diagnostics);
}
private BoundExpression MakeIsPatternExpression(SyntaxNode node, BoundExpression expression, BoundPattern pattern, TypeSymbol boolType, bool hasErrors, DiagnosticBag diagnostics)
{
// Note that these labels are for the convenience of the compilation of patterns, and are not actually emitted into the lowered code.
LabelSymbol whenTrueLabel = new GeneratedLabelSymbol("isPatternSuccess");
LabelSymbol whenFalseLabel = new GeneratedLabelSymbol("isPatternFailure");
BoundDecisionDag decisionDag = DecisionDagBuilder.CreateDecisionDagForIsPattern(
this.Compilation, pattern.Syntax, expression, pattern, whenTrueLabel: whenTrueLabel, whenFalseLabel: whenFalseLabel, diagnostics);
if (!hasErrors && !decisionDag.ReachableLabels.Contains(whenTrueLabel))
{
diagnostics.Add(ErrorCode.ERR_IsPatternImpossible, node.Location, expression.Type);
hasErrors = true;
}
BoundPattern pattern = BindPattern(node.Pattern, expressionType, hasErrors, diagnostics);
if (!hasErrors && pattern is BoundDeclarationPattern p && !p.IsVar && expression.ConstantValue == ConstantValue.Null)
if (expression.ConstantValue != null)
{
diagnostics.Add(ErrorCode.WRN_IsAlwaysFalse, node.Location, p.DeclaredType.Type);
decisionDag = decisionDag.SimplifyDecisionDagForConstantInput(expression, this.Conversions, diagnostics);
if (!hasErrors)
{
if (!decisionDag.ReachableLabels.Contains(whenTrueLabel))
{
diagnostics.Add(ErrorCode.WRN_GivenExpressionNeverMatchesPattern, node.Location);
}
else if (!decisionDag.ReachableLabels.Contains(whenFalseLabel) && pattern.Kind == BoundKind.ConstantPattern)
{
diagnostics.Add(ErrorCode.WRN_GivenExpressionAlwaysMatchesConstant, node.Location);
}
}
}
return new BoundIsPatternExpression(
node, expression, pattern, GetSpecialType(SpecialType.System_Boolean, diagnostics, node), hasErrors);
return new BoundIsPatternExpression(node, expression, pattern, decisionDag, whenTrueLabel: whenTrueLabel, whenFalseLabel: whenFalseLabel, boolType, hasErrors);
}
private BoundExpression BindSwitchExpression(SwitchExpressionSyntax node, DiagnosticBag diagnostics)
......
......@@ -107,23 +107,22 @@ private DecisionDagBuilder(CSharpCompilation compilation, LabelSymbol defaultLab
SyntaxNode syntax,
BoundExpression inputExpression,
BoundPattern pattern,
LabelSymbol defaultLabel,
DiagnosticBag diagnostics,
out LabelSymbol successLabel)
LabelSymbol whenTrueLabel,
LabelSymbol whenFalseLabel,
DiagnosticBag diagnostics)
{
var builder = new DecisionDagBuilder(compilation, defaultLabel, diagnostics);
return builder.CreateDecisionDagForIsPattern(syntax, inputExpression, pattern, out successLabel);
var builder = new DecisionDagBuilder(compilation, defaultLabel: whenFalseLabel, diagnostics);
return builder.CreateDecisionDagForIsPattern(syntax, inputExpression, pattern, whenTrueLabel);
}
private BoundDecisionDag CreateDecisionDagForIsPattern(
SyntaxNode syntax,
BoundExpression inputExpression,
BoundPattern pattern,
out LabelSymbol successLabel)
LabelSymbol whenTrueLabel)
{
successLabel = new GeneratedLabelSymbol("success");
var rootIdentifier = new BoundDagTemp(inputExpression.Syntax, inputExpression.Type, source: null, index: 0);
return MakeDecisionDag(syntax, ImmutableArray.Create(MakeTestsForPattern(index: 1, pattern.Syntax, rootIdentifier, pattern, whenClause: null, successLabel)));
return MakeDecisionDag(syntax, ImmutableArray.Create(MakeTestsForPattern(index: 1, pattern.Syntax, rootIdentifier, pattern, whenClause: null, whenTrueLabel)));
}
private BoundDecisionDag CreateDecisionDagForSwitchStatement(
......@@ -401,10 +400,14 @@ private static void Assert(bool condition, string message = null)
ArrayBuilder<(BoundExpression, BoundDagTemp)> bindings)
{
Debug.Assert(input.Type.IsErrorType() || input.Type == recursive.InputType);
if (recursive.DeclaredType != null && recursive.DeclaredType.Type != input.Type)
if (recursive.DeclaredType != null)
{
input = MakeConvertToType(input, recursive.Syntax, recursive.DeclaredType.Type, tests);
}
else
{
MakeCheckNotNull(input, recursive.Syntax, tests);
}
if (!recursive.Deconstruction.IsDefault)
{
......
......@@ -826,6 +826,7 @@
<Field Name="InnerLocals" Type="ImmutableArray&lt;LocalSymbol&gt;"/>
<Field Name="InnerLocalFunctions" Type="ImmutableArray&lt;LocalFunctionSymbol&gt;"/>
<Field Name="SwitchSections" Type="ImmutableArray&lt;BoundPatternSwitchSection&gt;"/>
<Field Name="DecisionDag" Type="BoundDecisionDag" Null="disallow" SkipInVisitor="true"/>
<Field Name="DefaultLabel" Type="BoundPatternSwitchLabel" Null="allow"/>
<Field Name="BreakLabel" Type="GeneratedLabelSymbol"/>
</Node>
......@@ -862,6 +863,7 @@
<Node Name="BoundSwitchExpression" Base="BoundExpression">
<Field Name="Expression" Type="BoundExpression" Null="disallow"/>
<Field Name="SwitchArms" Type="ImmutableArray&lt;BoundSwitchExpressionArm&gt;"/>
<Field Name="DecisionDag" Type="BoundDecisionDag" Null="disallow" SkipInVisitor="true"/>
<Field Name="DefaultLabel" Type="LabelSymbol" Null="allow"/>
</Node>
<Node Name="BoundSwitchExpressionArm" Base="BoundNode">
......@@ -1720,6 +1722,9 @@
<Node Name="BoundIsPatternExpression" Base="BoundExpression">
<Field Name="Expression" Type="BoundExpression" Null="disallow"/>
<Field Name="Pattern" Type="BoundPattern" Null="disallow"/>
<Field Name="DecisionDag" Type="BoundDecisionDag" Null="disallow" SkipInVisitor="true"/>
<Field Name="WhenTrueLabel" Type="LabelSymbol" Null="disallow"/>
<Field Name="WhenFalseLabel" Type="LabelSymbol" Null="disallow"/>
</Node>
<AbstractNode Name="BoundPattern" Base="BoundNode">
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class BoundPatternSwitchStatement
{
public BoundDecisionDag DecisionDag { get; }
public BoundPatternSwitchStatement(SyntaxNode syntax, BoundExpression expression, ImmutableArray<LocalSymbol> innerLocals, ImmutableArray<LocalFunctionSymbol> innerLocalFunctions, ImmutableArray<BoundPatternSwitchSection> switchSections, BoundPatternSwitchLabel defaultLabel, GeneratedLabelSymbol breakLabel, BoundDecisionDag decisionDag, bool hasErrors = false)
: this(syntax, expression, innerLocals, innerLocalFunctions, switchSections, defaultLabel, breakLabel, hasErrors)
{
this.DecisionDag = decisionDag;
}
public BoundPatternSwitchStatement Update(BoundExpression expression, ImmutableArray<LocalSymbol> innerLocals, ImmutableArray<LocalFunctionSymbol> innerLocalFunctions, ImmutableArray<BoundPatternSwitchSection> switchSections, BoundPatternSwitchLabel defaultLabel, GeneratedLabelSymbol breakLabel, BoundDecisionDag decisionDag)
{
if (expression != this.Expression || innerLocals != this.InnerLocals || innerLocalFunctions != this.InnerLocalFunctions || switchSections != this.SwitchSections || defaultLabel != this.DefaultLabel || breakLabel != this.BreakLabel || decisionDag != this.DecisionDag)
{
var result = new BoundPatternSwitchStatement(this.Syntax, expression, innerLocals, innerLocalFunctions, switchSections, defaultLabel, breakLabel, decisionDag, this.HasErrors);
result.WasCompilerGenerated = this.WasCompilerGenerated;
return result;
}
return this;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class BoundSwitchExpression
{
public BoundDecisionDag DecisionDag { get; }
public BoundSwitchExpression(SyntaxNode syntax, BoundExpression expression, ImmutableArray<BoundSwitchExpressionArm> switchArms, BoundDecisionDag decisionDag, LabelSymbol defaultLabel, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.SwitchExpression, syntax, type, hasErrors || expression.HasErrors() || switchArms.HasErrors() || decisionDag.HasErrors())
{
Debug.Assert(expression != null, "Field 'expression' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(!switchArms.IsDefault, "Field 'switchArms' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
this.Expression = expression;
this.SwitchArms = switchArms;
this.DecisionDag = decisionDag;
this.DefaultLabel = defaultLabel;
}
public BoundSwitchExpression Update(BoundExpression expression, ImmutableArray<BoundSwitchExpressionArm> switchArms, BoundDecisionDag decisionDag, LabelSymbol defaultLabel, TypeSymbol type)
{
if (expression != this.Expression || switchArms != this.SwitchArms || decisionDag != this.DecisionDag || defaultLabel != this.DefaultLabel || type != this.Type)
{
var result = new BoundSwitchExpression(this.Syntax, expression, switchArms, decisionDag, defaultLabel, type, this.HasErrors);
result.WasCompilerGenerated = this.WasCompilerGenerated;
return result;
}
return this;
}
}
}
......@@ -6100,6 +6100,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to An expression of type &apos;{0}&apos; can never match the provided pattern..
/// </summary>
internal static string ERR_IsPatternImpossible {
get {
return ResourceManager.GetString("ERR_IsPatternImpossible", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Yield statements may not appear at the top level in interactive code..
/// </summary>
......@@ -13546,6 +13555,42 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to The given expression always matches the provided constant..
/// </summary>
internal static string WRN_GivenExpressionAlwaysMatchesConstant {
get {
return ResourceManager.GetString("WRN_GivenExpressionAlwaysMatchesConstant", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The given expression always matches the provided constant..
/// </summary>
internal static string WRN_GivenExpressionAlwaysMatchesConstant_Title {
get {
return ResourceManager.GetString("WRN_GivenExpressionAlwaysMatchesConstant_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The given expression never matches the provided pattern..
/// </summary>
internal static string WRN_GivenExpressionNeverMatchesPattern {
get {
return ResourceManager.GetString("WRN_GivenExpressionNeverMatchesPattern", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The given expression never matches the provided pattern..
/// </summary>
internal static string WRN_GivenExpressionNeverMatchesPattern_Title {
get {
return ResourceManager.GetString("WRN_GivenExpressionNeverMatchesPattern_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Defining an alias named &apos;global&apos; is ill-advised since &apos;global::&apos; always references the global namespace and not an alias.
/// </summary>
......
......@@ -5372,4 +5372,19 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="IDS_FeatureIndexingMovableFixedBuffers" xml:space="preserve">
<value>indexing movable fixed buffers</value>
</data>
<data name="ERR_IsPatternImpossible" xml:space="preserve">
<value>An expression of type '{0}' can never match the provided pattern.</value>
</data>
<data name="WRN_GivenExpressionNeverMatchesPattern" xml:space="preserve">
<value>The given expression never matches the provided pattern.</value>
</data>
<data name="WRN_GivenExpressionNeverMatchesPattern_Title" xml:space="preserve">
<value>The given expression never matches the provided pattern.</value>
</data>
<data name="WRN_GivenExpressionAlwaysMatchesConstant" xml:space="preserve">
<value>The given expression always matches the provided constant.</value>
</data>
<data name="WRN_GivenExpressionAlwaysMatchesConstant_Title" xml:space="preserve">
<value>The given expression always matches the provided constant.</value>
</data>
</root>
......@@ -1596,6 +1596,9 @@ internal enum ErrorCode
ERR_ConstantPatternNamedUnderscore = 8412,
WRN_IsTypeNamedUnderscore = 8413,
ERR_ExpressionTreeContainsSwitchExpression = 8414,
ERR_IsPatternImpossible = 8415,
WRN_GivenExpressionNeverMatchesPattern = 8416,
WRN_GivenExpressionAlwaysMatchesConstant = 8417,
#endregion diagnostics introduced for recursive patterns
}
......
......@@ -325,6 +325,8 @@ internal static int GetWarningLevel(ErrorCode code)
case ErrorCode.WRN_TupleBinopLiteralNameMismatch:
case ErrorCode.WRN_SwitchExpressionNotExhaustive:
case ErrorCode.WRN_IsTypeNamedUnderscore:
case ErrorCode.WRN_GivenExpressionNeverMatchesPattern:
case ErrorCode.WRN_GivenExpressionAlwaysMatchesConstant:
return 1;
default:
return 0;
......
......@@ -1512,9 +1512,9 @@ protected override LocalState UnreachableState()
#region Visitors
public override void VisitPattern(BoundExpression expression, BoundPattern pattern)
public override void VisitPattern(BoundPattern pattern)
{
base.VisitPattern(expression, pattern);
base.VisitPattern(pattern);
var whenFail = StateWhenFalse;
SetState(StateWhenTrue);
AssignPatternVariables(pattern);
......@@ -1560,7 +1560,7 @@ private void AssignPatternVariables(BoundPattern pattern)
break;
}
default:
break;
throw ExceptionUtilities.UnexpectedValue(pattern.Kind);
}
}
......
......@@ -923,30 +923,25 @@ public override BoundNode VisitPassByCopy(BoundPassByCopy node)
public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node)
{
VisitRvalue(node.Expression);
VisitPattern(node.Expression, node.Pattern);
return null;
}
public virtual void VisitPattern(BoundExpression expression, BoundPattern pattern)
{
Split();
if (expression != null)
VisitPattern(node.Pattern);
var reachableLabels = node.DecisionDag.ReachableLabels;
if (!reachableLabels.Contains(node.WhenTrueLabel))
{
bool? knownMatch = CheckRefutations(expression, pattern);
switch (knownMatch)
SetState(this.StateWhenFalse);
SetConditionalState(UnreachableState(), this.State);
}
else if (!reachableLabels.Contains(node.WhenFalseLabel))
{
case true:
SetState(StateWhenTrue);
SetState(this.StateWhenTrue);
SetConditionalState(this.State, UnreachableState());
break;
case false:
SetState(StateWhenFalse);
SetConditionalState(UnreachableState(), this.State);
break;
case null:
break;
}
return null;
}
public virtual void VisitPattern(BoundPattern pattern)
{
Split();
}
public override BoundNode VisitConstantPattern(BoundConstantPattern node)
......@@ -955,42 +950,6 @@ public override BoundNode VisitConstantPattern(BoundConstantPattern node)
throw ExceptionUtilities.Unreachable;
}
/// <summary>
/// Check if the given expression is known to *always* match, or *always* fail against the given pattern.
/// Return true for known match, false for known fail, and null otherwise. Used for the "is pattern" expression.
/// </summary>
private bool? CheckRefutations(BoundExpression expression, BoundPattern pattern)
{
Debug.Assert(expression != null);
switch (pattern.Kind)
{
case BoundKind.DeclarationPattern:
{
var declPattern = (BoundDeclarationPattern)pattern;
if (declPattern.IsVar || // var pattern always matches
declPattern.DeclaredType?.Type?.IsValueType == true && declPattern.DeclaredType.Type == (object)expression.Type) // exact match
{
return true;
}
Debug.Assert(!declPattern.IsVar);
switch (expression.ConstantValue?.IsNull)
{
case true: return false;
case false: return true;
default: return null;
}
}
case BoundKind.ConstantPattern:
{
var constPattern = (BoundConstantPattern)pattern;
if (expression.ConstantValue == null || constPattern.ConstantValue == null) return null;
return Equals(expression.ConstantValue.Value, constPattern.ConstantValue.Value);
}
}
return null;
}
public override BoundNode VisitTupleLiteral(BoundTupleLiteral node)
{
return VisitTupleExpression(node);
......@@ -2767,16 +2726,17 @@ public override BoundNode VisitSwitchExpression(BoundSwitchExpression node)
VisitRvalue(node.Expression);
var dispatchState = this.State;
var endState = UnreachableState();
var reachableLabels = node.DecisionDag.ReachableLabels;
foreach (var arm in node.SwitchArms)
{
SetState(dispatchState.Clone());
VisitPattern(node.Expression, arm.Pattern);
VisitPattern(arm.Pattern);
SetState(StateWhenTrue);
if (arm.Pattern.HasErrors)
if (!reachableLabels.Contains(arm.Label) || arm.Pattern.HasErrors)
{
// suppress definite assignment errors on broken switch arms
SetUnreachable();
}
if (arm.WhenClause != null)
{
VisitCondition(arm.WhenClause);
......
......@@ -44,7 +44,7 @@ private void VisitPatternSwitchBlock(BoundPatternSwitchStatement node)
SetUnreachable();
}
VisitPattern(null, label.Pattern);
VisitPattern(label.Pattern);
SetState(StateWhenTrue);
if (label.WhenClause != null)
{
......
......@@ -43,9 +43,9 @@ protected override void Free()
_variablesDeclared = null;
}
public override void VisitPattern(BoundExpression expression, BoundPattern pattern)
public override void VisitPattern(BoundPattern pattern)
{
base.VisitPattern(expression, pattern);
base.VisitPattern(pattern);
NoteDeclaredPatternVariables(pattern);
}
......
......@@ -2843,20 +2843,22 @@ public BoundContinueStatement Update(GeneratedLabelSymbol label)
internal sealed partial class BoundPatternSwitchStatement : BoundStatement
{
public BoundPatternSwitchStatement(SyntaxNode syntax, BoundExpression expression, ImmutableArray<LocalSymbol> innerLocals, ImmutableArray<LocalFunctionSymbol> innerLocalFunctions, ImmutableArray<BoundPatternSwitchSection> switchSections, BoundPatternSwitchLabel defaultLabel, GeneratedLabelSymbol breakLabel, bool hasErrors = false)
: base(BoundKind.PatternSwitchStatement, syntax, hasErrors || expression.HasErrors() || switchSections.HasErrors() || defaultLabel.HasErrors())
public BoundPatternSwitchStatement(SyntaxNode syntax, BoundExpression expression, ImmutableArray<LocalSymbol> innerLocals, ImmutableArray<LocalFunctionSymbol> innerLocalFunctions, ImmutableArray<BoundPatternSwitchSection> switchSections, BoundDecisionDag decisionDag, BoundPatternSwitchLabel defaultLabel, GeneratedLabelSymbol breakLabel, bool hasErrors = false)
: base(BoundKind.PatternSwitchStatement, syntax, hasErrors || expression.HasErrors() || switchSections.HasErrors() || decisionDag.HasErrors() || defaultLabel.HasErrors())
{
Debug.Assert(expression != null, "Field 'expression' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(!innerLocals.IsDefault, "Field 'innerLocals' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(!innerLocalFunctions.IsDefault, "Field 'innerLocalFunctions' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(!switchSections.IsDefault, "Field 'switchSections' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(decisionDag != null, "Field 'decisionDag' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(breakLabel != null, "Field 'breakLabel' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
this.Expression = expression;
this.InnerLocals = innerLocals;
this.InnerLocalFunctions = innerLocalFunctions;
this.SwitchSections = switchSections;
this.DecisionDag = decisionDag;
this.DefaultLabel = defaultLabel;
this.BreakLabel = breakLabel;
}
......@@ -2870,6 +2872,8 @@ public BoundPatternSwitchStatement(SyntaxNode syntax, BoundExpression expression
public ImmutableArray<BoundPatternSwitchSection> SwitchSections { get; }
public BoundDecisionDag DecisionDag { get; }
public BoundPatternSwitchLabel DefaultLabel { get; }
public GeneratedLabelSymbol BreakLabel { get; }
......@@ -2879,11 +2883,11 @@ public override BoundNode Accept(BoundTreeVisitor visitor)
return visitor.VisitPatternSwitchStatement(this);
}
public BoundPatternSwitchStatement Update(BoundExpression expression, ImmutableArray<LocalSymbol> innerLocals, ImmutableArray<LocalFunctionSymbol> innerLocalFunctions, ImmutableArray<BoundPatternSwitchSection> switchSections, BoundPatternSwitchLabel defaultLabel, GeneratedLabelSymbol breakLabel)
public BoundPatternSwitchStatement Update(BoundExpression expression, ImmutableArray<LocalSymbol> innerLocals, ImmutableArray<LocalFunctionSymbol> innerLocalFunctions, ImmutableArray<BoundPatternSwitchSection> switchSections, BoundDecisionDag decisionDag, BoundPatternSwitchLabel defaultLabel, GeneratedLabelSymbol breakLabel)
{
if (expression != this.Expression || innerLocals != this.InnerLocals || innerLocalFunctions != this.InnerLocalFunctions || switchSections != this.SwitchSections || defaultLabel != this.DefaultLabel || breakLabel != this.BreakLabel)
if (expression != this.Expression || innerLocals != this.InnerLocals || innerLocalFunctions != this.InnerLocalFunctions || switchSections != this.SwitchSections || decisionDag != this.DecisionDag || defaultLabel != this.DefaultLabel || breakLabel != this.BreakLabel)
{
var result = new BoundPatternSwitchStatement(this.Syntax, expression, innerLocals, innerLocalFunctions, switchSections, defaultLabel, breakLabel, this.HasErrors);
var result = new BoundPatternSwitchStatement(this.Syntax, expression, innerLocals, innerLocalFunctions, switchSections, decisionDag, defaultLabel, breakLabel, this.HasErrors);
result.WasCompilerGenerated = this.WasCompilerGenerated;
return result;
}
......@@ -2935,15 +2939,17 @@ public BoundSwitchDispatch Update(BoundExpression expression, ImmutableArray<(Co
internal sealed partial class BoundSwitchExpression : BoundExpression
{
public BoundSwitchExpression(SyntaxNode syntax, BoundExpression expression, ImmutableArray<BoundSwitchExpressionArm> switchArms, LabelSymbol defaultLabel, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.SwitchExpression, syntax, type, hasErrors || expression.HasErrors() || switchArms.HasErrors())
public BoundSwitchExpression(SyntaxNode syntax, BoundExpression expression, ImmutableArray<BoundSwitchExpressionArm> switchArms, BoundDecisionDag decisionDag, LabelSymbol defaultLabel, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.SwitchExpression, syntax, type, hasErrors || expression.HasErrors() || switchArms.HasErrors() || decisionDag.HasErrors())
{
Debug.Assert(expression != null, "Field 'expression' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(!switchArms.IsDefault, "Field 'switchArms' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(decisionDag != null, "Field 'decisionDag' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
this.Expression = expression;
this.SwitchArms = switchArms;
this.DecisionDag = decisionDag;
this.DefaultLabel = defaultLabel;
}
......@@ -2952,6 +2958,8 @@ public BoundSwitchExpression(SyntaxNode syntax, BoundExpression expression, Immu
public ImmutableArray<BoundSwitchExpressionArm> SwitchArms { get; }
public BoundDecisionDag DecisionDag { get; }
public LabelSymbol DefaultLabel { get; }
public override BoundNode Accept(BoundTreeVisitor visitor)
......@@ -2959,11 +2967,11 @@ public override BoundNode Accept(BoundTreeVisitor visitor)
return visitor.VisitSwitchExpression(this);
}
public BoundSwitchExpression Update(BoundExpression expression, ImmutableArray<BoundSwitchExpressionArm> switchArms, LabelSymbol defaultLabel, TypeSymbol type)
public BoundSwitchExpression Update(BoundExpression expression, ImmutableArray<BoundSwitchExpressionArm> switchArms, BoundDecisionDag decisionDag, LabelSymbol defaultLabel, TypeSymbol type)
{
if (expression != this.Expression || switchArms != this.SwitchArms || defaultLabel != this.DefaultLabel || type != this.Type)
if (expression != this.Expression || switchArms != this.SwitchArms || decisionDag != this.DecisionDag || defaultLabel != this.DefaultLabel || type != this.Type)
{
var result = new BoundSwitchExpression(this.Syntax, expression, switchArms, defaultLabel, type, this.HasErrors);
var result = new BoundSwitchExpression(this.Syntax, expression, switchArms, decisionDag, defaultLabel, type, this.HasErrors);
result.WasCompilerGenerated = this.WasCompilerGenerated;
return result;
}
......@@ -6524,15 +6532,21 @@ public BoundStringInsert Update(BoundExpression value, BoundExpression alignment
internal sealed partial class BoundIsPatternExpression : BoundExpression
{
public BoundIsPatternExpression(SyntaxNode syntax, BoundExpression expression, BoundPattern pattern, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.IsPatternExpression, syntax, type, hasErrors || expression.HasErrors() || pattern.HasErrors())
public BoundIsPatternExpression(SyntaxNode syntax, BoundExpression expression, BoundPattern pattern, BoundDecisionDag decisionDag, LabelSymbol whenTrueLabel, LabelSymbol whenFalseLabel, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.IsPatternExpression, syntax, type, hasErrors || expression.HasErrors() || pattern.HasErrors() || decisionDag.HasErrors())
{
Debug.Assert(expression != null, "Field 'expression' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(pattern != null, "Field 'pattern' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(decisionDag != null, "Field 'decisionDag' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(whenTrueLabel != null, "Field 'whenTrueLabel' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(whenFalseLabel != null, "Field 'whenFalseLabel' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
this.Expression = expression;
this.Pattern = pattern;
this.DecisionDag = decisionDag;
this.WhenTrueLabel = whenTrueLabel;
this.WhenFalseLabel = whenFalseLabel;
}
......@@ -6540,16 +6554,22 @@ public BoundIsPatternExpression(SyntaxNode syntax, BoundExpression expression, B
public BoundPattern Pattern { get; }
public BoundDecisionDag DecisionDag { get; }
public LabelSymbol WhenTrueLabel { get; }
public LabelSymbol WhenFalseLabel { get; }
public override BoundNode Accept(BoundTreeVisitor visitor)
{
return visitor.VisitIsPatternExpression(this);
}
public BoundIsPatternExpression Update(BoundExpression expression, BoundPattern pattern, TypeSymbol type)
public BoundIsPatternExpression Update(BoundExpression expression, BoundPattern pattern, BoundDecisionDag decisionDag, LabelSymbol whenTrueLabel, LabelSymbol whenFalseLabel, TypeSymbol type)
{
if (expression != this.Expression || pattern != this.Pattern || type != this.Type)
if (expression != this.Expression || pattern != this.Pattern || decisionDag != this.DecisionDag || whenTrueLabel != this.WhenTrueLabel || whenFalseLabel != this.WhenFalseLabel || type != this.Type)
{
var result = new BoundIsPatternExpression(this.Syntax, expression, pattern, type, this.HasErrors);
var result = new BoundIsPatternExpression(this.Syntax, expression, pattern, decisionDag, whenTrueLabel, whenFalseLabel, type, this.HasErrors);
result.WasCompilerGenerated = this.WasCompilerGenerated;
return result;
}
......@@ -9979,8 +9999,9 @@ public override BoundNode VisitPatternSwitchStatement(BoundPatternSwitchStatemen
{
BoundExpression expression = (BoundExpression)this.Visit(node.Expression);
ImmutableArray<BoundPatternSwitchSection> switchSections = (ImmutableArray<BoundPatternSwitchSection>)this.VisitList(node.SwitchSections);
BoundDecisionDag decisionDag = node.DecisionDag;
BoundPatternSwitchLabel defaultLabel = (BoundPatternSwitchLabel)this.Visit(node.DefaultLabel);
return node.Update(expression, node.InnerLocals, node.InnerLocalFunctions, switchSections, defaultLabel, node.BreakLabel);
return node.Update(expression, node.InnerLocals, node.InnerLocalFunctions, switchSections, decisionDag, defaultLabel, node.BreakLabel);
}
public override BoundNode VisitSwitchDispatch(BoundSwitchDispatch node)
{
......@@ -9991,8 +10012,9 @@ public override BoundNode VisitSwitchExpression(BoundSwitchExpression node)
{
BoundExpression expression = (BoundExpression)this.Visit(node.Expression);
ImmutableArray<BoundSwitchExpressionArm> switchArms = (ImmutableArray<BoundSwitchExpressionArm>)this.VisitList(node.SwitchArms);
BoundDecisionDag decisionDag = node.DecisionDag;
TypeSymbol type = this.VisitType(node.Type);
return node.Update(expression, switchArms, node.DefaultLabel, type);
return node.Update(expression, switchArms, decisionDag, node.DefaultLabel, type);
}
public override BoundNode VisitSwitchExpressionArm(BoundSwitchExpressionArm node)
{
......@@ -10524,8 +10546,9 @@ public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node
{
BoundExpression expression = (BoundExpression)this.Visit(node.Expression);
BoundPattern pattern = (BoundPattern)this.Visit(node.Pattern);
BoundDecisionDag decisionDag = node.DecisionDag;
TypeSymbol type = this.VisitType(node.Type);
return node.Update(expression, pattern, type);
return node.Update(expression, pattern, decisionDag, node.WhenTrueLabel, node.WhenFalseLabel, type);
}
public override BoundNode VisitConstantPattern(BoundConstantPattern node)
{
......@@ -11287,6 +11310,7 @@ public override TreeDumperNode VisitPatternSwitchStatement(BoundPatternSwitchSta
new TreeDumperNode("innerLocals", node.InnerLocals, null),
new TreeDumperNode("innerLocalFunctions", node.InnerLocalFunctions, null),
new TreeDumperNode("switchSections", null, from x in node.SwitchSections select Visit(x, null)),
new TreeDumperNode("decisionDag", null, new TreeDumperNode[] { Visit(node.DecisionDag, null) }),
new TreeDumperNode("defaultLabel", null, new TreeDumperNode[] { Visit(node.DefaultLabel, null) }),
new TreeDumperNode("breakLabel", node.BreakLabel, null)
}
......@@ -11309,6 +11333,7 @@ public override TreeDumperNode VisitSwitchExpression(BoundSwitchExpression node,
{
new TreeDumperNode("expression", null, new TreeDumperNode[] { Visit(node.Expression, null) }),
new TreeDumperNode("switchArms", null, from x in node.SwitchArms select Visit(x, null)),
new TreeDumperNode("decisionDag", null, new TreeDumperNode[] { Visit(node.DecisionDag, null) }),
new TreeDumperNode("defaultLabel", node.DefaultLabel, null),
new TreeDumperNode("type", node.Type, null)
}
......@@ -12250,6 +12275,9 @@ public override TreeDumperNode VisitIsPatternExpression(BoundIsPatternExpression
{
new TreeDumperNode("expression", null, new TreeDumperNode[] { Visit(node.Expression, null) }),
new TreeDumperNode("pattern", null, new TreeDumperNode[] { Visit(node.Pattern, null) }),
new TreeDumperNode("decisionDag", null, new TreeDumperNode[] { Visit(node.DecisionDag, null) }),
new TreeDumperNode("whenTrueLabel", node.WhenTrueLabel, null),
new TreeDumperNode("whenFalseLabel", node.WhenFalseLabel, null),
new TreeDumperNode("type", node.Type, null)
}
);
......
......@@ -181,6 +181,8 @@ public static bool IsWarning(ErrorCode code)
case ErrorCode.WRN_TupleBinopLiteralNameMismatch:
case ErrorCode.WRN_SwitchExpressionNotExhaustive:
case ErrorCode.WRN_IsTypeNamedUnderscore:
case ErrorCode.WRN_GivenExpressionNeverMatchesPattern:
case ErrorCode.WRN_GivenExpressionAlwaysMatchesConstant:
return true;
default:
return false;
......
......@@ -370,14 +370,12 @@ private void LowerOneTest(BoundDagTest test)
}
}
public BoundExpression LowerIsPattern(BoundPattern pattern, CSharpCompilation compilation, DiagnosticBag diagnostics)
public BoundExpression LowerIsPattern(
BoundIsPatternExpression isPatternExpression, BoundPattern pattern, CSharpCompilation compilation, DiagnosticBag diagnostics)
{
LabelSymbol failureLabel = new GeneratedLabelSymbol("failure");
BoundDecisionDag dag = DecisionDagBuilder.CreateDecisionDagForIsPattern(compilation, pattern.Syntax, this._loweredInput, pattern, failureLabel, diagnostics, out LabelSymbol successLabel);
if (_loweredInput.ConstantValue != null)
{
dag = dag.SimplifyDecisionDagForConstantInput(_loweredInput, _localRewriter._compilation.Conversions, diagnostics);
}
BoundDecisionDag dag = isPatternExpression.DecisionDag;
LabelSymbol whenTrueLabel = isPatternExpression.WhenTrueLabel;
LabelSymbol whenFalseLabel = isPatternExpression.WhenFalseLabel;
// The optimization of sharing pattern-matching temps with user variables can always apply to
// an is-pattern expression because there is no when clause.
......@@ -404,7 +402,7 @@ public BoundExpression LowerIsPattern(BoundPattern pattern, CSharpCompilation co
break;
case BoundTestDecisionDagNode testNode:
{
Debug.Assert(testNode.WhenFalse is BoundLeafDecisionDagNode x && x.Label == failureLabel);
Debug.Assert(testNode.WhenFalse is BoundLeafDecisionDagNode x && x.Label == whenFalseLabel);
if (testNode.WhenTrue is BoundEvaluationDecisionDagNode e &&
TryLowerTypeTestAndCast(testNode.Test, e.Evaluation, out BoundExpression sideEffect, out BoundExpression testExpression))
{
......@@ -427,14 +425,14 @@ public BoundExpression LowerIsPattern(BoundPattern pattern, CSharpCompilation co
{
case BoundLeafDecisionDagNode leafNode:
{
if (leafNode.Label == failureLabel)
if (leafNode.Label == whenFalseLabel)
{
// It is not clear that this can occur given the dag "optimizations" we performed earlier.
AddConjunct(_factory.Literal(false));
}
else
{
Debug.Assert(leafNode.Label == successLabel);
Debug.Assert(leafNode.Label == whenTrueLabel);
}
}
......@@ -443,7 +441,7 @@ public BoundExpression LowerIsPattern(BoundPattern pattern, CSharpCompilation co
case BoundWhenDecisionDagNode whenNode:
{
Debug.Assert(whenNode.WhenExpression == null);
Debug.Assert(whenNode.WhenTrue is BoundLeafDecisionDagNode d && d.Label == successLabel);
Debug.Assert(whenNode.WhenTrue is BoundLeafDecisionDagNode d && d.Label == whenTrueLabel);
foreach ((BoundExpression left, BoundDagTemp dagTemp) in whenNode.Bindings)
{
BoundExpression right = _tempAllocator.GetTemp(dagTemp);
......@@ -552,7 +550,7 @@ public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node
BoundExpression loweredExpression = VisitExpression(node.Expression);
BoundPattern loweredPattern = LowerPattern(node.Pattern);
var isPatternRewriter = new IsPatternExpressionLocalRewriter(this, loweredExpression);
BoundExpression result = isPatternRewriter.LowerIsPattern(loweredPattern, this._compilation, this._diagnostics);
BoundExpression result = isPatternRewriter.LowerIsPattern(node, loweredPattern, this._compilation, this._diagnostics);
isPatternRewriter.Free();
return result;
}
......
......@@ -8810,6 +8810,31 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference
<target state="new">Do not use '_' to refer to the type in an is-type expression.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IsPatternImpossible">
<source>An expression of type '{0}' can never match the provided pattern.</source>
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern_Title">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant_Title">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8810,6 +8810,31 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett
<target state="new">Do not use '_' to refer to the type in an is-type expression.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IsPatternImpossible">
<source>An expression of type '{0}' can never match the provided pattern.</source>
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern_Title">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant_Title">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8810,6 +8810,31 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe
<target state="new">Do not use '_' to refer to the type in an is-type expression.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IsPatternImpossible">
<source>An expression of type '{0}' can never match the provided pattern.</source>
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern_Title">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant_Title">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8810,6 +8810,31 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé
<target state="new">Do not use '_' to refer to the type in an is-type expression.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IsPatternImpossible">
<source>An expression of type '{0}' can never match the provided pattern.</source>
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern_Title">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant_Title">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8810,6 +8810,31 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr
<target state="new">Do not use '_' to refer to the type in an is-type expression.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IsPatternImpossible">
<source>An expression of type '{0}' can never match the provided pattern.</source>
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern_Title">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant_Title">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8810,6 +8810,31 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">Do not use '_' to refer to the type in an is-type expression.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IsPatternImpossible">
<source>An expression of type '{0}' can never match the provided pattern.</source>
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern_Title">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant_Title">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8810,6 +8810,31 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">Do not use '_' to refer to the type in an is-type expression.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IsPatternImpossible">
<source>An expression of type '{0}' can never match the provided pattern.</source>
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern_Title">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant_Title">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8810,6 +8810,31 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w
<target state="new">Do not use '_' to refer to the type in an is-type expression.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IsPatternImpossible">
<source>An expression of type '{0}' can never match the provided pattern.</source>
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern_Title">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant_Title">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8810,6 +8810,31 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl
<target state="new">Do not use '_' to refer to the type in an is-type expression.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IsPatternImpossible">
<source>An expression of type '{0}' can never match the provided pattern.</source>
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern_Title">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant_Title">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8810,6 +8810,31 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">Do not use '_' to refer to the type in an is-type expression.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IsPatternImpossible">
<source>An expression of type '{0}' can never match the provided pattern.</source>
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern_Title">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant_Title">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8810,6 +8810,31 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T
<target state="new">Do not use '_' to refer to the type in an is-type expression.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IsPatternImpossible">
<source>An expression of type '{0}' can never match the provided pattern.</source>
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern_Title">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant_Title">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8810,6 +8810,31 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">Do not use '_' to refer to the type in an is-type expression.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IsPatternImpossible">
<source>An expression of type '{0}' can never match the provided pattern.</source>
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern_Title">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant_Title">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8810,6 +8810,31 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">Do not use '_' to refer to the type in an is-type expression.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IsPatternImpossible">
<source>An expression of type '{0}' can never match the provided pattern.</source>
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionNeverMatchesPattern_Title">
<source>The given expression never matches the provided pattern.</source>
<target state="new">The given expression never matches the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
<trans-unit id="WRN_GivenExpressionAlwaysMatchesConstant_Title">
<source>The given expression always matches the provided constant.</source>
<target state="new">The given expression always matches the provided constant.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -1127,5 +1127,85 @@ .maxstack 2
IL_0015: ret
}");
}
[Fact, WorkItem(17266, "https://github.com/dotnet/roslyn/issues/17266")]
public void IrrefutablePatternInIs01()
{
var source =
@"using System;
public class C
{
public static void Main()
{
if (Get() is int index) { }
Console.WriteLine(index);
}
public static int Get()
{
Console.WriteLine(""eval"");
return 1;
}
}";
var compilation = CreateCompilation(source, options: TestOptions.DebugExe);
compilation.VerifyDiagnostics();
var expectedOutput = @"eval
1";
var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
}
[Fact, WorkItem(17266, "https://github.com/dotnet/roslyn/issues/17266")]
public void IrrefutablePatternInIs02()
{
var source =
@"using System;
public class C
{
public static void Main()
{
if (Get() is Assignment(int left, var right)) { }
Console.WriteLine(left);
Console.WriteLine(right);
}
public static Assignment Get()
{
Console.WriteLine(""eval"");
return new Assignment(1, 2);
}
}
public struct Assignment
{
public int Left, Right;
public Assignment(int left, int right) => (Left, Right) = (left, right);
public void Deconstruct(out int left, out int right) => (left, right) = (Left, Right);
}
";
var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
compilation.VerifyDiagnostics();
var expectedOutput = @"eval
1
2";
var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
}
[Fact]
public void MissingNullCheck01()
{
var source =
@"class Program
{
public static void Main()
{
string s = null;
System.Console.WriteLine(s is string { Length: 3 });
}
}
";
var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
compilation.VerifyDiagnostics();
var expectedOutput = @"False";
var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
}
}
}
......@@ -201,7 +201,7 @@ public static void Main()
var s = nameof(Main);
byte b = 1;
if (s is string t) { } else Console.WriteLine(t);
if (null is dynamic t) { } // null not allowed
if (null is dynamic t2) { } // null not allowed
if (s is NullableInt x) { } // error: cannot use nullable type
if (s is long l) { } // error: cannot convert string to long
if (b is 1000) { } // error: cannot convert 1000 to byte
......@@ -212,9 +212,6 @@ public static void Main()
// (10,13): error CS8117: Invalid operand for pattern match; value required, but found '<null>'.
// if (null is dynamic t) { } // null not allowed
Diagnostic(ErrorCode.ERR_BadPatternExpression, "null").WithArguments("<null>").WithLocation(10, 13),
// (10,29): error CS0128: A local variable named 't' is already defined in this scope
// if (null is dynamic t) { } // null not allowed
Diagnostic(ErrorCode.ERR_LocalDuplicate, "t").WithArguments("t").WithLocation(10, 29),
// (11,18): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead.
// if (s is NullableInt x) { } // error: cannot use nullable type
Diagnostic(ErrorCode.ERR_PatternNullableType, "NullableInt").WithArguments("int?", "int").WithLocation(11, 18),
......@@ -3118,10 +3115,10 @@ public static void Main()
// (8,27): warning CS0184: The given expression is never of the provided ('int[]') type
// Console.WriteLine(1 is int[]); // warning: expression is never of the provided type
Diagnostic(ErrorCode.WRN_IsAlwaysFalse, "1 is int[]").WithArguments("int[]").WithLocation(8, 27),
// (10,33): error CS8121: An expression of type long cannot be handled by a pattern of type string.
// (10,33): error CS8121: An expression of type 'long' cannot be handled by a pattern of type 'string'.
// Console.WriteLine(1L is string s); // error: type mismatch
Diagnostic(ErrorCode.ERR_PatternWrongType, "string").WithArguments("long", "string").WithLocation(10, 33),
// (11,32): error CS8121: An expression of type int cannot be handled by a pattern of type int[].
// (11,32): error CS8121: An expression of type 'int' cannot be handled by a pattern of type 'int[]'.
// Console.WriteLine(1 is int[] a); // error: expression is never of the provided type
Diagnostic(ErrorCode.ERR_PatternWrongType, "int[]").WithArguments("int", "int[]").WithLocation(11, 32)
);
......@@ -3162,9 +3159,24 @@ public static void Main()
";
var compilation = CreateCompilation(source, options: TestOptions.DebugExe);
compilation.VerifyDiagnostics(
// (7,27): warning CS8417: The given expression always matches the provided constant.
// Console.WriteLine(1 is 1); // true
Diagnostic(ErrorCode.WRN_GivenExpressionAlwaysMatchesConstant, "1 is 1").WithLocation(7, 27),
// (8,27): warning CS8416: The given expression never matches the provided pattern.
// Console.WriteLine(1L is int.MaxValue); // OK, but false
Diagnostic(ErrorCode.WRN_GivenExpressionNeverMatchesPattern, "1L is int.MaxValue").WithLocation(8, 27),
// (9,27): warning CS8416: The given expression never matches the provided pattern.
// Console.WriteLine(1 is int.MaxValue); // false
Diagnostic(ErrorCode.WRN_GivenExpressionNeverMatchesPattern, "1 is int.MaxValue").WithLocation(9, 27),
// (10,27): warning CS8417: The given expression always matches the provided constant.
// Console.WriteLine(int.MaxValue is int.MaxValue); // true
Diagnostic(ErrorCode.WRN_GivenExpressionAlwaysMatchesConstant, "int.MaxValue is int.MaxValue").WithLocation(10, 27),
// (11,27): warning CS0183: The given expression is always of the provided ('string') type
// Console.WriteLine("goo" is System.String); // true
Diagnostic(ErrorCode.WRN_IsAlwaysTrue, @"""goo"" is System.String").WithArguments("string").WithLocation(11, 27)
Diagnostic(ErrorCode.WRN_IsAlwaysTrue, @"""goo"" is System.String").WithArguments("string").WithLocation(11, 27),
// (12,27): warning CS8417: The given expression always matches the provided constant.
// Console.WriteLine(Int32.MaxValue is Int32.MaxValue); // true
Diagnostic(ErrorCode.WRN_GivenExpressionAlwaysMatchesConstant, "Int32.MaxValue is Int32.MaxValue").WithLocation(12, 27)
);
CompileAndVerify(compilation, expectedOutput:
@"True
......@@ -4062,7 +4074,13 @@ class B
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "3 is One + 2").WithArguments("pattern matching", "7.0").WithLocation(15, 27),
// (16,27): error CS8059: Feature 'pattern matching' is not available in C# 6. Please use language version 7.0 or greater.
// Console.WriteLine(One + 2 is 3); // should print True
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "One + 2 is 3").WithArguments("pattern matching", "7.0").WithLocation(16, 27)
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "One + 2 is 3").WithArguments("pattern matching", "7.0").WithLocation(16, 27),
// (15,27): warning CS8417: The given expression always matches the provided constant.
// Console.WriteLine(3 is One + 2); // should print True
Diagnostic(ErrorCode.WRN_GivenExpressionAlwaysMatchesConstant, "3 is One + 2").WithLocation(15, 27),
// (16,27): warning CS8417: The given expression always matches the provided constant.
// Console.WriteLine(One + 2 is 3); // should print True
Diagnostic(ErrorCode.WRN_GivenExpressionAlwaysMatchesConstant, "One + 2 is 3").WithLocation(16, 27)
);
var expectedOutput =
@"5
......@@ -4071,7 +4089,14 @@ class B
True
True";
compilation = CreateCompilation(source, options: TestOptions.DebugExe);
compilation.VerifyDiagnostics();
compilation.VerifyDiagnostics(
// (15,27): warning CS8417: The given expression always matches the provided constant.
// Console.WriteLine(3 is One + 2); // should print True
Diagnostic(ErrorCode.WRN_GivenExpressionAlwaysMatchesConstant, "3 is One + 2").WithLocation(15, 27),
// (16,27): warning CS8417: The given expression always matches the provided constant.
// Console.WriteLine(One + 2 is 3); // should print True
Diagnostic(ErrorCode.WRN_GivenExpressionAlwaysMatchesConstant, "One + 2 is 3").WithLocation(16, 27)
);
var comp = CompileAndVerify(compilation, expectedOutput: expectedOutput);
}
......@@ -5192,7 +5217,7 @@ public static void Main()
compilation.VerifyDiagnostics(
// (7,32): error CS0266: Cannot implicitly convert type 'long' to 'byte'. An explicit conversion exists (are you missing a cast?)
// Console.WriteLine(b is 12L);
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "12L").WithArguments("long", "byte"),
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "12L").WithArguments("long", "byte").WithLocation(7, 32),
// (8,32): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type
// Console.WriteLine(1 is null);
Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(8, 32)
......@@ -5892,9 +5917,9 @@ void M1(int? i)
// (8,13): warning CS0184: The given expression is never of the provided ('string') type
// if (s is string) { }
Diagnostic(ErrorCode.WRN_IsAlwaysFalse, "s is string").WithArguments("string").WithLocation(8, 13),
// (9,13): warning CS0184: The given expression is never of the provided ('string') type
// (9,13): warning CS8416: The given expression never matches the provided pattern.
// if (s is string s2) { }
Diagnostic(ErrorCode.WRN_IsAlwaysFalse, "s is string s2").WithArguments("string").WithLocation(9, 13),
Diagnostic(ErrorCode.WRN_GivenExpressionNeverMatchesPattern, "s is string s2").WithLocation(9, 13),
// (14,13): warning CS0184: The given expression is never of the provided ('long') type
// if (i is long) { }
Diagnostic(ErrorCode.WRN_IsAlwaysFalse, "i is long").WithArguments("long").WithLocation(14, 13),
......@@ -6299,9 +6324,9 @@ static void Main()
// (7,13): warning CS0184: The given expression is never of the provided ('string') type
// if (s is string) {} else { Console.Write("Hello "); }
Diagnostic(ErrorCode.WRN_IsAlwaysFalse, "s is string").WithArguments("string").WithLocation(7, 13),
// (8,13): warning CS0184: The given expression is never of the provided ('string') type
// (8,13): warning CS8416: The given expression never matches the provided pattern.
// if (s is string t) {} else { Console.WriteLine("World"); }
Diagnostic(ErrorCode.WRN_IsAlwaysFalse, "s is string t").WithArguments("string").WithLocation(8, 13)
Diagnostic(ErrorCode.WRN_GivenExpressionNeverMatchesPattern, "s is string t").WithLocation(8, 13)
);
CompileAndVerify(compilation, expectedOutput: expectedOutput);
}
......
......@@ -154,6 +154,47 @@ private static bool Check<T>(T expected, T actual)
if (!object.Equals(expected, actual)) throw new Exception($""expected: {expected}; actual: {actual}"");
return true;
}
}";
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
// (9,22): error CS8415: An expression of type '(int x, int y)' can never match the provided pattern.
// Check(false, p is (1, 4) { x: 3 });
Diagnostic(ErrorCode.ERR_IsPatternImpossible, "p is (1, 4) { x: 3 }").WithArguments("(int x, int y)").WithLocation(9, 22),
// (10,22): error CS8415: An expression of type '(int x, int y)' can never match the provided pattern.
// Check(false, p is (3, 1) { y: 4 });
Diagnostic(ErrorCode.ERR_IsPatternImpossible, "p is (3, 1) { y: 4 }").WithArguments("(int x, int y)").WithLocation(10, 22),
// (11,22): error CS8415: An expression of type '(int x, int y)' can never match the provided pattern.
// Check(false, p is (3, 4) { x: 1 });
Diagnostic(ErrorCode.ERR_IsPatternImpossible, "p is (3, 4) { x: 1 }").WithArguments("(int x, int y)").WithLocation(11, 22),
// (13,22): error CS8415: An expression of type '(int x, int y)' can never match the provided pattern.
// Check(false, p is (1, 4) { x: 3 });
Diagnostic(ErrorCode.ERR_IsPatternImpossible, "p is (1, 4) { x: 3 }").WithArguments("(int x, int y)").WithLocation(13, 22),
// (15,22): error CS8415: An expression of type '(int x, int y)' can never match the provided pattern.
// Check(false, p is (3, 4) { x: 1 });
Diagnostic(ErrorCode.ERR_IsPatternImpossible, "p is (3, 4) { x: 1 }").WithArguments("(int x, int y)").WithLocation(15, 22)
);
}
[Fact]
public void Patterns2_04b()
{
var source =
@"
using System;
class Program
{
public static void Main()
{
var p = (x: 3, y: 4);
Check(true, p is (3, 4) q1 && Check(p, q1));
Check(true, p is (3, 4) { x: 3 } q2 && Check(p, q2));
Check(false, p is (3, 1) { x: 3 });
}
private static bool Check<T>(T expected, T actual)
{
if (!object.Equals(expected, actual)) throw new Exception($""expected: {expected}; actual: {actual}"");
return true;
}
}";
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
......@@ -1220,6 +1261,164 @@ void Test5()
);
}
[Fact]
public void ERR_IsPatternImpossible()
{
var source =
@"class Program
{
public static void Main()
{
System.Console.WriteLine(""frog"" is string { Length: 4, Length: 5 });
}
}
";
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
// (5,34): error CS8415: An expression of type 'string' can never match the provided pattern.
// System.Console.WriteLine("frog" is string { Length: 4, Length: 5 });
Diagnostic(ErrorCode.ERR_IsPatternImpossible, @"""frog"" is string { Length: 4, Length: 5 }").WithArguments("string").WithLocation(5, 34)
);
}
[Fact]
public void WRN_GivenExpressionNeverMatchesPattern01()
{
var source =
@"class Program
{
public static void Main()
{
System.Console.WriteLine(3 is 4);
}
}
";
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
// (5,34): warning CS8416: The given expression never matches the provided pattern.
// System.Console.WriteLine(3 is 4);
Diagnostic(ErrorCode.WRN_GivenExpressionNeverMatchesPattern, "3 is 4").WithLocation(5, 34)
);
}
[Fact]
public void WRN_GivenExpressionNeverMatchesPattern02()
{
var source =
@"class Program
{
public static void Main()
{
const string s = null;
System.Console.WriteLine(s is string { Length: 3 });
}
}
";
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
// (6,34): warning CS8416: The given expression never matches the provided pattern.
// System.Console.WriteLine(s is string { Length: 3 });
Diagnostic(ErrorCode.WRN_GivenExpressionNeverMatchesPattern, "s is string { Length: 3 }").WithLocation(6, 34)
);
}
[Fact]
public void DefiniteAssignmentForIsPattern01()
{
var source =
@"class Program
{
public static void Main()
{
string s = 300.ToString();
System.Console.WriteLine(s is string { Length: int j });
System.Console.WriteLine(j);
}
}
";
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
// (7,34): error CS0165: Use of unassigned local variable 'j'
// System.Console.WriteLine(j);
Diagnostic(ErrorCode.ERR_UseDefViolation, "j").WithArguments("j").WithLocation(7, 34)
);
}
[Fact]
public void DefiniteAssignmentForIsPattern02()
{
var source =
@"class Program
{
public static void Main()
{
const string s = ""300"";
System.Console.WriteLine(s is string { Length: int j });
System.Console.WriteLine(j);
}
}
";
var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
compilation.VerifyDiagnostics();
var expectedOutput = @"True
3";
var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
}
[Fact]
public void DefiniteAssignmentForIsPattern03()
{
var source =
@"class Program
{
public static void Main()
{
int j;
const string s = null;
if (s is string { Length: 3 })
{
System.Console.WriteLine(j);
}
}
}
";
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
// (7,13): warning CS8416: The given expression never matches the provided pattern.
// if (s is string { Length: 3 })
Diagnostic(ErrorCode.WRN_GivenExpressionNeverMatchesPattern, "s is string { Length: 3 }").WithLocation(7, 13)
);
}
[Fact]
public void RefutableConstantPattern01()
{
var source =
@"class Program
{
public static void Main()
{
int j;
const int N = 3;
const int M = 3;
if (N is M)
{
}
else
{
System.Console.WriteLine(j);
}
}
}
";
var compilation = CreatePatternCompilation(source);
compilation.VerifyDiagnostics(
// (8,13): warning CS8417: The given expression always matches the provided constant.
// if (N is M)
Diagnostic(ErrorCode.WRN_GivenExpressionAlwaysMatchesConstant, "N is M").WithLocation(8, 13)
);
}
[Fact, WorkItem(25591, "https://github.com/dotnet/roslyn/issues/25591")]
public void TupleSubsumptionError()
{
......
......@@ -246,6 +246,8 @@ public void WarningLevel_2()
case ErrorCode.WRN_TupleBinopLiteralNameMismatch:
case ErrorCode.WRN_SwitchExpressionNotExhaustive:
case ErrorCode.WRN_IsTypeNamedUnderscore:
case ErrorCode.WRN_GivenExpressionNeverMatchesPattern:
case ErrorCode.WRN_GivenExpressionAlwaysMatchesConstant:
Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode));
break;
case ErrorCode.WRN_MainIgnored:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册