Skip to content

Commit 21afafc

Browse files
Wilfredfacebook-github-bot
authored andcommitted
Fix false positive on shadowed variable lint due to lambdas
Summary: The shadowed variable lint is intended to warn users when they're unexpectedly overwriting a local variable by their `foreach` or `catch` block. ``` function demo(): void { $x = 99; foreach (vec[1, 2] as $x) { } $x; // 2 here } ``` However, we previously ignored that lambdas introduce a new scope. Ensure that we track local variables inside lambdas separately. Closes #8986 Reviewed By: mpu Differential Revision: D39003052 fbshipit-source-id: 6d6c0cf32dcea57bb0e7437e396631a00df7975f
1 parent 4284fd8 commit 21afafc

File tree

5 files changed

+57
-18
lines changed

5 files changed

+57
-18
lines changed

hphp/hack/src/lints/linter_foreach_shadow.ml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@
1010
open Linting_visitors
1111
open Aast
1212

13+
(**
14+
Lint on loop or catch variables that shadow local variables.
15+
16+
function foo(): void {
17+
$x = 99;
18+
19+
// Lint on this $x:
20+
foreach (vec[1, 2] as $x) {
21+
}
22+
23+
// $x is 2 here.
24+
$x;
25+
}
26+
*)
27+
1328
let lvar_visitor =
1429
object
1530
inherit [lid list] Nast.Visitor_DEPRECATED.visitor
@@ -75,6 +90,16 @@ module VisitorFunctor (Parent : BodyVisitorModule) : BodyVisitorModule = struct
7590

7691
method! on_catch () (_, var, block) =
7792
this#on_block_with_local_vars [var] block
93+
94+
method! on_expr () ((_, _, e_) as e) =
95+
match e_ with
96+
| Efun _
97+
| Lfun _ ->
98+
let old_frames = frames in
99+
frames <- [[]];
100+
parent#on_expr () e;
101+
frames <- old_frames
102+
| _ -> parent#on_expr () e
78103
end
79104
end
80105

hphp/hack/test/lint/foreach_shadow.php

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
<?hh
22

3-
function f1(array<int> $xs): int {
3+
function f1(vec<int> $xs): int {
44
$x = 0;
55
foreach ($xs as $x) {
66
$x++;
77
}
88
return $x;
99
}
1010

11-
function f2(array<int> $xs): int {
11+
function f2(vec<int> $xs): int {
1212
$x = 1;
1313
$x = $x - 1;
1414
foreach ($xs as $x) {
@@ -17,31 +17,31 @@ function f2(array<int> $xs): int {
1717
return $x;
1818
}
1919

20-
function f3(array<int, int> $xs): int {
20+
function f3(dict<int, int> $xs): int {
2121
$x = 0;
2222
foreach ($xs as $x => $_) {
2323
$x++;
2424
}
2525
return $x;
2626
}
2727

28-
function f4(array<int, int> $xs): int {
28+
function f4(dict<int, int> $xs): int {
2929
$x = 0;
3030
foreach ($xs as $_ => $x) {
3131
$x++;
3232
}
3333
return $x;
3434
}
3535

36-
function f5(array<int, (int, int)> $xs): int {
36+
function f5(dict<int, (int, int)> $xs): int {
3737
$x = 0;
3838
foreach ($xs as $_ => list($x, $_)) {
3939
$x++;
4040
}
4141
return $x;
4242
}
4343

44-
function f6(array<int> $xs): int {
44+
function f6(vec<int> $xs): int {
4545
$x = 0;
4646
if (true) {
4747
foreach ($xs as $x) {
@@ -51,7 +51,7 @@ function f6(array<int> $xs): int {
5151
return $x;
5252
}
5353

54-
function f7(array<array<int>> $xss): int {
54+
function f7(vec<vec<int>> $xss): int {
5555
$s = 0;
5656
foreach ($xss as $xs) {
5757
$x = 0;
@@ -63,8 +63,8 @@ function f7(array<array<int>> $xss): int {
6363
return $x;
6464
}
6565

66-
function f8(array<int> $xs): array<(int, int)> {
67-
$result = varray[];
66+
function f8(vec<int> $xs): vec<(int, int)> {
67+
$result = vec[];
6868
foreach ($xs as $x) {
6969
$x1 = $x;
7070
foreach ($xs as $x) {
@@ -75,15 +75,15 @@ function f8(array<int> $xs): array<(int, int)> {
7575
return $result;
7676
}
7777

78-
function f9(array<int> $xs): int {
79-
list($x, $_) = varray[0, 0];
78+
function f9(vec<int> $xs): int {
79+
list($x, $_) = vec[0, 0];
8080
foreach ($xs as $x) {
8181
$x++;
8282
}
8383
return $x;
8484
}
8585

86-
function f10(array<int> $xs): int {
86+
function f10(vec<int> $xs): int {
8787
$s = 0;
8888
try {
8989
throw new Exception();
@@ -96,7 +96,7 @@ function f10(array<int> $xs): int {
9696
return $s;
9797
}
9898

99-
function f11(array<int> $xs): int {
99+
function f11(vec<int> $xs): int {
100100
$s = 0;
101101
// The loop variable $x doesn't shadow $x defined after the loop.
102102
foreach ($xs as $x) {
@@ -107,8 +107,8 @@ function f11(array<int> $xs): int {
107107
return $s;
108108
}
109109

110-
function f12(array<int> $xs): int {
111-
if (false) {
110+
function f12(vec<int> $xs): int {
111+
if (1 === 2) {
112112
$x = 0;
113113
$s = $x;
114114
} else {
@@ -122,7 +122,7 @@ function f12(array<int> $xs): int {
122122
return $s;
123123
}
124124

125-
function f13(array<int, array<int>> $xss): int {
125+
function f13(dict<int, vec<int>> $xss): int {
126126
$s = 0;
127127
foreach ($xss as $_ => $xs) {
128128
// Variables named $_ are ignored

hphp/hack/test/lint/foreach_shadow.php.exp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,3 @@ File "foreach_shadow.php", line 79, characters 8-9: (Lint[5568])
2828
File "foreach_shadow.php", line 92, characters 21-22:
2929
Loop variable `$x` shadows a local variable defined or last assigned here:
3030
File "foreach_shadow.php", line 90, characters 22-23: (Lint[5568])
31-
File "foreach_shadow.php", line 112, characters 5-11:
32-
In function \f12: This assignment may be unreachable. It appears after a condition which is always false. (Other[6001])
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?hh
2+
3+
function foo(): void {
4+
$x = 0;
5+
6+
$f = () ==> {
7+
// This does not shadow the local in the outer scope, as the outer
8+
// $x is not affected.
9+
foreach (vec[] as $x) {
10+
}
11+
};
12+
}
13+
14+
// A second function to silence the naming lint about single definition files.
15+
function bar(): void {}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
No lint errors

0 commit comments

Comments
 (0)