Exception.pm6 (rakudo-2020.08.2) | : | Exception.pm6 (rakudo-2020.09) | ||
---|---|---|---|---|
skipping to change at line 156 | skipping to change at line 156 | |||
method message() { | method message() { | |||
"Dynamic variable $.name not found"; | "Dynamic variable $.name not found"; | |||
} | } | |||
} | } | |||
my class X::Method::NotFound is Exception { | my class X::Method::NotFound is Exception { | |||
has Mu $.invocant; | has Mu $.invocant; | |||
has $.method; | has $.method; | |||
has $.typename; | has $.typename; | |||
has Bool $.private; | has Bool $.private; | |||
has $.addendum; | has $.addendum; | |||
has @!suggestions; | ||||
has @!tips; | ||||
has $!message; | ||||
# This attribute is an implementation detail. Not to be documented. | # This attribute is an implementation detail. Not to be documented. | |||
has $.in-class-call; | has $.in-class-call; | |||
method of-type() { | method of-type() { | |||
nqp::eqaddr(nqp::decont($!invocant),IterationEnd) | nqp::eqaddr(nqp::decont($!invocant),IterationEnd) | |||
?? "IterationEnd" | ?? "IterationEnd" | |||
!! "of type '$.typename'" | !! "of type '$.typename'" | |||
} | } | |||
method message() { | method message() { $!message // self!create-message } | |||
method suggestions() { | ||||
self!create-message unless $!message; | ||||
@!suggestions | ||||
} | ||||
method tips() { | ||||
self!create-message unless $!message; | ||||
@!tips | ||||
} | ||||
method !create-message() { | ||||
my @message = $.private | my @message = $.private | |||
?? "No such private method '!$.method' for invocant $.of-type" | ?? "No such private method '!$.method' for invocant $.of-type" | |||
!! "No such method '$.method' for invocant $.of-type"; | !! "No such method '$.method' for invocant $.of-type"; | |||
@message.push: $.addendum if $.addendum; | @message.push: $.addendum if $.addendum; | |||
my @tips; | ||||
my $indirect-method = $.method.starts-with("!") | my $indirect-method = $.method.starts-with("!") | |||
?? $.method.substr(1) | ?? $.method.substr(1) | |||
!! ""; | !! ""; | |||
my %suggestions; | my %suggestions; | |||
my int $max_length = do given $.method.chars { | my int $max_length = do given $.method.chars { | |||
when 0..3 { 1 } | when 0..3 { 1 } | |||
when 4..8 { 2 } | when 4..8 { 2 } | |||
when 9..* { 3 } | when 9..* { 3 } | |||
} | } | |||
skipping to change at line 204 | skipping to change at line 217 | |||
} | } | |||
my sub code-name(Mu $meth) { | my sub code-name(Mu $meth) { | |||
# KnowHOW `methods` method returns a hash. Respectively, iteration o ver .^methods gives us Pairs. | # KnowHOW `methods` method returns a hash. Respectively, iteration o ver .^methods gives us Pairs. | |||
return $meth.key if $meth ~~ Pair; | return $meth.key if $meth ~~ Pair; | |||
my $code-obj := nqp::decont($meth); | my $code-obj := nqp::decont($meth); | |||
(try nqp::can($code-obj,'name') ?? $code-obj.name !! nqp::getcodenam e($code-obj)) // '?' | (try nqp::can($code-obj,'name') ?? $code-obj.name !! nqp::getcodenam e($code-obj)) // '?' | |||
} | } | |||
my $public_suggested = 0; | my $public_suggested = 0; | |||
sub find_public_suggestion($before, $after --> Nil) { | ||||
if $before.fc eq $after.fc { | ||||
$public_suggested = 1; | ||||
%suggestions{$after} = 0; # assume identity | ||||
} | ||||
else { | ||||
my $dist := StrDistance.new( | ||||
before => $before.fc, | ||||
after => $after.fc | ||||
); | ||||
if $dist <= $max_length { | ||||
$public_suggested = 1; | ||||
%suggestions{$after} = $dist; | ||||
} | ||||
} | ||||
} | ||||
if nqp::can($!invocant.HOW, 'methods') { | if nqp::can($!invocant.HOW, 'methods') { | |||
# $!invocant can be a KnowHOW which method `methods` returns a hash, not a list. | # $!invocant can be a KnowHOW which method `methods` returns a hash, not a list. | |||
my @invocant_methods = $!invocant.^methods(:local).map: { code-name( | my $invocant_methods := | |||
$_) }; | Set.new: $!invocant.^methods(:local).map: { code-name($_) }; | |||
for $!invocant.^methods(:all) -> $method_candidate { | for $!invocant.^methods(:all) -> $method_candidate { | |||
my $method_name = code-name($method_candidate); | my $method_name := code-name($method_candidate); | |||
# GH#1758 do not suggest a submethod from a parent | # GH#1758 do not suggest a submethod from a parent | |||
next if $method_candidate.^name eq 'Submethod' && !@invocant_met | next | |||
hods.first($method_name, :k).defined; | if $method_candidate.^name eq 'Submethod' # a submethod | |||
my $dist = StrDistance.new(:before($.method), :after(~$method_na | && !$invocant_methods{$method_name}; # unknown method | |||
me)); | ||||
if $dist <= $max_length { | find_public_suggestion($.method, $method_name); | |||
$public_suggested = 1; | } | |||
%suggestions{$method_name} = ~$dist; | ||||
} | # handle special unintrospectable cases | |||
for <HOW WHAT WHO> -> $method_name { | ||||
find_public_suggestion($.method, $method_name); | ||||
} | } | |||
} | } | |||
my $private_suggested = 0; | my $private_suggested = 0; | |||
if $.in-class-call && nqp::can($!invocant.HOW, 'private_method_table') { | if $.in-class-call && nqp::can($!invocant.HOW, 'private_method_table') { | |||
for $!invocant.^private_method_table.keys -> $method_name { | for $!invocant.^private_method_table.keys -> $method_name { | |||
my $dist = StrDistance.new(:before($.method), :after(~$method_na | my $dist = StrDistance.new( | |||
me)); | before => $.method.fc, | |||
after => $method_name.fc | ||||
); | ||||
if $dist <= $max_length { | if $dist <= $max_length { | |||
$private_suggested = 1; | $private_suggested = 1; | |||
%suggestions{"!$method_name"} = ~$dist | %suggestions{"!$method_name"} = $dist | |||
unless $indirect-method eq $method_name; | unless $indirect-method eq $method_name; | |||
} | } | |||
} | } | |||
} | } | |||
if $indirect-method && !$.private && $private_suggested { | if $indirect-method && !$.private && $private_suggested { | |||
@tips.push: "Method name starts with '!', did you mean 'self!\"$indi rect-method\"()'?"; | @!tips.push: "Method name starts with '!', did you mean 'self!\"$ind irect-method\"()'?"; | |||
} | } | |||
if +%suggestions == 1 { | @!suggestions = %suggestions.sort(*.value).map(*.key).head(4); | |||
@tips.push: "Did you mean '%suggestions.keys()'?"; | ||||
if @!suggestions == 1 { | ||||
@!tips.push: "Did you mean '@!suggestions[0]'?"; | ||||
} | } | |||
elsif +%suggestions > 1 { | elsif @!suggestions { | |||
@tips.push: "Did you mean any of these: { %suggestions.sort(*.value) | @!tips.push: "Did you mean any of these: { @!suggestions.map( { "'$_ | |||
>>.key.head(4).map( { "'$_'" } ).join(", ") }?"; | '" } ).join(", ") }?"; | |||
} | } | |||
if !$indirect-method | if !$indirect-method | |||
&&($private_suggested ^^ $public_suggested) | &&($private_suggested ^^ $public_suggested) | |||
&& ($private_suggested ^^ $.private) | && ($private_suggested ^^ $.private) | |||
{ | { | |||
@tips.push: "Perhaps a " ~ ($private_suggested ?? "private" !! "publ ic") ~ " method call must be used." | @!tips.push: "Perhaps a " ~ ($private_suggested ?? "private" !! "pub lic") ~ " method call must be used." | |||
} | } | |||
if +@tips > 1 { | if @!tips > 1 { | |||
@tips = @tips.map: "\n" ~ ("- " ~ *).naive-word-wrapper(:indent(" | @!tips = @!tips.map: "\n" ~ ("- " ~ *).naive-word-wrapper(:indent(" | |||
")); | ")); | |||
@message.push: ($.addendum ?? "Other possible" !! "Possible") ~ " ca uses are:"; | @message.push: ($.addendum ?? "Other possible" !! "Possible") ~ " ca uses are:"; | |||
} | } | |||
elsif @tips { | elsif @!tips { | |||
@message.push: @tips.shift; | @message.push: @!tips.shift; | |||
} | } | |||
@message[0] ~= "." if +@message > 1; | @message[0] ~= "." if @message > 1; | |||
@message.join(" ").naive-word-wrapper ~ @tips.join | $!message = @message.join(" ").naive-word-wrapper ~ @!tips.join | |||
} | } | |||
} | } | |||
my class X::Method::InvalidQualifier is Exception { | my class X::Method::InvalidQualifier is Exception { | |||
has $.method; | has $.method; | |||
has $.invocant; | has $.invocant; | |||
has $.qualifier-type; | has $.qualifier-type; | |||
method message() { | method message() { | |||
"Cannot dispatch to method $.method on {$.qualifier-type.^name} " | "Cannot dispatch to method $.method on {$.qualifier-type.^name} " | |||
~ "because it is not inherited or done by {$.invocant.^name}"; | ~ "because it is not inherited or done by {$.invocant.^name}"; | |||
skipping to change at line 2771 | skipping to change at line 2812 | |||
} | } | |||
if @.ambiguous[0].signature.gist.contains: ': ' { | if @.ambiguous[0].signature.gist.contains: ': ' { | |||
my $invocant = @bits.shift; | my $invocant = @bits.shift; | |||
my $first = @bits ?? @bits.shift !! ''; | my $first = @bits ?? @bits.shift !! ''; | |||
@bits.unshift($invocant ~ ': ' ~ $first); | @bits.unshift($invocant ~ ': ' ~ $first); | |||
} | } | |||
my $cap = '(' ~ @bits.join(", ") ~ ')'; | my $cap = '(' ~ @bits.join(", ") ~ ')'; | |||
@priors = flat "Earlier failures:\n", @priors, "\nFinal error:\n " if @p riors; | @priors = flat "Earlier failures:\n", @priors, "\nFinal error:\n " if @p riors; | |||
@priors.join ~ join "\n", | @priors.join ~ join "\n", | |||
"Ambiguous call to '$.dispatcher.name()$cap'; these signatures all m atch:", | "Ambiguous call to '$.dispatcher.name()$cap'; these signatures all m atch:", | |||
@.ambiguous.map(*.signature.raku) | @.ambiguous.map: { | |||
my $sig := .signature.raku.substr(1).subst(/ \s* "-->" <-[)]>+ / | ||||
); | ||||
.?default ?? " $sig is default" !! " $sig" | ||||
} | ||||
} | } | |||
} | } | |||
my class X::Multi::NoMatch is Exception { | my class X::Multi::NoMatch is Exception { | |||
has $.dispatcher; | has $.dispatcher; | |||
has $.capture; | has $.capture; | |||
method message { | method message { | |||
my @cand = $.dispatcher.dispatchees.map(*.signature.gist); | my @cand = $.dispatcher.dispatchees.map(*.signature.gist); | |||
my @un-rw-cand; | my @un-rw-cand; | |||
if first / 'is rw' /, @cand { | if first / 'is rw' /, @cand { | |||
skipping to change at line 3050 | skipping to change at line 3094 | |||
my class X::InvalidTypeSmiley does X::Comp { | my class X::InvalidTypeSmiley does X::Comp { | |||
has $.name; | has $.name; | |||
method message() { | method message() { | |||
"Invalid type smiley ':$.name' used, only ':D', ':U' and ':_' are allowe d"; | "Invalid type smiley ':$.name' used, only ':D', ':U' and ':_' are allowe d"; | |||
} | } | |||
} | } | |||
my class X::MultipleTypeSmiley does X::Comp { | my class X::MultipleTypeSmiley does X::Comp { | |||
method message() { | method message() { | |||
"Multiple type smileys cannot be used"; | "Multiple type smileys cannot be used, did you forget a ':' somewhere?"; | |||
} | } | |||
} | } | |||
my class X::Seq::Consumed is Exception { | my class X::Seq::Consumed is Exception { | |||
method message() { | method message() { | |||
"The iterator of this Seq is already in use/consumed by another Seq\n" ~ | "The iterator of this Seq is already in use/consumed by another Seq\n" ~ | |||
"(you might solve this by adding .cache on usages of the Seq, or\n" ~ | "(you might solve this by adding .cache on usages of the Seq, or\n" ~ | |||
"by assigning the Seq into an array)" | "by assigning the Seq into an array)" | |||
} | } | |||
} | } | |||
skipping to change at line 3157 | skipping to change at line 3201 | |||
} | } | |||
my class X::Proc::Unsuccessful is Exception { | my class X::Proc::Unsuccessful is Exception { | |||
has $.proc; | has $.proc; | |||
method message() { | method message() { | |||
"The spawned command '{$.proc.command[0]}' exited unsuccessfully (exit c ode: $.proc.exitcode(), signal: $.proc.signal())" | "The spawned command '{$.proc.command[0]}' exited unsuccessfully (exit c ode: $.proc.exitcode(), signal: $.proc.signal())" | |||
} | } | |||
} | } | |||
class CompUnit::DependencySpecification { ... } | class CompUnit::DependencySpecification { ... } | |||
class CompUnit::Repository::FileSystem { ... } | ||||
my class X::CompUnit::UnsatisfiedDependency is Exception { | my class X::CompUnit::UnsatisfiedDependency is Exception { | |||
has CompUnit::DependencySpecification $.specification; | has CompUnit::DependencySpecification $.specification; | |||
my sub is-core($name) { | my sub is-core($name) { | |||
my @parts = $name.split("::"); | my @parts = $name.split("::"); | |||
my $last := @parts.pop; | my $last := @parts.pop; | |||
my $ns := ::CORE.WHO; | my $ns := ::CORE.WHO; | |||
for @parts { | for @parts { | |||
return False unless $ns{$_}:exists; | return False unless $ns{$_}:exists; | |||
$ns := $ns{$_}.WHO; | $ns := $ns{$_}.WHO; | |||
}; | }; | |||
$ns{$last}:exists | $ns{$last}:exists | |||
and not nqp::istype(nqp::how($ns{$last}), Metamodel::PackageHOW) | and not nqp::istype(nqp::how($ns{$last}), Metamodel::PackageHOW) | |||
} | } | |||
method !is-missing-from-meta-file() { | ||||
$*REPO.isa(CompUnit::Repository::FileSystem) and $*REPO.prefix.add("META | ||||
6.json").e | ||||
} | ||||
method message() { | method message() { | |||
my $name = $.specification.short-name; | my $name = $.specification.short-name; | |||
is-core($name) | is-core($name) | |||
?? "{$name} is a builtin type, not an external module" | ?? "{$name} is a builtin type, not an external module" | |||
!! "Could not find $.specification in:\n" | !! "Could not find $.specification in:\n" | |||
~ $*REPO.repo-chain.map(*.path-spec).join("\n").indent(4) | ~ $*REPO.repo-chain.map(*.path-spec).join("\n").indent(4) | |||
~ ($.specification ~~ / $<name>=.+ '::from' $ / | ~ ($.specification ~~ / $<name>=.+ '::from' $ / | |||
?? "\n\nIf you meant to use the :from adverb, use" | ?? "\n\nIf you meant to use the :from adverb, use" | |||
~ " a single colon for it: $<name>:from<...>\n" | ~ " a single colon for it: $<name>:from<...>\n" | |||
!! '' | !! self!is-missing-from-meta-file | |||
?? "\n\nPlease note that a 'META6.json' file was found i | ||||
n '$*REPO.prefix.relative()'," | ||||
~ " of which the 'provides' section was used to dete | ||||
rmine if a dependency is available" | ||||
~ " or not. Perhaps you need to add '$!specificatio | ||||
n' in the <provides> section of" | ||||
~ " that file? Or need to specify a directory that | ||||
does *not* have a 'META6.json' file?" | ||||
!! '' | ||||
) | ) | |||
} | } | |||
} | } | |||
my class Exceptions::JSON { | my class Exceptions::JSON { | |||
method process($ex) { | method process($ex) { | |||
$*ERR.print: Rakudo::Internals::JSON.to-json($ex); | $*ERR.print: Rakudo::Internals::JSON.to-json($ex); | |||
False # done processing | False # done processing | |||
} | } | |||
} | } | |||
End of changes. 22 change blocks. | ||||
33 lines changed or deleted | 89 lines changed or added |