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