1 ##############################################################################
2 # $URL: http://perlcritic.tigris.org/svn/perlcritic/trunk/Perl-Critic/lib/Perl/Critic/Policy/RegularExpressions/ProhibitEscapedMetacharacters.pm $
3 # $Date: 2008-07-03 10:19:10 -0500 (Thu, 03 Jul 2008) $
6 ##############################################################################
8 package Perl::Critic::Policy::RegularExpressions::ProhibitEscapedMetacharacters;
15 use English qw(-no_match_vars);
16 use List::MoreUtils qw(any);
18 use Perl::Critic::Utils qw{ :booleans :severities hashify };
19 use Perl::Critic::Utils::PPIRegexp qw{ ppiify parse_regexp };
20 use base 'Perl::Critic::Policy';
22 our $VERSION = '1.088';
24 #-----------------------------------------------------------------------------
26 Readonly::Scalar my $DESC => q{Use character classes for literal metachars instead of escapes};
27 Readonly::Scalar my $EXPL => [247];
29 Readonly::Hash my %REGEXP_METACHARS =>
30 hashify split m/ /xms, '{ } ( ) . * + ? |'; ##no critic(Interpolation)
32 #-----------------------------------------------------------------------------
34 sub supported_parameters { return qw() }
35 sub default_severity { return $SEVERITY_LOWEST }
36 sub default_themes { return qw( core pbp cosmetic ) }
37 sub applies_to { return qw(PPI::Token::Regexp::Match
38 PPI::Token::Regexp::Substitute
39 PPI::Token::QuoteLike::Regexp) }
41 #-----------------------------------------------------------------------------
44 my ( $self, $elem, undef ) = @_;
46 # optimization: don't bother parsing the regexp if there are no escapes
47 return if $elem !~ m/\\/xms;
49 my $re = ppiify(parse_regexp($elem));
52 # Must pass a sub to find() because our node classes don't start with PPI::
53 my $exacts = $re->find(sub {$_[1]->isa('Perl::Critic::PPIRegexp::exact')});
55 for my $exact (@{$exacts}) {
56 my @escapes = $exact =~ m/\\(.)/gxms;
57 return $self->violation( $DESC, $EXPL, $elem ) if any { $REGEXP_METACHARS{$_} } @escapes;
67 #-----------------------------------------------------------------------------
75 Perl::Critic::Policy::RegularExpressions::ProhibitEscapedMetacharacters - Use character classes for literal meta-characters instead of escapes.
79 This Policy is part of the core L<Perl::Critic> distribution.
84 Ever heard of leaning toothpick syndrome? That comes from writing
85 regular expressions that match on characters that are significant in
86 regular expressions. For example, the expression to match four
87 forward slashes looks like:
91 Well, this policy doesn't solve that problem (write it as C<m{////}>
92 instead!) but solves a related one. As seen above, the escapes make
93 the expression hard to parse visually. One solution is to use
94 character classes. You see, inside of character classes, the only
95 characters that are special are C<\>, C<]>, C<^> and C<->, so you don't need
96 to escape the others. So instead of the following loose IPv4 address matcher:
98 m/ \d+ \. \d+ \. \d+ \. \d+ /x;
102 m/ \d+ [.] \d+ [.] \d+ [.] \d+ /x;
104 which is certainly more readable, if less recognizable prior the
105 publication of Perl Best Practices. (Of course, you should really use
106 L<Regexp::Common::net> to match IPv4 addresses!)
108 Specifically, this policy forbids backslashes immediately prior to the following characters:
112 We make special exception for C<$> because C</[$]/> turns into
113 C</[5.008006/> for Perl 5.8.6. We also make an exception for C<^>
114 because it has special meaning (negation) in a character class.
115 Finally, C<[> and C<]> are exempt, of course, because they are awkward
116 to represent in character classes.
118 Note that this policy does not forbid unnecessary escaping. So go
119 ahead and (pointlessly) escape C<!> characters.
124 This Policy is not configurable except for the standard options.
129 Perl treats C<m/[#]/x> in unexpected ways.
130 I think it's a bug in Perl itself, but am not 100% sure that I have
131 not simply misunderstood...
133 This part makes sense:
135 "#f" =~ m/[#]f/x; # match
136 "#f" =~ m/[#]a/x; # no match
141 "#f" =~ m/[#]$qr/x; # no match
145 print qr/[#]$qr/x; # yields '(?x-ism:[#]$qr
150 Initial development of this policy was supported by a grant from the Perl Foundation.
154 Chris Dolan <cdolan@cpan.org>
158 Copyright (c) 2007-2008 Chris Dolan. Many rights reserved.
160 This program is free software; you can redistribute it and/or modify
161 it under the same terms as Perl itself. The full text of this license
162 can be found in the LICENSE file included with this module
168 # cperl-indent-level: 4
170 # indent-tabs-mode: nil
171 # c-indentation-style: bsd
173 # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :