Unify Error and HamlError

as requested by my old TODO comment.

Related to https://github.com/haml/haml/issues/1082, while not
addressing the issue yet.
This commit is contained in:
Takashi Kokubun 2022-08-21 11:02:48 -07:00
parent 84cc3edf42
commit 23554f73ca
No known key found for this signature in database
GPG Key ID: 6FFC433B12EE23DD
6 changed files with 87 additions and 103 deletions

View File

@ -22,7 +22,7 @@ module Haml
end
def call(ast)
return runtime_error(ast) if ast.is_a?(HamlError)
return runtime_error(ast) if ast.is_a?(Error)
compile(ast)
rescue Error => e
runtime_error(e)

View File

@ -1,16 +1,66 @@
# frozen_string_literal: true
module Haml
# TODO: unify Haml::Error (former Hamlit::Error) and Haml::HamlError (former Haml::Error)
class Error < StandardError
MESSAGES = {
bad_script_indent: '"%s" is indented at wrong level: expected %d, but was at %d.',
cant_run_filter: 'Can\'t run "%s" filter; you must require its dependencies first',
cant_use_tabs_and_spaces: "Indentation can't use both tabs and spaces.",
deeper_indenting: "The line was indented %d levels deeper than the previous line.",
filter_not_defined: 'Filter "%s" is not defined.',
gem_install_filter_deps: '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
illegal_element: "Illegal element: classes and ids must have values.",
illegal_nesting_content: "Illegal nesting: nesting within a tag that already has content is illegal.",
illegal_nesting_header: "Illegal nesting: nesting within a header command is illegal.",
illegal_nesting_line: "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
illegal_nesting_plain: "Illegal nesting: nesting within plain text is illegal.",
illegal_nesting_self_closing: "Illegal nesting: nesting within a self-closing tag is illegal.",
inconsistent_indentation: "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
indenting_at_start: "Indenting at the beginning of the document is illegal.",
install_haml_contrib: 'To use the "%s" filter, please install the haml-contrib gem.',
invalid_attribute_list: 'Invalid attribute list: %s.',
invalid_filter_name: 'Invalid filter name ":%s".',
invalid_tag: 'Invalid tag: "%s".',
missing_if: 'Got "%s" with no preceding "if"',
no_ruby_code: "There's no Ruby code for %s to evaluate.",
self_closing_content: "Self-closing tags can't have content.",
unbalanced_brackets: 'Unbalanced brackets.',
no_end: <<-END
You don't need to use "- end" in Haml. Un-indent to close a block:
- if foo?
%strong Foo!
- else
Not foo.
%p This line is un-indented, so it isn't part of the "if" block
END
}.freeze
def self.message(key, *args)
string = MESSAGES[key] or raise "[HAML BUG] No error messages for #{key}"
(args.empty? ? string : string % args).rstrip
end
# The line of the template on which the error occurred.
#
# @return [Fixnum]
attr_reader :line
# @param message [String] The error message
# @param line [Fixnum] See \{#line}
def initialize(message = nil, line = nil)
super(message)
@line = line
end
end
# SyntaxError is the type of exception raised when Haml encounters an
# ill-formatted document.
# It's not particularly interesting,
# except in that it's a subclass of {Haml::Error}.
class SyntaxError < Error; end
class InternalError < Error; end
# An invalid filter name was used.
class FilterNotFound < Error; end
# InternalError means that you hit a bug of Haml itself.
class InternalError < Error; end
end

View File

@ -1,66 +0,0 @@
# frozen_string_literal: true
module Haml
# An exception raised by Haml code.
# TODO: unify Haml::Error (former Hamlit::Error) and Haml::HamlError (former Haml::Error)
class HamlError < StandardError
MESSAGES = {
bad_script_indent: '"%s" is indented at wrong level: expected %d, but was at %d.',
cant_run_filter: 'Can\'t run "%s" filter; you must require its dependencies first',
cant_use_tabs_and_spaces: "Indentation can't use both tabs and spaces.",
deeper_indenting: "The line was indented %d levels deeper than the previous line.",
filter_not_defined: 'Filter "%s" is not defined.',
gem_install_filter_deps: '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
illegal_element: "Illegal element: classes and ids must have values.",
illegal_nesting_content: "Illegal nesting: nesting within a tag that already has content is illegal.",
illegal_nesting_header: "Illegal nesting: nesting within a header command is illegal.",
illegal_nesting_line: "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
illegal_nesting_plain: "Illegal nesting: nesting within plain text is illegal.",
illegal_nesting_self_closing: "Illegal nesting: nesting within a self-closing tag is illegal.",
inconsistent_indentation: "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
indenting_at_start: "Indenting at the beginning of the document is illegal.",
install_haml_contrib: 'To use the "%s" filter, please install the haml-contrib gem.',
invalid_attribute_list: 'Invalid attribute list: %s.',
invalid_filter_name: 'Invalid filter name ":%s".',
invalid_tag: 'Invalid tag: "%s".',
missing_if: 'Got "%s" with no preceding "if"',
no_ruby_code: "There's no Ruby code for %s to evaluate.",
self_closing_content: "Self-closing tags can't have content.",
unbalanced_brackets: 'Unbalanced brackets.',
no_end: <<-END
You don't need to use "- end" in Haml. Un-indent to close a block:
- if foo?
%strong Foo!
- else
Not foo.
%p This line is un-indented, so it isn't part of the "if" block
END
}.freeze
def self.message(key, *args)
string = MESSAGES[key] or raise "[HAML BUG] No error messages for #{key}"
(args.empty? ? string : string % args).rstrip
end
# The line of the template on which the error occurred.
#
# @return [Fixnum]
attr_reader :line
# @param message [String] The error message
# @param line [Fixnum] See \{#line}
def initialize(message = nil, line = nil)
super(message)
@line = line
end
end
# SyntaxError is the type of exception raised when Haml encounters an
# ill-formatted document.
# It's not particularly interesting,
# except in that it's a subclass of {Haml::HamlError}.
class HamlSyntaxError < HamlError; end
class HamlInvalidAttributeNameError < HamlSyntaxError; end
end

View File

@ -2,7 +2,7 @@
require 'ripper'
require 'strscan'
require 'haml/haml_error'
require 'haml/error'
require 'haml/util'
module Haml
@ -125,7 +125,7 @@ module Haml
@indentation = nil
@line = next_line
raise HamlSyntaxError.new(HamlError.message(:indenting_at_start), @line.index) if @line.tabs != 0
raise SyntaxError.new(Error.message(:indenting_at_start), @line.index) if @line.tabs != 0
loop do
next_line
@ -148,7 +148,7 @@ module Haml
end
if !flat? && @next_line.tabs - @line.tabs > 1
raise HamlSyntaxError.new(HamlError.message(:deeper_indenting, @next_line.tabs - @line.tabs), @next_line.index)
raise SyntaxError.new(Error.message(:deeper_indenting, @next_line.tabs - @line.tabs), @next_line.index)
end
@line = @next_line
@ -156,7 +156,7 @@ module Haml
# Close all the open tags
close until @parent.type == :root
@root
rescue Haml::HamlError => e
rescue Haml::Error => e
e.backtrace.unshift "#{@options.filename}:#{(e.line ? e.line + 1 : @line.index + 1) + @options.line - 1}"
error_with_lineno(e)
end
@ -168,7 +168,7 @@ module Haml
@indentation = line.whitespace
if @indentation.include?(?\s) && @indentation.include?(?\t)
raise HamlSyntaxError.new(HamlError.message(:cant_use_tabs_and_spaces), line.index)
raise SyntaxError.new(Error.message(:cant_use_tabs_and_spaces), line.index)
end
@flat_spaces = @indentation * (@template_tabs+1) if flat?
@ -179,11 +179,11 @@ module Haml
return tabs if line.whitespace == @indentation * tabs
return @template_tabs + 1 if flat? && line.whitespace =~ /^#{@flat_spaces}/
message = HamlError.message(:inconsistent_indentation,
message = Error.message(:inconsistent_indentation,
human_indentation(line.whitespace),
human_indentation(@indentation)
)
raise HamlSyntaxError.new(message, line.index)
raise SyntaxError.new(message, line.index)
end
private
@ -195,7 +195,7 @@ module Haml
return error unless trace
line = trace.match(/\d+\z/).to_s.to_i
HamlSyntaxError.new(error.message, line)
SyntaxError.new(error.message, line)
end
# @private
@ -320,7 +320,7 @@ module Haml
def plain(line, escape_html = nil)
if block_opened?
raise HamlSyntaxError.new(HamlError.message(:illegal_nesting_plain), @next_line.index)
raise SyntaxError.new(Error.message(:illegal_nesting_plain), @next_line.index)
end
unless Util.contains_interpolation?(line.text)
@ -333,7 +333,7 @@ module Haml
end
def script(line, escape_html = nil, preserve = false)
raise HamlSyntaxError.new(HamlError.message(:no_ruby_code, '=')) if line.text.empty?
raise SyntaxError.new(Error.message(:no_ruby_code, '=')) if line.text.empty?
line = handle_ruby_multiline(line)
escape_html = @options.escape_html if escape_html.nil?
@ -345,12 +345,12 @@ module Haml
end
def flat_script(line, escape_html = nil)
raise HamlSyntaxError.new(HamlError.message(:no_ruby_code, '~')) if line.text.empty?
raise SyntaxError.new(Error.message(:no_ruby_code, '~')) if line.text.empty?
script(line, escape_html, :preserve)
end
def silent_script(line)
raise HamlSyntaxError.new(HamlError.message(:no_end), line.index) if line.text[1..-1].strip == 'end'
raise SyntaxError.new(Error.message(:no_end), line.index) if line.text[1..-1].strip == 'end'
line = handle_ruby_multiline(line)
keyword = block_keyword(line.text)
@ -359,7 +359,7 @@ module Haml
if ["else", "elsif", "when"].include?(keyword)
if @script_level_stack.empty?
raise Haml::HamlSyntaxError.new(HamlError.message(:missing_if, keyword), @line.index)
raise Haml::SyntaxError.new(Error.message(:missing_if, keyword), @line.index)
end
if keyword == 'when' and !@script_level_stack.last[2]
@ -370,8 +370,8 @@ module Haml
end
if @script_level_stack.last[1] != @line.tabs
message = HamlError.message(:bad_script_indent, keyword, @script_level_stack.last[1], @line.tabs)
raise Haml::HamlSyntaxError.new(message, @line.index)
message = Error.message(:bad_script_indent, keyword, @script_level_stack.last[1], @line.tabs)
raise Haml::SyntaxError.new(message, @line.index)
end
end
@ -460,12 +460,12 @@ module Haml
dynamic_attributes.old = attributes_hashes[:old] unless static_attributes || @options.suppress_eval
end
raise HamlSyntaxError.new(HamlError.message(:illegal_nesting_self_closing), @next_line.index) if block_opened? && self_closing
raise HamlSyntaxError.new(HamlError.message(:no_ruby_code, action), last_line - 1) if parse && value.empty?
raise HamlSyntaxError.new(HamlError.message(:self_closing_content), last_line - 1) if self_closing && !value.empty?
raise SyntaxError.new(Error.message(:illegal_nesting_self_closing), @next_line.index) if block_opened? && self_closing
raise SyntaxError.new(Error.message(:no_ruby_code, action), last_line - 1) if parse && value.empty?
raise SyntaxError.new(Error.message(:self_closing_content), last_line - 1) if self_closing && !value.empty?
if block_opened? && !value.empty? && !is_ruby_multiline?(value)
raise HamlSyntaxError.new(HamlError.message(:illegal_nesting_line, tag_name), @next_line.index)
raise SyntaxError.new(Error.message(:illegal_nesting_line, tag_name), @next_line.index)
end
self_closing ||= !!(!block_opened? && value.empty? && @options.autoclose.any? {|t| t === tag_name})
@ -509,7 +509,7 @@ module Haml
end
if block_opened? && !text.empty?
raise HamlSyntaxError.new(Haml::HamlError.message(:illegal_nesting_content), @next_line.index)
raise SyntaxError.new(Haml::Error.message(:illegal_nesting_content), @next_line.index)
end
ParseNode.new(:comment, @line.index + 1, :conditional => conditional, :text => text, :revealed => revealed, :parse => parse)
@ -517,13 +517,13 @@ module Haml
# Renders an XHTML doctype or XML shebang.
def doctype(text)
raise HamlSyntaxError.new(HamlError.message(:illegal_nesting_header), @next_line.index) if block_opened?
raise SyntaxError.new(Error.message(:illegal_nesting_header), @next_line.index) if block_opened?
version, type, encoding = text[3..-1].strip.downcase.scan(DOCTYPE_REGEX)[0]
ParseNode.new(:doctype, @line.index + 1, :version => version, :type => type, :encoding => encoding)
end
def filter(name)
raise HamlError.new(HamlError.message(:invalid_filter_name, name)) unless name =~ /^\w+$/
raise Error.new(Error.message(:invalid_filter_name, name)) unless name =~ /^\w+$/
if filter_opened?
@flat = true
@ -621,12 +621,12 @@ module Haml
# Parses a line into tag_name, attributes, attributes_hash, object_ref, action, value
def parse_tag(text)
match = text.scan(/%([-:\w]+)([-:\w.#\@]*)(.+)?/)[0]
raise HamlSyntaxError.new(HamlError.message(:invalid_tag, text)) unless match
raise SyntaxError.new(Error.message(:invalid_tag, text)) unless match
tag_name, attributes, rest = match
if !attributes.empty? && (attributes =~ /[.#](\.|#|\z)/)
raise HamlSyntaxError.new(HamlError.message(:illegal_element))
raise SyntaxError.new(Error.message(:illegal_element))
end
new_attributes_hash = old_attributes_hash = last_line = nil
@ -685,8 +685,8 @@ module Haml
# 1 more :on_embexpr_end (the last '}') than :on_embexpr_beg, and resurrects '{' afterwards.
balanced, rest = balance_tokens(text.sub(?{, METHOD_CALL_PREFIX), :on_embexpr_beg, :on_embexpr_end, count: 1)
attributes_hash = balanced.sub(METHOD_CALL_PREFIX, ?{)
rescue HamlSyntaxError => e
if e.message == HamlError.message(:unbalanced_brackets) && !@template.empty?
rescue SyntaxError => e
if e.message == Error.message(:unbalanced_brackets) && !@template.empty?
text << "\n#{@next_line.text}"
last_line += 1
next_line
@ -715,7 +715,7 @@ module Haml
if name == false
scanned = Haml::Util.balance(text, ?(, ?))
text = scanned ? scanned.first : text
raise Haml::HamlSyntaxError.new(HamlError.message(:invalid_attribute_list, text.inspect), last_line - 1)
raise Haml::SyntaxError.new(Error.message(:invalid_attribute_list, text.inspect), last_line - 1)
end
attributes[name] = value
scanner.scan(/\s*/)
@ -837,7 +837,7 @@ module Haml
end
def balance(*args)
Haml::Util.balance(*args) or raise(HamlSyntaxError.new(HamlError.message(:unbalanced_brackets)))
Haml::Util.balance(*args) or raise(SyntaxError.new(Error.message(:unbalanced_brackets)))
end
# Unlike #balance, this balances Ripper tokens to balance something like `{ a: "}" }` correctly.
@ -856,7 +856,7 @@ module Haml
return text, buf.sub(text, '')
end
end
raise HamlSyntaxError.new(HamlError.message(:unbalanced_brackets))
raise SyntaxError.new(Error.message(:unbalanced_brackets))
end
def block_opened?

View File

@ -1,8 +1,8 @@
describe Haml::Engine do
describe 'HamlSyntaxError' do
describe 'SyntaxError' do
it 'raises on runtime' do
code = Haml::Engine.new.call(" %a")
assert_raises(Haml::HamlSyntaxError) do
assert_raises(Haml::SyntaxError) do
eval code
end
end
@ -11,7 +11,7 @@ describe Haml::Engine do
code = Haml::Engine.new.call("\n\n %a")
begin
eval code
rescue Haml::HamlSyntaxError => e
rescue Haml::SyntaxError => e
assert_equal(2, e.line)
end
end
@ -27,7 +27,7 @@ describe Haml::Engine do
HAML
begin
eval code
rescue Haml::HamlSyntaxError => e
rescue Haml::SyntaxError => e
assert_equal(3, e.line)
end
end

View File

@ -100,6 +100,6 @@ class Haml::TestCase < BASE_TEST_CLASS
end
def self.error(*args)
Haml::HamlError.message(*args)
Haml::Error.message(*args)
end
end