File Coverage

blib/lib/Parse/Diagnostics.pm
Criterion Covered Total %
statement 43 54 79.6
branch 3 6 50.0
condition n/a
subroutine 8 9 88.8
pod 1 4 25.0
total 55 73 75.3


line stmt bran cond sub pod time code
1             package Parse::Diagnostics;
2             require Exporter;
3             @ISA = qw(Exporter);
4             @EXPORT_OK = qw/parse_diagnostics parse_diagnostics_pp parse_diagnostics_xs/;
5             %EXPORT_TAGS = (
6             all => \@EXPORT_OK,
7             );
8 2     2   35072 use warnings;
  2         3  
  2         70  
9 2     2   8 use strict;
  2         3  
  2         38  
10 2     2   6 use Carp;
  2         5  
  2         128  
11 2     2   1817 use Path::Tiny;
  2         23929  
  2         114  
12 2     2   1154 use C::Tokenize '$string_re';
  2         7125  
  2         2113  
13             our $VERSION = '0.03';
14              
15             our $message_re = qr
16             /
17             (
18             "([^"]|\\")+"
19             |
20             '([^']|\\')+'
21             )
22             (?:,.*?)?
23             /x;
24              
25             our $diagnostics_re = qr
26             /
27             (
28             croak
29             |
30             carp
31             |
32             die
33             |
34             warn
35             |
36             confess
37             |
38             cluck
39             )
40             \s*
41             (
42             \(
43             $message_re
44             \)
45             |
46             $message_re
47             )
48             /x;
49              
50             our $xs_diagnostics_re = qr
51             /
52             (
53             croak
54             |
55             warn
56             |
57             vwarn
58             |
59             vcroak
60             |
61             die
62             |
63             croak_sv
64             |
65             die_sv
66             )
67             \s*
68             \(
69             \s*
70             ($string_re)
71             /x;
72              
73             our $c_diagnostics_re = qr
74             /
75             (v?fprintf)
76             \s*\(\s*
77             stderr
78             \s*,\s*
79             ($string_re)
80             /x;
81              
82             # Match "$regex" to "$contents" globally, and record the lines of each
83             # match.
84              
85             sub regex_lines
86             {
87 2     2 0 5 my ($contents, $regex) = @_;
88             # Copy the contents, then delete chunks off the front of it as we
89             # find diagnostics, so we can keep track of the line numbers.
90 2         2 my $copycontents = $contents;
91 2         4 my @diagnostics;
92 2         2 my $line = 1;
93 2         743 while ($copycontents =~ s/^(.*?)$regex//s) {
94 3         22 my $leading = $1;
95 3         7 my $type = $2;
96 3         6 my $message = $3;
97             # Count the lines in $leading.
98 3         17 my $lines = ($leading =~ tr/\n//);
99 3         13 push @diagnostics, {
100             type => $type,
101             message => $message,
102             line => $line + $lines,
103             };
104             # print "$message ", $line + $lines, "\n";
105             # Add the lines in $lines and whatever lines may be in
106             # $message to the current line.
107 3         868 $line += $lines + ($message =~ tr/\n//);
108             }
109 2         8 return \@diagnostics;
110             }
111              
112             sub parse_diagnostics_pp
113             {
114 2     2 0 8 my ($contents, %options) = @_;
115 2         7 return regex_lines ($contents, $diagnostics_re);
116             }
117              
118             sub parse_diagnostics_xs
119             {
120 0     0 0 0 my ($contents, %options) = @_;
121 0         0 my @diagnostics;
122 0         0 my $xs = regex_lines ($contents, $xs_diagnostics_re);
123 0         0 push @diagnostics, @$xs;
124 0         0 my $c = regex_lines ($contents, $c_diagnostics_re);
125 0         0 push @diagnostics, @$c;
126 0         0 return \@diagnostics;
127             }
128              
129             sub parse_diagnostics
130             {
131 1     1 1 9901 my ($file, %options) = @_;
132 1         5 my $contents = path ($file)->slurp ();
133 1         301 my $diagnostics;
134 1 50       10 if ($file =~ /\.(c|xs)$/) {
135 0         0 $diagnostics = parse_diagnostics_xs ($contents, %options);
136             }
137             else {
138 1         4 $diagnostics = parse_diagnostics_pp ($contents, %options);
139             }
140             # Get user-defined diagnostics
141 1 50       4 if ($options{user_re}) {
142 0         0 my $udiagnostics = regex_lines ($contents, $options{user_re});
143 0         0 push @$diagnostics, @$udiagnostics;
144             }
145             # Hashmap of duplicates
146 1         1 my %dl;
147             my @diagnostics;
148             # Eliminate duplicate diagnostics
149 1         3 for my $d (@$diagnostics) {
150 1         4 my $key = $d->{message} . "-" . $d->{line};
151 1 50       4 if (! $dl{$key}) {
152 1         1 push @diagnostics, $d;
153 1         3 $dl{$key} = 1;
154             }
155             }
156             # Sort by line
157 1         3 @diagnostics = sort {$a->{line} <=> $b->{line}} @diagnostics;
  0         0  
158 1         4 return \@diagnostics;
159             }
160              
161             1;