Skip to content

Commit

Permalink
* Parser::Diagnostic: expose reason symbolically (closes #115, #116).
Browse files Browse the repository at this point in the history
  • Loading branch information
nevir authored and whitequark committed Nov 25, 2013
1 parent 8e4dd42 commit 8cb572c
Show file tree
Hide file tree
Showing 15 changed files with 139 additions and 131 deletions.
4 changes: 4 additions & 0 deletions lib/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ module Builders

# Parser warnings
:useless_else => 'else without rescue is useless',

# Rewriter diagnostics
:invalid_action => 'cannot %{action}',
:clobbered => 'clobbered by: %{action}',
}.freeze

##
Expand Down
12 changes: 5 additions & 7 deletions lib/parser/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -235,21 +235,20 @@ def check_kwarg_name(name_t)
when /^[a-z_]/
# OK
when /^[A-Z]/
diagnostic :error, :argument_const, name_t
diagnostic :error, :argument_const, nil, name_t
end
end

def diagnostic(level, kind, location_t, highlights_ts=[])
def diagnostic(level, reason, arguments, location_t, highlights_ts=[])
_, location = location_t

highlights = highlights_ts.map do |token|
_, range = token
range
end

message = ERRORS[kind]
@diagnostics.process(
Diagnostic.new(level, message, location, highlights))
Diagnostic.new(level, reason, arguments, location, highlights))

if level == :error
yyerror
Expand All @@ -260,9 +259,8 @@ def on_error(error_token_id, error_value, value_stack)
token_name = token_to_str(error_token_id)
_, location = error_value

message = ERRORS[:unexpected_token] % { :token => token_name }
@diagnostics.process(
Diagnostic.new(:error, message, location))
@diagnostics.process(Diagnostic.new(
:error, :unexpected_token, { :token => token_name }, location))
end
end

Expand Down
32 changes: 12 additions & 20 deletions lib/parser/builders/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def symbol_compose(begin_t, parts, end_t)
n(:sym, [ str.children.first.to_sym ],
collection_map(begin_t, str.loc.expression, end_t))
elsif @parser.version == 18 && parts.empty?
diagnostic(:error, ERRORS[:empty_symbol], loc(begin_t).join(loc(end_t)))
diagnostic :error, :empty_symbol, nil, loc(begin_t).join(loc(end_t))
else
n(:dsym, [ *parts ],
collection_map(begin_t, parts, end_t))
Expand Down Expand Up @@ -233,8 +233,7 @@ def pair(key, assoc_t, value)

def pair_list_18(list)
if list.size % 2 != 0
message = ERRORS[:odd_hash]
diagnostic :error, message, list.last.loc.expression
diagnostic :error, :odd_hash, nil, list.last.loc.expression
else
list.
each_slice(2).map do |key, value|
Expand Down Expand Up @@ -390,8 +389,7 @@ def assignable(node)

when :const
if @parser.in_def?
message = ERRORS[:dynamic_const]
diagnostic :error, message, node.loc.expression
diagnostic :error, :dynamic_const, nil, node.loc.expression
end

node.updated(:casgn)
Expand All @@ -404,12 +402,10 @@ def assignable(node)

when :nil, :self, :true, :false,
:__FILE__, :__LINE__, :__ENCODING__
message = ERRORS[:invalid_assignment]
diagnostic :error, message, node.loc.expression
diagnostic :error, :invalid_assignment, nil, node.loc.expression

when :back_ref, :nth_ref
message = ERRORS[:backref_assignment]
diagnostic :error, message, node.loc.expression
diagnostic :error, :backref_assignment, nil, node.loc.expression
end
end

Expand Down Expand Up @@ -442,8 +438,7 @@ def op_assign(lhs, op_t, rhs)
end

when :back_ref, :nth_ref
message = ERRORS[:backref_assignment]
diagnostic :error, message, lhs.loc.expression
diagnostic :error, :backref_assignment, nil, lhs.loc.expression
end
end

Expand Down Expand Up @@ -497,8 +492,7 @@ def def_singleton(def_t, definee, dot_t,
when :int, :str, :dstr, :sym, :dsym,
:regexp, :array, :hash

message = ERRORS[:singleton_literal]
diagnostic :error, message, definee.loc.expression
diagnostic :error, :singleton_literal, nil, definee.loc.expression

else
n(:defs, [ definee, value(name_t).to_sym, args, body ],
Expand Down Expand Up @@ -634,8 +628,7 @@ def block(method_call, begin_t, args, body, end_t)
last_arg = call_args.last

if last_arg && last_arg.type == :block_pass
diagnostic :error, ERRORS[:block_and_blockarg],
last_arg.loc.expression
diagnostic :error, :block_and_blockarg, nil, last_arg.loc.expression
end

n(:block, [ method_call, args, body ],
Expand Down Expand Up @@ -935,8 +928,7 @@ def begin_keyword(begin_t, body, end_t)
def check_condition(cond)
case cond.type
when :masgn
diagnostic :error, ERRORS[:masgn_as_condition],
cond.loc.expression
diagnostic :error, :masgn_as_condition, nil, cond.loc.expression

when :begin
if cond.children.count == 1
Expand Down Expand Up @@ -988,7 +980,7 @@ def check_duplicate_args(args, map={})
if that_arg.nil?
map[this_name] = this_arg
elsif arg_name_collides?(this_name, that_name)
diagnostic :error, ERRORS[:duplicate_argument],
diagnostic :error, :duplicate_argument, nil,
this_arg.loc.name, [ that_arg.loc.name ]
end

Expand Down Expand Up @@ -1341,9 +1333,9 @@ def loc(token)
token[1] if token && token[0]
end

def diagnostic(type, message, location, highlights=[])
def diagnostic(type, reason, arguments, location, highlights=[])
@parser.diagnostics.process(
Diagnostic.new(type, message, location, highlights))
Diagnostic.new(type, reason, arguments, location, highlights))

if type == :error
@parser.send :yyerror
Expand Down
28 changes: 23 additions & 5 deletions lib/parser/diagnostic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ module Parser
# @see LEVELS
# @return [Symbol] diagnostic level
#
# @!attribute [r] reason
# @see Parser::ERRORS
# @return [Symbol] reason for error
#
# @!attribute [r] arguments
# @see Parser::ERRORS
# @return [Symbol] extended arguments that describe the error
#
# @!attribute [r] message
# @return [String] error message
#
Expand All @@ -26,30 +34,40 @@ class Diagnostic
#
LEVELS = [:note, :warning, :error, :fatal].freeze

attr_reader :level, :message
attr_reader :level, :reason, :arguments
attr_reader :location, :highlights

##
# @param [Symbol] level
# @param [String] message
# @param [Symbol] reason
# @param [Hash] arguments
# @param [Parser::Source::Range] location
# @param [Array<Parser::Source::Range>] highlights
#
def initialize(level, message, location, highlights=[])
def initialize(level, reason, arguments, location, highlights=[])
unless LEVELS.include?(level)
raise ArgumentError,
"Diagnostic#level must be one of #{LEVELS.join(', ')}; " \
"#{level.inspect} provided."
end
raise 'Expected a location' unless location

@level = level
@message = message.to_s.dup.freeze
@reason = reason
@arguments = (arguments || {}).dup.freeze
@location = location
@highlights = highlights.dup.freeze

freeze
end

##
# @return [String] the rendered message.
#
def message
ERRORS[@reason] % @arguments
end

##
# Renders the diagnostic message as a clang-like diagnostic.
#
Expand All @@ -76,7 +94,7 @@ def render
highlight_line[range] = '^' * @location.size

[
"#{@location.to_s}: #{@level}: #{@message}",
"#{@location.to_s}: #{@level}: #{message}",
source_line,
highlight_line,
]
Expand Down
5 changes: 3 additions & 2 deletions lib/parser/diagnostic/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ module Parser
# end
#
# engine = Parser::Diagnostic::Engine.new(consumer)
# diagnostic = Parser::Diagnostic.new(:warning, 'warning!', buffer, 1..2)
# diagnostic = Parser::Diagnostic.new(
# :warning, :unexpected_token, { :token => 'abc' }, buffer, 1..2)
#
# engine.process(diagnostic) # => "warning!"
# engine.process(diagnostic) # => "unexpected token abc"
#
# @api public
#
Expand Down
Loading

1 comment on commit 8cb572c

@nevir
Copy link
Contributor Author

@nevir nevir commented on 8cb572c Nov 25, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome; thanks for merging this

Please sign in to comment.