Skip to content

Commit 174a51a

Browse files
And cover a few more cases (with a bunch of ToString still)
Iterating in the direction of covering basic cases - relates to #686
1 parent d7ede1d commit 174a51a

4 files changed

Lines changed: 106 additions & 11 deletions

File tree

CodeConverter/CSharp/ExpressionNodeVisitor.cs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,23 @@ public override async Task<CSharpSyntaxNode> DefaultVisit(SyntaxNode node)
9393
public override async Task<CSharpSyntaxNode> VisitXmlEmbeddedExpression(VBSyntax.XmlEmbeddedExpressionSyntax node) =>
9494
await node.Expression.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor);
9595

96+
public override async Task<CSharpSyntaxNode> VisitXmlDocument(VBasic.Syntax.XmlDocumentSyntax node)
97+
{
98+
var interpolationsList = SyntaxFactory.List(await node.PrecedingMisc.Concat(node.Root).Concat(node.FollowingMisc).SelectManyAsync(this.AcceptXmlInterpolated));
99+
return InterpolatedString(interpolationsList);
100+
}
101+
96102
public override async Task<CSharpSyntaxNode> VisitXmlElement(VBasic.Syntax.XmlElementSyntax node)
97103
{
98104
_extraUsingDirectives.Add("System.Xml.Linq");
99-
var interpolations = await AcceptXmlInterpolated(node);
100-
var interpolationsList = SyntaxFactory.List(interpolations);
101-
var xmlAsString = $"{node.StartTag}{interpolationsList}{node.EndTag}".Trim();
102-
var interpolatedString = SyntaxFactory.InterpolatedStringExpression(SyntaxFactory.Token(SyntaxKind.InterpolatedStringStartToken), interpolationsList, SyntaxFactory.Token(SyntaxKind.InterpolatedStringEndToken));
105+
var interpolationsList = SyntaxFactory.List(await AcceptXmlInterpolated(node));
106+
return InterpolatedString(interpolationsList);
107+
}
108+
109+
private CSharpSyntaxNode InterpolatedString(SyntaxList<InterpolatedStringContentSyntax> interpolationsList)
110+
{
111+
_extraUsingDirectives.Add("System.Xml.Linq");
112+
var interpolatedString = SyntaxFactory.InterpolatedStringExpression(SyntaxFactory.Token(SyntaxKind.InterpolatedVerbatimStringStartToken), interpolationsList, SyntaxFactory.Token(SyntaxKind.InterpolatedStringEndToken));
103113
return SyntaxFactory.InvocationExpression(
104114
SyntaxFactory.MemberAccessExpression(
105115
SyntaxKind.SimpleMemberAccessExpression,
@@ -114,14 +124,22 @@ private static InterpolatedStringTextSyntax InterpolatedStringText(string text)
114124
private async Task<IEnumerable<InterpolatedStringContentSyntax>> AcceptXmlInterpolated(VBSyntax.XmlNodeSyntax n)
115125
{
116126
if (n is VBSyntax.XmlElementSyntax xmlEs) {
117-
return InterpolatedStringText(xmlEs.StartTag.ToString()).Yield()
127+
return InterpolatedStringText(LiteralConversions.EscapeVerbatimQuotes(xmlEs.StartTag.ToString())).Yield()
118128
.Concat(await xmlEs.Content.SelectManyAsync(AcceptXmlInterpolated))
119-
.Concat(InterpolatedStringText(xmlEs.EndTag.ToString()));
129+
.Concat(InterpolatedStringText(LiteralConversions.EscapeVerbatimQuotes(xmlEs.EndTag.ToString())));
120130
}
121131
var expression = await n.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor);
122132
return new[] { SyntaxFactory.Interpolation(expression) };
123133
}
124134

135+
public override async Task<CSharpSyntaxNode> VisitXmlText(VBSyntax.XmlTextSyntax node) =>
136+
CommonConversions.Literal(node.TextTokens.Aggregate("", (a, b) => a + LiteralConversions.EscapeVerbatimQuotes(b.Text)));
137+
138+
public async override Task<CSharpSyntaxNode> VisitXmlEmptyElement(VBSyntax.XmlEmptyElementSyntax node) =>
139+
CommonConversions.Literal(LiteralConversions.EscapeVerbatimQuotes(node.ToString()));
140+
141+
142+
125143
/// <summary>
126144
/// https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/xml/accessing-xml
127145
/// </summary>
@@ -132,7 +150,7 @@ public override async Task<CSharpSyntaxNode> VisitXmlMemberAccessExpression(
132150

133151
var xElementMethodName = GetXElementMethodName(node);
134152

135-
MemberAccessExpressionSyntax elements = SyntaxFactory.MemberAccessExpression(
153+
var elements = SyntaxFactory.MemberAccessExpression(
136154
SyntaxKind.SimpleMemberAccessExpression,
137155
await node.Base.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor),
138156
SyntaxFactory.IdentifierName(xElementMethodName)

CodeConverter/CSharp/LiteralConversions.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,17 @@ internal static string GetQuotedStringTextForUser(string textForUser, string val
106106
internal static string EscapeQuotes(string unquotedTextForUser, string valueTextForCompiler, bool isVerbatimString)
107107
{
108108
if (isVerbatimString) {
109-
return valueTextForCompiler.Replace("\"", "\"\"");
109+
return EscapeVerbatimQuotes(valueTextForCompiler);
110110
} else {
111111
return unquotedTextForUser.Replace("\"\"", "\\\"");
112112
}
113113
}
114114

115+
public static string EscapeVerbatimQuotes(string valueTextForCompiler)
116+
{
117+
return valueTextForCompiler.Replace("\"", "\"\"");
118+
}
119+
115120
internal static string EscapeEscapeChar(string valueTextForCompiler)
116121
{
117122
return valueTextForCompiler.Replace("\\", "\\\\");

CodeConverter/CSharp/QueryConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ private static bool RequiredContinuation(VBSyntax.QueryClauseSyntax queryClauseS
244244
var collectionRangeVariableSyntax = vbFromClause.Variables.Single();
245245
var expression = (CSSyntax.ExpressionSyntax)await collectionRangeVariableSyntax.Expression.AcceptAsync(_triviaConvertingVisitor);
246246
var parentOperation = _semanticModel.GetOperation(collectionRangeVariableSyntax.Expression)?.Parent;
247-
if (parentOperation.IsImplicit && parentOperation is IInvocationOperation io &&
247+
if (parentOperation != null && parentOperation.IsImplicit && parentOperation is IInvocationOperation io &&
248248
io.TargetMethod.MethodKind == MethodKind.ReducedExtension && io.TargetMethod.Name == nameof(Enumerable.AsEnumerable)) {
249249
expression = SyntaxFactory.InvocationExpression(ValidSyntaxFactory.MemberAccess(expression, io.TargetMethod.Name), SyntaxFactory.ArgumentList());
250250
}

Tests/CSharp/ExpressionTests/XmlExpressionTests.cs

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ internal partial class TestClass
2525
private void TestMethod()
2626
{
2727
string hello = ""Hello"";
28-
XElement x = XElement.Parse($""<h1>{hello}</h1>"");
28+
XElement x = XElement.Parse($@""<h1>{hello}</h1>"");
2929
}
3030
}");
3131
}
32+
3233
[Fact]
3334
public async Task TwoLayerNestedXmlWithExpressionsAsync()
3435
{
@@ -46,9 +47,80 @@ private void TestMethod()
4647
{
4748
int var1 = 1;
4849
int var2 = 2;
49-
XElement x = XElement.Parse($""<h1>{var1}{var2}<span>{var2}{var1}</span> </h1>"");
50+
XElement x = XElement.Parse($@""<h1>{var1}{var2}<span>{var2}{var1}</span> </h1>"");
5051
}
5152
}");
5253
}
54+
55+
[Fact]
56+
public async Task MultiLineXmlExpressionsAsync()
57+
{
58+
//BUG: Newlines appear as \r\n
59+
await TestConversionVisualBasicToCSharpAsync(@"Class TestClass
60+
Private Sub TestMethod()
61+
Dim catalog =
62+
<?xml version=""1.0""?>
63+
<Catalog>
64+
<Book id=""bk101"">
65+
<Author>Garghentini, Davide</Author>
66+
<Title>XML Developer's Guide</Title>
67+
<Price>44.95</Price>
68+
<Description>
69+
An in-depth look at creating applications
70+
with <technology>XML</technology>. For
71+
<audience>beginners</audience> or
72+
<audience>advanced</audience> developers.
73+
</Description>
74+
</Book>
75+
<Book id=""bk331"">
76+
<Author>Spencer, Phil</Author>
77+
<Title>Developing Applications with Visual Basic .NET</Title>
78+
<Price>45.95</Price>
79+
<Description>
80+
Get the expert insights, practical code samples,
81+
and best practices you need
82+
to advance your expertise with <technology>Visual
83+
Basic .NET</technology>.
84+
Learn how to create faster, more reliable applications
85+
based on professional,
86+
pragmatic guidance by today's top <audience>developers</audience>.
87+
</Description>
88+
</Book>
89+
</Catalog>
90+
91+
Dim htmlOutput =
92+
<html>
93+
<body>
94+
<%= From book In catalog.<Catalog>.<Book>
95+
Select <div>
96+
<h1><%= book.<Title>.Value %></h1>
97+
<h3><%= ""By "" & book.<Author>.Value %></h3>
98+
<h3><%= ""Price = "" & book.<Price>.Value %></h3>
99+
<h2>Description</h2>
100+
<%= TransformDescription(book.<Description>(0)) %>
101+
<hr/>
102+
</div> %>
103+
</body>
104+
</html>
105+
End Sub
106+
End Class", @"using System.Linq;
107+
using System.Xml.Linq;
108+
109+
internal partial class TestClass
110+
{
111+
private void TestMethod()
112+
{
113+
XDocument catalog = XElement.Parse($@""<Catalog> <Book id=""""bk101""""> <Author>{""Garghentini, Davide""}</Author> <Title>{""XML Developer's Guide""}</Title> <Price>{""44.95""}</Price> <Description>{""\r\n An in-depth look at creating applications\r\n with ""}<technology>{""XML""}</technology>{"". For\r\n ""}<audience>{""beginners""}</audience>{"" or\r\n ""}<audience>{""advanced""}</audience>{"" developers.\r\n ""}</Description> </Book> <Book id=""""bk331""""> <Author>{""Spencer, Phil""}</Author> <Title>{""Developing Applications with Visual Basic .NET""}</Title> <Price>{""45.95""}</Price> <Description>{""\r\n Get the expert insights, practical code samples,\r\n and best practices you need\r\n to advance your expertise with ""}<technology>{""Visual\r\n Basic .NET""}</technology>{"".\r\n Learn how to create faster, more reliable applications\r\n based on professional,\r\n pragmatic guidance by today's top ""}<audience>{""developers""}</audience>{"".\r\n ""}</Description> </Book> </Catalog>"");
114+
XElement htmlOutput = XElement.Parse($@""<html> <body>{from book in catalog.Elements(""Catalog"").Elements(""Book"")
115+
select XElement.Parse($@""<div> <h1>{book.Elements(""Title"").Value}</h1> <h3>{""By "" + book.Elements(""Author"").Value}</h3> <h3>{""Price = "" + book.Elements(""Price"").Value}</h3> <h2>{""Description""}</h2>{TransformDescription(book.Elements(""Description"").ElementAtOrDefault(0))}{""<hr/>""}</div>"")}</body> </html>"");
116+
}
117+
}
118+
1 source compilation errors:
119+
BC36610: Name 'TransformDescription' is either not declared or not in the current scope.
120+
3 target compilation errors:
121+
CS0029: Cannot implicitly convert type 'System.Xml.Linq.XElement' to 'System.Xml.Linq.XDocument'
122+
CS1061: 'IEnumerable<XElement>' does not contain a definition for 'Value' and no accessible extension method 'Value' accepting a first argument of type 'IEnumerable<XElement>' could be found (are you missing a using directive or an assembly reference?)
123+
CS0103: The name 'TransformDescription' does not exist in the current context");
124+
}
53125
}
54126
}

0 commit comments

Comments
 (0)