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   1786 use 5.010001;
  40         119  
4 40     40   161 use strict;
  40         59  
  40         756  
5 40     40   125 use warnings;
  40         53  
  40         1664  
6              
7 40     40   181 use Readonly;
  40         57  
  40         2218  
8              
9 40     40   201 use File::Basename qw< basename >;
  40         49  
  40         2517  
10 40     40   190 use Scalar::Util qw< blessed >;
  40         75  
  40         1624  
11 40     40   1386 use String::Format qw< stringf >;
  40         2345  
  40         1933  
12              
13 40     40   676 use overload ( q{""} => 'to_string', cmp => '_compare' );
  40         1339  
  40         357  
14              
15 40     40   4073 use Perl::Critic::Utils qw< :characters :internal_lookup >;
  40         227  
  40         2015  
16 40         2094 use Perl::Critic::Utils::POD qw<
17             get_pod_section_for_module
18             trim_pod_section
19 40     40   10352 >;
  40         84  
20 40     40   1568 use Perl::Critic::Exception::Fatal::Internal qw< throw_internal >;
  40         80  
  40         46209  
21              
22             our $VERSION = '1.156';
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 297     297 1 25388 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 297 100       701 if ( @_ != $CONSTRUCTOR_ARG_COUNT ) {
46 1         5 throw_internal 'Wrong number of args to Violation->new()';
47             }
48              
49 296 100       409 if ( eval { $elem->isa( 'Perl::Critic::Document' ) } ) {
  296         1267  
50             # break the facade, return the real PPI::Document
51 6         21 $elem = $elem->ppi_document();
52             }
53              
54 296 100       429 if ( not eval { $elem->isa( 'PPI::Element' ) } ) {
  296         825  
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 295         787 ($desc, $expl) = _chomp_periods($desc, $expl);
61              
62             # Create object
63 295         676 my $self = bless {}, $class;
64 295         984 $self->{_description} = $desc;
65 295         512 $self->{_explanation} = $expl;
66 295         505 $self->{_severity} = $sev;
67 295         795 $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 295         635 $self->{_element_class} = blessed $elem;
73              
74 295         1022 my $top = $elem->top();
75 295 100       3786 $self->{_filename} = $top->can('filename') ? $top->filename() : undef;
76 295         1205 $self->{_source} = _line_containing_violation( $elem );
77             $self->{_location} =
78 295   50     513 $elem->location() || [ 0, 0, 0, 0, $self->filename() ];
79              
80 295         3642 return $self;
81             }
82              
83             #-----------------------------------------------------------------------------
84              
85 20     20 1 9232 sub set_format { return $format = verbosity_to_format( $_[0] ); } ## no critic(ArgUnpacking)
86 6     6 1 33 sub get_format { return $format; }
87              
88             #-----------------------------------------------------------------------------
89              
90             sub sort_by_location { ## no critic(ArgUnpacking)
91              
92 86 50   86 1 208 ref $_[0] || shift; # Can call as object or class method
93 86 100       1423 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 84         343 map {$_->[0]}
98 77 50       133 sort { ($a->[1] <=> $b->[1]) || ($a->[2] <=> $b->[2]) }
99 30   50     68 map {[$_, $_->location->[0] || 0, $_->location->[1] || 0]}
  84   100     123  
100             @_;
101             }
102              
103             #-----------------------------------------------------------------------------
104              
105             sub sort_by_severity { ## no critic(ArgUnpacking)
106              
107 1 50   1 1 7 ref $_[0] || shift; # Can call as object or class method
108 1 50       3 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         14 map {$_->[0]}
113 9         12 sort { $a->[1] <=> $b->[1] }
114 1   100     4 map {[$_, $_->severity() || 0]}
  6         8  
115             @_;
116             }
117              
118             #-----------------------------------------------------------------------------
119              
120             sub location {
121 474     474 1 552 my $self = shift;
122              
123 474         1208 return $self->{_location};
124             }
125              
126             #-----------------------------------------------------------------------------
127              
128             sub line_number {
129 4     4 1 10 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 34 my ($self) = @_;
138              
139 16         27 return $self->location()->[$LOCATION_LOGICAL_LINE_NUMBER];
140             }
141              
142             #-----------------------------------------------------------------------------
143              
144             sub column_number {
145 2     2 1 4 my ($self) = @_;
146              
147 2         7 return $self->location()->[$LOCATION_COLUMN_NUMBER];
148             }
149              
150             #-----------------------------------------------------------------------------
151              
152             sub visual_column_number {
153 14     14 1 21 my ($self) = @_;
154              
155 14         23 return $self->location()->[$LOCATION_VISUAL_COLUMN_NUMBER];
156             }
157              
158             #-----------------------------------------------------------------------------
159              
160             sub diagnostics {
161 7     7 1 86 my ($self) = @_;
162 7         14 my $policy = $self->policy();
163              
164 7 100       19 if ( not $diagnostics{$policy} ) {
165 3         5 eval { ## no critic (RequireCheckingReturnValueOfEval)
166 3   33     12 my $module_name = ref $policy || $policy;
167 3         15 $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         142 return $diagnostics{$policy};
175             }
176              
177             #-----------------------------------------------------------------------------
178              
179             sub description {
180 26     26 1 121 my $self = shift;
181 26         68 return $self->{_description};
182             }
183              
184             #-----------------------------------------------------------------------------
185              
186             sub explanation {
187 28     28 1 43 my $self = shift;
188 28         58 my $expl = $self->{_explanation};
189 28 100       76 if ( !$expl ) {
190 20         32 $expl = '(no explanation)';
191             }
192 28 100       68 if ( ref $expl eq 'ARRAY' ) {
193 2 100       3 my $page = @{$expl} > 1 ? 'pages' : 'page';
  2         6  
194 2         3 $page .= $SPACE . join $COMMA, @{$expl};
  2         7  
195 2         4 $expl = "See $page of PBP";
196             }
197 28         68 return $expl;
198             }
199              
200             #-----------------------------------------------------------------------------
201              
202             sub severity {
203 197     197 1 254 my $self = shift;
204 197         765 return $self->{_severity};
205             }
206              
207             #-----------------------------------------------------------------------------
208              
209             sub policy {
210 192     192 1 251 my $self = shift;
211 192         569 return $self->{_policy};
212             }
213              
214             #-----------------------------------------------------------------------------
215              
216             sub filename {
217 4     4 1 9 my $self = shift;
218 4         85 return $self->{_filename};
219             }
220              
221             #-----------------------------------------------------------------------------
222              
223             sub logical_filename {
224 4     4 1 9 my ($self) = @_;
225              
226 4         11 return $self->location()->[$LOCATION_LOGICAL_FILENAME];
227             }
228              
229             #-----------------------------------------------------------------------------
230              
231             sub source {
232 6     6 1 12 my $self = shift;
233 6         17 return $self->{_source};
234             }
235              
236             #-----------------------------------------------------------------------------
237              
238             sub element_class {
239 2     2 1 6 my ($self) = @_;
240              
241 2         9 return $self->{_element_class};
242             }
243              
244             #-----------------------------------------------------------------------------
245              
246             sub to_string {
247 24     24 1 2056 my $self = shift;
248              
249 24         61 my $long_policy = $self->policy();
250 24         56 (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   118 'f' => sub { $self->logical_filename() },
255 2     2   85 'F' => sub { basename( $self->logical_filename() ) },
256 2     2   116 'g' => sub { $self->filename() },
257 2     2   120 'G' => sub { basename( $self->filename() ) },
258 14     14   729 'l' => sub { $self->logical_line_number() },
259 2     2   114 'L' => sub { $self->line_number() },
260 12     12   321 '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   201 'd' => sub { $self->diagnostics() },
266 4     4   164 'r' => sub { $self->source() },
267 24         222 'P' => $long_policy,
268             'p' => $short_policy,
269             );
270 24         133 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 295     295   471 my ( $elem ) = @_;
291              
292 295   66     744 my $stmnt = $elem->statement() || $elem;
293 295   33     4156 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 295         10091 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 295   50     942 my $inx = ( $elem->line_number() || 0 ) -
      50        
306             ( $stmnt->line_number() || 0 );
307 295 50       11344 $inx > @lines and return $EMPTY;
308 295         790 return $lines[$inx];
309             }
310              
311             #-----------------------------------------------------------------------------
312              
313             sub _chomp_periods {
314 296     296   1398 my @args = @_;
315              
316 296         560 for (@args) {
317 596 100 100     1645 next if not defined or ref;
318 397         800 s{ [.]+ \z }{}xms;
319             }
320              
321 296         708 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 :