public function enterScope(NodeTraversal $traversal) { $typeInference = new TypeInference($traversal->getControlFlowGraph(), $this->reverseInterpreter, $this->functionInterpreter, $this->methodInterpreter, $this->commentParser, $traversal->getScope(), $this->registry, $this->logger); try { $typeInference->analyze(); } catch (MaxIterationsExceededException $ex) { $scopeRoot = $traversal->getScopeRoot(); $this->logger->warning($ex->getMessage() . ' - Scope-Root: ' . get_class($scopeRoot) . ' on line ' . $scopeRoot->getLine() . ' in ' . $this->file->getName()); } }
/** * Declares a refined type in {@code scope} for the name represented by * {@code node}. It must be possible to refine the type of the given node in * the given scope, as determined by {@link #getTypeIfRefinable}. */ protected function declareNameInScope(LinkedFlowScope $scope, \PHPParser_Node $node, PhpType $type) { switch (true) { case $node instanceof \PHPParser_Node_Expr_Variable: if (is_string($node->name)) { $scope->inferSlotType($node->name, $type); } break; case $node instanceof \PHPParser_Node_Expr_StaticPropertyFetch: case $node instanceof \PHPParser_Node_Expr_PropertyFetch: if (null !== ($qualifiedName = TypeInference::getQualifiedName($node))) { $origType = $node->getAttribute('type') ?: $this->typeRegistry->getNativeType('unknown'); $scope->inferQualifiedSlot($node, $qualifiedName, $origType, $type); } break; // Something like: if (is_array($functions = getFunctionNames())) { } // Something like: if (is_array($functions = getFunctionNames())) { } case $node instanceof \PHPParser_Node_Expr_Assign: case $node instanceof \PHPParser_Node_Expr_AssignRef: $this->declareNameInScope($scope, $node->var, $type); break; case $node instanceof \PHPParser_Node_Expr_ArrayDimFetch: $dim = \Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::getValue($node->dim); if ($dim->isDefined() && null !== ($slotType = $node->var->getAttribute('type')) && $slotType->isArrayType()) { $newSlotType = $slotType->inferItemType($dim->get(), $type); $this->declareNameInScope($scope, $node->var, $newSlotType); } break; } }
private function join(FlowScopeInterface $a, FlowScopeInterface $b) { return call_user_func(TypeInference::createJoinOperation(), array($a, $b)); }
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); }