Skip to content

// in an interpolated string's format specifier ({d:yyyy//MM}) is read as a comment: an expression body gets its ; after a trailing comment, and verbatim string lines get re-indented #165

Closed

Description

@matt-edmondson

What happens

Two recent fixes depend on VerbatimStringScanner to find where code ends and strings or comments begin:

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

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";
		}


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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions