-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Enhanced #line directives #54489
Enhanced #line directives #54489
Conversation
Please add an entry to feature status page with a test plan link. Thanks In reply to: 871838112 |
result = (this.CurrentToken.Kind == SyntaxKind.OpenParenToken) ? | ||
this.ParseLineSpanDirective(hash, lineKeyword, isActive) : | ||
this.ParseLineDirective(hash, lineKeyword, isActive); |
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.
nit: consider placing ?: tokens at the beginning of the line
result = (this.CurrentToken.Kind == SyntaxKind.OpenParenToken) ? | |
this.ParseLineSpanDirective(hash, lineKeyword, isActive) : | |
this.ParseLineDirective(hash, lineKeyword, isActive); | |
result = (this.CurrentToken.Kind == SyntaxKind.OpenParenToken) | |
? this.ParseLineSpanDirective(hash, lineKeyword, isActive) | |
: this.ParseLineDirective(hash, lineKeyword, isActive); |
var mappedLine = (previous.State == PositionState.RemappedSpan) ? | ||
unmappedLine : | ||
previous.MappedLine + directiveLineNumber - previous.UnmappedLine; |
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.
var mappedLine = (previous.State == PositionState.RemappedSpan) ? | |
unmappedLine : | |
previous.MappedLine + directiveLineNumber - previous.UnmappedLine; | |
var mappedLine = (previous.State == PositionState.RemappedSpan) | |
? unmappedLine | |
: previous.MappedLine + directiveLineNumber - previous.UnmappedLine; |
/// <summary> | ||
/// The optional offset in the syntax tree for the line immediately following an enhanced <c>#line</c> directive in C#. | ||
/// </summary> | ||
public readonly int? CharacterOffset { get; } |
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.
did API review have any conclusion about the choice to make this int?
versus int
with a special "not specified" or default value?
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.
This wasn't resolved at the API review (see comment). We might need to close on this separately.
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.
Let's address after preview2.
src/Compilers/CSharp/Test/Syntax/Parsing/LineSpanDirectiveParsingTests.cs
Show resolved
Hide resolved
Looking |
var comma = EatToken(SyntaxKind.CommaToken, reportError); | ||
if (comma.IsMissing) reportError = false; | ||
|
||
var character = ParseNumericLiteral(reportError); |
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.
The error we'll report here will be ERR_InvalidLineNumber
(" error CS1576: The line number specified for #line directive is missing or invalid") but this number doesn't refer to a line #Resolved
var openParen = EatToken(SyntaxKind.OpenParenToken, reportError); | ||
if (openParen.IsMissing) reportError = false; | ||
|
||
var line = ParseNumericLiteral(reportError); |
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.
It looks like we have some validation for original #line
directives, possibly reporting ERR_InvalidLineNumber
or WRN_TooManyLinesForDebugger
. Feels like we should keep those #Closed
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.
Another rule from the spec: [if] start line is equal to end line then end character is greater than start character.
var end = ParseLineDirectivePosition(ref reportError); | ||
var characterOffset = (reportError && CurrentToken.Kind == SyntaxKind.NumericLiteralToken) ? this.EatToken() : null; | ||
|
||
var file = EatToken(SyntaxKind.StringLiteralToken, ErrorCode.ERR_MissingPPFile, reportError: reportError); |
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.
From offline discussion, we probably need something like line 389 above
@@ -78,10 +92,26 @@ public LineMappingEntry(int unmappedLine) | |||
{ | |||
this.UnmappedLine = unmappedLine; | |||
this.MappedLine = mappedLine; | |||
this.MappedSpan = default; | |||
this.UnmappedCharacterOffset = null; | |||
this.MappedPathOpt = mappedPathOpt; | |||
this.State = state; |
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.
Consider asserting that the state isn't PositionState.RemappedSpan
#Closed
@@ -21,69 +21,143 @@ public CSharpLineDirectiveMap(SyntaxTree syntaxTree) | |||
// Add all active #line directives under trivia into the list, in source code order. | |||
protected override bool ShouldAddDirective(DirectiveTriviaSyntax directive) | |||
{ | |||
return directive.IsActive && directive.Kind() == SyntaxKind.LineDirectiveTrivia; | |||
return directive.IsActive && (directive.Kind() is SyntaxKind.LineDirectiveTrivia or SyntaxKind.LineSpanDirectiveTrivia); |
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.
There's a couple references to SyntaxKind.LineDirectiveTrivia in the IDE layer (relates to classification, in ClassifyTrivia and ClassifyPreprocessorDirective) and one to LineDirectiveTriviaSyntax in CSharpSyntaxFacts.TryGetExternalSourceInfo which will probably need looking at. #Pending
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.
Thanks, added to the test plan.
case LineDirectiveTriviaSyntax lineDirective: | ||
{ | ||
var mappedLine = (previous.State == PositionState.RemappedSpan) ? | ||
unmappedLine : |
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.
I didn't understand this. It seems strange to use something from "unmapped" domain directly in "mapped" domain without any sort of mapping.
Consider adding a comment: "only used in error cases" #Closed
throw ExceptionUtilities.UnexpectedValue(directive); | ||
} | ||
|
||
static bool tryGetOneBasedNumericLiteralValue(in SyntaxToken token, ref int value) |
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.
Consider making this an out
instead #Resolved
|
||
static bool tryGetOneBasedNumericLiteralValue(in SyntaxToken token, ref int value) | ||
{ | ||
if (!token.IsMissing && |
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.
It looks like these two checks (IsMissing
and Kind() == NumericLiteralToken
) were not in the original source code. Do we need them?
I'm fine if it's for extra safety... #Closed
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.
It looks like these two checks (
IsMissing
andKind() == NumericLiteralToken
) were not in the original source code. Do we need them?
The original checks are still there above the call to the local function, so these are redundant. In fact, the checks might be redundant from each of the callers but it seems useful for safety as you suggested.
if (entry.State == PositionState.Hidden || | ||
entry.State == PositionState.Unknown && GetUnknownStateVisibility(currentIndex) == LineVisibility.Hidden) | ||
{ | ||
return new LineMapping(unmapped, null, default); |
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.
nit: consider naming last two args #Closed
EOF(); | ||
} | ||
} | ||
} |
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.
nit: Consider adding a ParseIncompleteSyntax
test (like ParseIncompleteRecordSyntax
) #Closed
int value = (int)token.Value; | ||
if (value < minValue || value > maxValue) | ||
{ | ||
token = this.AddError(token, ErrorCode.ERR_LineSpanDirectiveInvalidValue); |
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.
For old #line
directives, one of these scenarios is just a warning: WRN_TooManyLinesForDebugger
. Do we want to maintain that?
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.
Done with review pass (iteration 6)
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.
LGTM Thanks (iteration 8)
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.
LGTM Thanks (iteration 9)
Marked as "Approved to merge" in @jaredpar's absence, given compiler feature review and IDE approval (blocking formatting issue was fixed). |
#line (startLine, startChar) - (endLine, endChar) charOffset "fileName"
Map lines after the
#line
directive to the span(startLine, startChar) - (endLine, endChar)
in"fileName"
where the origin of the unmapped span is the first line after the directive at charactercharOffset
.Proposal: dotnet/csharplang#4747
Test plan: #54509