line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package Devel::FIXME; |
4
|
3
|
|
|
3
|
|
31265
|
use fields qw/text line file package script time/; |
|
3
|
|
|
|
|
5068
|
|
|
3
|
|
|
|
|
19
|
|
5
|
|
|
|
|
|
|
|
6
|
3
|
|
|
3
|
|
527
|
use 5.008_000; # needs open to work on scalar ref |
|
3
|
|
|
|
|
11
|
|
|
3
|
|
|
|
|
151
|
|
7
|
|
|
|
|
|
|
|
8
|
3
|
|
|
3
|
|
28
|
use strict; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
118
|
|
9
|
3
|
|
|
3
|
|
16
|
use warnings; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
93
|
|
10
|
|
|
|
|
|
|
|
11
|
3
|
|
|
3
|
|
53
|
use Exporter; |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
209
|
|
12
|
3
|
|
|
3
|
|
21
|
use Scalar::Util qw/reftype/; |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
299
|
|
13
|
3
|
|
|
3
|
|
17
|
use List::Util qw/first/; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
362
|
|
14
|
3
|
|
|
3
|
|
18
|
use Carp qw/carp croak/; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
5471
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
our @EXPORT_OK = qw/FIXME SHOUT DROP CONT/; |
17
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( "constants" => \@EXPORT_OK ); |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
our $VERSION = 0.01; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# some constants for rules |
22
|
|
|
|
|
|
|
sub CONT () { 0 }; |
23
|
|
|
|
|
|
|
sub SHOUT () { 1 }; |
24
|
|
|
|
|
|
|
sub DROP () { 2 }; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
our $REPAIR_INC = undef; # do not "repair" @INC by default |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
my %lock; # to prevent recursion |
29
|
|
|
|
|
|
|
our %rets; # return value cache |
30
|
|
|
|
|
|
|
our $cur; # the current file, used in an eval |
31
|
|
|
|
|
|
|
our $err; # the current error, for rethrowal |
32
|
|
|
|
|
|
|
our $inited; # whether the code ref was installed in @INC, and all |
33
|
|
|
|
|
|
|
|
34
|
3
|
|
|
3
|
|
23
|
{ my $anon = ''; open my $fh, "<", \$anon or die $!; close $fh; } # otherwise perlio require stuff breaks |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
28
|
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub init { |
37
|
6
|
|
|
6
|
0
|
13
|
my $pkg = shift; |
38
|
6
|
100
|
|
|
|
27
|
unless($inited){ |
39
|
3
|
|
|
|
|
58
|
$pkg->readfile($_) for ($0, sort grep { $_ ne __FILE__ } (values %INC)); # readfile on everything loaded, but not us (we don't want to match our own docs) |
|
226
|
|
|
|
|
637
|
|
40
|
3
|
|
|
|
|
417
|
$pkg->install_inc; |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
6
|
|
|
|
|
19
|
$inited = 1; |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
our $carprec = 0; |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub install_inc { |
49
|
3
|
|
|
3
|
0
|
10
|
my $pkg = shift; |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
unshift @INC, sub { # YUCK! but tying %INC didn't work, and source filters are applied per caller. XS for source filter purposes is yucki/er/ |
52
|
12
|
|
|
12
|
|
1754
|
my $self = shift; |
53
|
12
|
|
|
|
|
20
|
my $file = shift; |
54
|
|
|
|
|
|
|
|
55
|
12
|
100
|
|
|
|
7056
|
return undef if $lock{$file}; # if we're already processing the file, then we're in the eval several lines down. return. |
56
|
6
|
|
|
|
|
20
|
local $lock{$file} = 1; # set this lock that prevents recursion |
57
|
|
|
|
|
|
|
|
58
|
6
|
50
|
33
|
|
|
53
|
unless (ref $INC[0] and $INC[0] == $self){ # if this happens, some stuff won't be filtered. It shouldn't happen often though. |
59
|
0
|
0
|
|
|
|
0
|
local @INC = grep { !ref or $_ != $self } @INC; # make sure we don't recurse when carp loads it's various innards, it causes a mess |
|
0
|
|
|
|
|
0
|
|
60
|
0
|
0
|
|
|
|
0
|
carp "FIXME's magic sub is no longer first in \@INC" . ($REPAIR_INC ? ", repairing" : ""); |
61
|
0
|
0
|
|
|
|
0
|
if ($REPAIR_INC){ |
62
|
0
|
|
|
|
|
0
|
my $i = 0; |
63
|
0
|
|
|
|
|
0
|
while ($i < @INC) { |
64
|
0
|
0
|
|
|
|
0
|
ref $INC[$i] or next; |
65
|
0
|
0
|
|
|
|
0
|
if ($INC[$i] == $self) { |
66
|
0
|
|
|
|
|
0
|
unshift @INC, splice(@INC, $i, 1); |
67
|
0
|
|
|
|
|
0
|
last; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
} continue { |
70
|
0
|
|
|
|
|
0
|
$i++; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# create some perl code that gives back the return value of the original package, and thus looks like you're really requiring the same thing |
76
|
6
|
|
|
|
|
18
|
my $buffer = "\${ delete \$Devel::FIXME::rets{q{$file}} };"; # return what the last module returned. I don't know why it doesn't work without refs |
77
|
|
|
|
|
|
|
# really load the file |
78
|
6
|
|
|
|
|
13
|
local $cur = $file; |
79
|
6
|
|
|
|
|
459
|
my $ret = eval 'require $Devel::FIXME::cur'; # require always evaluates the return from an evalfile in scalar context, so we don't need to worry about list |
80
|
|
|
|
|
|
|
|
81
|
6
|
|
|
|
|
5999
|
($err = "$@\n") =~ s/\nCompilation failed in require at \(eval \d+\)(?:\[.*?\])? line 1\.\n//s; # trim off the eval's appendix to the error |
82
|
6
|
50
|
|
|
|
23
|
$buffer = 'die $Devel::FIXME::err' if $@; # rethrow this way, so that base.pm shuts up |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# save the return value so that the original require can have it |
85
|
6
|
|
|
|
|
21
|
$rets{$file} = \$ret; # see above for why it's a ref |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# look for FIXME comments in the file that was really required |
88
|
6
|
50
|
|
|
|
59
|
$pkg->readfile($INC{$file}) if ($INC{$file}); |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
# return a filehandle containing source code that simply returns the value the real file did |
91
|
6
|
|
|
|
|
82
|
open my $fh, "<", \$buffer; |
92
|
6
|
|
|
|
|
209
|
$fh; |
93
|
3
|
|
|
|
|
45
|
}; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
sub regex { |
97
|
81999
|
|
|
81999
|
1
|
423608
|
qr/#\s*(?:FIXME|XXX)\s+(.*)$/; # match a FIXME or an XXX, in a comment, with some lax whitespace rules, and suck in anything afterwords as the text |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub readfile { # FIXME refactor to something classier |
101
|
152
|
|
|
152
|
0
|
362
|
my $pkg = shift; |
102
|
152
|
|
|
|
|
314
|
my $file = shift; |
103
|
|
|
|
|
|
|
|
104
|
152
|
50
|
|
|
|
7176
|
return unless -f $file; |
105
|
|
|
|
|
|
|
|
106
|
152
|
50
|
|
|
|
7759
|
open my $src, "<", $file or die "couldn't open $file: $!"; |
107
|
152
|
|
|
|
|
280
|
local $_; |
108
|
|
|
|
|
|
|
|
109
|
152
|
|
|
|
|
2268
|
while(<$src>){ |
110
|
81999
|
100
|
|
|
|
187894
|
$pkg->FIXME( # if the line matches the fixme, generate a fixme |
111
|
|
|
|
|
|
|
text => "$1", |
112
|
|
|
|
|
|
|
line => $., # the current line number for <$src> |
113
|
|
|
|
|
|
|
file => $file, |
114
|
|
|
|
|
|
|
) if $_ =~ $pkg->regex; |
115
|
81999
|
100
|
|
|
|
397317
|
} continue { last if eof $src }; # is this a platform bug on OSX? |
116
|
152
|
|
|
|
|
3214
|
close $src; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub eval { # evaluates all the rules on a fixme object |
120
|
25
|
|
|
25
|
0
|
49
|
my __PACKAGE__ $self = shift; |
121
|
|
|
|
|
|
|
|
122
|
25
|
100
|
|
|
|
220
|
foreach my $rule ($self->can("rules") ? $self->rules : ()){ |
123
|
|
|
|
|
|
|
|
124
|
17
|
|
|
|
|
144
|
my $action = &$rule($self); # run the rule as a class method, and get back a return value |
125
|
|
|
|
|
|
|
|
126
|
17
|
100
|
|
|
|
149
|
if ($action == SHOUT){ # if the rule said to shout, we shout and stop |
|
|
100
|
|
|
|
|
|
127
|
2
|
|
|
|
|
9
|
return $self->shout; |
128
|
|
|
|
|
|
|
} elsif ($action == DROP){ # if the rule says to drop, we stop |
129
|
13
|
|
|
|
|
51
|
return undef; |
130
|
|
|
|
|
|
|
} # otherwise we keep looping through the rules |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
10
|
|
|
|
|
52
|
$self->shout; # and shout if there are no more rules left. |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub shout { # generate a pretty string and send it to STDERR |
137
|
1
|
|
|
1
|
0
|
2
|
my __PACKAGE__ $self = shift; |
138
|
1
|
|
|
|
|
19
|
warn("# FIXME: $self->{text} at $self->{file} line $self->{line}.\n"); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub new { # an object per FIXME statement |
142
|
36
|
|
|
36
|
0
|
112
|
my $pkg = shift; |
143
|
|
|
|
|
|
|
|
144
|
36
|
|
|
|
|
57
|
my %args; |
145
|
|
|
|
|
|
|
|
146
|
36
|
100
|
|
|
|
4309
|
if (@_ == 1){ # if we only have one arg |
|
|
50
|
|
|
|
|
|
147
|
20
|
100
|
66
|
|
|
286
|
if (ref $_[0] and reftype($_[0]) eq 'HASH'){ # and it's a hash ref, then we take the hashref to be our args |
148
|
10
|
|
|
|
|
13
|
%args = %{ $_[0] }; |
|
10
|
|
|
|
|
68
|
|
149
|
|
|
|
|
|
|
} else { # if it's one arg and not a hashref, then it's our text |
150
|
10
|
|
|
|
|
41
|
%args = ( text => $_[0] ); |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
} elsif (@_ % 2 == 0){ # if there's an even number of arguments, they are key value pairs |
153
|
16
|
|
|
|
|
121
|
%args = @_; |
154
|
|
|
|
|
|
|
} else { # if the argument list is anything else we complain |
155
|
0
|
|
|
|
|
0
|
croak "Invalid arguments"; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
|
159
|
36
|
|
|
|
|
464
|
my __PACKAGE__ $self = $pkg->fields::new(); |
160
|
36
|
|
|
|
|
13732
|
%$self = %args; |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
# fill in some defaults |
163
|
36
|
|
66
|
|
|
337
|
$self->{package} ||= (caller(1))[0]; |
164
|
36
|
|
66
|
|
|
1102
|
$self->{file} ||= (caller(1))[1]; |
165
|
36
|
|
66
|
|
|
333
|
$self->{line} ||= (caller(1))[2]; |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
# these are mainly for rules |
168
|
36
|
|
66
|
|
|
255
|
$self->{script} ||= $0; |
169
|
36
|
|
66
|
|
|
1526
|
$self->{time} ||= localtime; |
170
|
|
|
|
|
|
|
|
171
|
36
|
|
|
|
|
245
|
$self; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
sub import { # export \&FIXME to our caller, /and/ generate a message if there is one to generate |
175
|
9
|
|
|
9
|
|
11242
|
my $pkg = $_[0]; |
176
|
9
|
100
|
|
|
|
53
|
$pkg->init unless @_ > 1; |
177
|
9
|
100
|
100
|
17
|
|
138
|
if (@_ == 1 or @_ > 2 or (@_ == 2 and first { $_[1] eq $_ or $_[1] eq "&$_" } @EXPORT_OK, map { ":$_" } keys %EXPORT_TAGS)){ |
|
17
|
100
|
66
|
|
|
107
|
|
|
4
|
|
66
|
|
|
35
|
|
178
|
7
|
|
|
|
|
9
|
shift; |
179
|
7
|
|
|
|
|
21
|
local $Exporter::ExportLevel = $Exporter::ExportLevel + 1; |
180
|
7
|
|
|
|
|
423
|
$pkg->Exporter::import(@_); |
181
|
|
|
|
|
|
|
} else { |
182
|
2
|
|
|
|
|
9
|
$pkg->init; |
183
|
2
|
|
|
|
|
11
|
goto \&FIXME; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub FIXME { # generate a method |
188
|
25
|
|
|
25
|
0
|
828
|
my $pkg = __PACKAGE__; |
189
|
25
|
100
|
66
|
|
|
971
|
$pkg = shift if UNIVERSAL::can($_[0],"isa") and $_[0]->isa(__PACKAGE__); # it's a method or function, we don't care |
190
|
25
|
|
|
|
|
115
|
$pkg->new(@_)->eval; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
*msg = \&FIXME; # booya. |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
__PACKAGE__ |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
__END__ |