From a38625c172c73d5257f387bc949873cd7d67ce2a Mon Sep 17 00:00:00 2001 From: Neal Gafter Date: Mon, 1 Jun 2020 17:42:02 -0700 Subject: [PATCH] Make value sets resilient in error recovery scenarios. (#44573) Fixes #44518 Fixes #44540 Fixes #44657 --- .../CSharp/Portable/Binder/Binder_Patterns.cs | 2 +- .../Portable/Binder/DecisionDagBuilder.cs | 2 +- .../Utilities/ValueSetFactory.BoolValueSet.cs | 4 +- .../ValueSetFactory.BoolValueSetFactory.cs | 5 +- .../Utilities/ValueSetFactory.ByteTC.cs | 2 +- .../Utilities/ValueSetFactory.CharTC.cs | 2 +- .../Utilities/ValueSetFactory.DecimalTC.cs | 2 +- .../Utilities/ValueSetFactory.DoubleTC.cs | 2 +- .../Utilities/ValueSetFactory.IntTC.cs | 2 +- .../Utilities/ValueSetFactory.LongTC.cs | 2 +- .../Utilities/ValueSetFactory.NintValueSet.cs | 4 +- .../ValueSetFactory.NuintValueSet.cs | 4 +- .../Utilities/ValueSetFactory.SByteTC.cs | 2 +- .../Utilities/ValueSetFactory.ShortTC.cs | 2 +- .../Utilities/ValueSetFactory.SingleTC.cs | 2 +- .../Utilities/ValueSetFactory.StringTC.cs | 2 +- .../Utilities/ValueSetFactory.UIntTC.cs | 2 +- .../Utilities/ValueSetFactory.ULongTC.cs | 2 +- .../Utilities/ValueSetFactory.UShortTC.cs | 2 +- .../Portable/Utilities/ValueSetFactory.cs | 1 + .../Semantics/PatternMatchingTests3.cs | 148 +++++++++++++++--- .../Test/Semantic/Utilities/ValueSetTests.cs | 35 +++++ 22 files changed, 186 insertions(+), 45 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 53cd4b61401..ded2ef10336 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -1248,7 +1248,7 @@ void addSubpatternsForTuple(ImmutableArray elementTypes) { case BinaryOperatorKind.Float: case BinaryOperatorKind.Double: - if (!hasErrors && constantValueOpt != null && double.IsNaN(constantValueOpt.DoubleValue)) + if (!hasErrors && constantValueOpt != null && !constantValueOpt.IsBad && double.IsNaN(constantValueOpt.DoubleValue)) { diagnostics.Add(ErrorCode.ERR_RelationalPatternWithNaN, node.Expression.Location); hasErrors = true; diff --git a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs index e24da00f603..e5927b417a5 100644 --- a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs +++ b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs @@ -983,7 +983,7 @@ StateForCase makeNext(Tests remainingTests) resultForRelation(BinaryOperatorKind relation, ConstantValue value) { var input = test.Input; - IValueSetFactory? valueFac = ValueSetFactory.ForType(input.Type.EnumUnderlyingTypeOrSelf()); + IValueSetFactory? valueFac = ValueSetFactory.ForType(input.Type); if (valueFac == null || value.IsBad) { // If it is a type we don't track yet, assume all values are possible diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSet.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSet.cs index 1936fad0957..170ec193d01 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSet.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSet.cs @@ -49,7 +49,7 @@ public bool Any(BinaryOperatorKind relation, bool value) case (Equal, false): return _hasFalse; default: - throw new ArgumentException("relation"); + return true; } } @@ -64,7 +64,7 @@ public bool All(BinaryOperatorKind relation, bool value) case (Equal, false): return !_hasTrue; default: - throw new ArgumentException("relation"); + return true; } } diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSetFactory.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSetFactory.cs index 6781142a9ac..66b20d0a282 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSetFactory.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSetFactory.cs @@ -32,7 +32,8 @@ public IValueSet Related(BinaryOperatorKind relation, bool value) case (Equal, false): return BoolValueSet.OnlyFalse; default: - throw new ArgumentException("relation"); + // for error recovery + return BoolValueSet.AllValues; } } @@ -55,7 +56,7 @@ IValueSet IValueSetFactory.Related(BinaryOperatorKind relation, ConstantValue va bool IValueSetFactory.Related(BinaryOperatorKind relation, ConstantValue left, ConstantValue right) { Debug.Assert(relation == BinaryOperatorKind.Equal); - return left.BooleanValue == right.BooleanValue; + return left.IsBad || right.IsBad || left.BooleanValue == right.BooleanValue; } } } diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ByteTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ByteTC.cs index aef128d00b0..355cf93d147 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ByteTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ByteTC.cs @@ -50,7 +50,7 @@ byte INumericTC.Prev(byte value) return (byte)(value - 1); } - byte INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.ByteValue; + byte INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? (byte)0 : constantValue.ByteValue; ConstantValue INumericTC.ToConstantValue(byte value) => ConstantValue.Create(value); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.CharTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.CharTC.cs index 8b9d7047f0f..71e8833e395 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.CharTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.CharTC.cs @@ -45,7 +45,7 @@ char INumericTC.Next(char value) return (char)(value + 1); } - char INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.CharValue; + char INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? (char)0 : constantValue.CharValue; string INumericTC.ToString(char c) { diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DecimalTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DecimalTC.cs index 391bb22c74d..e755e3d5a2e 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DecimalTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DecimalTC.cs @@ -30,7 +30,7 @@ private struct DecimalTC : INumericTC decimal INumericTC.MaxValue => decimal.MaxValue; - public decimal FromConstantValue(ConstantValue constantValue) => constantValue.DecimalValue; + public decimal FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? 0m : constantValue.DecimalValue; public ConstantValue ToConstantValue(decimal value) => ConstantValue.Create(value); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DoubleTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DoubleTC.cs index 634dfb3a84a..6ff9c00be96 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DoubleTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DoubleTC.cs @@ -78,7 +78,7 @@ bool INumericTC.Related(BinaryOperatorKind relation, double left, double } } - double INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.DoubleValue; + double INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? 0.0 : constantValue.DoubleValue; ConstantValue INumericTC.ToConstantValue(double value) => ConstantValue.Create(value); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.IntTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.IntTC.cs index 5588a510aa3..96652fab749 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.IntTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.IntTC.cs @@ -50,7 +50,7 @@ int INumericTC.Prev(int value) return value - 1; } - public int FromConstantValue(ConstantValue constantValue) => constantValue.Int32Value; + public int FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? 0 : constantValue.Int32Value; public ConstantValue ToConstantValue(int value) => ConstantValue.Create(value); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.LongTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.LongTC.cs index 230ff5ce4bc..5a35e264b25 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.LongTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.LongTC.cs @@ -50,7 +50,7 @@ long INumericTC.Prev(long value) return value - 1; } - long INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.Int64Value; + long INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? 0L : constantValue.Int64Value; ConstantValue INumericTC.ToConstantValue(long value) => ConstantValue.Create(value); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NintValueSet.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NintValueSet.cs index d57933c6b11..7cc48dfd0a3 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NintValueSet.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NintValueSet.cs @@ -53,7 +53,7 @@ public bool All(BinaryOperatorKind relation, int value) return _values.All(relation, value); } - bool IValueSet.All(BinaryOperatorKind relation, ConstantValue value) => All(relation, value.Int32Value); + bool IValueSet.All(BinaryOperatorKind relation, ConstantValue value) => value.IsBad || All(relation, value.Int32Value); public bool Any(BinaryOperatorKind relation, int value) { @@ -64,7 +64,7 @@ public bool Any(BinaryOperatorKind relation, int value) return _values.Any(relation, value); } - bool IValueSet.Any(BinaryOperatorKind relation, ConstantValue value) => Any(relation, value.Int32Value); + bool IValueSet.Any(BinaryOperatorKind relation, ConstantValue value) => value.IsBad || Any(relation, value.Int32Value); public IValueSet Complement() { diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NuintValueSet.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NuintValueSet.cs index cc0d6d5a797..74c8381f2b1 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NuintValueSet.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NuintValueSet.cs @@ -42,7 +42,7 @@ public bool All(BinaryOperatorKind relation, uint value) return _values.All(relation, value); } - bool IValueSet.All(BinaryOperatorKind relation, ConstantValue value) => All(relation, value.UInt32Value); + bool IValueSet.All(BinaryOperatorKind relation, ConstantValue value) => value.IsBad || All(relation, value.UInt32Value); public bool Any(BinaryOperatorKind relation, uint value) { @@ -51,7 +51,7 @@ public bool Any(BinaryOperatorKind relation, uint value) return _values.Any(relation, value); } - bool IValueSet.Any(BinaryOperatorKind relation, ConstantValue value) => Any(relation, value.UInt32Value); + bool IValueSet.Any(BinaryOperatorKind relation, ConstantValue value) => value.IsBad || Any(relation, value.UInt32Value); public IValueSet Complement() { diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SByteTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SByteTC.cs index 8784bc7a3b0..c3486ca3023 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SByteTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SByteTC.cs @@ -50,7 +50,7 @@ sbyte INumericTC.Prev(sbyte value) return (sbyte)(value - 1); } - sbyte INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.SByteValue; + sbyte INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? (sbyte)0 : constantValue.SByteValue; public ConstantValue ToConstantValue(sbyte value) => ConstantValue.Create(value); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ShortTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ShortTC.cs index 97056444d6d..1c07ce50fad 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ShortTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ShortTC.cs @@ -50,7 +50,7 @@ short INumericTC.Prev(short value) return (short)(value - 1); } - short INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.Int16Value; + short INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? (short)0 : constantValue.Int16Value; ConstantValue INumericTC.ToConstantValue(short value) => ConstantValue.Create(value); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SingleTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SingleTC.cs index 7a57a6552bd..52e21a673f7 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SingleTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SingleTC.cs @@ -82,7 +82,7 @@ bool INumericTC.Related(BinaryOperatorKind relation, float left, float ri } } - float INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.SingleValue; + float INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? 0.0F : constantValue.SingleValue; ConstantValue INumericTC.ToConstantValue(float value) => ConstantValue.Create(value); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.StringTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.StringTC.cs index 84a0ad63341..44f2fe5f028 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.StringTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.StringTC.cs @@ -15,7 +15,7 @@ private struct StringTC : IEquatableValueTC { string IEquatableValueTC.FromConstantValue(ConstantValue constantValue) { - var result = constantValue.StringValue; + var result = constantValue.IsBad ? string.Empty : constantValue.StringValue; Debug.Assert(result != null); return result; } diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UIntTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UIntTC.cs index efbab4f98e9..069f69e0515 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UIntTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UIntTC.cs @@ -44,7 +44,7 @@ uint INumericTC.Next(uint value) return value + 1; } - public uint FromConstantValue(ConstantValue constantValue) => constantValue.UInt32Value; + public uint FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? (uint)0 : constantValue.UInt32Value; public ConstantValue ToConstantValue(uint value) => ConstantValue.Create(value); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ULongTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ULongTC.cs index 5343e1d3fde..ce7558c5967 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ULongTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ULongTC.cs @@ -50,7 +50,7 @@ ulong INumericTC.Prev(ulong value) return value - 1; } - ulong INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.UInt64Value; + ulong INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? 0UL : constantValue.UInt64Value; ConstantValue INumericTC.ToConstantValue(ulong value) => ConstantValue.Create(value); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UShortTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UShortTC.cs index aaaef68bf8f..19440d0288c 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UShortTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UShortTC.cs @@ -44,7 +44,7 @@ ushort INumericTC.Next(ushort value) return (ushort)(value + 1); } - ushort INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.UInt16Value; + ushort INumericTC.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? (ushort)0 : constantValue.UInt16Value; ConstantValue INumericTC.ToConstantValue(ushort value) => ConstantValue.Create(value); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.cs index 04b8692a66b..b40f452f41f 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.cs @@ -73,6 +73,7 @@ internal static partial class ValueSetFactory public static IValueSetFactory? ForType(TypeSymbol type) { + type = type.EnumUnderlyingTypeOrSelf(); return ForSpecialType(type.SpecialType, type.IsNativeIntegerType); } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs index f31a53308cd..ddad83bf024 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs @@ -2923,14 +2923,18 @@ class C } } - [Fact] - public void Relational_SignedEnumExhaustive() + [Theory] + [InlineData("sbyte", true)] + [InlineData("short", true)] + [InlineData("int", true)] + [InlineData("long", true)] + [InlineData("sbyte", false)] + [InlineData("short", false)] + [InlineData("int", false)] + [InlineData("long", false)] + public void Relational_SignedEnumExhaustive(string typeName, bool withExhaustive) { - foreach (var typeName in new[] { "sbyte", "short", "int", "long" }) - { - foreach (var withExhaustive in new[] { false, true }) - { - var source = @" + var source = @" enum E : " + typeName + @" { Zero, @@ -2957,21 +2961,19 @@ class C " : "") + @" }; }"; - var compilation = CreateCompilation(source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Preview)); - if (withExhaustive) - { - compilation.VerifyDiagnostics( - ); - } - else - { - compilation.VerifyDiagnostics( - // (15,28): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). - // static int M(E c) => c switch - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(15, 28) - ); - } - } + var compilation = CreateCompilation(source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Preview)); + if (withExhaustive) + { + compilation.VerifyEmitDiagnostics( + ); + } + else + { + compilation.VerifyEmitDiagnostics( + // (15,28): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // static int M(E c) => c switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(15, 28) + ); } } @@ -5537,5 +5539,107 @@ static void M(int a) Diagnostic(ErrorCode.ERR_NoImplicitConv, "0").WithArguments("int", "bool").WithLocation(7, 21) ); } + + [Fact, WorkItem(44518, "https://github.com/dotnet/roslyn/issues/44518")] + public void ErrorRecovery_01() + { + var source = +@"class C +{ + void M() + { + _ = is < true; + } +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (5,13): error CS1525: Invalid expression term 'is' + // _ = is < true; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "is").WithArguments("is").WithLocation(5, 13) + ); + } + + [Fact, WorkItem(44540, "https://github.com/dotnet/roslyn/issues/44540")] + public void ErrorRecovery_02() + { + var source = +@"using System; +public class C { + public void M(nint x) { + int z = x switch{ + 1=>//1, + 2=>2, + }; + Console.WriteLine(z); + } + public void M(nuint x) { + int z = x switch{ + 1=>//1, + 2=>2, + }; + Console.WriteLine(z); + } +}"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (6,18): error CS1003: Syntax error, ',' expected + // 2=>2, + Diagnostic(ErrorCode.ERR_SyntaxError, "=>").WithArguments(",", "=>").WithLocation(6, 18), + // (6,18): error CS8504: Pattern missing + // 2=>2, + Diagnostic(ErrorCode.ERR_MissingPattern, "=>").WithLocation(6, 18), + // (13,18): error CS1003: Syntax error, ',' expected + // 2=>2, + Diagnostic(ErrorCode.ERR_SyntaxError, "=>").WithArguments(",", "=>").WithLocation(13, 18), + // (13,18): error CS8504: Pattern missing + // 2=>2, + Diagnostic(ErrorCode.ERR_MissingPattern, "=>").WithLocation(13, 18) + ); + } + + [Fact, WorkItem(44540, "https://github.com/dotnet/roslyn/issues/44540")] + public void ErrorRecovery_03() + { + var source = +@"public class C { + public void M(nint x) { + _ = x is >= 1 and; + } + public void M(nuint x) { + _ = x is >= 1 and; + } +}"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (3,26): error CS8504: Pattern missing + // _ = x is >= 1 and; + Diagnostic(ErrorCode.ERR_MissingPattern, ";").WithLocation(3, 26), + // (6,26): error CS8504: Pattern missing + // _ = x is >= 1 and; + Diagnostic(ErrorCode.ERR_MissingPattern, ";").WithLocation(6, 26) + ); + } + + [Fact, WorkItem(44540, "https://github.com/dotnet/roslyn/issues/44540")] + public void ErrorRecovery_04() + { + var source = +@"public class C { + public void M() { + _ = 0f is >= 0/0; + _ = 0d is >= 0/0; + } +}"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (3,22): error CS0020: Division by constant zero + // _ = 0f is >= 0/0; + Diagnostic(ErrorCode.ERR_IntDivByZero, "0/0").WithLocation(3, 22), + // (4,22): error CS0020: Division by constant zero + // _ = 0d is >= 0/0; + Diagnostic(ErrorCode.ERR_IntDivByZero, "0/0").WithLocation(4, 22) + ); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Utilities/ValueSetTests.cs b/src/Compilers/CSharp/Test/Semantic/Utilities/ValueSetTests.cs index 6150b628279..b1491a6e3f7 100644 --- a/src/Compilers/CSharp/Test/Semantic/Utilities/ValueSetTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Utilities/ValueSetTests.cs @@ -845,5 +845,40 @@ public void TestAllFuzz_04() Assert.True(s1.All(GreaterThanOrEqual, j - 1)); } } + + [Fact] + public void DoNotCrashOnBadInput() + { + // For error recovery, do not throw exceptions on bad inputs. + var ctors = new IValueSetFactory[] + { + ForByte, + ForSByte, + ForChar, + ForShort, + ForUShort, + ForInt, + ForUInt, + ForLong, + ForULong, + ForBool, + ForFloat, + ForDouble, + ForString, + ForDecimal, + ForNint, + ForNuint + }; + ConstantValue badConstant = ConstantValue.Bad; + foreach (IValueSetFactory fac in ctors) + { + foreach (BinaryOperatorKind relation in new[] { LessThan, Equal, NotEqual }) + { + IValueSet set = fac.Related(relation, badConstant); + _ = set.All(relation, badConstant); + _ = set.Any(relation, badConstant); + } + } + } } } -- GitLab