line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Error::Show; |
2
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
269220
|
use 5.024000; |
|
4
|
|
|
|
|
45
|
|
4
|
4
|
|
|
4
|
|
23
|
use strict; |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
83
|
|
5
|
4
|
|
|
4
|
|
19
|
use warnings; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
115
|
|
6
|
4
|
|
|
4
|
|
21
|
use feature "say"; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
415
|
|
7
|
4
|
|
|
4
|
|
25
|
use Carp; |
|
4
|
|
|
|
|
15
|
|
|
4
|
|
|
|
|
268
|
|
8
|
4
|
|
|
4
|
|
2055
|
use POSIX; #For _exit; |
|
4
|
|
|
|
|
25482
|
|
|
4
|
|
|
|
|
22
|
|
9
|
4
|
|
|
4
|
|
13176
|
use IPC::Open3; |
|
4
|
|
|
|
|
15642
|
|
|
4
|
|
|
|
|
228
|
|
10
|
4
|
|
|
4
|
|
31
|
use Symbol 'gensym'; # vivify a separate handle for STDERR |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
157
|
|
11
|
4
|
|
|
4
|
|
25
|
use Scalar::Util qw; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
175
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
#use Exporter qw; |
14
|
4
|
|
|
4
|
|
34
|
use base "Exporter"; |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
845
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => [ qw( |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
) ] ); |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} }); |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
our @EXPORT = qw(); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
our $VERSION = 'v0.2.1'; |
27
|
4
|
|
|
4
|
|
29
|
use constant DEBUG=>undef; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
265
|
|
28
|
4
|
|
|
|
|
25
|
use enum ("PACKAGE=0",qw
|
29
|
|
|
|
|
|
|
HASARGS WANTARRAY EVALTEXT IS_REQUIRE HINTS BITMASK |
30
|
4
|
|
|
4
|
|
1949
|
HINT_HASH MESSAGE SEQUENCE CODE_LINES>); |
|
4
|
|
|
|
|
4546
|
|
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
################################ |
34
|
|
|
|
|
|
|
# my $buffer=""; # |
35
|
|
|
|
|
|
|
# open THITHER ,">",\$buffer; # |
36
|
|
|
|
|
|
|
################################ |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# |
39
|
|
|
|
|
|
|
# A list of top level file paths or scalar refs to check for syntax errors |
40
|
|
|
|
|
|
|
# |
41
|
|
|
|
|
|
|
my @IINC; |
42
|
|
|
|
|
|
|
sub context; |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub import { |
46
|
4
|
|
|
4
|
|
42
|
my $package=shift; |
47
|
4
|
|
|
|
|
17
|
my @caller=caller; |
48
|
4
|
|
|
|
|
10
|
my @options=@_; |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# We don't export anything. Return when we are used withing code |
52
|
|
|
|
|
|
|
# Continue if caller has no line number, meaning from the CLI |
53
|
|
|
|
|
|
|
# |
54
|
4
|
50
|
|
|
|
3708
|
return if($caller[LINE]); |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# |
57
|
|
|
|
|
|
|
# CLI Options include |
58
|
|
|
|
|
|
|
# |
59
|
0
|
|
|
|
|
0
|
my %options; |
60
|
|
|
|
|
|
|
|
61
|
0
|
|
|
|
|
0
|
my $clean=grep /clean/i, @options; |
62
|
0
|
|
|
|
|
0
|
my $splain=grep /splain/i, @options; |
63
|
0
|
|
|
|
|
0
|
my $do_warn=grep /warn/i, @options; |
64
|
|
|
|
|
|
|
|
65
|
0
|
0
|
|
|
|
0
|
my @warn=$do_warn?():"-MError::Show::Internal"; |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# |
69
|
|
|
|
|
|
|
# 1. Command line argument activation ie -MError::Show |
70
|
|
|
|
|
|
|
# |
71
|
|
|
|
|
|
|
# Find out any extra lib paths used. To do this we: |
72
|
|
|
|
|
|
|
# |
73
|
|
|
|
|
|
|
# a. fork/exec a new perl process using the value of $^X. |
74
|
|
|
|
|
|
|
# b. The new process dumps the @INC array to STDOUT |
75
|
|
|
|
|
|
|
# c. This process reads the output and stores in @IINC |
76
|
|
|
|
|
|
|
# |
77
|
|
|
|
|
|
|
# Only run it the first time its used |
78
|
|
|
|
|
|
|
# Is this the best way? Not sure. At least this way there is no argument |
79
|
|
|
|
|
|
|
# processing, perl process does it for us. |
80
|
|
|
|
|
|
|
# |
81
|
|
|
|
|
|
|
|
82
|
0
|
0
|
|
|
|
0
|
@IINC=map {chomp; $_} do { |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
83
|
0
|
0
|
|
|
|
0
|
open my $fh, "-|", $^X . q| -E 'map print("$_\n"), @INC'| or die "$!"; |
84
|
0
|
|
|
|
|
0
|
<$fh>; |
85
|
|
|
|
|
|
|
} unless @IINC; |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# |
88
|
|
|
|
|
|
|
# 2. Extract the extra include paths |
89
|
|
|
|
|
|
|
# |
90
|
|
|
|
|
|
|
# Built up the 'extra' array of any include paths not already listed |
91
|
|
|
|
|
|
|
# from the STDOUT dumping above |
92
|
|
|
|
|
|
|
# |
93
|
0
|
|
|
|
|
0
|
my @extra=map {("-I", $_)} grep {my $i=$_; !grep { $i eq $_} @IINC} @INC; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# |
98
|
|
|
|
|
|
|
# 3. Syntax checking the program |
99
|
|
|
|
|
|
|
# |
100
|
|
|
|
|
|
|
# Now we have the include paths sorted, |
101
|
|
|
|
|
|
|
# a. fork/exec again, this time with the -c switch for perl to check syntax |
102
|
|
|
|
|
|
|
# b. slurp STDERR from child process |
103
|
|
|
|
|
|
|
# c. execute the context routine to parse and show more source code context |
104
|
|
|
|
|
|
|
# d. print! |
105
|
|
|
|
|
|
|
# The proc |
106
|
|
|
|
|
|
|
|
107
|
0
|
|
|
|
|
0
|
local $/=undef; |
108
|
0
|
|
|
|
|
0
|
my $file=$0; |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
#push @file, @ARGV; |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
#my $runnable=not $^C;#$options{check}; |
113
|
|
|
|
|
|
|
#for my $file(@file){ |
114
|
0
|
0
|
0
|
|
|
0
|
die "Error::Show cannot process STDIN, -e and -E programs" if $file eq "-e" or $file eq "-E" or $file eq "-"; |
|
|
|
0
|
|
|
|
|
115
|
0
|
0
|
|
|
|
0
|
die "Error::Show cannot access \"$file\"" unless -f $file; |
116
|
0
|
|
|
|
|
0
|
my @cmd= ($^X ,@warn, @extra, "-c", $file); |
117
|
|
|
|
|
|
|
|
118
|
0
|
|
|
|
|
0
|
my $pid; |
119
|
|
|
|
|
|
|
my $result; |
120
|
0
|
|
|
|
|
0
|
eval { |
121
|
0
|
|
|
|
|
0
|
$pid=open3(my $chld_in, my $chld_out, my $chld_err = gensym, @cmd); |
122
|
0
|
|
|
|
|
0
|
$result=<$chld_err>; |
123
|
0
|
|
|
|
|
0
|
close $chld_in; |
124
|
0
|
|
|
|
|
0
|
close $chld_out; |
125
|
0
|
|
|
|
|
0
|
close $chld_err; |
126
|
0
|
|
|
|
|
0
|
wait; |
127
|
|
|
|
|
|
|
}; |
128
|
0
|
0
|
0
|
|
|
0
|
if(!$pid and $@){ |
129
|
0
|
|
|
|
|
0
|
die "Error::Show failed to syntax check"; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# |
134
|
|
|
|
|
|
|
# 4. Status code from child indicates success |
135
|
|
|
|
|
|
|
# When 0 this means syntax was ok. Otherwise error |
136
|
|
|
|
|
|
|
# Attempt to propogate code to exit status |
137
|
|
|
|
|
|
|
# |
138
|
0
|
0
|
|
|
|
0
|
my $code=$?>255? (0xFF & ~$?): $?; |
139
|
|
|
|
|
|
|
|
140
|
0
|
|
|
|
|
0
|
my $runnable=$?==0; |
141
|
|
|
|
|
|
|
#say "SYNTAX RUNNABLE: $runnable"; |
142
|
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
0
|
my $status=context(splain=>$splain, clean=>$clean, error=>$result )."\n"; |
144
|
|
|
|
|
|
|
|
145
|
0
|
0
|
|
|
|
0
|
if($^C){ |
146
|
0
|
0
|
|
|
|
0
|
if($runnable){ |
147
|
|
|
|
|
|
|
#only print status if we want warnings |
148
|
0
|
0
|
|
|
|
0
|
print STDERR $do_warn?$status: "$file syntax OK\n"; |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
else{ |
152
|
|
|
|
|
|
|
#Not runnable, thus syntax error. Always print |
153
|
0
|
|
|
|
|
0
|
print STDERR $status; |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
} |
156
|
0
|
|
|
|
|
0
|
POSIX::_exit $code; |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
else{ |
160
|
|
|
|
|
|
|
#not checking, we want to run |
161
|
0
|
0
|
|
|
|
0
|
if($runnable){ |
162
|
|
|
|
|
|
|
# don't bother with warnings |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
else{ |
166
|
|
|
|
|
|
|
#Not runnable, thus syntax error. Always print |
167
|
0
|
|
|
|
|
0
|
print STDERR $status; |
168
|
0
|
|
|
|
|
0
|
POSIX::_exit $code; |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
sub process_string_error{ |
175
|
13
|
|
|
13
|
0
|
21
|
my $error=pop; |
176
|
13
|
|
|
|
|
42
|
my %opts=@_; |
177
|
|
|
|
|
|
|
|
178
|
13
|
|
|
|
|
28
|
my @error_lines; |
179
|
|
|
|
|
|
|
my @errors; |
180
|
|
|
|
|
|
|
#my @entry; |
181
|
13
|
|
|
|
|
0
|
my %entry; |
182
|
13
|
50
|
|
|
|
44
|
if(defined $error){ |
183
|
|
|
|
|
|
|
#local $_=$error; |
184
|
|
|
|
|
|
|
#Substitue with a line number relative to the start marker |
185
|
|
|
|
|
|
|
#Reported line numbers are 1 based, stored lines are 0 based |
186
|
|
|
|
|
|
|
#my $translation=$opts{translation}; |
187
|
|
|
|
|
|
|
#my $start=$opts{start}; |
188
|
|
|
|
|
|
|
|
189
|
13
|
|
|
|
|
21
|
my $i=0; |
190
|
13
|
|
|
|
|
46
|
for(split "\n", $error){ |
191
|
10
|
|
|
|
|
16
|
DEBUG and say STDERR "ERROR LINE: ".$_; |
192
|
10
|
50
|
33
|
|
|
78
|
if(/at (.*?) line (\d+)/ |
193
|
|
|
|
|
|
|
or /Missing right curly or square bracket at (.*?) (\d+) at end of line/){ |
194
|
|
|
|
|
|
|
# |
195
|
|
|
|
|
|
|
# Group by file names |
196
|
|
|
|
|
|
|
# |
197
|
10
|
|
|
|
|
14
|
DEBUG and say STDERR "PROCESSING: ".$_; |
198
|
10
|
|
|
|
|
13
|
DEBUG and say STDERR "file: $1 and line $2"; |
199
|
10
|
|
50
|
|
|
52
|
my $entry=$entry{$1}//=[]; |
200
|
|
|
|
|
|
|
#push @$entry, {file=>$1, line=>$2,message=>$_, sequence=>$i++}; |
201
|
10
|
|
|
|
|
20
|
my $a=[]; |
202
|
10
|
|
|
|
|
25
|
$a->[FILENAME]=$1; |
203
|
10
|
|
|
|
|
28
|
$a->[LINE]=$2-1; |
204
|
10
|
|
|
|
|
23
|
$a->[MESSAGE]=$_; |
205
|
10
|
50
|
|
|
|
21
|
$a->[MESSAGE]=$opts{message} if $opts{message}; |
206
|
10
|
|
|
|
|
19
|
$a->[SEQUENCE]=$i++; |
207
|
10
|
100
|
|
|
|
21
|
$a->[EVALTEXT]=$opts{program} if $opts{program}; |
208
|
10
|
|
|
|
|
46
|
push @$entry, $a; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
else { |
215
|
|
|
|
|
|
|
#Assume a target line |
216
|
|
|
|
|
|
|
#push @error_lines, $opts{line}-1; |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
#Key is file name |
220
|
|
|
|
|
|
|
# value is a hash of filename,line number, perl error string and the sequence number |
221
|
|
|
|
|
|
|
|
222
|
13
|
|
|
|
|
49
|
\%entry; |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# Takes a hash ref error sources |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
sub text_output { |
229
|
23
|
|
|
23
|
0
|
37
|
my $info_ref=pop; |
230
|
23
|
|
|
|
|
75
|
my %opts=@_; |
231
|
23
|
|
|
|
|
48
|
my $total=""; |
232
|
|
|
|
|
|
|
|
233
|
23
|
|
|
|
|
28
|
DEBUG and say STDERR "Reverse flag in text output set to: $opts{reverse}"; |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# Sort by sequence number |
236
|
|
|
|
|
|
|
# Errors are stored by filename internally. Sort by sequence number. |
237
|
|
|
|
|
|
|
# |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
my @sorted_info= |
240
|
3
|
|
|
|
|
12
|
sort {$a->[SEQUENCE] <=> $b->[SEQUENCE] } |
241
|
23
|
|
|
|
|
70
|
map { $_->@* } values %$info_ref; |
|
20
|
|
|
|
|
70
|
|
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
# Reverse the order if we want the first error listed last |
244
|
|
|
|
|
|
|
# |
245
|
23
|
100
|
|
|
|
70
|
@sorted_info=reverse (@sorted_info) if $opts{reverse}; |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
# Process each of the errors in sequence |
248
|
23
|
|
|
|
|
37
|
my $counter=0; |
249
|
23
|
|
50
|
|
|
68
|
my $limit=$opts{limit}//100; |
250
|
23
|
|
|
|
|
47
|
for my $info (@sorted_info){ |
251
|
20
|
50
|
33
|
|
|
59
|
last if $counter>=$limit and $limit >0; |
252
|
20
|
|
|
|
|
30
|
$counter++; |
253
|
20
|
50
|
|
|
|
44
|
unless(exists $info->[CODE_LINES]){ |
254
|
20
|
|
|
|
|
28
|
my @code; |
255
|
|
|
|
|
|
|
|
256
|
20
|
100
|
|
|
|
40
|
if($info->[EVALTEXT]){ |
257
|
4
|
|
|
|
|
28
|
@code=split "\n", $info->[EVALTEXT]; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
else { |
260
|
16
|
|
|
|
|
22
|
@code=split "\n", do { |
261
|
16
|
50
|
|
|
|
603
|
open my $fh, "<", $info->[FILENAME] or warn "Could not open file for reading: $info->[FILENAME]"; |
262
|
16
|
|
|
|
|
96
|
local $/=undef; |
263
|
16
|
|
|
|
|
879
|
<$fh>; |
264
|
|
|
|
|
|
|
}; |
265
|
|
|
|
|
|
|
} |
266
|
20
|
|
|
|
|
72
|
$info->[CODE_LINES]=\@code; |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
# At this point we have lines of code in an array |
270
|
|
|
|
|
|
|
# |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
#Find start mark and end mark |
273
|
|
|
|
|
|
|
# |
274
|
20
|
|
|
|
|
33
|
my $start_line=0; |
275
|
20
|
100
|
|
|
|
59
|
if($opts{start_mark}){ |
276
|
2
|
|
|
|
|
4
|
my $counter=0; |
277
|
2
|
|
|
|
|
4
|
my $start_mark=$opts{start_mark}; |
278
|
2
|
|
|
|
|
5
|
for($info->[CODE_LINES]->@*){ |
279
|
8
|
100
|
|
|
|
48
|
if(/$start_mark/){ |
280
|
2
|
|
|
|
|
5
|
$start_line+=$counter+1; |
281
|
2
|
|
|
|
|
4
|
last; |
282
|
|
|
|
|
|
|
} |
283
|
6
|
|
|
|
|
8
|
$counter++; |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
# Don't include the start marker in the results |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
20
|
|
|
|
|
53
|
my $end_line=$info->[CODE_LINES]->@*-1; |
289
|
|
|
|
|
|
|
|
290
|
20
|
100
|
|
|
|
44
|
if($opts{end_mark}){ |
291
|
2
|
|
|
|
|
4
|
my $counter=0; |
292
|
2
|
|
|
|
|
4
|
my $end_mark=$opts{end_mark}; |
293
|
2
|
|
|
|
|
4
|
for (reverse($info->[CODE_LINES]->@*)){ |
294
|
8
|
100
|
|
|
|
25
|
if(/$end_mark/){ |
295
|
2
|
|
|
|
|
3
|
$end_line-=$counter; |
296
|
2
|
|
|
|
|
3
|
last; |
297
|
|
|
|
|
|
|
} |
298
|
6
|
|
|
|
|
9
|
$counter++; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
20
|
50
|
|
|
|
45
|
$start_line+=$opts{start_offset} if $opts{start_offset}; |
303
|
20
|
50
|
|
|
|
39
|
$end_line-=$opts{end_offset } if $opts{end_offset}; |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# preclamp the error line to within this range so that 'Unmatched ' errors |
306
|
|
|
|
|
|
|
# at least show ssomething. |
307
|
|
|
|
|
|
|
# |
308
|
20
|
50
|
|
|
|
47
|
$info->[LINE]=$end_line if $info->[LINE]>$end_line; |
309
|
|
|
|
|
|
|
|
310
|
20
|
|
|
|
|
23
|
DEBUG and say "START LINE after offset: $start_line"; |
311
|
20
|
|
|
|
|
30
|
DEBUG and say "END LINE after offset: $end_line"; |
312
|
|
|
|
|
|
|
# At this point the file min and max lines we should consider are |
313
|
|
|
|
|
|
|
# start_line and end line inclusive. The $start_line is also used as an |
314
|
|
|
|
|
|
|
# offset to shift error sources |
315
|
|
|
|
|
|
|
# |
316
|
|
|
|
|
|
|
|
317
|
20
|
|
|
|
|
37
|
my $min=$info->[LINE]-$opts{pre_lines}; |
318
|
20
|
|
|
|
|
47
|
my $max=$info->[LINE]+$opts{post_lines}; |
319
|
|
|
|
|
|
|
|
320
|
20
|
|
|
|
|
27
|
my $target= $info->[LINE];#-$start_line; |
321
|
20
|
|
|
|
|
28
|
DEBUG and say "TARGET: $target"; |
322
|
|
|
|
|
|
|
|
323
|
20
|
100
|
|
|
|
54
|
$min=$min<$start_line ? $start_line: $min; |
324
|
|
|
|
|
|
|
|
325
|
20
|
100
|
|
|
|
40
|
$max=$max>$end_line?$end_line:$max; |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
# |
328
|
|
|
|
|
|
|
# format counter on the largest number to be expected |
329
|
|
|
|
|
|
|
# |
330
|
20
|
|
|
|
|
38
|
my $f_len=length("$max"); |
331
|
|
|
|
|
|
|
|
332
|
20
|
|
|
|
|
54
|
my $out="$opts{indent}$info->[FILENAME]\n"; |
333
|
|
|
|
|
|
|
|
334
|
20
|
|
50
|
|
|
50
|
my $indent=$opts{indent}//""; |
335
|
20
|
|
|
|
|
44
|
my $format="$indent%${f_len}d% 2s %s\n"; |
336
|
20
|
|
|
|
|
38
|
my $mark=""; |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
#Change min and max to one based index |
339
|
|
|
|
|
|
|
#$min++; |
340
|
|
|
|
|
|
|
#$max--; |
341
|
20
|
|
|
|
|
23
|
DEBUG and say STDERR "min before print $min"; |
342
|
20
|
|
|
|
|
28
|
DEBUG and say STDERR "max before print $max"; |
343
|
20
|
|
|
|
|
58
|
for my $l($min..$max){ |
344
|
191
|
|
|
|
|
290
|
$mark=""; |
345
|
|
|
|
|
|
|
|
346
|
191
|
|
|
|
|
251
|
my $a=$l-$start_line+1; |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
#Perl line number is 1 based |
349
|
191
|
100
|
|
|
|
756
|
$mark="=>" if $l==$target; |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# Print lines as per the index in file array |
353
|
191
|
|
|
|
|
460
|
$out.=sprintf $format, $a, $mark, $info->[CODE_LINES][$l]; |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
|
356
|
20
|
|
|
|
|
47
|
$total.=$out; |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
# Modifiy the message now with updated line numbers |
359
|
|
|
|
|
|
|
# TODO: Tidy this up |
360
|
20
|
50
|
|
|
|
95
|
$info->[MESSAGE]=~s/line (\d+)(?:\.|,)/(($1-1)>$max?$max:$1-1)-$start_line+1/e; |
|
10
|
|
|
|
|
60
|
|
361
|
|
|
|
|
|
|
|
362
|
20
|
50
|
|
|
|
78
|
$total.=$info->[MESSAGE]."\n" unless $opts{clean}; |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
} |
365
|
23
|
50
|
|
|
|
50
|
if($opts{splain}){ |
366
|
0
|
|
|
|
|
0
|
$total=splain($total); |
367
|
|
|
|
|
|
|
} |
368
|
23
|
|
|
|
|
80
|
$total; |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
#Take an error string and attempt to contextualize it |
373
|
|
|
|
|
|
|
# context options_pairs, error string |
374
|
|
|
|
|
|
|
sub _context{ |
375
|
|
|
|
|
|
|
#use feature ":all"; |
376
|
23
|
|
|
23
|
|
35
|
DEBUG and say STDERR "IN context call"; |
377
|
|
|
|
|
|
|
#my ($package, $file, $caller_line)=caller; |
378
|
|
|
|
|
|
|
# |
379
|
|
|
|
|
|
|
# Error is set by single argument, key/value pair, or if no |
380
|
|
|
|
|
|
|
# argument $@ is used |
381
|
|
|
|
|
|
|
# |
382
|
23
|
|
|
|
|
57
|
my %opts=@_; |
383
|
|
|
|
|
|
|
|
384
|
23
|
|
|
|
|
49
|
my $error= $opts{error}; |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
#$opts{start_mark};#//=qr|.*|; #regex which matches the start of the code |
390
|
23
|
|
50
|
|
|
94
|
$opts{pre_lines}//=5; #Number of lines to show before target line |
391
|
23
|
|
50
|
|
|
81
|
$opts{post_lines}//=5; #Number of lines to show after target line |
392
|
23
|
|
50
|
|
|
95
|
$opts{start_offset}//=0; #Offset past start mark to consider as min line |
393
|
23
|
|
50
|
|
|
78
|
$opts{end_offset}//=0; #Offset before end to consider as max line |
394
|
23
|
|
50
|
|
|
115
|
$opts{translation}//=0; #A static value added to the line numbering |
395
|
23
|
|
100
|
|
|
71
|
$opts{indent}//=""; |
396
|
23
|
|
50
|
|
|
81
|
$opts{file}//=""; |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
# Get the all the info we need to process |
399
|
23
|
|
|
|
|
29
|
my $info_ref; |
400
|
23
|
100
|
66
|
|
|
93
|
if(defined($error) and ref($error) eq ""){ |
401
|
|
|
|
|
|
|
#A string error. A normal string die/warn or compile time errors/warnings |
402
|
13
|
|
|
|
|
46
|
$info_ref=process_string_error %opts, $error; |
403
|
|
|
|
|
|
|
#say "infor ref ".join ", ", $info_ref; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
else{ |
406
|
|
|
|
|
|
|
#Some kind of object, converted into line and file hash |
407
|
10
|
|
|
|
|
33
|
$info_ref= {$error->[FILENAME]=>[$error]};# {$error->{file}=>[$error]}; |
408
|
10
|
|
100
|
|
|
33
|
$error->[MESSAGE]=$opts{message}//""; #Store the message |
409
|
10
|
50
|
|
|
|
35
|
$error->[EVALTEXT]=$opts{program} if $opts{program}; |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# Override text/file to search |
413
|
23
|
|
|
|
|
37
|
my $output; |
414
|
23
|
|
|
|
|
71
|
$output=text_output %opts, $info_ref; |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
#TODO: |
417
|
|
|
|
|
|
|
# |
418
|
23
|
|
|
|
|
125
|
$output; |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
# |
424
|
|
|
|
|
|
|
# Front end to the main processing sub. Configures and checks the inputs |
425
|
|
|
|
|
|
|
# |
426
|
|
|
|
|
|
|
my $msg= "Trace must be a ref to array of {file=>.., line=>..} pairs"; |
427
|
|
|
|
|
|
|
sub context{ |
428
|
16
|
|
|
16
|
1
|
6840
|
my %opts; |
429
|
|
|
|
|
|
|
my $out; |
430
|
16
|
100
|
|
|
|
74
|
if(@_==0){ |
|
|
100
|
|
|
|
|
|
431
|
3
|
|
|
|
|
10
|
$opts{error}=$@; |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
elsif(@_==1){ |
434
|
3
|
|
|
|
|
9
|
$opts{error}=shift; |
435
|
|
|
|
|
|
|
} |
436
|
|
|
|
|
|
|
else { |
437
|
10
|
|
|
|
|
37
|
%opts=@_; |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
|
440
|
16
|
100
|
|
|
|
48
|
if($opts{frames}){ |
441
|
3
|
|
|
|
|
8
|
$opts{error}=delete $opts{frames}; |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
# Convert from supported exceptions classes to internal format |
445
|
|
|
|
|
|
|
|
446
|
16
|
|
|
|
|
34
|
my $ref=ref $opts{error}; |
447
|
16
|
|
|
|
|
25
|
my $dstf="Devel::StackTrace::Frame"; |
448
|
|
|
|
|
|
|
|
449
|
16
|
50
|
50
|
|
|
157
|
if((blessed($opts{error})//"") eq $dstf){ |
|
|
50
|
66
|
|
|
|
|
|
|
100
|
33
|
|
|
|
|
|
|
50
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
450
|
|
|
|
|
|
|
# Single DSTF stack frame. Convert to an array |
451
|
0
|
|
|
|
|
0
|
$opts{error}=[$opts{error}]; |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
elsif($ref eq "ARRAY" and ref($opts{error}[0]) eq ""){ |
454
|
|
|
|
|
|
|
# Array of scalars - a normal stack frame - wrap it |
455
|
0
|
|
|
|
|
0
|
$opts{error}=[[$opts{error}->@*]]; |
456
|
|
|
|
|
|
|
} |
457
|
|
|
|
|
|
|
elsif($ref eq ""){ |
458
|
|
|
|
|
|
|
# Not a reference - A string error |
459
|
|
|
|
|
|
|
} |
460
|
|
|
|
|
|
|
elsif($ref eq "ARRAY" and ref($opts{error}[0]) eq "ARRAY"){ |
461
|
|
|
|
|
|
|
# Array of arrays of scalars |
462
|
3
|
|
|
|
|
9
|
$opts{error}=[map { [$_->@*] } $opts{error}->@* ]; |
|
10
|
|
|
|
|
34
|
|
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
elsif($ref eq "ARRAY" and blessed($opts{error}[0]) eq $dstf){ |
466
|
|
|
|
|
|
|
#Array of DSTF object |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
else { |
469
|
|
|
|
|
|
|
# Force stringification of error as a last ditch attempt |
470
|
0
|
|
|
|
|
0
|
$opts{error}="$opts{error}"; |
471
|
|
|
|
|
|
|
} |
472
|
|
|
|
|
|
|
|
473
|
16
|
|
|
|
|
25
|
DEBUG and say STDERR "Reverse flag set to: $opts{reverse}"; |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
# Reverse the ordering of errors here if requested |
476
|
|
|
|
|
|
|
# |
477
|
16
|
100
|
|
|
|
40
|
$opts{error}->@*=reverse $opts{error}->@* if $opts{reverse}; |
478
|
|
|
|
|
|
|
# Check for trace kv pair. If this is present. We ignore the error |
479
|
|
|
|
|
|
|
# |
480
|
16
|
100
|
66
|
|
|
74
|
if(ref($opts{error}) eq "ARRAY" and ref $opts{error}[0]){ |
481
|
|
|
|
|
|
|
# Iterate through the list |
482
|
3
|
|
50
|
|
|
19
|
my $_indent=$opts{indent}//=" "; |
483
|
3
|
|
|
|
|
6
|
my $current_indent=""; |
484
|
|
|
|
|
|
|
|
485
|
3
|
|
|
|
|
13
|
my %_opts=%opts; |
486
|
3
|
|
|
|
|
6
|
my $i=0; #Sequence number |
487
|
3
|
|
|
|
|
9
|
for my $e ($opts{error}->@*) { |
488
|
|
|
|
|
|
|
|
489
|
10
|
50
|
50
|
|
|
54
|
if((blessed($e)//"") eq "Devel::StackTrace::Frame"){ |
490
|
|
|
|
|
|
|
#Convert to an array |
491
|
0
|
|
|
|
|
0
|
my @a; |
492
|
0
|
|
|
|
|
0
|
$a[PACKAGE]=$e->package; |
493
|
0
|
|
|
|
|
0
|
$a[FILENAME]=$e->filename; |
494
|
0
|
|
|
|
|
0
|
$a[LINE]=$e->line; |
495
|
0
|
|
|
|
|
0
|
$a[SUBROUTINE]=$e->subroutine; |
496
|
0
|
|
|
|
|
0
|
$a[HASARGS]=$e->hasargs; |
497
|
0
|
|
|
|
|
0
|
$a[WANTARRAY]=$e->wantarray; |
498
|
0
|
|
|
|
|
0
|
$a[EVALTEXT]=$e->evaltext; |
499
|
0
|
|
|
|
|
0
|
$a[IS_REQUIRE]=$e->is_require; |
500
|
0
|
|
|
|
|
0
|
$a[HINTS]=$e->hints; |
501
|
0
|
|
|
|
|
0
|
$a[BITMASK]=$e->bitmask; |
502
|
0
|
|
|
|
|
0
|
$a[HINT_HASH]=$e->hints; |
503
|
0
|
|
|
|
|
0
|
$e=\@a; |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
|
507
|
10
|
50
|
33
|
|
|
58
|
if($e->[FILENAME] and $e->[LINE]){ |
508
|
10
|
|
50
|
|
|
43
|
$e->[MESSAGE]//=""; |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
#Force a message if one is provided |
511
|
10
|
|
|
|
|
18
|
$e->[LINE]--; #Make the error 0 based |
512
|
10
|
100
|
|
|
|
21
|
$e->[MESSAGE]=$opts{message} if $opts{message}; |
513
|
10
|
|
|
|
|
18
|
$e->[SEQUENCE]=$i++; |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
# Generate the context here |
516
|
|
|
|
|
|
|
# |
517
|
10
|
|
|
|
|
16
|
$_opts{indent}=$current_indent; |
518
|
10
|
|
|
|
|
16
|
$_opts{error}=$e; |
519
|
10
|
|
|
|
|
29
|
$out.=_context %_opts; |
520
|
10
|
|
|
|
|
28
|
$current_indent.=$_indent; |
521
|
|
|
|
|
|
|
} |
522
|
|
|
|
|
|
|
else{ |
523
|
0
|
|
|
|
|
0
|
die $msg; |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
else { |
529
|
|
|
|
|
|
|
#say "NOT AN ARRAY: ". join ", ", %opts; |
530
|
|
|
|
|
|
|
|
531
|
13
|
|
|
|
|
39
|
$out=_context %opts; |
532
|
|
|
|
|
|
|
} |
533
|
16
|
|
|
|
|
108
|
$out; |
534
|
|
|
|
|
|
|
} |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
my ($chld_in, $chld_out, $chld_err); |
537
|
|
|
|
|
|
|
my @cmd="splain"; |
538
|
|
|
|
|
|
|
my $pid; |
539
|
|
|
|
|
|
|
sub splain { |
540
|
0
|
|
|
0
|
1
|
|
my $out; |
541
|
|
|
|
|
|
|
#Attempt to open splain process if it isn't already |
542
|
0
|
0
|
|
|
|
|
unless($pid){ |
543
|
0
|
|
|
|
|
|
eval{ |
544
|
0
|
|
|
|
|
|
$pid= open3($chld_in, $chld_out, $chld_err = gensym, @cmd); |
545
|
|
|
|
|
|
|
#$chld_in->autoflush(1); |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
}; |
548
|
0
|
0
|
0
|
|
|
|
if(!$pid and $@){ |
549
|
0
|
|
|
|
|
|
warn "Error::Show Could not splain the results"; |
550
|
|
|
|
|
|
|
} |
551
|
|
|
|
|
|
|
}; |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
#Attempt to write to the process and read from it |
554
|
0
|
|
|
|
|
|
eval { |
555
|
0
|
|
|
|
|
|
print $chld_in $_[0], "\n";; |
556
|
0
|
|
|
|
|
|
close $chld_in; |
557
|
0
|
|
|
|
|
|
$out=<$chld_out>; |
558
|
0
|
|
|
|
|
|
close $chld_out; |
559
|
0
|
|
|
|
|
|
close $chld_err; |
560
|
|
|
|
|
|
|
}; |
561
|
|
|
|
|
|
|
|
562
|
0
|
0
|
|
|
|
|
if($@){ |
563
|
0
|
|
|
|
|
|
$pid=undef; |
564
|
0
|
|
|
|
|
|
close $chld_in; |
565
|
0
|
|
|
|
|
|
close $chld_out; |
566
|
0
|
|
|
|
|
|
close $chld_err; |
567
|
0
|
|
|
|
|
|
warn "Error::Show Could not splain the results"; |
568
|
|
|
|
|
|
|
} |
569
|
0
|
|
|
|
|
|
$out; |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
#sub wrap_eval{ |
573
|
|
|
|
|
|
|
# my $program=shift; |
574
|
|
|
|
|
|
|
# "sub { $program }"; |
575
|
|
|
|
|
|
|
#} |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
1; |
578
|
|
|
|
|
|
|
__END__ |