File Coverage

blib/lib/Perl/Lint/Policy/RegularExpressions/ProhibitFixedStringMatches.pm
Criterion Covered Total %
statement 72 75 96.0
branch 31 38 81.5
condition 27 36 75.0
subroutine 8 8 100.0
pod 0 1 0.0
total 138 158 87.3


line stmt bran cond sub pod time code
1             package Perl::Lint::Policy::RegularExpressions::ProhibitFixedStringMatches;
2 134     134   96087 use strict;
  134         260  
  134         4818  
3 134     134   604 use warnings;
  134         201  
  134         3299  
4 134     134   955 use Perl::Lint::Constants::Type;
  134         230  
  134         85377  
5 134     134   72694 use Regexp::Lexer qw(tokenize);
  134         388344  
  134         11284  
6 134     134   988 use Regexp::Lexer::TokenType;
  134         211  
  134         2712  
7 134     134   543 use parent "Perl::Lint::Policy";
  134         205  
  134         587  
8              
9             use constant {
10 134         84345 DESC => 'Use "eq" or hash instead of fixed-pattern regexps',
11             EXPL => [271, 272],
12 134     134   8708 };
  134         239  
13              
14             # to use sanitize
15             my $alternation_id = Regexp::Lexer::TokenType::Alternation->{id};
16             my $lparen_id = Regexp::Lexer::TokenType::LeftParenthesis->{id};
17             my $rparen_id = Regexp::Lexer::TokenType::RightParenthesis->{id};
18             my $question_id = Regexp::Lexer::TokenType::Question->{id};
19             my $colon_id = Regexp::Lexer::TokenType::Colon->{id};
20              
21             # to use check fixed string
22             my $character_id = Regexp::Lexer::TokenType::Character->{id};
23             my $escaped_character_id = Regexp::Lexer::TokenType::EscapedCharacter->{id};
24              
25             # anchors
26             my $beginning_of_line_id = Regexp::Lexer::TokenType::BeginningOfLine->{id};
27             my $end_of_line_id = Regexp::Lexer::TokenType::EndOfLine->{id};
28             my $escaped_beginning_of_line_id = Regexp::Lexer::TokenType::EscapedBeginningOfString->{id};
29             my $escaped_end_of_line_id = Regexp::Lexer::TokenType::EscapedEndOfString->{id};
30              
31             sub evaluate {
32 13     13 0 38 my ($class, $file, $tokens, $src, $args) = @_;
33              
34 13         21 my @violations;
35 13         24 my $is_reg_quoted = 0;
36 13         137 for (my $i = 0, my $token_type; my $token = $tokens->[$i]; $i++) {
37 388         375 $token_type = $token->{type};
38              
39 388 100 100     2100 if ($token_type == REG_EXP || $token_type == REG_REPLACE_FROM) {
    100 100        
40 65 100       109 if ($is_reg_quoted) {
41 10         4 $is_reg_quoted = 0;
42 10         18 next;
43             }
44              
45 55         48 my $maybe_regopt;
46 55 100       94 if ($token_type == REG_EXP) {
47 46         74 $maybe_regopt = $tokens->[$i+2];
48             }
49             else {
50 9         15 $maybe_regopt = $tokens->[$i+4];
51 9 50       24 if ($maybe_regopt->{type} == REG_DELIM) { # if it use brackets as delimiter
52 0         0 $maybe_regopt = $tokens->[$i+5];
53             }
54             }
55              
56 55         51 my $is_with_m_opt = 0;
57 55 50       100 if ($maybe_regopt) {
58 55 100 100     362 if ($maybe_regopt->{type} == REG_OPT && $maybe_regopt->{data} =~ /m/) {
59 5         8 $is_with_m_opt = 1;
60             }
61             }
62              
63 55         76 my @regexp_tokens = eval {
64 55         51 @{tokenize(qr/$token->{data}/)->{tokens}};
  55         1182  
65             };
66              
67 55 50       7274 if ($@) {
68             # XXX First aid!
69             # Maybe regexp is produced by `tr///` or `y///` operator if it reaches here.
70 0         0 next;
71             }
72              
73 55 50       123 if (scalar @regexp_tokens < 2) {
74 0         0 next;
75             }
76              
77 55         226 my $first_token_type_id = (shift @regexp_tokens)->{type}->{id};
78 55         121 my $last_token_type_id = (pop @regexp_tokens)->{type}->{id};
79              
80 55 50 33     273 if (defined $first_token_type_id && defined $last_token_type_id) {
81 55 100       94 if ($is_with_m_opt) {
82 5 100 66     20 if ($first_token_type_id == $beginning_of_line_id || $last_token_type_id == $end_of_line_id) {
83 4         25 next;
84             }
85             }
86              
87 51 50 100     335 if (
      66        
      66        
88             ($first_token_type_id == $beginning_of_line_id || $first_token_type_id == $escaped_beginning_of_line_id) &&
89             ($last_token_type_id == $end_of_line_id || $last_token_type_id == $escaped_end_of_line_id)
90             ) {
91 39         62 my @not_character_tokens = ();
92              
93 39         99 for (my $j = 0, my $type_id; my $regexp_token = $regexp_tokens[$j]; $j++) {
94 508         504 $type_id = $regexp_token->{type}->{id};
95 508 100 100     1408 if (
96             $type_id == $alternation_id ||
97             $type_id == $rparen_id
98             ) {
99 34         65 next;
100             }
101              
102 474 100       604 if ($type_id == $lparen_id) {
103 15         23 my $next_regexp_token = $regexp_tokens[$j+1];
104 15 100 66     63 if (defined $next_regexp_token && $next_regexp_token->{type}->{id} == $question_id) {
105 8         9 $next_regexp_token = $regexp_tokens[$j+2];
106 8 50 33     32 if (defined $next_regexp_token && $next_regexp_token->{type}->{id} == $colon_id) {
107 8         8 $j += 2;
108 8         17 next;
109             }
110             }
111              
112 7         20 next;
113             }
114              
115 459 100 66     1239 if ($type_id != $character_id && $type_id != $escaped_character_id) {
116 14         15 push @not_character_tokens, $regexp_token;
117 14         31 next;
118             }
119             }
120              
121 39 100       76 if (@not_character_tokens) {
122 7         49 next;
123             }
124              
125 32         393 push @violations, {
126             filename => $file,
127             line => $token->{line},
128             description => DESC,
129             explanation => EXPL,
130             policy => __PACKAGE__,
131             };
132             }
133             }
134             }
135             elsif ($token_type == REG_QUOTE || $token_type == REG_DOUBLE_QUOTE) {
136 10         19 $is_reg_quoted = 1;
137             }
138             }
139              
140 13         84 return \@violations;
141             }
142              
143             1;
144