File Coverage

blib/lib/Perl/Critic/Violation.pm
Criterion Covered Total %
statement 136 138 98.5
branch 25 30 83.3
condition 17 26 65.3
subroutine 42 44 95.4
pod 20 20 100.0
total 240 258 93.0


line stmt bran cond sub pod time code
1             package Perl::Critic::Violation;
2              
3 40     40   2105 use 5.010001;
  40         144  
4 40     40   251 use strict;
  40         89  
  40         826  
5 40     40   224 use warnings;
  40         111  
  40         1160  
6              
7 40     40   284 use Readonly;
  40         86  
  40         2304  
8              
9 40     40   377 use File::Basename qw< basename >;
  40         96  
  40         2739  
10 40     40   306 use Scalar::Util qw< blessed >;
  40         223  
  40         2286  
11 40     40   1686 use String::Format qw< stringf >;
  40         2447  
  40         2193  
12              
13 40     40   1503 use overload ( q{""} => 'to_string', cmp => '_compare' );
  40         1137  
  40         379  
14              
15 40     40   4433 use Perl::Critic::Utils qw< :characters :internal_lookup >;
  40         141  
  40         2071  
16 40         2242 use Perl::Critic::Utils::POD qw<
17             get_pod_section_for_module
18             trim_pod_section
19 40     40   12084 >;
  40         104  
20 40     40   1463 use Perl::Critic::Exception::Fatal::Internal qw< throw_internal >;
  40         85  
  40         58553  
21              
22             our $VERSION = '1.150';
23              
24             Readonly::Scalar my $NO_EXCEPTION_NO_SPLIT_LIMIT => -1;
25             Readonly::Scalar my $LOCATION_LINE_NUMBER => 0;
26             Readonly::Scalar my $LOCATION_COLUMN_NUMBER => 1;
27             Readonly::Scalar my $LOCATION_VISUAL_COLUMN_NUMBER => 2;
28             Readonly::Scalar my $LOCATION_LOGICAL_LINE_NUMBER => 3;
29             Readonly::Scalar my $LOCATION_LOGICAL_FILENAME => 4;
30              
31             # Class variables...
32             my $format = "%m at line %l, column %c. %e.\n"; # Default stringy format
33             my %diagnostics;
34              
35             #-----------------------------------------------------------------------------
36              
37             Readonly::Scalar my $CONSTRUCTOR_ARG_COUNT => 5;
38              
39             sub new {
40 285     285 1 28741 my ( $class, $desc, $expl, $elem, $sev ) = @_;
41              
42             # Check arguments to help out developers who might
43             # be creating new Perl::Critic::Policy modules.
44              
45 285 100       690 if ( @_ != $CONSTRUCTOR_ARG_COUNT ) {
46 1         5 throw_internal 'Wrong number of args to Violation->new()';
47             }
48              
49 284 100       435 if ( eval { $elem->isa( 'Perl::Critic::Document' ) } ) {
  284         1398  
50             # break the facade, return the real PPI::Document
51 4         49 $elem = $elem->ppi_document();
52             }
53              
54 284 100       493 if ( not eval { $elem->isa( 'PPI::Element' ) } ) {
  284         981  
55 1         4 throw_internal '3rd arg to Violation->new() must be a PPI::Element';
56             }
57              
58             # Strip punctuation. These are controlled by the user via the
59             # formats. He/She can use whatever makes sense to them.
60 283         754 ($desc, $expl) = _chomp_periods($desc, $expl);
61              
62             # Create object
63 283         728 my $self = bless {}, $class;
64 283         1049 $self->{_description} = $desc;
65 283         547 $self->{_explanation} = $expl;
66 283         531 $self->{_severity} = $sev;
67 283         713 $self->{_policy} = caller;
68              
69             # PPI eviscerates the Elements in a Document when the Document gets
70             # DESTROY()ed, and thus they aren't useful after it is gone. So we have
71             # to preemptively grab everything we could possibly want.
72 283         956 $self->{_element_class} = blessed $elem;
73              
74 283         1075 my $top = $elem->top();
75 283 100       3963 $self->{_filename} = $top->can('filename') ? $top->filename() : undef;
76 283         1057 $self->{_source} = _line_containing_violation( $elem );
77             $self->{_location} =
78 283   50     639 $elem->location() || [ 0, 0, 0, 0, $self->filename() ];
79              
80 283         4014 return $self;
81             }
82              
83             #-----------------------------------------------------------------------------
84              
85 20     20 1 8027 sub set_format { return $format = verbosity_to_format( $_[0] ); } ## no critic(ArgUnpacking)
86 6     6 1 38 sub get_format { return $format; }
87              
88             #-----------------------------------------------------------------------------
89              
90             sub sort_by_location { ## no critic(ArgUnpacking)
91              
92 85 50   85 1 261 ref $_[0] || shift; # Can call as object or class method
93 85 100       1176 return scalar @_ if ! wantarray; # In case we are called in scalar context
94              
95             ## TODO: What if $a and $b are not Violation objects?
96             return
97 75         327 map {$_->[0]}
98 56 50       141 sort { ($a->[1] <=> $b->[1]) || ($a->[2] <=> $b->[2]) }
99 29   50     76 map {[$_, $_->location->[0] || 0, $_->location->[1] || 0]}
  75   100     144  
100             @_;
101             }
102              
103             #-----------------------------------------------------------------------------
104              
105             sub sort_by_severity { ## no critic(ArgUnpacking)
106              
107 1 50   1 1 16 ref $_[0] || shift; # Can call as object or class method
108 1 50       6 return scalar @_ if ! wantarray; # In case we are called in scalar context
109              
110             ## TODO: What if $a and $b are not Violation objects?
111             return
112 6         18 map {$_->[0]}
113 9         18 sort { $a->[1] <=> $b->[1] }
114 1   100     3 map {[$_, $_->severity() || 0]}
  6         10  
115             @_;
116             }
117              
118             #-----------------------------------------------------------------------------
119              
120             sub location {
121 447     447 1 682 my $self = shift;
122              
123 447         1186 return $self->{_location};
124             }
125              
126             #-----------------------------------------------------------------------------
127              
128             sub line_number {
129 4     4 1 16 my ($self) = @_;
130              
131 4         11 return $self->location()->[$LOCATION_LINE_NUMBER];
132             }
133              
134             #-----------------------------------------------------------------------------
135              
136             sub logical_line_number {
137 16     16 1 32 my ($self) = @_;
138              
139 16         38 return $self->location()->[$LOCATION_LOGICAL_LINE_NUMBER];
140             }
141              
142             #-----------------------------------------------------------------------------
143              
144             sub column_number {
145 2     2 1 6 my ($self) = @_;
146              
147 2         6 return $self->location()->[$LOCATION_COLUMN_NUMBER];
148             }
149              
150             #-----------------------------------------------------------------------------
151              
152             sub visual_column_number {
153 14     14 1 31 my ($self) = @_;
154              
155 14         27 return $self->location()->[$LOCATION_VISUAL_COLUMN_NUMBER];
156             }
157              
158             #-----------------------------------------------------------------------------
159              
160             sub diagnostics {
161 7     7 1 71 my ($self) = @_;
162 7         15 my $policy = $self->policy();
163              
164 7 100       19 if ( not $diagnostics{$policy} ) {
165 3         7 eval { ## no critic (RequireCheckingReturnValueOfEval)
166 3   33     14 my $module_name = ref $policy || $policy;
167 3         13 $diagnostics{$policy} =
168             trim_pod_section(
169             get_pod_section_for_module( $module_name, 'DESCRIPTION' )
170             );
171             };
172 3   100     16 $diagnostics{$policy} ||= " No diagnostics available\n";
173             }
174 7         100 return $diagnostics{$policy};
175             }
176              
177             #-----------------------------------------------------------------------------
178              
179             sub description {
180 26     26 1 138 my $self = shift;
181 26         80 return $self->{_description};
182             }
183              
184             #-----------------------------------------------------------------------------
185              
186             sub explanation {
187 28     28 1 91 my $self = shift;
188 28         47 my $expl = $self->{_explanation};
189 28 100       73 if ( !$expl ) {
190 20         36 $expl = '(no explanation)';
191             }
192 28 100       73 if ( ref $expl eq 'ARRAY' ) {
193 2 100       7 my $page = @{$expl} > 1 ? 'pages' : 'page';
  2         13  
194 2         9 $page .= $SPACE . join $COMMA, @{$expl};
  2         10  
195 2         8 $expl = "See $page of PBP";
196             }
197 28         78 return $expl;
198             }
199              
200             #-----------------------------------------------------------------------------
201              
202             sub severity {
203 188     188 1 297 my $self = shift;
204 188         676 return $self->{_severity};
205             }
206              
207             #-----------------------------------------------------------------------------
208              
209             sub policy {
210 183     183 1 278 my $self = shift;
211 183         619 return $self->{_policy};
212             }
213              
214             #-----------------------------------------------------------------------------
215              
216             sub filename {
217 4     4 1 10 my $self = shift;
218 4         63 return $self->{_filename};
219             }
220              
221             #-----------------------------------------------------------------------------
222              
223             sub logical_filename {
224 4     4 1 9 my ($self) = @_;
225              
226 4         10 return $self->location()->[$LOCATION_LOGICAL_FILENAME];
227             }
228              
229             #-----------------------------------------------------------------------------
230              
231             sub source {
232 6     6 1 11 my $self = shift;
233 6         21 return $self->{_source};
234             }
235              
236             #-----------------------------------------------------------------------------
237              
238             sub element_class {
239 2     2 1 14 my ($self) = @_;
240              
241 2         9 return $self->{_element_class};
242             }
243              
244             #-----------------------------------------------------------------------------
245              
246             sub to_string {
247 24     24 1 2496 my $self = shift;
248              
249 24         50 my $long_policy = $self->policy();
250 24         59 (my $short_policy = $long_policy) =~ s/ \A Perl::Critic::Policy:: //xms;
251              
252             # Wrap the more expensive ones in sub{} to postpone evaluation
253             my %fspec = (
254 2     2   96 'f' => sub { $self->logical_filename() },
255 2     2   105 'F' => sub { basename( $self->logical_filename() ) },
256 2     2   92 'g' => sub { $self->filename() },
257 2     2   98 'G' => sub { basename( $self->filename() ) },
258 14     14   838 'l' => sub { $self->logical_line_number() },
259 2     2   98 'L' => sub { $self->line_number() },
260 12     12   351 'c' => sub { $self->visual_column_number() },
261 0     0   0 'C' => sub { $self->element_class() },
262             'm' => $self->description(),
263             'e' => $self->explanation(),
264             's' => $self->severity(),
265 4     4   323 'd' => sub { $self->diagnostics() },
266 4     4   237 'r' => sub { $self->source() },
267 24         249 'P' => $long_policy,
268             'p' => $short_policy,
269             );
270 24         134 return stringf($format, %fspec);
271             }
272              
273             #-----------------------------------------------------------------------------
274             # Apparently, some perls do not implicitly stringify overloading
275             # objects before doing a comparison. This causes a couple of our
276             # sorting tests to fail. To work around this, we overload C<cmp> to
277             # do it explicitly.
278             #
279             # 20060503 - More information: This problem has been traced to
280             # Test::Simple versions <= 0.60, not perl itself. Upgrading to
281             # Test::Simple v0.62 will fix the problem. But rather than forcing
282             # everyone to upgrade, I have decided to leave this workaround in
283             # place.
284              
285 0     0   0 sub _compare { return "$_[0]" cmp "$_[1]" }
286              
287             #-----------------------------------------------------------------------------
288              
289             sub _line_containing_violation {
290 283     283   537 my ( $elem ) = @_;
291              
292 283   66     757 my $stmnt = $elem->statement() || $elem;
293 283   33     4403 my $code_string = $stmnt->content() || $EMPTY;
294              
295             # Split into individual lines
296             # From `perldoc -f split`:
297             # If LIMIT is negative, it is treated as if it were instead
298             # arbitrarily large; as many fields as possible are produced.
299             #
300             # If it's omitted, it's the same except trailing empty fields, so we need
301             # without a limit for the split and without an exception
302 283         10067 my @lines = split qr{ \n }xms, $code_string, $NO_EXCEPTION_NO_SPLIT_LIMIT;
303              
304             # Take the line containing the element that is in violation
305 283   50     1086 my $inx = ( $elem->line_number() || 0 ) -
      50        
306             ( $stmnt->line_number() || 0 );
307 283 50       12065 $inx > @lines and return $EMPTY;
308 283         794 return $lines[$inx];
309             }
310              
311             #-----------------------------------------------------------------------------
312              
313             sub _chomp_periods {
314 284     284   1140 my @args = @_;
315              
316 284         607 for (@args) {
317 572 100 100     1902 next if not defined or ref;
318 382         865 s{ [.]+ \z }{}xms;
319             }
320              
321 284         793 return @args;
322             }
323              
324             #-----------------------------------------------------------------------------
325              
326             1;
327              
328             #-----------------------------------------------------------------------------
329              
330             __END__
331              
332             =head1 NAME
333              
334             Perl::Critic::Violation - A violation of a Policy found in some source code.
335              
336              
337             =head1 SYNOPSIS
338              
339             use PPI;
340             use Perl::Critic::Violation;
341              
342             my $elem = $doc->child(0); # $doc is a PPI::Document object
343             my $desc = 'Offending code'; # Describe the violation
344             my $expl = [1,45,67]; # Page numbers from PBP
345             my $sev = 5; # Severity level of this violation
346              
347             my $vio = Perl::Critic::Violation->new($desc, $expl, $node, $sev);
348              
349              
350             =head1 DESCRIPTION
351              
352             Perl::Critic::Violation is the generic representation of an individual
353             Policy violation. Its primary purpose is to provide an abstraction
354             layer so that clients of L<Perl::Critic|Perl::Critic> don't have to
355             know anything about L<PPI|PPI>. The C<violations> method of all
356             L<Perl::Critic::Policy|Perl::Critic::Policy> subclasses must return a
357             list of these Perl::Critic::Violation objects.
358              
359              
360             =head1 INTERFACE SUPPORT
361              
362             This is considered to be a public class. Any changes to its interface
363             will go through a deprecation cycle.
364              
365              
366             =head1 CONSTRUCTOR
367              
368             =over
369              
370             =item C<new( $description, $explanation, $element, $severity )>
371              
372             Returns a reference to a new C<Perl::Critic::Violation> object. The
373             arguments are a description of the violation (as string), an
374             explanation for the policy (as string) or a series of page numbers in
375             PBP (as an ARRAY ref), a reference to the L<PPI|PPI> element that
376             caused the violation, and the severity of the violation (as an
377             integer).
378              
379              
380             =back
381              
382              
383             =head1 METHODS
384              
385             =over
386              
387             =item C<description()>
388              
389             Returns a brief description of the specific violation. In other
390             words, this value may change on a per violation basis.
391              
392              
393             =item C<explanation()>
394              
395             Returns an explanation of the policy as a string or as reference to an
396             array of page numbers in PBP. This value will generally not change
397             based upon the specific code violating the policy.
398              
399              
400             =item C<location()>
401              
402             Don't use this method. Use the C<line_number()>,
403             C<logical_line_number()>, C<column_number()>,
404             C<visual_column_number()>, and C<logical_filename()> methods instead.
405              
406             Returns a five-element array reference containing the line and real &
407             virtual column and logical numbers and logical file name where this
408             Violation occurred, as in L<PPI::Element|PPI::Element>.
409              
410              
411             =item C<line_number()>
412              
413             Returns the physical line number that the violation was found on.
414              
415              
416             =item C<logical_line_number()>
417              
418             Returns the logical line number that the violation was found on. This
419             can differ from the physical line number when there were C<#line>
420             directives in the code.
421              
422              
423             =item C<column_number()>
424              
425             Returns the physical column that the violation was found at. This
426             means that hard tab characters count as a single character.
427              
428              
429             =item C<visual_column_number()>
430              
431             Returns the column that the violation was found at, as it would appear
432             if hard tab characters were expanded, based upon the value of
433             L<PPI::Document/"tab_width [ $width ]">.
434              
435              
436             =item C<filename()>
437              
438             Returns the path to the file where this Violation occurred. In some
439             cases, the path may be undefined because the source code was not read
440             directly from a file.
441              
442              
443             =item C<logical_filename()>
444              
445             Returns the logical path to the file where the Violation occurred.
446             This can differ from C<filename()> when there was a C<#line> directive
447             in the code.
448              
449              
450             =item C<severity()>
451              
452             Returns the severity of this Violation as an integer ranging from 1 to
453             5, where 5 is the "most" severe.
454              
455              
456             =item C<sort_by_severity( @violation_objects )>
457              
458             If you need to sort Violations by severity, use this handy routine:
459              
460             @sorted = Perl::Critic::Violation::sort_by_severity(@violations);
461              
462              
463             =item C<sort_by_location( @violation_objects )>
464              
465             If you need to sort Violations by location, use this handy routine:
466              
467             @sorted = Perl::Critic::Violation::sort_by_location(@violations);
468              
469              
470             =item C<diagnostics()>
471              
472             Returns a formatted string containing a full discussion of the
473             motivation for and details of the Policy module that created this
474             Violation. This information is automatically extracted from the
475             C<DESCRIPTION> section of the Policy module's POD.
476              
477              
478             =item C<policy()>
479              
480             Returns the name of the L<Perl::Critic::Policy|Perl::Critic::Policy>
481             that created this Violation.
482              
483              
484             =item C<source()>
485              
486             Returns the string of source code that caused this exception. If the
487             code spans multiple lines (e.g. multi-line statements, subroutines or
488             other blocks), then only the line containing the violation will be
489             returned.
490              
491              
492             =item C<element_class()>
493              
494             Returns the L<PPI::Element|PPI::Element> subclass of the code that caused this
495             exception.
496              
497              
498             =item C<set_format( $format )>
499              
500             Class method. Sets the format for all Violation objects when they are
501             evaluated in string context. The default is C<'%d at line %l, column
502             %c. %e'>. See L<"OVERLOADS"> for formatting options.
503              
504              
505             =item C<get_format()>
506              
507             Class method. Returns the current format for all Violation objects
508             when they are evaluated in string context.
509              
510              
511             =item C<to_string()>
512              
513             Returns a string representation of this violation. The content of the
514             string depends on the current value of the C<$format> package
515             variable. See L<"OVERLOADS"> for the details.
516              
517              
518             =back
519              
520              
521             =head1 OVERLOADS
522              
523             Perl::Critic::Violation overloads the C<""> operator to produce neat
524             little messages when evaluated in string context.
525              
526             Formats are a combination of literal and escape characters similar to
527             the way C<sprintf> works. If you want to know the specific formatting
528             capabilities, look at L<String::Format|String::Format>. Valid escape
529             characters are:
530              
531             Escape Meaning
532             ------- ----------------------------------------------------------------
533             %c Column number where the violation occurred
534             %d Full diagnostic discussion of the violation (DESCRIPTION in POD)
535             %e Explanation of violation or page numbers in PBP
536             %F Just the name of the logical file where the violation occurred.
537             %f Path to the logical file where the violation occurred.
538             %G Just the name of the physical file where the violation occurred.
539             %g Path to the physical file where the violation occurred.
540             %l Logical line number where the violation occurred
541             %L Physical line number where the violation occurred
542             %m Brief description of the violation
543             %P Full name of the Policy module that created the violation
544             %p Name of the Policy without the Perl::Critic::Policy:: prefix
545             %r The string of source code that caused the violation
546             %C The class of the PPI::Element that caused the violation
547             %s The severity level of the violation
548              
549             Explanation of the C<%F>, C<%f>, C<%G>, C<%G>, C<%l>, and C<%L> formats:
550             Using C<#line> directives, you can affect what perl thinks the current line
551             number and file name are; see L<perlsyn/Plain Old Comments (Not!)> for
552             the details. Under normal circumstances, the values of C<%F>, C<%f>, and
553             C<%l> will match the values of C<%G>, C<%g>, and C<%L>, respectively. In the
554             presence of a C<#line> directive, the values of C<%F>, C<%f>, and C<%l> will
555             change to take that directive into account. The values of C<%G>, C<%g>, and
556             C<%L> are unaffected by those directives.
557              
558             Here are some examples:
559              
560             Perl::Critic::Violation::set_format("%m at line %l, column %c.\n");
561             # looks like "Mixed case variable name at line 6, column 23."
562              
563             Perl::Critic::Violation::set_format("%m near '%r'\n");
564             # looks like "Mixed case variable name near 'my $theGreatAnswer = 42;'"
565              
566             Perl::Critic::Violation::set_format("%l:%c:%p\n");
567             # looks like "6:23:NamingConventions::Capitalization"
568              
569             Perl::Critic::Violation::set_format("%m at line %l. %e. \n%d\n");
570             # looks like "Mixed case variable name at line 6. See page 44 of PBP.
571             Conway's recommended naming convention is to use lower-case words
572             separated by underscores. Well-recognized acronyms can be in ALL
573             CAPS, but must be separated by underscores from other parts of the
574             name."
575              
576              
577             =head1 AUTHOR
578              
579             Jeffrey Ryan Thalhammer <jeff@imaginative-software.com>
580              
581              
582             =head1 COPYRIGHT
583              
584             Copyright (c) 2005-2023 Imaginative Software Systems. All rights reserved.
585              
586             This program is free software; you can redistribute it and/or modify
587             it under the same terms as Perl itself. The full text of this license
588             can be found in the LICENSE file included with this module.
589              
590             =cut
591              
592             # Local Variables:
593             # mode: cperl
594             # cperl-indent-level: 4
595             # fill-column: 78
596             # indent-tabs-mode: nil
597             # c-indentation-style: bsd
598             # End:
599             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :