Skip to content
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

Errors: Changes reporting and handling #84

Merged
merged 1 commit into from
Apr 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions lib/syntax_tree/erb/nodes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -370,10 +370,20 @@ def prepare_content(content)
end
rescue SyntaxTree::Parser::ParseError
# Try to add the keyword to see if it parses
result = ErbContent.new(value: [keyword, *content])
@keyword = nil

result
begin
result = ErbContent.new(value: [keyword, *content])
@keyword = nil

result
rescue SyntaxTree::Parser::ParseError => error
raise(
SyntaxTree::Parser::ParseError.new(
"Could not parse ERB-tag: #{error.message}",
@opening_tag.location.start_line + error.lineno - 1,
error.column
)
)
end
end
end

Expand Down
56 changes: 18 additions & 38 deletions lib/syntax_tree/erb/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def parse_any_tag
if @found_doctype
raise(
SyntaxTree::Parser::ParseError.new(
"Only one doctype element is allowed",
"Duplicate doctype declaration",
tag.location.start_line,
0
)
Expand Down Expand Up @@ -129,7 +129,7 @@ def make_tokens
else
raise(
SyntaxTree::Parser::ParseError.new(
"Unexpected character at #{index}: #{source[index]}",
"Unexpected character: #{source[index]}",
line,
0
)
Expand Down Expand Up @@ -218,7 +218,7 @@ def make_tokens
else
raise(
SyntaxTree::Parser::ParseError.new(
"Unexpected character in string at #{index}: #{source[index]}",
"Unexpected character, #{source[index]}, when looking for closing single quote",
line,
0
)
Expand Down Expand Up @@ -246,7 +246,7 @@ def make_tokens
else
raise(
SyntaxTree::Parser::ParseError.new(
"Unexpected character in string at #{index}: #{source[index]}",
"Unexpected character, #{source[index]}, when looking for closing double quote",
line,
0
)
Expand Down Expand Up @@ -305,7 +305,7 @@ def make_tokens
else
raise(
SyntaxTree::Parser::ParseError.new(
"Unexpected character at #{index}: #{source[index]}",
"Unexpected character, #{source[index]}, when parsing HTML- or ERB-tag",
line,
0
)
Expand Down Expand Up @@ -406,7 +406,7 @@ def parse_html_opening_tag
if name.value =~ /\A[@:#]/
raise(
SyntaxTree::Parser::ParseError.new(
"Invalid html-tag name #{name}",
"Invalid HTML-tag name #{name.value}",
name.location.start_line,
0
)
Expand Down Expand Up @@ -470,7 +470,7 @@ def parse_html_element
if closing.nil?
raise(
SyntaxTree::Parser::ParseError.new(
"Missing closing tag for <#{opening.name.value}> at #{opening.location}",
"Missing closing tag for <#{opening.name.value}>",
opening.location.start_line,
0
)
Expand All @@ -480,7 +480,7 @@ def parse_html_element
if closing.name.value != opening.name.value
raise(
SyntaxTree::Parser::ParseError.new(
"Expected closing tag for <#{opening.name.value}> but got <#{closing.name.value}> at #{closing.location}",
"Expected closing tag for <#{opening.name.value}> but got <#{closing.name.value}>",
closing.location.start_line,
0
)
Expand All @@ -505,10 +505,11 @@ def parse_erb_case(erb_node)

unless erb_tag.is_a?(ErbCaseWhen) || erb_tag.is_a?(ErbElse) ||
erb_tag.is_a?(ErbEnd)
location = erb_tag&.location || erb_node.location
raise(
SyntaxTree::Parser::ParseError.new(
"Found no matching erb-tag to the if-tag at #{erb_node.location}",
erb_node.location.start_line,
"No matching ERB-tag for the <% #{erb_node.keyword.value} %>",
location.start_line,
0
)
)
Expand All @@ -532,7 +533,7 @@ def parse_erb_case(erb_node)
else
raise(
SyntaxTree::Parser::ParseError.new(
"Found no matching when- or else-tag to the case-tag at #{erb_node.location}",
"No matching when- or else-tag for the case-tag",
erb_node.location.start_line,
0
)
Expand All @@ -552,7 +553,7 @@ def parse_erb_if(erb_node)
unless erb_tag.is_a?(ErbControl) || erb_tag.is_a?(ErbEnd)
raise(
SyntaxTree::Parser::ParseError.new(
"Found no matching erb-tag to the if-tag at #{erb_node.location}",
"No matching ERB-tag for the <% if %>",
erb_node.location.start_line,
0
)
Expand Down Expand Up @@ -584,7 +585,7 @@ def parse_erb_if(erb_node)
else
raise(
SyntaxTree::Parser::ParseError.new(
"Found no matching elsif- or else-tag to the if-tag at #{erb_node.location}",
"No matching <% elsif %> or <% else %> for the <% if %>",
erb_node.location.start_line,
0
)
Expand All @@ -600,7 +601,7 @@ def parse_erb_else(erb_node)
unless erb_end.is_a?(ErbEnd)
raise(
SyntaxTree::Parser::ParseError.new(
"Found no matching end-tag for the else-tag at #{erb_node.location}",
"No matching <% end %> for the <% else %>",
erb_node.location.start_line,
0
)
Expand Down Expand Up @@ -642,8 +643,8 @@ def parse_erb_tag
if !closing_tag.is_a?(ErbClose)
raise(
SyntaxTree::Parser::ParseError.new(
"Found no matching closing tag for the erb-tag at #{opening_tag.location}",
opening_tag.location.start_line,
"No matching closing tag for the <% #{keyword.value} %>",
closing_tag.location.start_line,
0
)
)
Expand Down Expand Up @@ -678,7 +679,7 @@ def parse_erb_tag
unless erb_end.is_a?(ErbEnd)
raise(
SyntaxTree::Parser::ParseError.new(
"Found no matching end-tag for the do-tag at #{erb_node.location}",
"No matching <% end %> for the <% do %>",
erb_node.location.start_line,
0
)
Expand All @@ -695,27 +696,6 @@ def parse_erb_tag
erb_node
end
end
rescue SyntaxTree::Parser::ParseError => error
# If we have parsed tokens that we cannot process after we parsed <%, we should throw a ParseError
# and not let it be handled by a `maybe`.
if opening_tag
message =
if error.message.include?("Could not parse ERB-tag")
error.message
else
"Could not parse ERB-tag: #{error.message}"
end

raise(
SyntaxTree::Parser::ParseError.new(
message,
opening_tag.location.start_line,
0
)
)
else
raise(error)
end
end

def parse_until_erb_close
Expand Down
87 changes: 68 additions & 19 deletions test/erb_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,47 @@ def test_empty_file
end

def test_missing_erb_end_tag
assert_raises(SyntaxTree::Parser::ParseError) do
ERB.parse("<% if no_end_tag %>")
end
example = <<-HTML
<ul>
<% if condition %>
<li>A</li>
<li>B</li>
<li><%= "C" %></li>
</ul>
HTML
ERB.parse(example)
rescue SyntaxTree::Parser::ParseError => error
assert_equal(2, error.lineno)
assert_equal(0, error.column)
assert_match(/No matching ERB-tag for the <% if %>/, error.message)
end

def test_missing_erb_block_end_tag
assert_raises(SyntaxTree::Parser::ParseError) do
ERB.parse("<% no_end_tag do %>")
end
example = <<-HTML
<% no_end_tag do %>
<h1>What</h1>
HTML
ERB.parse(example)
rescue SyntaxTree::Parser::ParseError => error
assert_equal(1, error.lineno)
assert_equal(0, error.column)
assert_match(/No matching <% end %> for the <% do %>/, error.message)
end

def test_missing_erb_case_end_tag
assert_raises(SyntaxTree::Parser::ParseError) do
ERB.parse("<% case variabel %>\n<% when 1>\n Hello\n")
end
example = <<-HTML
<% case variabel %>
<% when 1 %>
Hello
<% when 2 %>
World
<h1>What</h1>
HTML
ERB.parse(example)
rescue SyntaxTree::Parser::ParseError => error
assert_equal(4, error.lineno)
assert_equal(0, error.column)
assert_match(/No matching ERB-tag for the <% when %>/, error.message)
end

def test_erb_code_with_non_ascii
Expand All @@ -35,19 +61,42 @@ def test_erb_code_with_non_ascii
assert_instance_of(SyntaxTree::ERB::ErbNode, parsed.elements.first)
end

def test_erb_errors
def test_erb_syntax_error
example = <<-HTML
<ul>
<% if @items.each do |i|%>
<li><%= i %></li>
<% end.blank? %>
<li>No items</li>
<% end %>
</ul>
HTML
<ul>
<% if @items.each do |i|%>
<li><%= i %></li>
<% end.blank? %>
<li>No items</li>
<% end %>
</ul>
HTML
ERB.parse(example)
rescue SyntaxTree::Parser::ParseError => error
assert_equal(2, error.lineno)
assert_equal(4, error.lineno)
assert_equal(0, error.column)
assert_match(/Could not parse ERB-tag/, error.message)
end

def test_erb_syntax_error2
example = <<-HTML
<%= content_tag :header do %>
<div class="flex-1 min-w-0">
<h2>
<%= yield :page_header %>
</h2>
<%= content_tag :div do %>
<%= yield :page_subheader %>
<% end if content_for?(:page_subheader) %>
</div>
<%= content_tag :div do %>
<%= yield :page_actions %>
<% end if content_for?(:page_actions) %>
<% end if content_for?(:page_header) %>
HTML
ERB.parse(example)
rescue SyntaxTree::Parser::ParseError => error
assert_equal(8, error.lineno)
assert_equal(0, error.column)
assert_match(/Could not parse ERB-tag/, error.message)
end
Expand Down
Loading