提交 6524864f 编写于 作者: D Dustin Campbell

Handle parenthesis expansion and simplification in C# interpolations

In string interpolations, parentheses can't be removed if they surround
some expression that includes a : or :: token. Othewise, a parsing
ambuity will be introduced due to the : that delimits the format clause
(i.e. $"{number:x4}").

Fixes Issue #724.
上级 de93c18d
......@@ -3507,6 +3507,52 @@ bool M(string[] args)
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInlineTemporary)]
public void TestAliasQualifiedNameIntoInterpolation()
{
Test(
@"
class A
{
void M()
{
var [|g|] = global::System.Guid.Empty;
var s = $""{g}"";
}
}
", @"
class A
{
void M()
{
var s = $""{(global::System.Guid.Empty)}"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInlineTemporary)]
public void TestConditionalExpressionIntoInterpolation()
{
Test(
@"
class A
{
bool M(bool b)
{
var [|x|] = b ? 19 : 23;
var s = $""{x}"";
}
}
", @"
class A
{
bool M(bool b)
{
var s = $""{(b ? 19 : 23)}"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInlineTemporary)]
public void TestConditionalExpressionIntoInterpolationWithFormatClause()
{
......
......@@ -628,7 +628,7 @@ class C
{
void M()
{
var x = $"{true ? 1 : 0}";
var x = $"{(true ? 1 : 0)}";
}
}
</code>
......@@ -663,6 +663,146 @@ class C
var x = $"{(true ? 1 : 0):x}";
}
}
</code>
Test(input, expected)
End Sub
<WorkItem(724, "#724")>
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Sub CSharp_SimplifyParenthesesInsideInterpolation3()
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class C
{
void M()
{
var x = $"{{|Simplify:(global::System.Guid.Empty)|}}";
}
}
</Document>
</Project>
</Workspace>
Dim expected =
<code>
class C
{
void M()
{
var x = $"{(global::System.Guid.Empty)}";
}
}
</code>
Test(input, expected)
End Sub
<WorkItem(724, "#724")>
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Sub CSharp_SimplifyParenthesesInsideInterpolation4()
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class C
{
void M()
{
var g = System.Guid.NewGuid();
var x = $"{g == {|Simplify:(global::System.Guid.Empty)|}}";
}
}
</Document>
</Project>
</Workspace>
Dim expected =
<code>
class C
{
void M()
{
var g = System.Guid.NewGuid();
var x = $"{g == (global::System.Guid.Empty)}";
}
}
</code>
Test(input, expected)
End Sub
<WorkItem(724, "#724")>
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Sub CSharp_SimplifyParenthesesInsideInterpolation5()
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class C
{
void M()
{
var g = System.Guid.NewGuid();
var x = $"{{|Simplify:(g == (global::System.Guid.Empty))|}}";
}
}
</Document>
</Project>
</Workspace>
Dim expected =
<code>
class C
{
void M()
{
var g = System.Guid.NewGuid();
var x = $"{g == (global::System.Guid.Empty)}";
}
}
</code>
Test(input, expected)
End Sub
<WorkItem(724, "#724")>
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Sub CSharp_SimplifyParenthesesInsideInterpolation6()
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class C
{
void M()
{
var x = 19;
var y = 23;
var z = $"{{|Simplify:((true ? x : y) == 42)|}}";
}
}
</Document>
</Project>
</Workspace>
Dim expected =
<code>
class C
{
void M()
{
var x = 19;
var y = 23;
var z = $"{(true ? x : y) == 42}";
}
}
</code>
Test(input, expected)
......
......@@ -3,6 +3,7 @@
using System;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;
using System.Collections.Generic;
namespace Microsoft.CodeAnalysis.CSharp.Extensions
{
......@@ -54,7 +55,8 @@ public static bool CanRemoveParentheses(this ParenthesizedExpressionSyntax node)
// Handle expression-level ambiguities
if (RemovalMayIntroduceCastAmbiguity(node) ||
RemovalMayIntroduceCommaListAmbiguity(node))
RemovalMayIntroduceCommaListAmbiguity(node) ||
RemovalMayIntroduceInterpolationAmbiguity(node))
{
return false;
}
......@@ -68,8 +70,7 @@ public static bool CanRemoveParentheses(this ParenthesizedExpressionSyntax node)
// Cases:
// $"{(x)}" -> $"{x}"
if (node.IsParentKind(SyntaxKind.Interpolation) &&
!RemovalMayIntroduceInterpolationAmbiguity(node))
if (node.IsParentKind(SyntaxKind.Interpolation))
{
return true;
}
......@@ -122,19 +123,61 @@ public static bool CanRemoveParentheses(this ParenthesizedExpressionSyntax node)
: false;
}
[ThreadStatic]
private static Stack<SyntaxNode> s_nodeStack;
private static bool RemovalMayIntroduceInterpolationAmbiguity(ParenthesizedExpressionSyntax node)
{
if (node.IsParentKind(SyntaxKind.Interpolation))
// First, find the parenting interpolation. If we find a parenthesize expression first,
// we can bail out early.
InterpolationSyntax interpolation = null;
foreach (var ancestor in node.Parent.AncestorsAndSelf())
{
// Can't remove parentheses in this case:
// $"{(true ? == 0 : 1):x"
var interpolation = (InterpolationSyntax)node.Parent;
if (node.Expression.IsKind(SyntaxKind.ConditionalExpression) &&
interpolation.AlignmentClause == null &&
interpolation.FormatClause != null &&
!interpolation.FormatClause.ColonToken.IsMissing)
switch (ancestor.Kind())
{
return true;
case SyntaxKind.ParenthesizedExpression:
return false;
case SyntaxKind.Interpolation:
interpolation = (InterpolationSyntax)ancestor;
break;
}
}
if (interpolation == null)
{
return false;
}
if (s_nodeStack == null)
{
s_nodeStack = new Stack<SyntaxNode>();
}
else
{
s_nodeStack.Clear();
}
s_nodeStack.Push(node.Expression);
while (s_nodeStack.Count > 0)
{
var expression = s_nodeStack.Pop();
foreach (var nodeOrToken in expression.ChildNodesAndTokens())
{
// Note: There's no need drill into other parenthesized expressions, since any colons in them would be unambiguous.
if (nodeOrToken.IsNode && !nodeOrToken.IsKind(SyntaxKind.ParenthesizedExpression))
{
s_nodeStack.Push(nodeOrToken.AsNode());
}
else if (nodeOrToken.IsToken)
{
if (nodeOrToken.IsKind(SyntaxKind.ColonToken) || nodeOrToken.IsKind(SyntaxKind.ColonColonToken))
{
s_nodeStack.Clear();
return true;
}
}
}
}
......
......@@ -200,6 +200,18 @@ public override SyntaxNode VisitBinaryExpression(BinaryExpressionSyntax node)
return result;
}
public override SyntaxNode VisitInterpolation(InterpolationSyntax node)
{
var result = (InterpolationSyntax)base.VisitInterpolation(node);
if (result.Expression != null && !result.Expression.IsKind(SyntaxKind.ParenthesizedExpression))
{
result = result.WithExpression(result.Expression.Parenthesize());
}
return result;
}
public override SyntaxNode VisitXmlNameAttribute(XmlNameAttributeSyntax node)
{
_cancellationToken.ThrowIfCancellationRequested();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册