Handle #line directives in get_source_expressions() (#3106) - #3109
Handle #line directives in get_source_expressions() (#3106)#3109youdie006 wants to merge 1 commit into
Conversation
A #line directive resets the R parser line numbering. As a result getParseData() can report a LINE_DIRECTIVE with parent 0, so top_level_expressions() counts it as a top-level expression, while xmlparsedata does not always surface it as a top-level node. The two lists then desync and maybe_append_expression_xml() crashes with "subscript out of bounds" when zipping them. Exclude LINE_DIRECTIVE rows from top_level_expressions() and the matching /exprlist/* XML nodes, keeping the two lists aligned. A #line directive is a parser directive, not an expression, so dropping it loses nothing. Fixes r-lib#3106
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3109 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 129 129
Lines 7468 7468
=========================================
Hits 7468 7468 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
MichaelChirico
left a comment
There was a problem hiding this comment.
thanks! I don't think this is the right direction to go. I think we should basically try and overcome the reported R bug and treat LINE_DIRECTIVE like a COMMENT whenever we see it. That's ~basically what the parser itself wants to do -- note that getParseData doesn't attempt to interpret the line/file info at all, for example.
One option is to try and get equivalent behavior to my proposed R patch ex-post in get_source_expressions by fixing up parent as-if it were a COMMENT.
But I think probably the best approach for now is wait-and-see -- I'd like to have R core weigh in on the bug first before fixing a path forward for lintr since this case is a little bit ambiguous.
|
Thanks for the review and the context, that's helpful. Treating Agreed on wait-and-see until R core weighs in, given the ambiguity. I'll leave this open and can either update it to the COMMENT-reparent approach or close it in favor of your patch, whichever you prefer once there's a direction. |
Fixes #3106.
Root cause
A
#linedirective resets the R parser's line numbering. Because of that,getParseData()can report theLINE_DIRECTIVErow withparent == 0, sotop_level_expressions()(which iswhich(pc$parent <= 0L)) counts it as a top-level expression. Butxmlparsedatadoes not always surface the directive as a top-level/exprlist/*node. The two lists then have different lengths, andmaybe_append_expression_xml()zips them positionally:Repro from the issue:
I confirmed the desync directly: for that input
top_level_expressions()returns 2 rows (expr+LINE_DIRECTIVE) while/exprlist/*returns 1 (expr).Fix
LINE_DIRECTIVEis a parser directive, not an expression, so it should never be paired with an XML expression node. I exclude it on both sides so the two lists stay aligned in every configuration:top_level_expressions():which(pc$parent <= 0L & pc$token != "LINE_DIRECTIVE")/exprlist/*[not(self::LINE_DIRECTIVE)]Excluding it on only one side would re-introduce a desync in the plain top-level
#linecase (wherexmlparsedatadoes list the directive), so both are needed. I verified alignment (and that linting still works and reports physical line numbers) across: a#lineinside a block, a#lineat top level, multiple#linedirectives, plain comments, and no directive.This follows your suggestion in the issue to "add it to the set of things that we fix up coming from the R parser." Happy to adjust if you'd prefer neutralizing the directive earlier in the pipeline instead.
Test
Added a regression test in
test-get_source_expressions.R. Red-green verified withdevtools::test(filter = "get_source_expressions"): before the change the new test errors withsubscript out of bounds; after, the whole file passes.lintr::lint()on the changed files is clean;NEWS.mdupdated.Disclosure: I used AI assistance (Claude) while preparing this change. I root-caused the desync, ran the tests (red-green), and take responsibility for the contribution.