=head1 VERSION
-Version 0.02
+Version 0.04
=cut
-our $VERSION = '0.02';
+our $VERSION = '0.04';
=head1 DESCRIPTION
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;
no indirect hook => sub { push \@errs, [ \@_ ] };
{
;
+#line 1 "$file"
$src
}
}
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 ];
}
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;
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 audited 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>.
L<Perl::Critic>, L<Perl::Critic::Dynamic>.
-L<indirect>.
+L<indirect> 0.20.
+
+=head1 SEE ALSO
+
+L<Perl::Critic::Policy::Objects::ProhibitIndirectSyntax> is a L<Perl::Critic> policy that statically checks for indirect constructs.
+But to be static it has to be very restricted : you have to manually specify which subroutine names are methods for which the indirect form should be forbidden.
+This can lead to false positives (a subroutine with the name you gave is defined in the current scope) and negatives (indirect constructs for methods you didn't specify).
+But you don't need to actually compile (or run, as it's more or less the same thing) the code.
=head1 AUTHOR
=head1 COPYRIGHT & LICENSE
-Copyright 2009 Vincent Pit, all rights reserved.
+Copyright 2009,2010 Vincent Pit, all rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.