From 9f1bd7aebd489b492d9cc5273461de3d126ce44d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 2 Dec 2025 16:09:23 -0800 Subject: [PATCH] Set "trailing" on the comment node Comment nodes can know if they are trailing comments by looking at the previous token. If we set this on the comment object then we don't need to look at source slices when attaching comments --- ext/prism/extension.c | 7 +++++-- include/prism/parser.h | 2 ++ lib/prism/parse_result.rb | 20 ++++++++------------ src/prism.c | 11 ++++++++--- templates/lib/prism/serialize.rb.erb | 8 ++++++-- templates/src/serialize.c.erb | 3 +++ 6 files changed, 32 insertions(+), 19 deletions(-) diff --git a/ext/prism/extension.c b/ext/prism/extension.c index 71c2d91b98..0fde54f4d1 100644 --- a/ext/prism/extension.c +++ b/ext/prism/extension.c @@ -471,9 +471,12 @@ parser_location(const pm_parser_t *parser, VALUE source, bool freeze, const uint */ static inline VALUE parser_comment(const pm_parser_t *parser, VALUE source, bool freeze, const pm_comment_t *comment) { - VALUE argv[] = { PARSER_LOCATION_LOC(parser, source, freeze, comment->location) }; + VALUE argv[] = { + comment->trailing ? Qtrue : Qfalse, + PARSER_LOCATION_LOC(parser, source, freeze, comment->location) + }; VALUE type = (comment->type == PM_COMMENT_EMBDOC) ? rb_cPrismEmbDocComment : rb_cPrismInlineComment; - return rb_class_new_instance_freeze(1, argv, type, freeze); + return rb_class_new_instance_freeze(2, argv, type, freeze); } /** diff --git a/include/prism/parser.h b/include/prism/parser.h index 95d7aac710..70dadadae3 100644 --- a/include/prism/parser.h +++ b/include/prism/parser.h @@ -467,6 +467,8 @@ typedef struct pm_comment { /** The type of comment that we've found. */ pm_comment_type_t type; + + bool trailing; } pm_comment_t; /** diff --git a/lib/prism/parse_result.rb b/lib/prism/parse_result.rb index 3570af136a..9df46cd8af 100644 --- a/lib/prism/parse_result.rb +++ b/lib/prism/parse_result.rb @@ -513,8 +513,15 @@ class Comment # The location of this comment in the source. attr_reader :location + # Returns true if this comment happens on the same line as other code and + # false if the comment is by itself. + def trailing? + @trailing + end + # Create a new comment object with the given location. - def initialize(location) + def initialize(trailing, location) + @trailing = trailing @location = location end @@ -532,12 +539,6 @@ def slice # InlineComment objects are the most common. They correspond to comments in # the source file like this one that start with #. class InlineComment < Comment - # Returns true if this comment happens on the same line as other code and - # false if the comment is by itself. - def trailing? - !location.start_line_slice.strip.empty? - end - # Returns a string representation of this comment. def inspect "#" @@ -547,11 +548,6 @@ def inspect # EmbDocComment objects correspond to comments that are surrounded by =begin # and =end. class EmbDocComment < Comment - # This can only be true for inline comments. - def trailing? - false - end - # Returns a string representation of this comment. def inspect "#" diff --git a/src/prism.c b/src/prism.c index cd4d166a12..b25e321ab0 100644 --- a/src/prism.c +++ b/src/prism.c @@ -9291,12 +9291,13 @@ parser_lex_callback(pm_parser_t *parser) { * Return a new comment node of the specified type. */ static inline pm_comment_t * -parser_comment(pm_parser_t *parser, pm_comment_type_t type) { +parser_comment(pm_parser_t *parser, pm_comment_type_t type, bool trailing) { pm_comment_t *comment = (pm_comment_t *) xcalloc(1, sizeof(pm_comment_t)); if (comment == NULL) return NULL; *comment = (pm_comment_t) { .type = type, + .trailing = trailing, .location = { parser->current.start, parser->current.end } }; @@ -9324,7 +9325,7 @@ lex_embdoc(pm_parser_t *parser) { parser_lex_callback(parser); // Now, create a comment that is going to be attached to the parser. - pm_comment_t *comment = parser_comment(parser, PM_COMMENT_EMBDOC); + pm_comment_t *comment = parser_comment(parser, PM_COMMENT_EMBDOC, false); if (comment == NULL) return PM_TOKEN_EOF; // Now, loop until we find the end of the embedded documentation or the end @@ -9831,7 +9832,11 @@ parser_lex(pm_parser_t *parser) { // If we found a comment while lexing, then we're going to // add it to the list of comments in the file and keep // lexing. - pm_comment_t *comment = parser_comment(parser, PM_COMMENT_INLINE); + bool trailing = true; + if (parser->previous.type == PM_TOKEN_EOF || parser->previous.type == PM_TOKEN_NEWLINE) { + trailing = false; + } + pm_comment_t *comment = parser_comment(parser, PM_COMMENT_INLINE, trailing); pm_list_append(&parser->comment_list, (pm_list_node_t *) comment); if (ending) parser->current.end++; diff --git a/templates/lib/prism/serialize.rb.erb b/templates/lib/prism/serialize.rb.erb index 526be67431..fb207859e6 100644 --- a/templates/lib/prism/serialize.rb.erb +++ b/templates/lib/prism/serialize.rb.erb @@ -295,8 +295,8 @@ module Prism Array.new(load_varuint) do comment = case load_varuint - when 0 then InlineComment.new(load_location_object(freeze)) - when 1 then EmbDocComment.new(load_location_object(freeze)) + when 0 then InlineComment.new(load_boolean, load_location_object(freeze)) + when 1 then EmbDocComment.new(load_boolean, load_location_object(freeze)) end comment.freeze if freeze @@ -450,6 +450,10 @@ module Prism value end + def load_boolean + io.getbyte != 0 + end + def load_double io.read(8).unpack1("D") end diff --git a/templates/src/serialize.c.erb b/templates/src/serialize.c.erb index 3e15a11039..1cd3c35b1a 100644 --- a/templates/src/serialize.c.erb +++ b/templates/src/serialize.c.erb @@ -173,6 +173,9 @@ pm_serialize_comment(pm_parser_t *parser, pm_comment_t *comment, pm_buffer_t *bu // serialize type pm_buffer_append_byte(buffer, (uint8_t) comment->type); + // serialize trailing + pm_buffer_append_byte(buffer, (uint8_t) comment->trailing); + // serialize location pm_serialize_location(parser, &comment->location, buffer); }