Skip to content

Commit

Permalink
Refactor check_single_def_error_message (#10196)
Browse files Browse the repository at this point in the history
* Refactor check_single_def_error_message

* Extract no_overload_matches_message
  • Loading branch information
straight-shoota authored Jan 13, 2021
1 parent ce68a8b commit 424582e
Showing 1 changed file with 51 additions and 56 deletions.
107 changes: 51 additions & 56 deletions src/compiler/crystal/semantic/call_error.cr
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,8 @@ class Crystal::Call
end

if defs_matching_args_size.size > 0
str = IO::Memory.new
if check_single_def_error_message(defs_matching_args_size, named_args_types, str)
raise str.to_s
if message = single_def_error_message(defs, named_args)
raise message
else
if block && defs_matching_args_size.all? { |a_def| !a_def.yields }
raise "'#{full_name(owner, def_name)}' is not expected to be invoked with a block, but a block was given"
Expand Down Expand Up @@ -147,33 +146,8 @@ class Crystal::Call
owner_trace = inner_exception
end

arg_names = [] of Array(String)

message = String.build do |msg|
unless check_single_def_error_message(defs, named_args_types, msg)
msg << "no overload matches '#{full_name(owner, def_name)}'"
unless args.empty?
msg << " with type"
msg << 's' if arg_types.size > 1 || named_args_types
msg << ' '
arg_types.join(msg, ", ")
end

if named_args_types
named_args_types.each do |named_arg|
msg << ", "
msg << named_arg.name
msg << ": "
msg << named_arg.type
end
end

msg << '\n'

defs.each do |a_def|
arg_names << a_def.args.map(&.name)
end
end
no_overload_matches_message(msg, full_name(owner, def_name), defs, args, arg_types, named_args_types)

msg << "Overloads are:"
append_matches(defs, arg_types, msg)
Expand All @@ -182,7 +156,8 @@ class Crystal::Call
cover = matches.cover
if cover.is_a?(Cover)
missing = cover.missing
uniq_arg_names = arg_names.uniq!

uniq_arg_names = defs.map(&.args.map(&.name)).uniq!
uniq_arg_names = uniq_arg_names.size == 1 ? uniq_arg_names.first : nil
unless missing.empty?
msg << "\nCouldn't find overloads for these types:"
Expand Down Expand Up @@ -211,6 +186,32 @@ class Crystal::Call
raise message, owner_trace
end

private def no_overload_matches_message(io, full_name, defs, args, arg_types, named_args_types)
if message = single_def_error_message(defs, named_args_types)
io << message
return
end

io << "no overload matches '#{full_name}'"
unless args.empty?
io << " with type"
io << 's' if arg_types.size > 1 || named_args_types
io << ' '
arg_types.join(io, ", ")
end

if named_args_types
named_args_types.each do |named_arg|
io << ", "
io << named_arg.name
io << ": "
io << named_arg.type
end
end

io << '\n'
end

private def raise_undefined_method(owner, def_name, obj)
check_macro_wrong_number_of_arguments(def_name)

Expand Down Expand Up @@ -298,7 +299,9 @@ class Crystal::Call
all_arguments_sizes.uniq!.sort!

raise(String.build do |str|
unless check_single_def_error_message(defs, named_args_types, str)
if single_message = single_def_error_message(defs, named_args_types)
str << single_message
else
str << "wrong number of arguments for '"
str << full_name(owner, def_name)
str << "' (given "
Expand Down Expand Up @@ -331,23 +334,24 @@ class Crystal::Call
end
end

# If there's only one def that could match, and there are named
# arguments in this call, we can give a better error message.
def check_single_def_error_message(defs, named_args, io)
return false unless defs.size == 1
def single_def_error_message(defs, named_args)
if defs.size == 1
missing_argument_message(defs.first, named_args)
end
end

a_def = defs.first
def missing_argument_message(a_def, named_args)
missing_args = extract_missing_args(a_def, named_args)
return unless missing_args

if msg = check_named_args_and_splats(a_def, named_args)
io << msg
io.puts
return true
if missing_args.size == 1
"missing argument: #{missing_args.first}"
else
"missing arguments: #{missing_args.join ", "}"
end

false
end

def check_named_args_and_splats(a_def, named_args)
def extract_missing_args(a_def, named_args)
splat_index = a_def.splat_index
return if !splat_index && !named_args
return if splat_index == a_def.args.size - 1
Expand Down Expand Up @@ -376,16 +380,9 @@ class Crystal::Call
missing_args << arg.external_name
end

case missing_args.size
when 0
# Nothing
when 1
return "missing argument: #{missing_args.first}"
else
return "missing arguments: #{missing_args.join ", "}"
end
return if missing_args.size.zero?

return nil
missing_args
end

def append_error_when_no_matching_defs(owner, def_name, all_arguments_sizes, real_args_size, min_splat, defs, io)
Expand Down Expand Up @@ -529,10 +526,8 @@ class Crystal::Call
return unless macros.is_a?(Array(Macro))
macros = macros.reject &.visibility.private?

if macros.size == 1
if msg = check_named_args_and_splats(macros.first, named_args)
raise msg
end
if msg = single_def_error_message(macros, named_args)
raise msg
end

all_arguments_sizes = Set(String).new
Expand Down

0 comments on commit 424582e

Please sign in to comment.