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

Make value sets resilient in error recovery scenarios. (#44573)

Fixes #44518
Fixes #44540
Fixes #44657
上级 be437c73
......@@ -1248,7 +1248,7 @@ void addSubpatternsForTuple(ImmutableArray<TypeWithAnnotations> 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;
......
......@@ -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
......
......@@ -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;
}
}
......
......@@ -32,7 +32,8 @@ public IValueSet<bool> 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;
}
}
}
......
......@@ -50,7 +50,7 @@ byte INumericTC<byte>.Prev(byte value)
return (byte)(value - 1);
}
byte INumericTC<byte>.FromConstantValue(ConstantValue constantValue) => constantValue.ByteValue;
byte INumericTC<byte>.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? (byte)0 : constantValue.ByteValue;
ConstantValue INumericTC<byte>.ToConstantValue(byte value) => ConstantValue.Create(value);
......
......@@ -45,7 +45,7 @@ char INumericTC<char>.Next(char value)
return (char)(value + 1);
}
char INumericTC<char>.FromConstantValue(ConstantValue constantValue) => constantValue.CharValue;
char INumericTC<char>.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? (char)0 : constantValue.CharValue;
string INumericTC<char>.ToString(char c)
{
......
......@@ -30,7 +30,7 @@ private struct DecimalTC : INumericTC<decimal>
decimal INumericTC<decimal>.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);
......
......@@ -78,7 +78,7 @@ bool INumericTC<double>.Related(BinaryOperatorKind relation, double left, double
}
}
double INumericTC<double>.FromConstantValue(ConstantValue constantValue) => constantValue.DoubleValue;
double INumericTC<double>.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? 0.0 : constantValue.DoubleValue;
ConstantValue INumericTC<double>.ToConstantValue(double value) => ConstantValue.Create(value);
......
......@@ -50,7 +50,7 @@ int INumericTC<int>.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);
......
......@@ -50,7 +50,7 @@ long INumericTC<long>.Prev(long value)
return value - 1;
}
long INumericTC<long>.FromConstantValue(ConstantValue constantValue) => constantValue.Int64Value;
long INumericTC<long>.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? 0L : constantValue.Int64Value;
ConstantValue INumericTC<long>.ToConstantValue(long value) => ConstantValue.Create(value);
......
......@@ -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<int> Complement()
{
......
......@@ -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<uint> Complement()
{
......
......@@ -50,7 +50,7 @@ sbyte INumericTC<sbyte>.Prev(sbyte value)
return (sbyte)(value - 1);
}
sbyte INumericTC<sbyte>.FromConstantValue(ConstantValue constantValue) => constantValue.SByteValue;
sbyte INumericTC<sbyte>.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? (sbyte)0 : constantValue.SByteValue;
public ConstantValue ToConstantValue(sbyte value) => ConstantValue.Create(value);
......
......@@ -50,7 +50,7 @@ short INumericTC<short>.Prev(short value)
return (short)(value - 1);
}
short INumericTC<short>.FromConstantValue(ConstantValue constantValue) => constantValue.Int16Value;
short INumericTC<short>.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? (short)0 : constantValue.Int16Value;
ConstantValue INumericTC<short>.ToConstantValue(short value) => ConstantValue.Create(value);
......
......@@ -82,7 +82,7 @@ bool INumericTC<float>.Related(BinaryOperatorKind relation, float left, float ri
}
}
float INumericTC<float>.FromConstantValue(ConstantValue constantValue) => constantValue.SingleValue;
float INumericTC<float>.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? 0.0F : constantValue.SingleValue;
ConstantValue INumericTC<float>.ToConstantValue(float value) => ConstantValue.Create(value);
......
......@@ -15,7 +15,7 @@ private struct StringTC : IEquatableValueTC<string>
{
string IEquatableValueTC<string>.FromConstantValue(ConstantValue constantValue)
{
var result = constantValue.StringValue;
var result = constantValue.IsBad ? string.Empty : constantValue.StringValue;
Debug.Assert(result != null);
return result;
}
......
......@@ -44,7 +44,7 @@ uint INumericTC<uint>.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);
......
......@@ -50,7 +50,7 @@ ulong INumericTC<ulong>.Prev(ulong value)
return value - 1;
}
ulong INumericTC<ulong>.FromConstantValue(ConstantValue constantValue) => constantValue.UInt64Value;
ulong INumericTC<ulong>.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? 0UL : constantValue.UInt64Value;
ConstantValue INumericTC<ulong>.ToConstantValue(ulong value) => ConstantValue.Create(value);
......
......@@ -44,7 +44,7 @@ ushort INumericTC<ushort>.Next(ushort value)
return (ushort)(value + 1);
}
ushort INumericTC<ushort>.FromConstantValue(ConstantValue constantValue) => constantValue.UInt16Value;
ushort INumericTC<ushort>.FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? (ushort)0 : constantValue.UInt16Value;
ConstantValue INumericTC<ushort>.ToConstantValue(ushort value) => ConstantValue.Create(value);
......
......@@ -73,6 +73,7 @@ internal static partial class ValueSetFactory
public static IValueSetFactory? ForType(TypeSymbol type)
{
type = type.EnumUnderlyingTypeOrSelf();
return ForSpecialType(type.SpecialType, type.IsNativeIntegerType);
}
}
......
......@@ -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)
);
}
}
}
......@@ -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);
}
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册