Skip Menu |

Preferred bug tracker

Please visit the preferred bug tracker to report your issue.

This queue is for tickets about the Perl-Critic CPAN distribution.

Report information
The Basics
Id: 36081
Status: resolved
Priority: 0/
Queue: Perl-Critic

People
Owner: Nobody in particular
Requestors: EDAVIS [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: 1.082
Fixed in:
  • 1.104
  • 1.108



Subject: RegularExpressions::ProhibitCaptureWithoutTest flags cases when $1, etc. clearly are set
The intention of the test RegularExpressions::ProhibitCaptureWithoutTest is given by page 253 of PBP: Show quoted text
>Use the numeric capture variables only when you're sure >that the preceding match succeeded.
One way to check for that is to always use them inside an if-test, but it's not the only way. Throwing an exception if the regexp match failed seems an equally good way, especially if you expect it to always match. This test program #!/usr/bin/perl use warnings; use strict; use 5.010; use Carp qw(croak); my $s = 'abc'; $s =~ /(\w+)/x or croak "bad string '$s'"; say $1; run through perlcritic -3 produces the warning test:9:5:Capture variable used outside conditional [RegularExpressions::ProhibitCaptureWithoutTest] (See page 253 of PBP) IMHO, the test should not be for 'used outside conditional' but 'used without checking that the regexp matched'. perlcritic can't solve the halting problem and cannot determine all cases where it is certain the regexp matched. So there will always be some false positives. But it would be a big improvement to note that testing the return value of the =~ operator and jumping away if false means that later code can use $1, $2 etc. knowing that the match succeeded.
Proposed fix committed as SVN revision 3284. When backing through the code looking for a conditional statement, instead of just failing if a match or substitute is found, we pass if the match (etc) is followed by 'or' and 'die', 'croak', or 'confess'. Configuration option 'exception source' allows the user to add other exception-throwing routines. Tom Wyant
Has this fix made it into the 1.103 release? I find the test program still gives the same warning as before.
Subject: Re: [rt.cpan.org #36081] RegularExpressions::ProhibitCaptureWithoutTest flags cases when $1, etc. clearly are set
Date: Wed, 19 Aug 2009 22:29:39 -0500
To: bug-Perl-Critic [...] rt.cpan.org
From: Elliot Shank <perl [...] galumph.com>
EDAVIS via RT wrote: Show quoted text
> Has this fix made it into the 1.103 release? I find the test program > still gives the same warning as before.
No. 1.103 is basically the same as 1.098 with fixes for Test::Pod 1.40 and PPI 1.206. There's a bit of a backlog. We're working on it!
Subject: Re: [rt.cpan.org #36081] RegularExpressions::ProhibitCaptureWithoutTest flags cases when $1, etc. clearly are set
Date: Tue, 25 Aug 2009 22:20:05 -0500
To: bug-Perl-Critic [...] rt.cpan.org
From: Elliot Shank <perl [...] galumph.com>
Fixed in v1.104. Thanks.
Thanks for fixing this. Could you also add 'next', 'last', 'redo', and 'goto' to the list of statements that transfer control? These can be manually added to exception_source in the config file but they should be recognized by default.
Also 'return' should go on the list.
Another case which it needs to recognize is #!/usr/bin/perl # $Id $ use warnings; use strict; use 5.010; my $VERSION = 1; given ('abc') { when (/(a)/) { say $1; } default { say 'no'; } } It says that $1 is used outside a conditional, but 'when' is a conditional.
Subject: Re: [rt.cpan.org #36081] RegularExpressions::ProhibitCaptureWithoutTest flags cases when $1, etc. clearly are set
Date: Wed, 02 Sep 2009 23:52:20 -0500
To: bug-Perl-Critic [...] rt.cpan.org
From: Elliot Shank <perl [...] galumph.com>
EDAVIS via RT wrote: Show quoted text
> It says that $1 is used outside a conditional, but 'when' is a conditional.
Nothing in P::C is really adapted to 5.10 stuff yet. Another policy with a problem with given/when/default is RequireFinalReturn.
Proposed fix for '36081 redux' committed as svn revision 3632. Did not attempt 'when'. The revised code handles 'next', 'last', 'redo', and 'return' pretty much like 'die'. The three forms of 'goto' were dealt with thusly: 'goto &foo' (co-routine call) is accepted. 'goto LABEL' is rejected if the label appears between the regex and the capture variable, and accepted otherwise. 'goto EXPRESSION' is always rejected. Also took a whack at 'die unless m/(foo)/', from the BUGS section of the POD. All the stuff involving infix and suffix conditionals will only recognize fairly simple code on the other side of the conditional from the regex. Weasel words have been added to the POD reflecting this. Tom Wyant
Thanks for looking at this.
On Wed Sep 09 08:05:59 2009, EDAVIS wrote: Show quoted text
> Thanks for looking at this.
Proposed fix for 'when' committed as revision 3755. It is handled the same way as 'if' and friends.
From: rvandam00 [...] gmail.com
I was working on a patch for a specific false positive we see a lot when I realized there was already some work in svn in this direction. Our common case is: return $1 if /(\d+)/ My patch against 1.105 was essentially just this chunk added to _is_inconditional_structure just after $stmt is determined: # if this is a return, next, last, redo then the conditional might be in postfix position if ($stmt->isa('PPI::Statement::Break')) { my $nsib = $elem->snext_sibling; if ($nsib && $nsib->isa('PPI::Token::Word')) { while ($nsib) { return 1 if $nsib->isa( 'PPI::Token::Regexp::Substitute' ) || $nsib->isa( 'PPI::Token::Regexp::Match' ); $nsib = $nsib->snext_sibling; } } } It has some odd false negatives such as 'FOO: { next $1 if /(FOO)/ }' which compiles and is parsed fine by PPI but gives a runtime error on a successful match but it handles both return $1 and goto $1 correctly. It does still fail on exit $1, die $1, etc. The reason I'm not giving a real patch is because I haven't had time yet to study the current svn version since it appears to be doing some of the same work. However, I have tested the latest revision and it gives the following results: print $1 if /(FOO)/; # not ok return $1 if /(FOO)/; # not ok goto $1 if /(FOO)/; # not ok next $1 if /(FOO)/; # not ok exit $1 if /(FOO)/; # not ok die $1 if /(FOO)/; # not ok The code above gives this result: print $1 if /(FOO)/; # ok return $1 if /(FOO)/; # ok goto $1 if /(FOO)/; # ok next $1 if /(FOO)/; # ok exit $1 if /(FOO)/; # not ok die $1 if /(FOO)/; # not ok which could probably be fixed easily enough to catch those last two as well. I just wanted to post this until I have some time to understand the current revision and figure where to best make my changes.