1use strict;
2use warnings;
3
4use English qw(-no_match_vars);
5use Carp qw(confess);
6
7use B::Keywords qw();
8use List::MoreUtils qw< apply uniq >;
9
10my $this_program = __FILE__;
11(my $test_file_name = $this_program) =~ s/[.]PL\Z/.t/ms;
12if ($this_program eq $test_file_name) {
13    confess
14        'Was not able to figure out the name of the file to generate.'
15        . "This program: $this_program.";
16}
17
18print "\n\nGenerating $test_file_name.\n";
19
20my @globals = (
21    @B::Keywords::Arrays,
22    @B::Keywords::Hashes,
23    @B::Keywords::Scalars,
24);
25push @globals, uniq apply { s/ \A ([^*]) /*$1/xms } @B::Keywords::Filehandles;
26my %exemptions = map {$_ => 1} qw(
27    $_
28    $ARG
29    @_
30);
31
32my $carat_re = qr/\A [\$%]\^\w+ /xms;
33
34my $numvars = @globals - keys %exemptions;
35my $numcarats = grep {!$exemptions{$_} && m/ $carat_re /xms} @globals;
36
37open my $test_file, '>', $test_file_name
38    or confess "Could not open $test_file_name: $ERRNO";
39
40print_header($test_file);
41print_pass_local($test_file, \@globals);
42print_pass_local_deref($test_file, \@globals);
43print_pass_non_local_exception($test_file, \@globals);
44print_fail_non_local($test_file, \@globals, $numvars, $numcarats);
45print_fail_non_local_deref($test_file, \@globals);
46print_footer($test_file);
47
48close $test_file
49    or confess "Could not close $test_file_name: $ERRNO";
50
51print "Done.\n\n";
52
53sub print_header {
54    my ($test_file) = @_;
55
56    print {$test_file} "# DO NOT EDIT!!! This test suite generated by $this_program\n";
57    print {$test_file} <<'END_CODE';
58
59use strict;
60use warnings;
61use Perl::Lint::Policy::Variables::RequireLocalizedPunctuationVars;
62use t::Policy::Util qw/fetch_violations/;
63use Test::Base::Less;
64
65my $class_name = 'Variables::RequireLocalizedPunctuationVars';
66
67filters {
68    params => [qw/eval/], # TODO wrong!
69};
70
71for my $block (blocks) {
72    my $violations = fetch_violations($class_name, $block->input, $block->params);
73    is scalar @$violations, $block->failures, $block->dscr;
74}
75
76done_testing;
77
78__DATA__
79
80===
81--- dscr: Named magic variables, special case passes
82--- failures: 0
83--- params:
84--- input
85local ($_, $RS) = ();
86local $SIG{__DIE__} = sub { print "AAAAAAARRRRRGGGGHHHHH....\n"; };
87$_ = 1;
88$ARG = 1;
89@_ = (1, 2, 3);
90END_CODE
91
92    return;
93}
94
95sub print_pass_local {
96    my ($test_file, $globals) = @_;
97
98    print {$test_file} <<'END_CODE';
99
100===
101--- dscr: Named magic variables, pass local
102--- failures: 0
103--- params:
104--- input
105END_CODE
106
107    for my $varname (@{$globals}) {
108        print {$test_file} "local $varname = ();\n";
109    }
110
111    print {$test_file} <<"END_CODE";
112
113===
114--- dscr: Named magic variables, pass local()
115--- failures: 0
116--- params:
117--- input
118END_CODE
119
120    for my $varname (@{$globals}) {
121        print {$test_file} "local ($varname) = ();\n";
122    }
123
124    print {$test_file} <<"END_CODE";
125
126===
127--- dscr: Named magic variables, pass (local)
128--- failures: 0
129--- params:
130--- input
131END_CODE
132
133    for my $varname (@{$globals}) {
134        print {$test_file} "(local $varname) = ();\n";
135    }
136
137    print {$test_file} <<"END_CODE";
138
139===
140--- dscr: Named magic variables, pass = (local) =
141--- failures: 0
142--- params:
143--- input
144END_CODE
145
146    for my $varname (@{$globals}) {
147        print {$test_file} "\@foo = (local $varname) = ();\n";
148    }
149
150    return;
151}
152
153
154sub print_pass_local_deref {
155    my ($test_file, $globals) = @_;
156
157    my %subscript = (
158        '%' => '{foo}',
159        '@' => '[0]',
160    );
161
162    my @derefs = grep { $subscript{substr $_, 0, 1} } @{ $globals };
163
164    print {$test_file} <<"END_CODE";
165
166===
167--- dscr: Named magic variables, pass local dereferenced
168--- failures: 0
169--- params:
170--- input
171END_CODE
172
173    foreach my $varname ( @derefs ) {
174        my ($sigil, $barename) = $varname =~ m/ (.)(.*) /smx;
175        print {$test_file} 'local $', $barename,
176            $subscript{$sigil}, " = 'bar';\n";
177    }
178
179}
180
181
182sub print_pass_non_local_exception {
183    my ($test_file, $globals) = @_;
184
185    (my $except = "@$globals") =~ s< ([\\']) ><\\$1>gmsx;
186    print {$test_file} <<"END_CODE";
187
188===
189--- dscr: Named magic variables, pass non-local but in exception list
190--- failures: 0
191--- params: {require_localized_punctuation_vars =>  {allow => '$except'}}
192--- input
193END_CODE
194
195    foreach my $varname (@{$globals}) {
196        next if $exemptions{$varname};
197        print {$test_file} "$varname = ();\n";
198    }
199}
200
201
202sub print_fail_non_local {
203    my ($test_file, $globals, $numvars, $numcarats) = @_;
204
205    print {$test_file} <<"END_CODE";
206
207===
208--- dscr: Named magic variables, fail non-local, non-carats
209--- failures: @{[$numvars - $numcarats]}
210--- params:
211--- input
212END_CODE
213
214    for my $varname (@{$globals}) {
215        next if $exemptions{$varname};
216        next if $varname =~ m/ $carat_re /xms;
217        print {$test_file} "$varname = ();\n";
218    }
219
220    print {$test_file} <<"END_CODE";
221
222===
223--- dscr: Named magic variables, fail non-local, carats
224--- failures: $numcarats
225--- params:
226--- input
227END_CODE
228
229    for my $varname (@{$globals}) {
230        next if $exemptions{$varname};
231        next if $varname !~ m/ $carat_re /xms;
232        print {$test_file} "$varname = ();\n";
233    }
234
235    print {$test_file} <<"END_CODE";
236
237===
238--- dscr: Named magic variables, fail non-local, carats, no space
239--- failures: $numcarats
240--- params:
241--- input
242END_CODE
243
244    for my $varname (@{$globals}) {
245        next if $exemptions{$varname};
246        next if $varname !~ m/ $carat_re /xms;
247        print {$test_file} "$varname= ();\n";
248    }
249
250    print {$test_file} <<"END_CODE";
251
252===
253--- dscr: Named magic variables, fail = (non-local) =
254--- failures: $numvars
255--- params:
256--- input
257END_CODE
258
259    for my $varname (@{$globals}) {
260        next if $exemptions{$varname};
261        print {$test_file} "\@foo = ($varname) = ();\n";
262    }
263
264    print {$test_file} <<"END_CODE";
265
266===
267--- dscr: Named magic variables, fail (non-local)
268--- failures: $numvars
269--- params:
270--- input
271END_CODE
272
273    for my $varname (@{$globals}) {
274        next if $exemptions{$varname};
275        print {$test_file} "($varname) = ();\n";
276    }
277
278    return;
279}
280
281
282sub print_fail_non_local_deref {
283    my ($test_file, $globals) = @_;
284
285    my %subscript = (
286        '%' => '{foo}',
287        '@' => '[0]',
288    );
289
290    my @derefs = grep { $subscript{substr $_, 0, 1} && !$exemptions{$_} }
291        @{ $globals };
292    my $numvars = scalar @derefs;
293
294    print {$test_file} <<"END_CODE";
295
296===
297--- dscr: Named magic variables, fail non-local dereferenced
298--- failures: $numvars
299--- params:
300--- input
301END_CODE
302
303    foreach my $varname ( @derefs ) {
304        my ($sigil, $barename) = $varname =~ m/ (.)(.*) /smx;
305        print {$test_file} '$', $barename,
306            $subscript{$sigil}, " = 'bar';\n";
307    }
308
309}
310
311
312sub print_footer {
313    my ($test_file) = @_;
314
315    print {$test_file} <<'END_CODE';
316
317===
318--- dscr: Allowing a variable with a particular sigil doesn't allow other variables with the same name but different sigils
319--- failures: 1
320--- params: {require_localized_punctuation_vars => {allow => '$ARGV'}}
321--- input
322@ARGV = (1, 2, 3);
323
324===
325--- dscr: Allow "my" as well, RT #33937
326--- failures: 0
327--- params:
328--- input
329for my $entry (
330   sort {
331       my @a = split m{,}xms, $a;
332       my @b = split m{,}xms, $b;
333       $a[0] cmp $b[0] || $a[1] <=> $b[1]
334   } qw( b,6 c,3 )
335   )
336{
337   print;
338}
339
340END_CODE
341
342    return;
343}
344
345