| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Error::Show; |
|
2
|
|
|
|
|
|
|
|
|
3
|
3
|
|
|
3
|
|
200948
|
use 5.024000; |
|
|
3
|
|
|
|
|
28
|
|
|
4
|
3
|
|
|
3
|
|
17
|
use strict; |
|
|
3
|
|
|
|
|
4
|
|
|
|
3
|
|
|
|
|
59
|
|
|
5
|
3
|
|
|
3
|
|
15
|
use warnings; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
71
|
|
|
6
|
3
|
|
|
3
|
|
16
|
use feature "say"; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
311
|
|
|
7
|
3
|
|
|
3
|
|
20
|
use Carp; |
|
|
3
|
|
|
|
|
12
|
|
|
|
3
|
|
|
|
|
177
|
|
|
8
|
3
|
|
|
3
|
|
1584
|
use POSIX; #For _exit; |
|
|
3
|
|
|
|
|
19528
|
|
|
|
3
|
|
|
|
|
17
|
|
|
9
|
3
|
|
|
3
|
|
9859
|
use IPC::Open3; |
|
|
3
|
|
|
|
|
11845
|
|
|
|
3
|
|
|
|
|
170
|
|
|
10
|
3
|
|
|
3
|
|
23
|
use Symbol 'gensym'; # vivify a separate handle for STDERR |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
118
|
|
|
11
|
3
|
|
|
3
|
|
20
|
use Scalar::Util qw; |
|
|
3
|
|
|
|
|
7
|
|
|
|
3
|
|
|
|
|
125
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
#use Exporter qw; |
|
14
|
3
|
|
|
3
|
|
22
|
use base "Exporter"; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
631
|
|
|
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.0'; |
|
27
|
3
|
|
|
3
|
|
23
|
use constant DEBUG=>undef; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
197
|
|
|
28
|
3
|
|
|
|
|
18
|
use enum ("PACKAGE=0",qw
|
|
29
|
|
|
|
|
|
|
HASARGS WANTARRAY EVALTEXT IS_REQUIRE HINTS BITMASK |
|
30
|
3
|
|
|
3
|
|
1499
|
HINT_HASH MESSAGE SEQUENCE CODE_LINES>); |
|
|
3
|
|
|
|
|
3385
|
|
|
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
|
3
|
|
|
3
|
|
29
|
my $package=shift; |
|
47
|
3
|
|
|
|
|
12
|
my @caller=caller; |
|
48
|
3
|
|
|
|
|
8
|
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
|
3
|
50
|
|
|
|
1782
|
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
|
22
|
my $error=pop; |
|
176
|
13
|
|
|
|
|
47
|
my %opts=@_; |
|
177
|
|
|
|
|
|
|
|
|
178
|
13
|
|
|
|
|
34
|
my @error_lines; |
|
179
|
|
|
|
|
|
|
my @errors; |
|
180
|
|
|
|
|
|
|
#my @entry; |
|
181
|
13
|
|
|
|
|
0
|
my %entry; |
|
182
|
13
|
50
|
|
|
|
25
|
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
|
|
|
|
|
18
|
my $i=0; |
|
190
|
13
|
|
|
|
|
61
|
for(split "\n", $error){ |
|
191
|
10
|
|
|
|
|
14
|
DEBUG and say STDERR "ERROR LINE: ".$_; |
|
192
|
10
|
50
|
33
|
|
|
111
|
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
|
|
|
|
|
15
|
DEBUG and say STDERR "PROCESSING: ".$_; |
|
198
|
10
|
|
|
|
|
14
|
DEBUG and say STDERR "file: $1 and line $2"; |
|
199
|
10
|
|
50
|
|
|
61
|
my $entry=$entry{$1}//=[]; |
|
200
|
|
|
|
|
|
|
#push @$entry, {file=>$1, line=>$2,message=>$_, sequence=>$i++}; |
|
201
|
10
|
|
|
|
|
20
|
my $a=[]; |
|
202
|
10
|
|
|
|
|
32
|
$a->[FILENAME]=$1; |
|
203
|
10
|
|
|
|
|
30
|
$a->[LINE]=$2-1; |
|
204
|
10
|
|
|
|
|
25
|
$a->[MESSAGE]=$_; |
|
205
|
10
|
50
|
|
|
|
21
|
$a->[MESSAGE]=$opts{message} if $opts{message}; |
|
206
|
10
|
|
|
|
|
20
|
$a->[SEQUENCE]=$i++; |
|
207
|
10
|
100
|
|
|
|
25
|
$a->[EVALTEXT]=$opts{program} if $opts{program}; |
|
208
|
10
|
|
|
|
|
25
|
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
|
|
|
|
|
55
|
\%entry; |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
} |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# Takes a hash ref error sources |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
sub text_output { |
|
229
|
17
|
|
|
17
|
0
|
25
|
my $info_ref=pop; |
|
230
|
17
|
|
|
|
|
53
|
my %opts=@_; |
|
231
|
17
|
|
|
|
|
31
|
my $total=""; |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
# Sort by sequence number |
|
234
|
|
|
|
|
|
|
# Errors are stored by filename internally. Sort by sequence number. |
|
235
|
|
|
|
|
|
|
# |
|
236
|
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
my @sorted_info= |
|
239
|
3
|
|
|
|
|
11
|
sort { $a->[SEQUENCE] <=> $b->[SEQUENCE] } |
|
240
|
17
|
|
|
|
|
49
|
map { $_->@* } values %$info_ref; |
|
|
14
|
|
|
|
|
48
|
|
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# Reverse the order if we want the first error listed last |
|
243
|
|
|
|
|
|
|
# |
|
244
|
17
|
50
|
|
|
|
47
|
@sorted_info=reverse (@sorted_info)if $opts{reverse}; |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# Process each of the errors in sequence |
|
247
|
17
|
|
|
|
|
31
|
my $counter=0; |
|
248
|
17
|
|
50
|
|
|
65
|
my $limit=$opts{limit}//100; |
|
249
|
17
|
|
|
|
|
37
|
for my $info (@sorted_info){ |
|
250
|
14
|
50
|
33
|
|
|
55
|
last if $counter>=$limit and $limit >0; |
|
251
|
14
|
|
|
|
|
21
|
$counter++; |
|
252
|
14
|
50
|
|
|
|
34
|
unless(exists $info->[CODE_LINES]){ |
|
253
|
14
|
|
|
|
|
19
|
my @code; |
|
254
|
|
|
|
|
|
|
|
|
255
|
14
|
100
|
|
|
|
27
|
if($info->[EVALTEXT]){ |
|
256
|
4
|
|
|
|
|
27
|
@code=split "\n", $info->[EVALTEXT]; |
|
257
|
|
|
|
|
|
|
} |
|
258
|
|
|
|
|
|
|
else { |
|
259
|
10
|
|
|
|
|
15
|
@code=split "\n", do { |
|
260
|
10
|
50
|
|
|
|
370
|
open my $fh, "<", $info->[FILENAME] or warn "Could not open file for reading: $info->[FILENAME]"; |
|
261
|
10
|
|
|
|
|
56
|
local $/=undef; |
|
262
|
10
|
|
|
|
|
479
|
<$fh>; |
|
263
|
|
|
|
|
|
|
}; |
|
264
|
|
|
|
|
|
|
} |
|
265
|
14
|
|
|
|
|
51
|
$info->[CODE_LINES]=\@code; |
|
266
|
|
|
|
|
|
|
} |
|
267
|
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
# At this point we have lines of code in an array |
|
269
|
|
|
|
|
|
|
# |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
#Find start mark and end mark |
|
272
|
|
|
|
|
|
|
# |
|
273
|
14
|
|
|
|
|
21
|
my $start_line=0; |
|
274
|
14
|
100
|
|
|
|
38
|
if($opts{start_mark}){ |
|
275
|
2
|
|
|
|
|
5
|
my $counter=0; |
|
276
|
2
|
|
|
|
|
6
|
my $start_mark=$opts{start_mark}; |
|
277
|
2
|
|
|
|
|
5
|
for($info->[CODE_LINES]->@*){ |
|
278
|
8
|
100
|
|
|
|
32
|
if(/$start_mark/){ |
|
279
|
2
|
|
|
|
|
8
|
$start_line+=$counter+1; |
|
280
|
2
|
|
|
|
|
4
|
last; |
|
281
|
|
|
|
|
|
|
} |
|
282
|
6
|
|
|
|
|
19
|
$counter++; |
|
283
|
|
|
|
|
|
|
} |
|
284
|
|
|
|
|
|
|
# Don't include the start marker in the results |
|
285
|
|
|
|
|
|
|
} |
|
286
|
|
|
|
|
|
|
|
|
287
|
14
|
|
|
|
|
29
|
my $end_line=$info->[CODE_LINES]->@*-1; |
|
288
|
|
|
|
|
|
|
|
|
289
|
14
|
100
|
|
|
|
35
|
if($opts{end_mark}){ |
|
290
|
2
|
|
|
|
|
4
|
my $counter=0; |
|
291
|
2
|
|
|
|
|
3
|
my $end_mark=$opts{end_mark}; |
|
292
|
2
|
|
|
|
|
4
|
for (reverse($info->[CODE_LINES]->@*)){ |
|
293
|
8
|
100
|
|
|
|
26
|
if(/$end_mark/){ |
|
294
|
2
|
|
|
|
|
3
|
$end_line-=$counter; |
|
295
|
2
|
|
|
|
|
13
|
last; |
|
296
|
|
|
|
|
|
|
} |
|
297
|
6
|
|
|
|
|
8
|
$counter++; |
|
298
|
|
|
|
|
|
|
} |
|
299
|
|
|
|
|
|
|
} |
|
300
|
|
|
|
|
|
|
|
|
301
|
14
|
50
|
|
|
|
33
|
$start_line+=$opts{start_offset} if $opts{start_offset}; |
|
302
|
14
|
50
|
|
|
|
26
|
$end_line-=$opts{end_offset } if $opts{end_offset}; |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# preclamp the error line to within this range so that 'Unmatched ' errors |
|
305
|
|
|
|
|
|
|
# at least show ssomething. |
|
306
|
|
|
|
|
|
|
# |
|
307
|
14
|
50
|
|
|
|
42
|
$info->[LINE]=$end_line if $info->[LINE]>$end_line; |
|
308
|
|
|
|
|
|
|
|
|
309
|
14
|
|
|
|
|
20
|
DEBUG and say "START LINE after offset: $start_line"; |
|
310
|
14
|
|
|
|
|
25
|
DEBUG and say "END LINE after offset: $end_line"; |
|
311
|
|
|
|
|
|
|
# At this point the file min and max lines we should consider are |
|
312
|
|
|
|
|
|
|
# start_line and end line inclusive. The $start_line is also used as an |
|
313
|
|
|
|
|
|
|
# offset to shift error sources |
|
314
|
|
|
|
|
|
|
# |
|
315
|
|
|
|
|
|
|
|
|
316
|
14
|
|
|
|
|
27
|
my $min=$info->[LINE]-$opts{pre_lines}; |
|
317
|
14
|
|
|
|
|
26
|
my $max=$info->[LINE]+$opts{post_lines}; |
|
318
|
|
|
|
|
|
|
|
|
319
|
14
|
|
|
|
|
21
|
my $target= $info->[LINE];#-$start_line; |
|
320
|
14
|
|
|
|
|
16
|
DEBUG and say "TARGET: $target"; |
|
321
|
|
|
|
|
|
|
|
|
322
|
14
|
100
|
|
|
|
38
|
$min=$min<$start_line ? $start_line: $min; |
|
323
|
|
|
|
|
|
|
|
|
324
|
14
|
100
|
|
|
|
24
|
$max=$max>$end_line?$end_line:$max; |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
# |
|
327
|
|
|
|
|
|
|
# format counter on the largest number to be expected |
|
328
|
|
|
|
|
|
|
# |
|
329
|
14
|
|
|
|
|
28
|
my $f_len=length("$max"); |
|
330
|
|
|
|
|
|
|
|
|
331
|
14
|
|
|
|
|
40
|
my $out="$opts{indent}$info->[FILENAME]\n"; |
|
332
|
|
|
|
|
|
|
|
|
333
|
14
|
|
50
|
|
|
32
|
my $indent=$opts{indent}//""; |
|
334
|
14
|
|
|
|
|
31
|
my $format="$indent%${f_len}d% 2s %s\n"; |
|
335
|
14
|
|
|
|
|
25
|
my $mark=""; |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
#Change min and max to one based index |
|
338
|
|
|
|
|
|
|
#$min++; |
|
339
|
|
|
|
|
|
|
#$max--; |
|
340
|
14
|
|
|
|
|
16
|
DEBUG and say "min before print $min"; |
|
341
|
14
|
|
|
|
|
17
|
DEBUG and say "max before print $max"; |
|
342
|
14
|
|
|
|
|
42
|
for my $l($min..$max){ |
|
343
|
131
|
|
|
|
|
171
|
$mark=""; |
|
344
|
|
|
|
|
|
|
|
|
345
|
131
|
|
|
|
|
169
|
my $a=$l-$start_line+1; |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
#Perl line number is 1 based |
|
348
|
131
|
100
|
|
|
|
206
|
$mark="=>" if $l==$target; |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
# Print lines as per the index in file array |
|
352
|
131
|
|
|
|
|
300
|
$out.=sprintf $format, $a, $mark, $info->[CODE_LINES][$l]; |
|
353
|
|
|
|
|
|
|
} |
|
354
|
|
|
|
|
|
|
|
|
355
|
14
|
|
|
|
|
33
|
$total.=$out; |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
# Modifiy the message now with updated line numbers |
|
358
|
|
|
|
|
|
|
# TODO: Tidy this up |
|
359
|
14
|
50
|
|
|
|
90
|
$info->[MESSAGE]=~s/line (\d+)(?:\.|,)/(($1-1)>$max?$max:$1-1)-$start_line+1/e; |
|
|
10
|
|
|
|
|
56
|
|
|
360
|
|
|
|
|
|
|
|
|
361
|
14
|
50
|
|
|
|
61
|
$total.=$info->[MESSAGE]."\n" unless $opts{clean}; |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
} |
|
364
|
17
|
50
|
|
|
|
38
|
if($opts{splain}){ |
|
365
|
0
|
|
|
|
|
0
|
$total=splain($total); |
|
366
|
|
|
|
|
|
|
} |
|
367
|
17
|
|
|
|
|
54
|
$total; |
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
#Take an error string and attempt to contextualize it |
|
372
|
|
|
|
|
|
|
# context options_pairs, error string |
|
373
|
|
|
|
|
|
|
sub _context{ |
|
374
|
|
|
|
|
|
|
#use feature ":all"; |
|
375
|
17
|
|
|
17
|
|
24
|
DEBUG and say STDERR "IN context call"; |
|
376
|
|
|
|
|
|
|
#my ($package, $file, $caller_line)=caller; |
|
377
|
|
|
|
|
|
|
# |
|
378
|
|
|
|
|
|
|
# Error is set by single argument, key/value pair, or if no |
|
379
|
|
|
|
|
|
|
# argument $@ is used |
|
380
|
|
|
|
|
|
|
# |
|
381
|
17
|
|
|
|
|
43
|
my %opts=@_; |
|
382
|
|
|
|
|
|
|
|
|
383
|
17
|
|
|
|
|
32
|
my $error= $opts{error}; |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
#$opts{start_mark};#//=qr|.*|; #regex which matches the start of the code |
|
389
|
17
|
|
50
|
|
|
72
|
$opts{pre_lines}//=5; #Number of lines to show before target line |
|
390
|
17
|
|
50
|
|
|
75
|
$opts{post_lines}//=5; #Number of lines to show after target line |
|
391
|
17
|
|
50
|
|
|
61
|
$opts{start_offset}//=0; #Offset past start mark to consider as min line |
|
392
|
17
|
|
50
|
|
|
59
|
$opts{end_offset}//=0; #Offset before end to consider as max line |
|
393
|
17
|
|
50
|
|
|
58
|
$opts{translation}//=0; #A static value added to the line numbering |
|
394
|
17
|
|
100
|
|
|
71
|
$opts{indent}//=""; |
|
395
|
17
|
|
50
|
|
|
63
|
$opts{file}//=""; |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# Get the all the info we need to process |
|
398
|
17
|
|
|
|
|
21
|
my $info_ref; |
|
399
|
17
|
100
|
66
|
|
|
59
|
if(defined($error) and ref($error) eq ""){ |
|
400
|
|
|
|
|
|
|
#A string error. A normal string die/warn or compile time errors/warnings |
|
401
|
13
|
|
|
|
|
42
|
$info_ref=process_string_error %opts, $error; |
|
402
|
|
|
|
|
|
|
#say "infor ref ".join ", ", $info_ref; |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
else{ |
|
405
|
|
|
|
|
|
|
#Some kind of object, converted into line and file hash |
|
406
|
4
|
|
|
|
|
20
|
$info_ref= {$error->[FILENAME]=>[$error]};# {$error->{file}=>[$error]}; |
|
407
|
4
|
|
50
|
|
|
11
|
$error->[MESSAGE]=$opts{message}//""; #Store the message |
|
408
|
4
|
50
|
|
|
|
12
|
$error->[EVALTEXT]=$opts{program} if $opts{program}; |
|
409
|
|
|
|
|
|
|
} |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
# Override text/file to search |
|
412
|
17
|
|
|
|
|
30
|
my $output; |
|
413
|
17
|
|
|
|
|
54
|
$output=text_output %opts, $info_ref; |
|
414
|
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
#TODO: |
|
416
|
|
|
|
|
|
|
# |
|
417
|
17
|
|
|
|
|
96
|
$output; |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
} |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
# |
|
423
|
|
|
|
|
|
|
# Front end to the main processing sub. Configures and checks the inputs |
|
424
|
|
|
|
|
|
|
# |
|
425
|
|
|
|
|
|
|
my $msg= "Trace must be a ref to array of {file=>.., line=>..} pairs"; |
|
426
|
|
|
|
|
|
|
sub context{ |
|
427
|
14
|
|
|
14
|
1
|
6825
|
my %opts; |
|
428
|
|
|
|
|
|
|
my $out; |
|
429
|
14
|
100
|
|
|
|
56
|
if(@_==0){ |
|
|
|
100
|
|
|
|
|
|
|
430
|
3
|
|
|
|
|
9
|
$opts{error}=$@; |
|
431
|
|
|
|
|
|
|
} |
|
432
|
|
|
|
|
|
|
elsif(@_==1){ |
|
433
|
3
|
|
|
|
|
9
|
$opts{error}=shift; |
|
434
|
|
|
|
|
|
|
} |
|
435
|
|
|
|
|
|
|
else { |
|
436
|
8
|
|
|
|
|
31
|
%opts=@_; |
|
437
|
|
|
|
|
|
|
} |
|
438
|
|
|
|
|
|
|
|
|
439
|
14
|
100
|
|
|
|
41
|
if($opts{frames}){ |
|
440
|
1
|
|
|
|
|
5
|
$opts{error}=delete $opts{frames}; |
|
441
|
|
|
|
|
|
|
} |
|
442
|
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
# Convert from supported exceptions classes to internal format |
|
445
|
|
|
|
|
|
|
|
|
446
|
14
|
|
|
|
|
27
|
my $ref=ref $opts{error}; |
|
447
|
14
|
|
|
|
|
22
|
my $dstf="Devel::StackTrace::Frame"; |
|
448
|
|
|
|
|
|
|
|
|
449
|
14
|
50
|
50
|
|
|
124
|
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
|
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
} |
|
464
|
|
|
|
|
|
|
elsif($ref eq "ARRAY" and blessed($opts{error}[0]) eq $dstf){ |
|
465
|
|
|
|
|
|
|
#Array of DSTF object |
|
466
|
|
|
|
|
|
|
} |
|
467
|
|
|
|
|
|
|
else { |
|
468
|
|
|
|
|
|
|
#warn "Expecting a string, caller() type array or a $dstf object, or arrays of these"; |
|
469
|
0
|
|
|
|
|
0
|
$opts{error}="$opts{error}"; |
|
470
|
|
|
|
|
|
|
} |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
# Check for trace kv pair. If this is present. We ignore the error |
|
475
|
|
|
|
|
|
|
# |
|
476
|
14
|
100
|
66
|
|
|
48
|
if(ref($opts{error}) eq "ARRAY" and ref $opts{error}[0]){ |
|
477
|
|
|
|
|
|
|
# Iterate through the list |
|
478
|
1
|
|
50
|
|
|
7
|
my $_indent=$opts{indent}//=" "; |
|
479
|
1
|
|
|
|
|
2
|
my $current_indent=""; |
|
480
|
|
|
|
|
|
|
|
|
481
|
1
|
|
|
|
|
6
|
my %_opts=%opts; |
|
482
|
1
|
|
|
|
|
4
|
for my $e ($opts{error}->@*) { |
|
483
|
|
|
|
|
|
|
|
|
484
|
4
|
50
|
50
|
|
|
21
|
if((blessed($e)//"") eq "Devel::StackTrace::Frame"){ |
|
485
|
|
|
|
|
|
|
#Convert to an array |
|
486
|
0
|
|
|
|
|
0
|
my @a; |
|
487
|
0
|
|
|
|
|
0
|
$a[PACKAGE]=$e->package; |
|
488
|
0
|
|
|
|
|
0
|
$a[FILENAME]=$e->filename; |
|
489
|
0
|
|
|
|
|
0
|
$a[LINE]=$e->line; |
|
490
|
0
|
|
|
|
|
0
|
$a[SUBROUTINE]=$e->subroutine; |
|
491
|
0
|
|
|
|
|
0
|
$a[HASARGS]=$e->hasargs; |
|
492
|
0
|
|
|
|
|
0
|
$a[WANTARRAY]=$e->wantarray; |
|
493
|
0
|
|
|
|
|
0
|
$a[EVALTEXT]=$e->evaltext; |
|
494
|
0
|
|
|
|
|
0
|
$a[IS_REQUIRE]=$e->is_require; |
|
495
|
0
|
|
|
|
|
0
|
$a[HINTS]=$e->hints; |
|
496
|
0
|
|
|
|
|
0
|
$a[BITMASK]=$e->bitmask; |
|
497
|
0
|
|
|
|
|
0
|
$a[HINT_HASH]=$e->hints; |
|
498
|
0
|
|
|
|
|
0
|
$e=\@a; |
|
499
|
|
|
|
|
|
|
} |
|
500
|
4
|
50
|
33
|
|
|
35
|
if($e->[FILENAME] and $e->[LINE]){ |
|
501
|
4
|
|
50
|
|
|
18
|
$e->[MESSAGE]//=""; |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
#Force a message if one is provided |
|
504
|
4
|
|
|
|
|
6
|
$e->[LINE]--; #Make the error 0 based |
|
505
|
4
|
50
|
|
|
|
11
|
$e->[MESSAGE]=$opts{message} if $opts{message}; |
|
506
|
4
|
|
|
|
|
8
|
$_opts{indent}=$current_indent; |
|
507
|
|
|
|
|
|
|
|
|
508
|
4
|
|
|
|
|
5
|
$_opts{error}=$e; |
|
509
|
4
|
|
|
|
|
12
|
$out.=_context %_opts; |
|
510
|
|
|
|
|
|
|
|
|
511
|
4
|
|
|
|
|
12
|
$current_indent.=$_indent; |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
else{ |
|
514
|
0
|
|
|
|
|
0
|
die $msg; |
|
515
|
|
|
|
|
|
|
} |
|
516
|
|
|
|
|
|
|
} |
|
517
|
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
} |
|
519
|
|
|
|
|
|
|
else { |
|
520
|
|
|
|
|
|
|
#say "NOT AN ARRAY: ". join ", ", %opts; |
|
521
|
|
|
|
|
|
|
|
|
522
|
13
|
|
|
|
|
40
|
$out=_context %opts; |
|
523
|
|
|
|
|
|
|
} |
|
524
|
14
|
|
|
|
|
48
|
$out; |
|
525
|
|
|
|
|
|
|
} |
|
526
|
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
my ($chld_in, $chld_out, $chld_err); |
|
528
|
|
|
|
|
|
|
my @cmd="splain"; |
|
529
|
|
|
|
|
|
|
my $pid; |
|
530
|
|
|
|
|
|
|
sub splain { |
|
531
|
0
|
|
|
0
|
1
|
|
my $out; |
|
532
|
|
|
|
|
|
|
#Attempt to open splain process if it isn't already |
|
533
|
0
|
0
|
|
|
|
|
unless($pid){ |
|
534
|
0
|
|
|
|
|
|
eval{ |
|
535
|
0
|
|
|
|
|
|
$pid= open3($chld_in, $chld_out, $chld_err = gensym, @cmd); |
|
536
|
|
|
|
|
|
|
#$chld_in->autoflush(1); |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
}; |
|
539
|
0
|
0
|
0
|
|
|
|
if(!$pid and $@){ |
|
540
|
0
|
|
|
|
|
|
warn "Error::Show Could not splain the results"; |
|
541
|
|
|
|
|
|
|
} |
|
542
|
|
|
|
|
|
|
}; |
|
543
|
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
#Attempt to write to the process and read from it |
|
545
|
0
|
|
|
|
|
|
eval { |
|
546
|
0
|
|
|
|
|
|
print $chld_in $_[0], "\n";; |
|
547
|
0
|
|
|
|
|
|
close $chld_in; |
|
548
|
0
|
|
|
|
|
|
$out=<$chld_out>; |
|
549
|
0
|
|
|
|
|
|
close $chld_out; |
|
550
|
0
|
|
|
|
|
|
close $chld_err; |
|
551
|
|
|
|
|
|
|
}; |
|
552
|
|
|
|
|
|
|
|
|
553
|
0
|
0
|
|
|
|
|
if($@){ |
|
554
|
0
|
|
|
|
|
|
$pid=undef; |
|
555
|
0
|
|
|
|
|
|
close $chld_in; |
|
556
|
0
|
|
|
|
|
|
close $chld_out; |
|
557
|
0
|
|
|
|
|
|
close $chld_err; |
|
558
|
0
|
|
|
|
|
|
warn "Error::Show Could not splain the results"; |
|
559
|
|
|
|
|
|
|
} |
|
560
|
0
|
|
|
|
|
|
$out; |
|
561
|
|
|
|
|
|
|
} |
|
562
|
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
#sub wrap_eval{ |
|
564
|
|
|
|
|
|
|
# my $program=shift; |
|
565
|
|
|
|
|
|
|
# "sub { $program }"; |
|
566
|
|
|
|
|
|
|
#} |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
1; |
|
569
|
|
|
|
|
|
|
__END__ |