Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F1430538
LoopVariableReuseVisitor.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
LoopVariableReuseVisitor.php
View Options
<?php
declare
(
strict_types
=
1
);
namespace
Phan\Plugin\Internal
;
use
ast
;
use
ast\Node
;
use
Phan\Language\Context
;
use
Phan\Language\Element\Variable
;
use
Phan\PluginV3\PluginAwarePostAnalysisVisitor
;
use
function
array_intersect_key
;
use
function
count
;
use
function
in_array
;
use
function
is_string
;
/**
* This visitor implements the checks for reuse of loop variables.
*/
class
LoopVariableReuseVisitor
extends
PluginAwarePostAnalysisVisitor
{
// A plugin's visitors should not override visit() unless they need to.
/**
* @var list<Node> set by plugin framework
* @suppress PhanReadOnlyProtectedProperty
*/
protected
$parent_node_list
;
/**
* @override checks for reuse of variables in a node of kind ast\AST_FOREACH
*/
public
function
visitForeach
(
Node
$node
):
Context
{
$this
->
findVariableReuse
(
$this
->
extractLoopVariablesOfForeach
(
$node
));
return
$this
->
context
;
}
/**
* Extract "loop variables" of foreach loops.
*
* @return array<string|int,Node>
*/
public
function
extractLoopVariablesOfForeach
(
Node
$node
):
array
{
return
$this
->
extractVariables
(
$node
->
children
[
'key'
])
+
$this
->
extractVariables
(
$node
->
children
[
'value'
]);
}
/**
* @override checks for reuse of variables in a node of kind ast\AST_FOR
*/
public
function
visitFor
(
Node
$node
):
Context
{
$this
->
findVariableReuse
(
$this
->
extractLoopVariablesOfFor
(
$node
));
return
$this
->
context
;
}
/**
* Extract "loop variables" of for loops.
*
* @param Node $node a node of kind ast\AST_FOR
* @return array<string|int,Node>
*/
public
function
extractLoopVariablesOfFor
(
Node
$node
):
array
{
$directions
=
RedundantConditionLoopCheck
::
extractComparisonDirections
(
$node
->
children
[
'cond'
])
+
RedundantConditionLoopCheck
::
extractIncrementDirections
(
$this
->
code_base
,
$this
->
context
,
$node
->
children
[
'loop'
]);
if
(!
$directions
)
{
return
[];
}
$variables
=
self
::
extractVariables
(
$node
->
children
[
'cond'
])
+
self
::
extractVariables
(
$node
->
children
[
'loop'
]);
return
array_intersect_key
(
$variables
,
$directions
);
}
/**
* @override checks for reuse of variables in a node of kind ast\AST_WHILE
*/
public
function
visitWhile
(
Node
$node
):
Context
{
$this
->
findVariableReuse
(
$this
->
extractLoopVariablesOfWhile
(
$node
));
return
$this
->
context
;
}
/**
* Extract "loop variables" of while loops.
*
* @param Node $node a node of kind ast\AST_WHILE
* @return array<string|int,Node>
*/
public
function
extractLoopVariablesOfWhile
(
Node
$node
):
array
{
$directions
=
RedundantConditionLoopCheck
::
extractComparisonDirections
(
$node
->
children
[
'cond'
]);
if
(!
$directions
)
{
return
[];
}
return
array_intersect_key
(
self
::
extractVariables
(
$node
->
children
[
'cond'
]),
$directions
);
}
/**
* @param array<string|int,Node> $variables
*/
private
function
findVariableReuse
(
array
$variables
):
void
{
if
(!
$variables
)
{
return
;
}
for
(
$i
=
count
(
$this
->
parent_node_list
)
-
1
;
$i
>=
0
;
$i
--)
{
$parent_node
=
$this
->
parent_node_list
[
$i
];
$outer_variables
=
[];
switch
(
$parent_node
->
kind
)
{
case
ast\AST_FOREACH
:
$outer_variables
=
$this
->
extractLoopVariablesOfForeach
(
$parent_node
);
break
;
case
ast\AST_FOR
:
$outer_variables
=
$this
->
extractLoopVariablesOfFor
(
$parent_node
);
break
;
case
ast\AST_WHILE
:
$outer_variables
=
$this
->
extractLoopVariablesOfWhile
(
$parent_node
);
break
;
case
ast\AST_FUNC_DECL
:
case
ast\AST_CLOSURE
:
case
ast\AST_ARROW_FUNC
:
case
ast\AST_METHOD
:
case
ast\AST_CLASS
:
return
;
default
:
continue
2
;
}
$common_outer_variables
=
array_intersect_key
(
$outer_variables
,
$variables
);
if
(
$common_outer_variables
)
{
$this
->
warnCommonOuterVariables
(
$variables
,
$common_outer_variables
);
return
;
}
}
}
/**
* @param array<string|int,Node> $variables
* @param array<string|int,Node> $common_outer_variables
*/
private
function
warnCommonOuterVariables
(
array
$variables
,
array
$common_outer_variables
):
void
{
foreach
(
$common_outer_variables
as
$variable_name
=>
$node
)
{
$inner_node
=
$variables
[
$variable_name
];
$this
->
emitPluginIssue
(
$this
->
code_base
,
(
clone
$this
->
context
)->
withLineNumberStart
(
$inner_node
->
lineno
),
'PhanPluginLoopVariableReuse'
,
'Variable ${VARIABLE} used in loop was also used in an outer loop on line {LINE}'
,
[
$variable_name
,
$node
->
lineno
]
);
}
}
/**
* @param Node|string|int|float|null $node
* @return array<int|string,Node> a list of all variable nodes in this foreach
*/
public
function
extractVariables
(
$node
):
array
{
if
(!
$node
instanceof
Node
)
{
return
[];
}
switch
(
$node
->
kind
)
{
case
ast\AST_VAR
:
if
(
$node
->
kind
===
ast\AST_VAR
)
{
$var_name
=
$node
->
children
[
'name'
];
if
(
is_string
(
$var_name
))
{
if
(
in_array
(
$var_name
,
[
'this'
,
'_'
],
true
)
||
Variable
::
isHardcodedVariableInScopeWithName
(
$var_name
,
$this
->
context
->
isInGlobalScope
()))
{
return
[];
}
return
[
$var_name
=>
$node
];
}
}
break
;
// Kinds of nodes we don't bother checking
case
ast\AST_STATIC_PROP
:
case
ast\AST_PROP
:
// Kinds of declarations creating a new scope.
case
ast\AST_FUNC_DECL
:
case
ast\AST_CLOSURE
:
case
ast\AST_ARROW_FUNC
:
case
ast\AST_METHOD
:
case
ast\AST_CLASS
:
// FUNC_DECL and METHOD are probably unreachable.
return
[];
}
$result
=
[];
foreach
(
$node
->
children
as
$child_node
)
{
$result
+=
$this
->
extractVariables
(
$child_node
);
}
return
$result
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, May 16, 19:10 (5 h, 51 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
0f/c2/18169824e0389315fa269b98fa0a
Default Alt Text
LoopVariableReuseVisitor.php (6 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment