From 430056a6bbd1263f85945db4fb4e7ecd388545e8 Mon Sep 17 00:00:00 2001 From: Neal Gafter Date: Tue, 23 Jun 2020 11:43:02 -0700 Subject: [PATCH] Do not permit default literals in relational patterns (#45375) Fixes #45292 --- .../CSharp/Portable/Binder/Binder_Patterns.cs | 26 ++++++- .../Portable/Binder/SwitchBinder_Patterns.cs | 1 - .../Semantics/PatternMatchingTests2.cs | 78 ++++++++++++++++++- 3 files changed, 102 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index f425958c6f4bf..0d14e5253e038 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -180,7 +180,7 @@ internal BoundPattern BindConstantPatternWithFallbackToTypePattern( bool hasErrors, DiagnosticBag diagnostics) { - ExpressionSyntax innerExpression = expression.SkipParens(); + ExpressionSyntax innerExpression = SkipParensAndNullSuppressions(expression); if (innerExpression.Kind() == SyntaxKind.DefaultLiteralExpression) { diagnostics.Add(ErrorCode.ERR_DefaultPattern, innerExpression.Location); @@ -204,6 +204,24 @@ internal BoundPattern BindConstantPatternWithFallbackToTypePattern( } } + private ExpressionSyntax SkipParensAndNullSuppressions(ExpressionSyntax e) + { + while (true) + { + switch (e) + { + case ParenthesizedExpressionSyntax p: + e = p.Expression; + break; + case PostfixUnaryExpressionSyntax { RawKind: (int)SyntaxKind.SuppressNullableWarningExpression } p: + e = p.Operand; + break; + default: + return e; + } + } + } + /// /// Binds the expression for a pattern. Sets if it was a type rather than an expression, /// and in that case it returns a . @@ -1239,6 +1257,12 @@ private BoundPattern BindRelationalPattern( DiagnosticBag diagnostics) { BoundExpression value = BindExpressionForPattern(inputType, node.Expression, ref hasErrors, diagnostics, out var constantValueOpt, out _); + ExpressionSyntax innerExpression = SkipParensAndNullSuppressions(node.Expression); + if (innerExpression.Kind() == SyntaxKind.DefaultLiteralExpression) + { + diagnostics.Add(ErrorCode.ERR_DefaultPattern, innerExpression.Location); + hasErrors = true; + } RoslynDebug.Assert(value.Type is { }); BinaryOperatorKind operation = tokenKindToBinaryOperatorKind(node.OperatorToken.Kind()); if (operation == BinaryOperatorKind.Equal) diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs index 8a5090ddf7d69..14095889a81a9 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs @@ -237,7 +237,6 @@ private BoundSwitchLabel BindSwitchSectionLabel( case SyntaxKind.CaseSwitchLabel: { var caseLabelSyntax = (CaseSwitchLabelSyntax)node; - SyntaxNode innerExpression = caseLabelSyntax.Value.SkipParens(); bool hasErrors = node.HasErrors; BoundPattern pattern = sectionBinder.BindConstantPatternWithFallbackToTypePattern( caseLabelSyntax.Value, caseLabelSyntax.Value, SwitchGoverningType, hasErrors, diagnostics); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs index 187d4b65adea0..f5baad8ca0e21 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs @@ -347,6 +347,22 @@ public static void Main() switch (i) { case default: break; } // error 3 switch (i) { case default when true: break; } // error 4 switch ((1, 2)) { case (1, default): break; } // error 5 + + if (i is < default) {} // error 6 + switch (i) { case < default: break; } // error 7 + if (i is < ((default))) {} // error 8 + switch (i) { case < ((default)): break; } // error 9 + + if (i is default!) {} // error 10 + if (i is (default!)) {} // error 11 + if (i is < ((default)!)) {} // error 12 + if (i is default!!) {} // error 13 + if (i is (default!!)) {} // error 14 + if (i is < ((default)!!)) {} // error 15 + + // These are not accepted by the parser. See https://github.com/dotnet/roslyn/issues/45387 + if (i is (default)!) {} // error 16 + if (i is ((default)!)) {} // error 17 } }"; var compilation = CreatePatternCompilation(source); @@ -365,7 +381,67 @@ public static void Main() Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(9, 27), // (10,36): error CS8505: 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 '_'. // switch ((1, 2)) { case (1, default): break; } // error 5 - Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(10, 36) + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(10, 36), + // (12,20): error CS8505: 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 6 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(12, 20), + // (13,29): error CS8505: 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 '_'. + // switch (i) { case < default: break; } // error 7 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(13, 29), + // (14,22): error CS8505: 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 8 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(14, 22), + // (15,31): error CS8505: 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 '_'. + // switch (i) { case < ((default)): break; } // error 9 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(15, 31), + // (17,18): error CS8505: 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 10 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(17, 18), + // (18,19): error CS8505: 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 11 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(18, 19), + // (19,22): error CS8505: 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 12 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(19, 22), + // (20,18): error CS8505: 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 13 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(20, 18), + // (21,19): error CS8505: 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 14 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(21, 19), + // (22,22): error CS8715: Duplicate null suppression operator ('!') + // if (i is < ((default)!!)) {} // error 15 + Diagnostic(ErrorCode.ERR_DuplicateNullSuppression, "default").WithLocation(22, 22), + // (22,22): error CS8505: 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 15 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(22, 22), + // (25,19): error CS8505: 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 16 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(25, 19), + // (25,27): error CS1026: ) expected + // if (i is (default)!) {} // error 16 + Diagnostic(ErrorCode.ERR_CloseParenExpected, "!").WithLocation(25, 27), + // (25,28): error CS1525: Invalid expression term ')' + // if (i is (default)!) {} // error 16 + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(25, 28), + // (25,28): error CS1002: ; expected + // if (i is (default)!) {} // error 16 + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(25, 28), + // (25,28): error CS1513: } expected + // if (i is (default)!) {} // error 16 + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(25, 28), + // (26,18): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int', with 2 out parameters and a void return type. + // if (i is ((default)!)) {} // error 17 + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "((default)!)").WithArguments("int", "2").WithLocation(26, 18), + // (26,20): error CS8505: 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 17 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(26, 20), + // (26,28): error CS1003: Syntax error, ',' expected + // if (i is ((default)!)) {} // error 17 + Diagnostic(ErrorCode.ERR_SyntaxError, "!").WithArguments(",", "!").WithLocation(26, 28), + // (26,29): error CS1525: Invalid expression term ')' + // if (i is ((default)!)) {} // error 17 + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(26, 29) ); }