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

Simulate the C# 7 compiler's behavior on traditional switch statements with...

Simulate the C# 7 compiler's behavior on traditional switch statements with old language versions (#32818)

Fixes #32806
This was a "forward compatibility" bug, where new compilers would accept code in old language versions that the older compilers would not have accepted.
上级 6663ccef
// 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;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
......@@ -61,7 +63,7 @@ private void VisitSwitchBlock(BoundSwitchStatement node)
Join(ref afterSwitchState, ref this.State);
}
if (reachableLabels.Contains(node.BreakLabel))
if (reachableLabels.Contains(node.BreakLabel) || node.DefaultLabel == null && IsTraditionalSwitch(node))
{
Join(ref afterSwitchState, ref initialState);
}
......@@ -69,6 +71,43 @@ private void VisitSwitchBlock(BoundSwitchStatement node)
ResolveBreaks(afterSwitchState, node.BreakLabel);
}
/// <summary>
/// Is the switch statement one that could be interpreted as a C# 6 or earlier switch statement?
/// </summary>
private bool IsTraditionalSwitch(BoundSwitchStatement node)
{
// Before recursive patterns were introduced, we did not consider handling both 'true' and 'false' to
// completely handle all case of a switch on a bool unless there was some patterny syntax or semantics
// in the switch. We had two different bound nodes and separate flow analysis handling for
// "traditional" switch statements and "pattern-based" switch statements. We simulate that behavior
// by testing to see if this switch would have been handled under the old rules by the old compiler.
// If we are in a recent enough language version, we treat the switch as a fully pattern-based switch
// for the purposes of flow analysis.
if (((CSharpParseOptions)node.Syntax.SyntaxTree.Options).LanguageVersion >= MessageID.IDS_FeatureRecursivePatterns.RequiredVersion())
{
return false;
}
if (!node.Expression.Type.IsValidV6SwitchGoverningType())
{
return false;
}
foreach (var sectionSyntax in ((SwitchStatementSyntax)node.Syntax).Sections)
{
foreach (var label in sectionSyntax.Labels)
{
if (label.Kind() == SyntaxKind.CasePatternSwitchLabel)
{
return false;
}
}
}
return true;
}
protected virtual void VisitSwitchSection(BoundSwitchSection node, bool isLastSection)
{
SetState(UnreachableState());
......
......@@ -995,7 +995,7 @@ internal static bool IsValidV6SwitchGoverningType(this TypeSymbol type, bool isT
// SPEC: 1) If the type of the switch expression is sbyte, byte, short, ushort, int, uint,
// SPEC: long, ulong, bool, char, string, or an enum-type, or if it is the nullable type
// SPEC: corresponding to one of these types, then that is the governing type of the switch statement.
// SPEC: 2) Otherwise, exactly one user-defined implicit conversion (§6.4) must exist from the
// SPEC: 2) Otherwise, exactly one user-defined implicit conversion must exist from the
// SPEC: type of the switch expression to one of the following possible governing types:
// SPEC: sbyte, byte, short, ushort, int, uint, long, ulong, char, string, or, a nullable type
// SPEC: corresponding to one of those types
......
......@@ -2685,6 +2685,72 @@ public static int Main()
Diagnostic(ErrorCode.ERR_UseDefViolation, "goo").WithArguments("goo").WithLocation(40, 27));
}
[Fact, WorkItem(32806, "https://github.com/dotnet/roslyn/issues/32806")]
public void TraditionalSwitchIsIncomplete_01()
{
var text = @"
class SwitchTest
{
static int Main(string[] args)
{
bool? test = null;
switch (test)
{
case true:
return 1;
case false:
return 0;
case null:
return -1;
}
}
}
";
CreateCompilation(text, parseOptions: TestOptions.Regular6).VerifyDiagnostics(
// (4,16): error CS0161: 'SwitchTest.Main(string[])': not all code paths return a value
// static int Main(string[] args)
Diagnostic(ErrorCode.ERR_ReturnExpected, "Main").WithArguments("SwitchTest.Main(string[])").WithLocation(4, 16)
);
CreateCompilation(text, parseOptions: TestOptions.Regular7_3).VerifyDiagnostics(
// (4,16): error CS0161: 'SwitchTest.Main(string[])': not all code paths return a value
// static int Main(string[] args)
Diagnostic(ErrorCode.ERR_ReturnExpected, "Main").WithArguments("SwitchTest.Main(string[])").WithLocation(4, 16)
);
CreateCompilation(text, parseOptions: TestOptions.Regular8).VerifyDiagnostics();
}
[Fact, WorkItem(32806, "https://github.com/dotnet/roslyn/issues/32806")]
public void TraditionalSwitchIsIncomplete_02()
{
var text = @"
class SwitchTest
{
static int Main(string[] args)
{
bool? test = null;
switch (test)
{
case true when true:
return 1;
case false:
return 0;
case null:
return -1;
}
}
}
";
CreateCompilation(text, parseOptions: TestOptions.Regular6).VerifyDiagnostics(
// (10,13): error CS8059: Feature 'pattern matching' is not available in C# 6. Please use language version 7.0 or greater.
// case true when true:
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "case true when true:").WithArguments("pattern matching", "7.0").WithLocation(10, 13)
);
CreateCompilation(text, parseOptions: TestOptions.Regular7_3).VerifyDiagnostics();
CreateCompilation(text, parseOptions: TestOptions.Regular8).VerifyDiagnostics();
}
#endregion
#region regressions
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册