File Coverage

blib/lib/Devel/DidYouMean.pm
Criterion Covered Total %
statement 57 57 100.0
branch 12 14 85.7
condition n/a
subroutine 10 10 100.0
pod n/a
total 79 81 97.5


line stmt bran cond sub pod time code
1 1     1   26694 use 5.008;
  1         3  
  1         31  
2 1     1   4 use warnings;
  1         1  
  1         51  
3             package Devel::DidYouMean;
4             $Devel::DidYouMean::VERSION = '0.04';
5 1     1   6 use vars qw($AUTOLOAD);
  1         7  
  1         73  
6 1     1   499 use Text::Levenshtein;
  1         692  
  1         35  
7 1     1   395 use Perl::Builtins;
  1         255  
  1         23  
8 1     1   4 use Carp 'croak';
  1         1  
  1         40  
9 1     1   5 no warnings 'once';
  1         2  
  1         49  
10 1     1   7 no strict qw/refs subs/;
  1         1  
  1         416  
11              
12             # ABSTRACT: Intercepts failed function and method calls, suggesting the nearest matching alternative.
13              
14              
15              
16              
17             our $DYM_MATCHING_SUBS = [];
18              
19             CHECK {
20              
21             # add autoload to main
22 1     1   1127 *{ main::AUTOLOAD } = Devel::DidYouMean::AUTOLOAD;
23              
24             # add to every other module in memory
25 1         34 for (keys %INC)
26             {
27 73         53 my $module = $_;
28 73         113 $module =~ s/\//::/g;
29 73         71 $module = substr($module, 0, -3);
30 73         50 $module .= '::AUTOLOAD';
31            
32             # skip if the package already has an autoload
33 73 100       46 next if defined *{ $module };
  73         962  
34            
35 62         37 *{ $module } = Devel::DidYouMean::AUTOLOAD;
  62         182  
36             }
37             }
38              
39             sub AUTOLOAD
40             {
41 3     3   1892 my @sub_path = split /::/, $AUTOLOAD;
42 3         8 my $sub = pop @sub_path;
43              
44             # ignore these calls
45 3 50       58 return if grep /$sub/, qw/AUTOLOAD BEGIN CHECK INIT DESTROY END/;
46              
47 3         9 my $package = join '::', @sub_path;
48 3         6 my $package_namespace = $package . '::';
49              
50 3         7 my %valid_subs = ();
51              
52 3         194 for (keys %$package_namespace)
53             {
54 554         21839 my $absolute_name = $package_namespace . $_;
55 554 100       350 if (defined &{$absolute_name})
  554         1383  
56             {
57 107         204 $valid_subs{$_} = Text::Levenshtein::fastdistance($sub, $_);
58             }
59             }
60              
61             # if package is main, add in builtin functions
62 3 100       56 if ($package eq 'main')
63             {
64 2         8 for (Perl::Builtins::list)
65             {
66 482         74452 $valid_subs{$_} = Text::Levenshtein::fastdistance($sub, $_);
67             }
68             }
69              
70 3         123 $DYM_MATCHING_SUBS = [];
71 3         6 my $match_score;
72              
73             # return similarly named functions
74 3         118 for (sort { $valid_subs{$a} <=> $valid_subs{$b} } keys %valid_subs)
  3612         2666  
75             {
76 6 50       17 next if $_ eq 'AUTOLOAD';
77 6 100       15 $match_score = $valid_subs{$_} unless $match_score;
78              
79 6 100       14 if ($match_score < $valid_subs{$_})
80             {
81 3         101 croak "Undefined subroutine '$sub' not found in $package. Did you mean "
82             . join(', ', @$DYM_MATCHING_SUBS) . '?';
83             }
84 3         11 push @$DYM_MATCHING_SUBS, $_;
85             }
86             }
87              
88             1;
89              
90             __END__