-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow newlines in the holes inside interpolated strings #56853
Changes from all commits
8ed75ec
fe7c2d9
10a0fb5
06afc0b
3a8f99b
7dffb4b
dde9fdc
61bcfbb
bde11ad
437fe79
edd0497
da75881
9dd3189
d5eb8f6
1b05285
f0b1cd8
3ec40db
2459df5
1e445cb
65e91b2
59e4649
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,9 @@ private BoundExpression BindInterpolatedString(InterpolatedStringExpressionSynta | |
} | ||
else | ||
{ | ||
var isNonVerbatimInterpolatedString = node.StringStartToken.Kind() != SyntaxKind.InterpolatedVerbatimStringStartToken; | ||
var newLinesInInterpolationsAllowed = this.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureNewLinesInInterpolations); | ||
|
||
var intType = GetSpecialType(SpecialType.System_Int32, diagnostics, node); | ||
foreach (var content in node.Contents) | ||
{ | ||
|
@@ -37,6 +40,33 @@ private BoundExpression BindInterpolatedString(InterpolatedStringExpressionSynta | |
case SyntaxKind.Interpolation: | ||
{ | ||
var interpolation = (InterpolationSyntax)content; | ||
|
||
// If we're prior to C# 11 then we don't allow newlines in the interpolations of | ||
// non-verbatim interpolated strings. Check for that here and report an error | ||
// if the interpolation spans multiple lines (and thus must have a newline). | ||
// | ||
// Note: don't bother doing this if the interpolation is otherwise malformed or | ||
// we've already reported some other error within it. No need to spam the user | ||
// with multiple errors (esp as a malformed interpolation may commonly span multiple | ||
// lines due to error recovery). | ||
if (isNonVerbatimInterpolatedString && | ||
!interpolation.GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error) && | ||
!newLinesInInterpolationsAllowed && | ||
!interpolation.OpenBraceToken.IsMissing && | ||
!interpolation.CloseBraceToken.IsMissing) | ||
{ | ||
var text = node.SyntaxTree.GetText(); | ||
if (text.Lines.GetLineFromPosition(interpolation.OpenBraceToken.SpanStart).LineNumber != | ||
text.Lines.GetLineFromPosition(interpolation.CloseBraceToken.SpanStart).LineNumber) | ||
{ | ||
diagnostics.Add( | ||
ErrorCode.ERR_NewlinesAreNotAllowedInsideANonVerbatimInterpolatedString, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just remembered... We'll need to add this error code the the UpgradeProject fixer. That's the fixer that handles diagnostics that include a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure. |
||
interpolation.CloseBraceToken.GetLocation(), | ||
this.Compilation.LanguageVersion.ToDisplayString(), | ||
new CSharpRequiredLanguageVersion(MessageID.IDS_FeatureNewLinesInInterpolations.RequiredVersion())); | ||
} | ||
} | ||
|
||
var value = BindValue(interpolation.Expression, diagnostics, BindValueKind.RValue); | ||
|
||
// We need to ensure the argument is not a lambda, method group, etc. It isn't nice to wait until lowering, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -137,7 +137,7 @@ private ExpressionSyntax ParseInterpolatedStringToken() | |
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. view PR with whitespace changes off. |
||
|
||
// Add a token for text following the last interpolation | ||
var lastText = Substring(originalText, interpolations[interpolations.Count - 1].CloseBracePosition + 1, closeQuoteIndex - 1); | ||
var lastText = Substring(originalText, interpolations[^1].CloseBracePosition + 1, closeQuoteIndex - 1); | ||
if (lastText.Length > 0) | ||
{ | ||
var token = MakeStringToken(lastText, lastText, isVerbatim, SyntaxKind.InterpolatedStringTextToken); | ||
|
@@ -171,29 +171,26 @@ private InterpolationSyntax ParseInterpolation(string text, Lexer.Interpolation | |
using (var tempLexer = new Lexer(Text.SourceText.From(parsedText), this.Options, allowPreprocessorDirectives: false, interpolationFollowedByColon: interpolation.HasColon)) | ||
{ | ||
// TODO: some of the trivia in the interpolation maybe should be trailing trivia of the openBraceToken | ||
using (var tempParser = new LanguageParser(tempLexer, null, null)) | ||
using var tempParser = new LanguageParser(tempLexer, oldTree: null, changes: null); | ||
|
||
tempParser.ParseInterpolationStart(out openBraceToken, out expression, out var commaToken, out var alignmentExpression); | ||
if (alignmentExpression != null) | ||
{ | ||
SyntaxToken commaToken = null; | ||
ExpressionSyntax alignmentExpression = null; | ||
tempParser.ParseInterpolationStart(out openBraceToken, out expression, out commaToken, out alignmentExpression); | ||
if (alignmentExpression != null) | ||
{ | ||
alignment = SyntaxFactory.InterpolationAlignmentClause(commaToken, alignmentExpression); | ||
} | ||
alignment = SyntaxFactory.InterpolationAlignmentClause(commaToken, alignmentExpression); | ||
} | ||
|
||
var extraTrivia = tempParser.CurrentToken.GetLeadingTrivia(); | ||
if (interpolation.HasColon) | ||
{ | ||
var colonToken = SyntaxFactory.Token(SyntaxKind.ColonToken).TokenWithLeadingTrivia(extraTrivia); | ||
var formatText = Substring(text, interpolation.ColonPosition + 1, interpolation.FormatEndPosition); | ||
var formatString = MakeStringToken(formatText, formatText, isVerbatim, SyntaxKind.InterpolatedStringTextToken); | ||
format = SyntaxFactory.InterpolationFormatClause(colonToken, formatString); | ||
} | ||
else | ||
{ | ||
// Move the leading trivia from the insertion's EOF token to the following token. | ||
closeBraceToken = closeBraceToken.TokenWithLeadingTrivia(extraTrivia); | ||
} | ||
var extraTrivia = tempParser.CurrentToken.GetLeadingTrivia(); | ||
if (interpolation.HasColon) | ||
{ | ||
var colonToken = SyntaxFactory.Token(SyntaxKind.ColonToken).TokenWithLeadingTrivia(extraTrivia); | ||
var formatText = Substring(text, interpolation.ColonPosition + 1, interpolation.FormatEndPosition); | ||
var formatString = MakeStringToken(formatText, formatText, isVerbatim, SyntaxKind.InterpolatedStringTextToken); | ||
format = SyntaxFactory.InterpolationFormatClause(colonToken, formatString); | ||
} | ||
else | ||
{ | ||
// Move the leading trivia from the insertion's EOF token to the following token. | ||
closeBraceToken = closeBraceToken.TokenWithLeadingTrivia(extraTrivia); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: i can also do this by just walking the tree. up to you if want me to go that route or not. this is def the easiest and most fool proof mechanism to determine this though.