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   70736 use strict;
  134         187  
  134         3168  
3 134     134   402 use warnings;
  134         153  
  134         2404  
4 134     134   779 use Perl::Lint::Constants::Type;
  134         160  
  134         61354  
5 134     134   54496 use Regexp::Lexer qw(tokenize);
  134         280561  
  134         7768  
6 134     134   640 use Regexp::Lexer::TokenType;
  134         177  
  134         2037  
7 134     134   406 use parent "Perl::Lint::Policy";
  134         149  
  134         402  
8              
9             use constant {
10 134         64547 DESC => 'Use "eq" or hash instead of fixed-pattern regexps',
11             EXPL => [271, 272],
12 134     134   6477 };
  134         165  
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 22 my ($class, $file, $tokens, $src, $args) = @_;
33              
34 13         9 my @violations;
35 13         13 my $is_reg_quoted = 0;
36 13         35 for (my $i = 0, my $token_type; my $token = $tokens->[$i]; $i++) {
37 388         268 $token_type = $token->{type};
38              
39 388 100 100     1574 if ($token_type == REG_EXP || $token_type == REG_REPLACE_FROM) {
    100 100        
40 65 100       98 if ($is_reg_quoted) {
41 10         9 $is_reg_quoted = 0;
42 10         13 next;
43             }
44              
45 55         36 my $maybe_regopt;
46 55 100       66 if ($token_type == REG_EXP) {
47 46         53 $maybe_regopt = $tokens->[$i+2];
48             }
49             else {
50 9         9 $maybe_regopt = $tokens->[$i+4];
51 9 50       14 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         35 my $is_with_m_opt = 0;
57 55 50       81 if ($maybe_regopt) {
58 55 100 100     149 if ($maybe_regopt->{type} == REG_OPT && $maybe_regopt->{data} =~ /m/) {
59 5         3 $is_with_m_opt = 1;
60             }
61             }
62              
63 55         60 my @regexp_tokens = eval {
64 55         33 @{tokenize(qr/$token->{data}/)->{tokens}};
  55         666  
65             };
66              
67 55 50       4586 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       97 if (scalar @regexp_tokens < 2) {
74 0         0 next;
75             }
76              
77 55         68 my $first_token_type_id = (shift @regexp_tokens)->{type}->{id};
78 55         66 my $last_token_type_id = (pop @regexp_tokens)->{type}->{id};
79              
80 55 50 33     183 if (defined $first_token_type_id && defined $last_token_type_id) {
81 55 100       66 if ($is_with_m_opt) {
82 5 100 66     12 if ($first_token_type_id == $beginning_of_line_id || $last_token_type_id == $end_of_line_id) {
83 4         13 next;
84             }
85             }
86              
87 51 50 100     245 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         34 my @not_character_tokens = ();
92              
93 39         73 for (my $j = 0, my $type_id; my $regexp_token = $regexp_tokens[$j]; $j++) {
94 508         344 $type_id = $regexp_token->{type}->{id};
95 508 100 100     1080 if (
96             $type_id == $alternation_id ||
97             $type_id == $rparen_id
98             ) {
99 34         44 next;
100             }
101              
102 474 100       482 if ($type_id == $lparen_id) {
103 15         17 my $next_regexp_token = $regexp_tokens[$j+1];
104 15 100 66     40 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     26 if (defined $next_regexp_token && $next_regexp_token->{type}->{id} == $colon_id) {
107 8         5 $j += 2;
108 8         14 next;
109             }
110             }
111              
112 7         14 next;
113             }
114              
115 459 100 66     938 if ($type_id != $character_id && $type_id != $escaped_character_id) {
116 14         8 push @not_character_tokens, $regexp_token;
117 14         22 next;
118             }
119             }
120              
121 39 100       54 if (@not_character_tokens) {
122 7         19 next;
123             }
124              
125             push @violations, {
126             filename => $file,
127             line => $token->{line},
128 32         230 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         16 $is_reg_quoted = 1;
137             }
138             }
139              
140 13         41 return \@violations;
141             }
142              
143             1;
144