/** * @dataProvider getTests */ public function testIntegration($sourceFile) { $parser = new \PHPParser_Parser(); $ast = $parser->parse(new \PHPParser_Lexer(file_get_contents($sourceFile))); $traverser = new \PHPParser_NodeTraverser(); $traverser->addVisitor(new \PHPParser_NodeVisitor_NameResolver()); $traverser->addVisitor(new NormalizingNodeVisitor()); $ast = $traverser->traverse($ast); if (count($ast) > 1) { $ast = array(new BlockNode($ast)); } $traverser = new \PHPParser_NodeTraverser(); $traverser->addVisitor(new \PHPParser_NodeVisitor_NodeConnector()); $traverser->traverse($ast); // do _NOT_ assign the ast here as it clones the nodes on traversal and leads to problems $cfa = new ControlFlowAnalysis(); $cfa->process($ast[0]); $cfg = $cfa->getGraph(); $serializer = new GraphvizSerializer(); $dot = $serializer->serialize($cfg); $dotFile = substr($sourceFile, 0, -4) . '.dot'; if (!is_file($dotFile)) { file_put_contents($dotFile . '.tmp', $dot); $this->fail(sprintf('Dotfile "%s" does not exist, wrote current output to "%s".', $dotFile, basename($dotFile . '.tmp'))); } $this->assertSame($dot, file_get_contents($dotFile)); }
private function getCfg(\PHPParser_Node $ast) { $traverser = new \PHPParser_NodeTraverser(); $traverser->addVisitor(new \PHPParser_NodeVisitor_NodeConnector()); $traverser->traverse(array($ast)); $cfa = new ControlFlowAnalysis(); $cfa->process($ast); return $this->graph = $cfa->getGraph(); }
/** * @return ControlFlowGraph */ public function getControlFlowGraph() { if ($cfg = $this->cfgs->top()) { return $cfg; } $cfa = new ControlFlowAnalysis(); $cfa->process($this->getScopeRoot()); $this->cfgs->pop(); $this->cfgs->push($cfg = $cfa->getGraph()); return $cfg; }
/** * @group foo */ public function testCfgDoesNotTraverseExpressionTrees() { $expr = new \PHPParser_Node_Expr_Assign(new \PHPParser_Node_Expr_Variable('foo'), new \PHPParser_Node_Scalar_String('foo')); $this->normalizeAst(array($expr)); $this->cfa->process($expr); $graph = $this->cfa->getGraph(); $this->assertSame($expr, $graph->getEntryPoint()->getAstNode()); $this->assertSame(1, count($out = $graph->getOutEdges($expr))); $this->assertSame($out[0]->getDest(), $graph->getImplicitReturn()); $this->assertSame(1, count($graph->getNodes())); }
private function handleCase(\PHPParser_Node_Stmt_Case $node) { if (!$this->getSetting('non_commented_switch_fallthrough')) { return; } if (0 === count($node->stmts)) { return; } // Check if the next statement is a CASE statement. $nextCase = $node->getAttribute('next'); if (!$nextCase instanceof \PHPParser_Node_Stmt_Case) { return; } // If the last statement of the CASE node is a BREAK/CONTINUE/RETURN // THROW/GOTO statement, then we can always rule out a FALL-THROUGH. The // opposite, i.e. when there is none, is not true though. foreach ($node->stmts as $lastStmt) { } if ($lastStmt instanceof \PHPParser_Node_Stmt_Break || $lastStmt instanceof \PHPParser_Node_Stmt_Continue || $lastStmt instanceof \PHPParser_Node_Stmt_Return || $lastStmt instanceof \PHPParser_Node_Stmt_Throw || $lastStmt instanceof \PHPParser_Node_Stmt_Goto) { return; } $cfa = new ControlFlowAnalysis(); $cfa->process($node); $graph = $cfa->getGraph(); // $serializer = new GraphvizSerializer(); // echo $serializer->serialize($graph); // In case of a fallthrough, the control flow falls through to the statements block // of the next CASE statement. If that node is not part of the control flow graph, // then the control flow cannot reach from this starting node. if (!$graph->hasNode($nextCase->stmts)) { return; } foreach ($node->stmts as $lastChild) { } $previousLineEnd = $lastChild->getLine(); if ($previousLineEnd === $nextCase->getLine()) { $this->phpFile->addComment($previousLineEnd, Comment::warning('coding_style.unreadable_case', 'Consider moving this CASE statement to a new line.')); return; } $code = explode("\n", $this->phpFile->getContent()); $foundComment = false; for ($i = $previousLineEnd, $c = $nextCase->getLine(); $i < $c; $i++) { if (empty($code[$i])) { continue; } $lineTokens = token_get_all('<?php ' . $code[$i]); foreach ($lineTokens as $token) { if (!is_array($token)) { continue; } if (T_CASE === $token[0] || T_DEFAULT === $token[0]) { break 2; } if (T_COMMENT === $token[0] || T_DOC_COMMENT === $token[0]) { $foundComment = true; break 2; } } } // TODO: Improve where the comment is being applied. If there is only one fall-through // it would be nice to add the comment directly where the fall-through is // happening. If there are multiple fall-throughs, then we can apply the // comment at the end of the entire block as is the current behavior. if (!$foundComment) { $this->phpFile->addComment($previousLineEnd, Comment::warning('suspicious_code.non_empty_switch_fallthrough', 'Consider adding a comment if this fall-through is intended.')); } }
private function computeDefUse($code) { $code = sprintf('<?php function foo($param1, $param2) { %s }', $code); $ast = ParseUtils::parse($code); $scope = (new SyntacticScopeCreator())->createScope($ast); $cfa = new ControlFlowAnalysis(); $cfa->process($ast); $cfg = $cfa->getGraph(); $this->analysis = new MustBeReachingDefAnalysis($cfg, $scope); $this->analysis->analyze(); $this->def = $this->use = null; NodeTraversal::traverseWithCallback($ast, new PreOrderCallback(function (NodeTraversal $t, \PHPParser_Node $node) { if (!$node instanceof \PHPParser_Node_Stmt_Label) { return; } if ('D' === $node->name) { $this->def = $node->getAttribute('next'); } else { if ('U' === $node->name) { $this->use = $node->getAttribute('next'); } else { if (0 === strpos($node->name, 'DP_')) { $className = 'PHPParser_Node_' . substr($node->name, 3); $parent = $node; while (null !== ($parent = $parent->getAttribute('parent'))) { if ($parent instanceof $className) { $this->def = $parent; return; } } throw new \RuntimeException(sprintf('Did not find any parent of class "%s".', $className)); } } } })); $this->assertNotNull($this->def, 'Did not find "D" (definition) label in the code.'); $this->assertNotNull($this->use, 'Did not find "U" (usage) label in the code.'); }
private function computeLiveness($src) { $src = '<?php class Foo { public function foo($param1, $param2) { ' . $src . ' } }'; $parser = new \PHPParser_Parser(); $lexer = new \PHPParser_Lexer($src); $ast = $parser->parse($lexer); $traverser = new \PHPParser_NodeTraverser(); $traverser->addVisitor(new \PHPParser_NodeVisitor_NameResolver()); $traverser->addVisitor(new NormalizingNodeVisitor()); $ast = $traverser->traverse($ast); $traverser = new \PHPParser_NodeTraverser(); $traverser->addVisitor(new \PHPParser_NodeVisitor_NodeConnector()); $traverser->traverse($ast); $scopeCreator = new SyntacticScopeCreator(); $scope = $scopeCreator->createScope($ast[0]->stmts[0], new Scope($ast[0])); $cfa = new ControlFlowAnalysis(); $cfa->process($ast[0]->stmts[0]); $cfg = $cfa->getGraph(); $lva = new LiveVariablesAnalysis($cfg, $scope); $lva->analyze(); return $lva; }
private function inMethod($phpCode) { // Parse the body of the function. $ast = $this->parser->parse(new \PHPParser_Lexer('<?php class Foo { function foo() {' . $phpCode . '} }')); // Normalize the AST. $traverser = new \PHPParser_NodeTraverser(); $traverser->addVisitor(new \PHPParser_NodeVisitor_NameResolver()); $traverser->addvisitor(new NormalizingNodeVisitor()); $ast = $traverser->traverse($ast); $traverser = new \PHPParser_NodeTraverser(); $traverser->addVisitor(new \PHPParser_NodeVisitor_NodeConnector()); $traverser->traverse($ast); $root = $ast[0]; $node = $root->stmts[0]->stmts; // Create the scope with the assumptions. $scopeCreator = new TypedScopeCreator($this->registry); $assumedScope = $scopeCreator->createScope($node, $scopeCreator->createScope($root, null)); foreach ($this->assumptions as $symbolName => $type) { $var = $assumedScope->getVar($symbolName); if (!$var) { $assumedScope->declareVar($symbolName, $type); } else { $var->setType($type); } } // Create the control graph. $cfa = new ControlFlowAnalysis(); $cfa->process($node); $cfg = $cfa->getGraph(); // Create a simple reverse abstract interpreter. $rai = new SemanticReverseAbstractInterpreter($this->registry); $fi = new ArrayFunctionInterpreter($this->registry); $mi = $this->getMock('Scrutinizer\\PhpAnalyzer\\DataFlow\\TypeInference\\MethodInterpreter\\MethodInterpreterInterface'); $commentParser = new \Scrutinizer\PhpAnalyzer\PhpParser\DocCommentParser($this->registry); // Do the type inference by data-flow analysis. $dfa = new TypeInference($cfg, $rai, $fi, $mi, $commentParser, $assumedScope, $this->registry); $dfa->analyze(); // Get the scope of the implicit return. $this->returnScope = $cfg->getImplicitReturn()->getAttribute(DataFlowAnalysis::ATTR_FLOW_STATE_IN); $this->astGraph = \Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::dump($node); }