Repository navigation
Description
What happens
Two recent fixes depend on VerbatimStringScanner to find where code ends and strings or comments begin:
- An expression body whose last line ends in a // comment gets its semicolon inside the comment:
public int P => x // note;does not compile (CS1002) #144 puts an expression body's;before a trailing line comment. - Template bodies re-indent every line of a multi-line verbatim string, silently changing the generated string's value #121 leaves the continuation lines of a verbatim string unindented.
The scanner treats the inside of an interpolation hole as code. In ScanCode → TrySkipCommentOrLiteral, a // inside a hole starts a line comment. But everything after a top-level : in a hole is a format specifier, not code. In {d:yyyy//MM}, the // is just part of the date format. Reading it as a comment swallows the hole's closing } and the string's closing ", so the scanner loses its place in the line.
The code is at
CodeBlocker/CodeBlocker/Templates/VerbatimStringScanner.cs
Lines 79 to 96 in 21ac04c
| if (isInterpolationHole && IsClosingBrace(text[position], ref braceDepth)) | |
| { | |
| return; | |
| } | |
| position++; | |
| } | |
| } | |
| /// <summary> | |
| /// Moves past the comment or literal that starts at <paramref name="position"/>, if one does. | |
| /// </summary> | |
| private static bool TrySkipCommentOrLiteral(string text, ref int position, bool[] inVerbatimText) | |
| { | |
| char c = text[position]; | |
| if (c == '/' && At(text, position + 1) == '/') | |
| { | |
| SkipLineComment(text, ref position); |
Repro
Run against main at 21ac04c, and parsed with Roslyn.
A. An expression body with a trailing comment
new PropertyTemplate { Name = "P", Type = "object",
ExpressionBodyFactory = b => b.Write("$\"{System.DateTime.Now:yyyy//MM}\" // c") }- Output:
object P => $"{System.DateTime.Now:yyyy//MM}" // c; - Roslyn:
error CS1002: ; expected, because the;is now inside the comment. - Expected output:
object P => $"{System.DateTime.Now:yyyy//MM}"; // c
B. A verbatim interpolated string over two lines in a method body
The body writes return $@"{d:yyyy//MM} followed by line2";. Output:
string M()
{
var d = System.DateTime.Now;
return $@"{d:yyyy//MM}
line2";
}
line2has been indented, which changes the value of the string. That is the Template bodies re-indent every line of a multi-line verbatim string, silently changing the generated string's value #121 bug coming back.- The method is followed by an extra blank line.
The same body written with {d:yyyy/MM} comes out correctly: line2 stays at its original column, and only one blank line follows the method.
Why it matters
Date and time format strings with slashes are an ordinary input. When one appears, the generator either produces code that doesn't compile (A) or quietly changes a string literal's value (B). Neither problem shows up when the code is generated.
Suggested fix
While scanning an interpolation hole, keep a depth count of (, [ and {. Once a : appears at depth 0, skip raw characters up to the hole's closing }, with no comment, char or string detection. The : must not be part of a :: alias qualifier, and must not be inside a nested conditional ? :, which already sits inside parentheses at depth 1 or more.
Acceptance criteria
- Repro A ends in
"; // cand compiles. - In repro B,
line2is left as written. {x:yyyy//MM}and{x:yyyy/MM}produce the same output layout.- Existing tests for Template bodies re-indent every line of a multi-line verbatim string, silently changing the generated string's value #121 and An expression body whose last line ends in a // comment gets its semicolon inside the comment:
public int P => x // note;does not compile (CS1002) #144 still pass.
Activity