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: 44515
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.098
Fixed in: 1.104



Subject: ProhibitMutatingListFunctions should not prohibit nonmutating tr/// (test case and fix included)
The tr function is often used just to count characters: my $count_colons = ($str =~ tr/://); In this case it does not change the input string. You can also use it with map or grep, for example my @bad = grep { tr/A-Za-z//c } @in; die "nonalphabetic characters in: @bad" if @bad; Currently ControlStructures::ProhibitMutatingListFunctions bans all use of tr in a map or grep block. It should complain only if the call changes the $_ string. A tr with an empty substitution list, and no /s or /d flag, is the normal perlish way to count characters and should be allowed. A patch is attached adding test cases for this and fixing the code. PPI doesn't provide methods to get the substitution list or flags from the tr call, so some string matching is needed.
Subject: perl-Perl-Critic-1.096-nonmutating-tr.diff
Index: Perl-Critic/t/ControlStructures/ProhibitMutatingListFunctions.run =================================================================== --- Perl-Critic/t/ControlStructures/ProhibitMutatingListFunctions.run (revision 3246) +++ Perl-Critic/t/ControlStructures/ProhibitMutatingListFunctions.run (working copy) @@ -84,6 +84,15 @@ @bar = map {m/4/} @foo; @bar = map {my $s=$_; chomp $s; $s} @foo; +## name Non-mutatating tr/// +## failures 0 +## cut + +@bar = map {tr/a-z//} @foo; +@bar = map {tr/123//c} @foo; +@bar = map {tr{A-Z}{}c} @foo; +@bar = map {tr<foo><>c} @foo; + #----------------------------------------------------------------------------- ## name Value given for list_funcs passing Index: Perl-Critic/lib/Perl/Critic/Policy/ControlStructures/ProhibitMutatingListFunctions.pm =================================================================== --- Perl-Critic/lib/Perl/Critic/Policy/ControlStructures/ProhibitMutatingListFunctions.pm (revision 3246) +++ Perl-Critic/lib/Perl/Critic/Policy/ControlStructures/ProhibitMutatingListFunctions.pm (working copy) @@ -153,6 +153,38 @@ return if ! ( $elem->isa('PPI::Token::Regexp::Substitute') || $elem->isa('PPI::Token::Regexp::Transliterate') ); + if ($elem->isa('PPI::Token::Regexp::Transliterate')) { + # Not all tr/// operators are mutators. It can be used just to count + # characters, typically by having an empty replacement list and no /d + # or /s flag. + # + # PPI doesn't provide methods to check this so we must look at the + # string. This is obviously a bit icky, but try to be conservative + # when deciding that an expression does not change the string. + # There are some operations such as tr/foo/foo/ which do not alter + # the string but we don't spot. + # + my $str = $elem->content; + if ($str =~ s/\A(?:tr|y)//) { + # It begins tr or y, find the delimiter characters. + if ($str =~ /\A(\W).*\1\1c?\z/) { + # A single-character delimiter like tr/whatever//. + return 0; + } + if ($str =~ /\A(\W).*(\W)\1\2c?\z/) { + # Test for matched delimiters. + my $delims = $1 . $2; + my @allowed = ('()', '{}', '[]', '<>'); + foreach (@allowed) { + return 0 if $delims eq $_; + } + } + } + else { + # Strange, but let it go. + } + } + # If the previous sibling does not exist, then # the regex implicitly binds to $_ my $prevsib = $elem->sprevious_sibling;
Proposed fix committed as SVN revision 3279. The change allows tr/fubar// (no substitute string) with no flag or with /c, but not with /d or /s. It also allows tr/fubar/fubar/ (match string equals substitute string) with no flag or with /d, but not /c or /s. Testing with Readonly::Scalar says that the allowed cases truly do not alter the operand, as opposed to changing it into itself. Tom Wyant
Subject: Re: [rt.cpan.org #44515] ProhibitMutatingListFunctions should not prohibit nonmutating tr/// (test case and fix included)
Date: Tue, 25 Aug 2009 22:17:51 -0500
To: bug-Perl-Critic [...] rt.cpan.org
From: Elliot Shank <perl [...] galumph.com>
Released in version 1.104. Thank you.