Skip to content

Commit 173332a

Browse files
committed
Improving detection of unterminated, multiple-line statements
1 parent 1ed8e92 commit 173332a

File tree

6 files changed

+77
-23
lines changed

6 files changed

+77
-23
lines changed

ReadableExpressions.UnitTests/WhenFormattingCode.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ public void ShouldVarAssignAVariableUsedInNestedConstructs()
422422
{
423423
throw new NotSupportedException();
424424
}
425-
}
425+
};
426426
}
427427
428428
var fileStream = stream as FileStream;
@@ -441,7 +441,7 @@ public void ShouldVarAssignAVariableUsedInNestedConstructs()
441441
{
442442
throw new IOException();
443443
}
444-
}
444+
};
445445
}
446446
447447
return 0L;

ReadableExpressions.UnitTests/WhenTranslatingAssignments.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -528,8 +528,7 @@ public void ShouldTranslateANestedBlockAssignment()
528528
[TestMethod]
529529
public void ShouldTranslateMultiStatementValueBlockAssignments()
530530
{
531-
ParameterExpression existingInts;
532-
var valueConditional = GetReturnStatementBlock(out existingInts);
531+
var valueConditional = GetReturnStatementBlock(out var existingInts);
533532

534533
Expression<Action> consoleRead = () => Console.Read();
535534

@@ -567,7 +566,7 @@ public void ShouldTranslateMultiStatementValueBlockAssignments()
567566
}
568567
569568
return ints;
570-
}
569+
};
571570
}";
572571
Assert.AreEqual(EXPECTED.TrimStart(), translated);
573572
}
@@ -576,8 +575,7 @@ public void ShouldTranslateMultiStatementValueBlockAssignments()
576575
[TestMethod]
577576
public void ShouldTranslateSingleStatementValueBlockAssignments()
578577
{
579-
ParameterExpression existingInts;
580-
var valueConditional = GetReturnStatementBlock(out existingInts);
578+
var valueConditional = GetReturnStatementBlock(out var existingInts);
581579

582580
var singleStatementValueBlock = Expression.Block(
583581
new[] { existingInts },
@@ -610,7 +608,7 @@ public void ShouldTranslateSingleStatementValueBlockAssignments()
610608
}
611609
612610
return ints;
613-
}
611+
};
614612
}";
615613
Assert.AreEqual(EXPECTED.TrimStart(), translated);
616614
}

ReadableExpressions.UnitTests/WhenTranslatingBlocks.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,45 @@ public void ShouldNotTerminateMethodCallArguments()
281281
Assert.AreEqual("File.OpenText(((int)o).ToString())", translated);
282282
}
283283

284+
[TestMethod]
285+
public void ShouldTerminateAMultipleLineMemberInitAssignment()
286+
{
287+
Expression<Action> writeWat = () => Console.WriteLine("Wat");
288+
Expression<Func<long>> read = () => Console.Read();
289+
290+
var newMemoryStream = Expression.New(typeof(MemoryStream));
291+
var positionProperty = newMemoryStream.Type.GetProperty("Position");
292+
var valueBlock = Expression.Block(writeWat.Body, read.Body);
293+
// ReSharper disable once AssignNullToNotNullAttribute
294+
var positionInit = Expression.Bind(positionProperty, valueBlock);
295+
var memoryStreamInit = Expression.MemberInit(newMemoryStream, positionInit);
296+
297+
var streamVariable = Expression.Variable(typeof(Stream), "stream");
298+
299+
var assignStream = Expression.Assign(streamVariable, memoryStreamInit);
300+
301+
var streamIsNull = Expression.Equal(streamVariable, Expression.Default(typeof(Stream)));
302+
303+
var ifNullAssign = Expression.IfThen(streamIsNull, assignStream);
304+
305+
var translated = ifNullAssign.ToReadableString();
306+
307+
const string EXPECTED = @"
308+
if (stream == null)
309+
{
310+
stream = new MemoryStream
311+
{
312+
Position =
313+
{
314+
Console.WriteLine(""Wat"");
315+
316+
return ((long)Console.Read());
317+
}
318+
};
319+
}";
320+
Assert.AreEqual(EXPECTED.TrimStart(), translated);
321+
}
322+
284323
[TestMethod]
285324
public void ShouldTranslateASwitchWithMultipleVariableAssignments()
286325
{

ReadableExpressions.UnitTests/WhenTranslatingGotos.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ public void ShouldTranslateAReturnStatementWithABlock()
189189
i = i + 2;
190190
191191
return i;
192-
}";
192+
};";
193193

194194
var translated = returnBlock.ToReadableString();
195195

ReadableExpressions/StringExtensions.cs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,41 @@ public static bool IsTerminated(this string codeLine)
1919

2020
var lastNewLine = codeLine.LastIndexOf(Environment.NewLine, StringComparison.Ordinal);
2121

22-
var index = (lastNewLine != -1)
23-
? lastNewLine + Environment.NewLine.Length
24-
: 0;
22+
if (lastNewLine == -1)
23+
{
24+
return false;
25+
}
26+
27+
var codeLines = codeLine.SplitToLines();
2528

26-
for (var end = codeLine.Length - 1; index < end; ++index)
29+
while (codeLines[0].StartsWith(IndentSpaces, StringComparison.Ordinal))
2730
{
28-
if (codeLine[index] != ' ')
31+
for (var i = 0; i < codeLines.Length; i++)
2932
{
30-
return false;
33+
codeLines[i] = codeLines[i].Substring(IndentSpaces.Length);
3134
}
3235
}
3336

34-
return true;
37+
var lastNonIndentedCodeLine = codeLines
38+
.Last(line => line.IsNonIndentedCodeLine());
39+
40+
if ((lastNonIndentedCodeLine == "catch") ||
41+
lastNonIndentedCodeLine.StartsWith("if ", StringComparison.Ordinal) ||
42+
lastNonIndentedCodeLine.StartsWith("while ", StringComparison.Ordinal) ||
43+
(lastNonIndentedCodeLine == "finally") ||
44+
lastNonIndentedCodeLine.StartsWith("else if ", StringComparison.Ordinal) ||
45+
(lastNonIndentedCodeLine == "else") ||
46+
lastNonIndentedCodeLine.StartsWith("switch ", StringComparison.Ordinal))
47+
{
48+
return true;
49+
}
50+
51+
return false;
52+
}
53+
54+
public static bool IsNonIndentedCodeLine(this string line)
55+
{
56+
return line.IsNotIndented() && !line.StartsWith('{') && !line.StartsWith('}');
3557
}
3658

3759
public static string Unterminated(this string codeLine)

ReadableExpressions/Translators/Formatting/CodeBlock.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static bool IsSingleStatement(IList<string> blockLines)
3434

3535
var numberOfNonIndentedLines = blockLines[0]
3636
.SplitToLines()
37-
.Count(RelevantNonIndentedLine);
37+
.Count(line => line.IsNonIndentedCodeLine());
3838

3939
return numberOfNonIndentedLines == 1;
4040
}
@@ -199,11 +199,6 @@ private void AddSemiColonIfRequired()
199199
}
200200
}
201201

202-
private static bool RelevantNonIndentedLine(string line)
203-
{
204-
return line.IsNotIndented() && !line.StartsWith('{') && !line.StartsWith('}');
205-
}
206-
207202
private class BlockInfo
208203
{
209204
private readonly string[] _blockLines;
@@ -217,7 +212,7 @@ public BlockInfo(string[] blockLines)
217212
_lastNonIndentedStatement = blockLines.Last(line => line.IsNotIndented());
218213

219214
_lastStatementLines = _lastNonIndentedStatement.SplitToLines();
220-
_lastNonIndentedLine = _lastStatementLines.Last(RelevantNonIndentedLine);
215+
_lastNonIndentedLine = _lastStatementLines.Last(line => line.IsNonIndentedCodeLine());
221216
}
222217

223218
public bool LastLineHasReturnKeyword =>

0 commit comments

Comments
 (0)