Skip to content

Commit

Permalink
Fix constants with heredoc values
Browse files Browse the repository at this point in the history
Signed-off-by: Alexandre Terrasa <alexandre.terrasa@shopify.com>
  • Loading branch information
Morriar committed Feb 17, 2025
1 parent 951e2cb commit 4f02508
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 22 deletions.
100 changes: 78 additions & 22 deletions lib/rbi/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,17 @@ def node_string(node)
def node_string!(node)
T.must(node_string(node))
end

sig { params(node: Prism::Node).returns(Prism::Location) }
def adjust_prism_location_for_heredoc(node)
visitor = HeredocLocationVisitor.new(
node.location.send(:source),
node.location.start_offset,
node.location.end_offset,
)
visitor.visit(node)
visitor.location
end
end

class TreeBuilder < Visitor
Expand Down Expand Up @@ -226,30 +237,40 @@ def visit_constant_assign(node)

current_scope << if struct
struct
elsif type_variable_definition?(node.value)
TypeMember.new(
case node
when Prism::ConstantWriteNode
node.name.to_s
when Prism::ConstantPathWriteNode
node_string!(node.target)
end,
node_string!(node.value),
loc: node_loc(node),
comments: node_comments(node),
)
else
Const.new(
case node
when Prism::ConstantWriteNode
node.name.to_s
when Prism::ConstantPathWriteNode
node_string!(node.target)
end,
node_string!(node.value),
loc: node_loc(node),
comments: node_comments(node),
adjusted_node_location = adjust_prism_location_for_heredoc(node)

adjusted_value_location = Prism::Location.new(
node.value.location.send(:source),
node.value.location.start_offset,
adjusted_node_location.end_offset - node.value.location.start_offset,
)

if type_variable_definition?(node.value)
TypeMember.new(
case node
when Prism::ConstantWriteNode
node.name.to_s
when Prism::ConstantPathWriteNode
node_string!(node.target)
end,
adjusted_value_location.slice,
loc: Loc.from_prism(@file, adjusted_node_location),
comments: node_comments(node),
)
else
Const.new(
case node
when Prism::ConstantWriteNode
node.name.to_s
when Prism::ConstantPathWriteNode
node_string!(node.target)
end,
adjusted_value_location.slice,
loc: Loc.from_prism(@file, adjusted_node_location),
comments: node_comments(node),
)
end
end
end

Expand Down Expand Up @@ -900,5 +921,40 @@ def visit_assoc_node(node)
)
end
end

class HeredocLocationVisitor < Prism::Visitor
extend T::Sig

sig { params(source: Prism::Source, begin_offset: Integer, end_offset: Integer).void }
def initialize(source, begin_offset, end_offset)
super()
@source = source
@begin_offset = begin_offset
@end_offset = end_offset
@offset_last_newline = T.let(false, T::Boolean)
end

sig { override.params(node: Prism::StringNode).void }
def visit_string_node(node)
return unless node.heredoc?

closing_loc = node.closing_loc
return unless closing_loc

if closing_loc.end_offset > @end_offset
@end_offset = closing_loc.end_offset
@offset_last_newline = true if node.content.end_with?("\n")
end
end

sig { returns(Prism::Location) }
def location
Prism::Location.new(
@source,
@begin_offset,
@end_offset - @begin_offset - (@offset_last_newline ? 1 : 0),
)
end
end
end
end
47 changes: 47 additions & 0 deletions test/rbi/parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,53 @@ def test_parse_constants
assert_equal(rbi, tree.string)
end

def test_parse_constants_with_heredoc
rbi = <<~RBI
A = <<-EOF
foo
foo
EOF
B = <<~EOF.strip
bar
EOF
C = <<-'EOF'
baz
EOF
D = %Q(
qux
)
E = T.let(<<~EOF, String)
foo
EOF
F = "foo" \\
"bar"
RBI

tree = parse_rbi(rbi)
assert_equal(rbi, tree.string)
end

def test_parse_constants_multiline_calls
rbi = <<~RBI
A = foo(
<<~EOF,
bar
EOF
baz,
)
RBI

tree = parse_rbi(rbi)
assert_equal(rbi, tree.string)
end

def test_parse_constants_with_newlines
rbi = <<~RBI
sig { returns(Foo::
Expand Down

0 comments on commit 4f02508

Please sign in to comment.