private function handleAssign(NodeTraversal $t, \PHPParser_Node_Expr_Assign $node) { if (!$node->var instanceof \PHPParser_Node_Expr_Variable || !is_string($node->var->name)) { return; } $scope = $t->getScope(); if (null === ($var = $scope->getVar($node->var->name))) { return; } if ($var->isReference()) { return; } $nameNode = $var->getNameNode(); if ($nameNode instanceof \PHPParser_Node_Param && $this->getSetting('overriding_parameter')) { $this->phpFile->addComment($node->getLine(), Comment::warning('suspicious_code.assignment_to_parameter', 'Consider using a different name than the parameter ``$%param_name%``. This often makes code more readable.', array('param_name' => $node->var->name))); } else { if ($nameNode instanceof \PHPParser_Node_Expr_ClosureUse && $this->getSetting('overriding_closure_use')) { $this->phpFile->addComment($node->getLine(), Comment::warning('suspicious_code.assignment_to_closure_import', 'Consider using a different name than the imported variable ``$%variable_name%``, or did you forget to import by reference?', array('variable_name' => $node->var->name))); } } }
private function tryRemoveAssignment(NodeTraversal $t, \PHPParser_Node $n, \PHPParser_Node $exprRoot = null, $inState, $outState) { $parent = $n->getAttribute('parent'); if (NodeUtil::isAssignmentOp($n)) { $lhs = $n->var; $rhs = $n->expr; if ($n instanceof \PHPParser_Node_Expr_AssignList) { $i = 0; foreach ($n->vars as $var) { if (null === $var) { $i += 1; continue; } $newNode = new \PHPParser_Node_Expr_Assign($var, new \PHPParser_Node_Expr_ArrayDimFetch($rhs, new \PHPParser_Node_Scalar_LNumber($i)), $n->getLine()); $newNode->setAttribute('is_list_assign', true); $this->tryRemoveAssignment($t, $newNode, $exprRoot, $inState, $outState); $i += 1; } return; } // Recurse first. Example: dead_x = dead_y = 1; We try to clean up dead_y first. if (null !== $rhs) { $this->tryRemoveAssignment($t, $rhs, $exprRoot, $inState, $outState); $rhs = $lhs->getAttribute('next'); } $scope = $t->getScope(); if (!$lhs instanceof \PHPParser_Node_Expr_Variable || !is_string($lhs->name)) { return; } if (!$scope->isDeclared($lhs->name)) { return; } if (in_array($lhs->name, NodeUtil::$superGlobalNames, true)) { return; } $var = $scope->getVar($lhs->name); $escaped = $this->liveness->getEscapedLocals(); if (isset($escaped[$var])) { return; // Local variable that might be escaped due to closures. } // If we have an identity assignment such as a=a, always remove it // regardless of what the liveness results because it does not // change the result afterward. if (null !== $rhs && $rhs instanceof \PHPParser_Node_Expr_Variable && $var->getName() === $rhs->name && $n instanceof \PHPParser_Node_Expr_Assign) { $this->phpFile->addComment($n->getLine(), Comment::warning('dead_assignment.assignment_to_itself', 'Why assign ``%variable%`` to itself?', array('variable' => '$' . $rhs->name))); return; } // At the moment we miss some dead assignments if a variable is killed, // and defined in the same node of the control flow graph. // Example: $a = 'foo'; return $a = 'bar'; if ($outState->isLive($var)) { return; // Variable is not dead. } // Assignments to references do not need to be used in the // current scope. if ($var->isReference()) { return; } // if ($inState->isLive($var) // /*&& $this->isVariableStillLiveWithinExpression($n, $exprRoot, $var->getName()) */) { // The variable is killed here but it is also live before it. // This is possible if we have say: // if ($X = $a && $a = $C) {..} ; .......; $a = $S; // In this case we are safe to remove "$a = $C" because it is dead. // However if we have: // if ($a = $C && $X = $a) {..} ; .......; $a = $S; // removing "a = C" is NOT correct, although the live set at the node // is exactly the same. // TODO: We need more fine grain CFA or we need to keep track // of GEN sets when we recurse here. Maybe add the comment anyway, and let the user decide? // return; // } if ($n instanceof \PHPParser_Node_Expr_Assign) { if ($n->getAttribute('is_list_assign', false)) { $this->phpFile->addComment($n->getLine(), Comment::warning('dead_assignment.unnecessary_list_assign', 'The assignment to ``$%variable%`` is dead. Consider omitting it like so ``list($first,,$third)``.', array('variable' => $var->getName()))); return; } $this->phpFile->addComment($n->getLine(), Comment::warning('dead_assignment.unnecessary_var_assign', 'The assignment to ``$%variable%`` is dead and can be removed.', array('variable' => $var->getName()))); return; } } }