]> git.vpit.fr Git - perl/modules/Perl-Critic-Policy-Dynamic-NoIndirect.git/blobdiff - lib/Perl/Critic/Policy/Dynamic/NoIndirect.pm
Force the correct line number and file name when eval'ing the auditted code
[perl/modules/Perl-Critic-Policy-Dynamic-NoIndirect.git] / lib / Perl / Critic / Policy / Dynamic / NoIndirect.pm
index 5236bda9a6516ef6e048775d6741e1674119c6b8..3769d30fd9c7d084336710312b8b34e0493d4508 100644 (file)
@@ -11,11 +11,11 @@ Perl::Critic::Policy::Dynamic::NoIndirect - Perl::Critic policy against indirect
 
 =head1 VERSION
 
-Version 0.02
+Version 0.04
 
 =cut
 
-our $VERSION = '0.02';
+our $VERSION = '0.04';
 
 =head1 DESCRIPTION
 
@@ -34,22 +34,29 @@ sub default_severity { $SEVERITY_HIGH }
 sub default_themes   { qw/dynamic maintenance/ }
 sub applies_to       { 'PPI::Document' }
 
+my $tag_obj = sub {
+ my $obj = '' . $_[0];
+ $obj = '{' if $obj =~ /^\s*\{/;
+ $obj;
+};
+
 sub violates_dynamic {
  my ($self, undef, $doc) = @_;
 
- my $src;
-
+ my ($src, $file);
  if ($doc->isa('PPI::Document::File')) {
-  my $file = $doc->filename;
+  $file = $doc->filename;
   open my $fh, '<', $file
       or do { require Carp; Carp::confess("Can't open $file for reading: $!") };
   $src = do { local $/; <$fh> };
  } else {
-  $src = $doc->serialize;
+  $file = '(eval 0)';
+  $src  = $doc->serialize;
  }
 
+ $file =~ s/(?<!\\)((\\\\)*)"/$1\\"/g;
+
  my @errs;
- my $offset  = 6;
  my $wrapper = <<" WRAPPER";
  {
   return;
@@ -57,6 +64,7 @@ sub violates_dynamic {
   no indirect hook => sub { push \@errs, [ \@_ ] };
   {
    ;
+#line 1 "$file"
    $src
   }
  }
@@ -77,8 +85,7 @@ sub violates_dynamic {
   my %errs_tags;
   for (@errs) {
    my ($obj, $meth, $line) = @$_[0, 1, 3];
-   $line -= $offset;
-   my $tag = join "\0", $line, $meth, $obj;
+   my $tag = join "\0", $line, $meth, $tag_obj->($obj);
    push @{$errs_tags{$tag}}, [ $obj, $meth ];
   }
 
@@ -87,7 +94,7 @@ sub violates_dynamic {
    my $pos = $elt->location;
    return 0 unless $pos;
 
-   my $tag = join "\0", $pos->[0], $elt, $elt->snext_sibling;
+   my $tag = join "\0", $pos->[0], $elt, $tag_obj->($elt->snext_sibling);
    if (my $errs = $errs_tags{$tag}) {
     push @violations, do { my $e = pop @$errs; push @$e, $elt; $e };
     delete $errs_tags{$tag} unless @$errs;
@@ -100,14 +107,23 @@ sub violates_dynamic {
 
  return map {
   my ($obj, $meth, $elt) = @$_;
+  $obj = ($obj =~ /^\s*\{/) ? "a block" : "object \"$obj\"";
   $self->violation(
-   "Indirect call of method \"$meth\" on object \"$obj\"",
+   "Indirect call of method \"$meth\" on $obj",
    "You really wanted $obj\->$meth",
    $elt,
   );
  } @violations;
 }
 
+=head1 CAVEATS
+
+The uses of the L<indirect> pragma inside the auditted code take precedence over this policy.
+Hence no violations will be reported for indirect method calls that are located inside the lexical scope of C<use indirect> or C<< no indirect hook => ... >>.
+Occurrences of C<no indirect> won't be a problem.
+
+Since the reports generated by L<indirect> are remapped to the corresponding L<PPI::Element> objects, the order in which the violations are returned is different from the order given by L<indirect> : the former is the document order (top to bottom, left to right) while the latter is the optree order (arguments before function calls).
+
 =head1 DEPENDENCIES
 
 L<perl> 5.8, L<Carp>.