作者:actan
项目:pha
/**
* Check to see if the given Clazz is a duplicate
*
* @return null
*/
public static function analyzeDuplicateClass(CodeBase $code_base, Clazz $clazz)
{
// Determine if its a duplicate by looking to see if
// the FQSEN is suffixed with an alternate ID.
if (!$clazz->getFQSEN()->isAlternate()) {
return;
}
$original_fqsen = $clazz->getFQSEN()->getCanonicalFQSEN();
if (!$code_base->hasClassWithFQSEN($original_fqsen)) {
// If there's a missing class we'll catch that
// elsewhere
return;
}
// Get the original class
$original_class = $code_base->getClassByFQSEN($original_fqsen);
// Check to see if the original definition was from
// an internal class
if ($original_class->isInternal()) {
Issue::emit(Issue::RedefineClassInternal, $clazz->getContext()->getFile(), $clazz->getContext()->getLineNumberStart(), (string) $clazz, $clazz->getContext()->getFile(), $clazz->getContext()->getLineNumberStart(), (string) $original_class);
// Otherwise, print the coordinates of the original
// definition
} else {
Issue::emit(Issue::RedefineClass, $clazz->getContext()->getFile(), $clazz->getContext()->getLineNumberStart(), (string) $clazz, $clazz->getContext()->getFile(), $clazz->getContext()->getLineNumberStart(), (string) $original_class, $original_class->getContext()->getFile(), $original_class->getContext()->getLineNumberStart());
}
return;
}
作者:tpun
项目:pha
/**
* Check to see if the given Clazz is a duplicate
*
* @return null
*/
public static function analyzeDuplicateFunction(CodeBase $code_base, FunctionInterface $method)
{
$fqsen = $method->getFQSEN();
if (!$fqsen->isAlternate()) {
return;
}
$original_fqsen = $fqsen->getCanonicalFQSEN();
if ($original_fqsen instanceof FullyQualifiedFunctionName) {
if (!$code_base->hasFunctionWithFQSEN($original_fqsen)) {
return;
}
$original_method = $code_base->getFunctionByFQSEN($original_fqsen);
} else {
if (!$code_base->hasMethodWithFQSEN($original_fqsen)) {
return;
}
$original_method = $code_base->getMethodByFQSEN($original_fqsen);
}
$method_name = $method->getName();
if (!$method->hasSuppressIssue(Issue::RedefineFunction)) {
if ($original_method->isInternal()) {
Issue::maybeEmit($code_base, $method->getContext(), Issue::RedefineFunctionInternal, $method->getFileRef()->getLineNumberStart(), $method_name, $method->getFileRef()->getFile(), $method->getFileRef()->getLineNumberStart());
} else {
Issue::maybeEmit($code_base, $method->getContext(), Issue::RedefineFunction, $method->getFileRef()->getLineNumberStart(), $method_name, $method->getFileRef()->getFile(), $method->getFileRef()->getLineNumberStart(), $original_method->getFileRef()->getFile(), $original_method->getFileRef()->getLineNumberStart());
}
}
}
作者:actan
项目:pha
/**
* Check to see if the given Clazz is a duplicate
*
* @return null
*/
public static function analyzeParentConstructorCalled(CodeBase $code_base, Clazz $clazz)
{
// Only look at classes configured to require a call
// to its parent constructor
if (!in_array($clazz->getName(), Config::get()->parent_constructor_required)) {
return;
}
// Don't worry about internal classes
if ($clazz->isInternal()) {
return;
}
// Don't worry if there's no parent class
if (!$clazz->hasParentClassFQSEN()) {
return;
}
if (!$code_base->hasClassWithFQSEN($clazz->getParentClassFQSEN())) {
// This is an error, but its caught elsewhere. We'll
// just roll through looking for other errors
return;
}
$parent_clazz = $code_base->getClassByFQSEN($clazz->getParentClassFQSEN());
if (!$parent_clazz->isAbstract() && !$clazz->getIsParentConstructorCalled()) {
Issue::emit(Issue::TypeParentConstructorCalled, $clazz->getContext()->getFile(), $clazz->getContext()->getLineNumberStart(), (string) $clazz->getFQSEN(), (string) $parent_clazz->getFQSEN());
}
}
作者:tpun
项目:pha
/**
* Check to see if the given Clazz is a duplicate
*
* @return null
*/
public static function analyzePropertyTypes(CodeBase $code_base, Clazz $clazz)
{
foreach ($clazz->getPropertyList($code_base) as $property) {
try {
$union_type = $property->getUnionType();
} catch (IssueException $exception) {
Issue::maybeEmitInstance($code_base, $property->getContext(), $exception->getIssueInstance());
continue;
}
// Look at each type in the parameter's Union Type
foreach ($union_type->getTypeSet() as $type) {
// If its a native type or a reference to
// self, its OK
if ($type->isNativeType() || $type->isSelfType()) {
continue;
}
if ($type instanceof TemplateType) {
if ($property->isStatic()) {
Issue::maybeEmit($code_base, $property->getContext(), Issue::TemplateTypeStaticProperty, $property->getFileRef()->getLineNumberStart(), (string) $property->getFQSEN());
}
} else {
// Make sure the class exists
$type_fqsen = $type->asFQSEN();
if (!$code_base->hasClassWithFQSEN($type_fqsen) && !$type instanceof TemplateType && (!$property->hasDefiningFQSEN() || $property->getDefiningFQSEN() == $property->getFQSEN())) {
Issue::maybeEmit($code_base, $property->getContext(), Issue::UndeclaredTypeProperty, $property->getFileRef()->getLineNumberStart(), (string) $property->getFQSEN(), (string) $type_fqsen);
}
}
}
}
}
作者:ets
项目:pha
/**
* @param string $string String to check against
*
* @dataProvider invalidUTF8StringsProvider
*/
public function testUTF8CharactersDoNotCauseDOMAttrToFail($string)
{
$output = new BufferedOutput();
$printer = new CheckstylePrinter();
$printer->configureOutput($output);
$printer->print(new IssueInstance(Issue::fromType(Issue::SyntaxError), 'test.php', 0, [$string]));
$printer->flush();
}
作者:tpun
项目:pha
/**
* @return bool
* True if the FQSEN exists. If not, a log line is emitted
*/
private static function fqsenExistsForClass(FQSEN $fqsen, CodeBase $code_base, Clazz $clazz, string $issue_type) : bool
{
if (!$code_base->hasClassWithFQSEN($fqsen)) {
Issue::maybeEmit($code_base, $clazz->getContext(), $issue_type, $clazz->getFileRef()->getLineNumberStart(), (string) $fqsen);
return false;
}
return true;
}
作者:tpun
项目:pha
/**
* @param string $string String to check against
* @param string $messageExpected Message component of expected CSV line
*
* @dataProvider specialCharacterCasesProvider
*/
public function testSpecialCharactersAreProperlyEncoded($string, $messageExpected)
{
$output = new BufferedOutput();
$printer = new CSVPrinter();
$printer->configureOutput($output);
$printer->print(new IssueInstance(Issue::fromType(Issue::SyntaxError), 'test.php', 0, [$string]));
$printer->flush();
$expected = 'test.php,0,10,critical,UndefError,PhanSyntaxError,' . $messageExpected;
$actual = explode("\n", $output->fetch())[1];
// Ignore header
$this->assertEquals($expected, $actual);
}
作者:black-silenc
项目:pha
/**
* Check to see if the given Clazz is a duplicate
*
* @return null
*/
public static function analyzeDuplicateFunction(CodeBase $code_base, FunctionInterface $method)
{
$fqsen = $method->getFQSEN();
if (!$fqsen->isAlternate()) {
return;
}
$original_fqsen = $fqsen->getCanonicalFQSEN();
if (!$code_base->hasMethod($original_fqsen)) {
return;
}
$original_method = $code_base->getMethod($original_fqsen);
$method_name = $method->getName();
if ($original_method->isInternal()) {
Issue::emit(Issue::RedefineFunctionInternal, $method->getFileRef()->getFile(), $method->getFileRef()->getLineNumberStart(), $method_name, $method->getFileRef()->getFile(), $method->getFileRef()->getLineNumberStart());
} else {
Issue::emit(Issue::RedefineFunction, $method->getFileRef()->getFile(), $method->getFileRef()->getLineNumberStart(), $method_name, $method->getFileRef()->getFile(), $method->getFileRef()->getLineNumberStart(), $original_method->getFileRef()->getFile(), $original_method->getFileRef()->getLineNumberStart());
}
}
作者:Jvbzephi
项目:pha
/**
* Check to see if the given Clazz is a duplicate
*
* @return null
*/
public static function analyzePropertyTypes(CodeBase $code_base, Clazz $clazz)
{
foreach ($clazz->getPropertyList($code_base) as $property) {
$union_type = $property->getUnionType();
// Look at each type in the parameter's Union Type
foreach ($union_type->getTypeSet() as $type) {
// If its a native type or a reference to
// self, its OK
if ($type->isNativeType() || $type->isSelfType()) {
continue;
}
// Otherwise, make sure the class exists
$type_fqsen = $type->asFQSEN();
if (!$code_base->hasClassWithFQSEN($type_fqsen)) {
Issue::emit(Issue::UndeclaredTypeProperty, $property->getContext()->getFile(), $property->getContext()->getLineNumberStart(), (string) $type_fqsen);
}
}
}
}
作者:black-silenc
项目:pha
/**
* Check to see if the given Clazz is a duplicate
*
* @return null
*/
public static function analyzeParameterTypes(CodeBase $code_base, FunctionInterface $method)
{
// Look at each method parameter
foreach ($method->getParameterList() as $parameter) {
$union_type = $parameter->getUnionType();
// Look at each type in the parameter's Union Type
foreach ($union_type->getTypeSet() as $type) {
// If its a native type or a reference to
// self, its OK
if ($type->isNativeType() || $type->isSelfType()) {
continue;
}
// Otherwise, make sure the class exists
$type_fqsen = $type->asFQSEN();
if (!$code_base->hasClassWithFQSEN($type_fqsen)) {
Issue::emit(Issue::UndeclaredTypeParameter, $method->getFileRef()->getFile(), $method->getFileRef()->getLineNumberStart(), (string) $type_fqsen);
}
}
}
}
作者:nagyistg
项目:pha
/**
* @return Clazz[]
* A list of classes associated with the given node
*
* @throws IssueException
* An exception is thrown if we can't find a class for
* the given type
*/
private function classListFromNode(Node $node)
{
// Get the types associated with the node
$union_type = self::unionTypeFromNode($this->code_base, $this->context, $node);
// Iterate over each viable class type to see if any
// have the constant we're looking for
foreach ($union_type->nonNativeTypes()->getTypeSet() as $class_type) {
// Get the class FQSEN
$class_fqsen = $class_type->asFQSEN();
// See if the class exists
if (!$this->code_base->hasClassWithFQSEN($class_fqsen)) {
throw new IssueException(Issue::fromType(Issue::UndeclaredClassReference)($this->context->getFile(), $node->lineno ?? 0, [(string) $class_fqsen]));
}
(yield $this->code_base->getClassByFQSEN($class_fqsen));
}
}
作者:ablyle
项目:pha
/**
* @param Node $node
* A node to parse
*
* @return Context
* A new or an unchanged context resulting from
* parsing the node
*/
public function visitProp(Node $node) : Context
{
$property_name = $node->children['prop'];
// Things like $foo->$bar
if (!is_string($property_name)) {
return $this->context;
}
assert(is_string($property_name), "Property must be string in context {$this->context}");
try {
$class_list = (new ContextNode($this->code_base, $this->context, $node->children['expr']))->getClassList();
} catch (CodeBaseException $exception) {
// This really shouldn't happen since the code
// parsed cleanly. This should fatal.
// throw $exception;
return $this->context;
} catch (\Exception $exception) {
// If we can't figure out what kind of a class
// this is, don't worry about it
return $this->context;
}
foreach ($class_list as $clazz) {
// Check to see if this class has the property or
// a setter
if (!$clazz->hasPropertyWithName($this->code_base, $property_name)) {
if (!$clazz->hasMethodWithName($this->code_base, '__set')) {
continue;
}
}
try {
$property = $clazz->getPropertyByNameInContext($this->code_base, $property_name, $this->context);
} catch (IssueException $exception) {
Issue::maybeEmitInstance($this->code_base, $this->context, $exception->getIssueInstance());
return $this->context;
}
if (!$this->right_type->canCastToExpandedUnionType($property->getUnionType(), $this->code_base)) {
$this->emitIssue(Issue::TypeMismatchProperty, $node->lineno ?? 0, (string) $this->right_type, "{$clazz->getFQSEN()}::{$property->getName()}", (string) $property->getUnionType());
return $this->context;
}
// After having checked it, add this type to it
$property->getUnionType()->addUnionType($this->right_type);
return $this->context;
}
if (Config::get()->allow_missing_properties) {
try {
// Create the property
(new ContextNode($this->code_base, $this->context, $node))->getOrCreateProperty($property_name);
} catch (\Exception $exception) {
// swallow it
}
} elseif (!empty($class_list)) {
$this->emitIssue(Issue::UndeclaredProperty, $node->lineno ?? 0, $property_name);
} else {
// If we hit this part, we couldn't figure out
// the class, so we ignore the issue
}
return $this->context;
}
作者:ablyle
项目:pha
/**
* Once we know what the universe looks like we
* can scan for more complicated issues.
*
* @param CodeBase $code_base
* The global code base holding all state
*
* @param string $file_path
* A list of files to scan
*
* @return Context
*/
public static function analyzeFile(CodeBase $code_base, string $file_path) : Context
{
// Set the file on the context
$context = (new Context())->withFile($file_path);
// Convert the file to an Abstract Syntax Tree
// before passing it on to the recursive version
// of this method
try {
$node = \ast\parse_file($file_path, Config::get()->ast_version);
} catch (\ParseError $parse_error) {
Issue::maybeEmit($code_base, $context, Issue::SyntaxError, $parse_error->getLine(), $parse_error->getMessage());
return $context;
}
// Ensure we have some content
if (empty($node)) {
Issue::maybeEmit($code_base, $context, Issue::EmptyFile, 0, $file_path);
return $context;
}
// Whenever we enter a file, we copy all global scope
// variables to the local scope
$context->getScope()->copyGlobalToLocal();
// Start recursively analyzing the tree
return self::analyzeNodeInContext($code_base, $context, $node);
}
作者:Jvbzephi
项目:pha
/**
* Once we know what the universe looks like we
* can scan for more complicated issues.
*
* @param CodeBase $code_base
* The global code base holding all state
*
* @param string $file_path
* A list of files to scan
*
* @return Context
*/
public static function analyzeFile(CodeBase $code_base, string $file_path) : Context
{
// Convert the file to an Abstract Syntax Tree
// before passing it on to the recursive version
// of this method
$node = \ast\parse_file($file_path, Config::get()->ast_version);
// Set the file on the context
$context = (new Context())->withFile($file_path);
// Ensure we have some content
if (empty($node)) {
Issue::emit(Issue::EmptyFile, $file_path, 0, $file_path);
return $context;
}
// Start recursively analyzing the tree
return self::analyzeNodeInContext($code_base, $context, $node);
}
作者:ablyle
项目:pha
/**
* Perform some backwards compatibility checks on a node
*
* @return void
*/
public function analyzeBackwardCompatibility()
{
if (!Config::get()->backward_compatibility_checks) {
return;
}
if (empty($this->node->children['expr'])) {
return;
}
if ($this->node->kind === \ast\AST_STATIC_CALL || $this->node->kind === \ast\AST_METHOD_CALL) {
return;
}
$llnode = $this->node;
if ($this->node->kind !== \ast\AST_DIM) {
if (!$this->node->children['expr'] instanceof Node) {
return;
}
if ($this->node->children['expr']->kind !== \ast\AST_DIM) {
(new ContextNode($this->code_base, $this->context, $this->node->children['expr']))->analyzeBackwardCompatibility();
return;
}
$temp = $this->node->children['expr']->children['expr'];
$llnode = $this->node->children['expr'];
$lnode = $temp;
} else {
$temp = $this->node->children['expr'];
$lnode = $temp;
}
if (!($temp->kind == \ast\AST_PROP || $temp->kind == \ast\AST_STATIC_PROP)) {
return;
}
while ($temp instanceof Node && ($temp->kind == \ast\AST_PROP || $temp->kind == \ast\AST_STATIC_PROP)) {
$llnode = $lnode;
$lnode = $temp;
// Lets just hope the 0th is the expression
// we want
$temp = array_values($temp->children)[0];
}
if (!$temp instanceof Node) {
return;
}
// Foo::$bar['baz'](); is a problem
// Foo::$bar['baz'] is not
if ($lnode->kind === \ast\AST_STATIC_PROP && $this->node->kind !== \ast\AST_CALL) {
return;
}
// $this->$bar['baz']; is a problem
// $this->bar['baz'] is not
if ($lnode->kind === \ast\AST_PROP && !$lnode->children['prop'] instanceof Node && !$llnode->children['prop'] instanceof Node) {
return;
}
if (($lnode->children['prop'] instanceof Node && $lnode->children['prop']->kind == \ast\AST_VAR || !empty($lnode->children['class']) && $lnode->children['class'] instanceof Node && ($lnode->children['class']->kind == \ast\AST_VAR || $lnode->children['class']->kind == \ast\AST_NAME) || !empty($lnode->children['expr']) && $lnode->children['expr'] instanceof Node && ($lnode->children['expr']->kind == \ast\AST_VAR || $lnode->children['expr']->kind == \ast\AST_NAME)) && ($temp->kind == \ast\AST_VAR || $temp->kind == \ast\AST_NAME)) {
$ftemp = new \SplFileObject($this->context->getFile());
$ftemp->seek($this->node->lineno - 1);
$line = $ftemp->current();
unset($ftemp);
if (strpos($line, '}[') === false || strpos($line, ']}') === false || strpos($line, '>{') === false) {
Issue::maybeEmit($this->code_base, $this->context, Issue::CompatiblePHP7, $this->node->lineno ?? 0);
}
}
}
作者:ets
项目:pha
/**
* Analyze the parameters and arguments for a call
* to the given method or function
*
* @param CodeBase $code_base
* @param Method $method
* @param Node $node
*
* @return null
*/
private function analyzeCallToMethod(CodeBase $code_base, FunctionInterface $method, Node $node)
{
$method->addReference($this->context);
// Create variables for any pass-by-reference
// parameters
$argument_list = $node->children['args'];
foreach ($argument_list->children as $i => $argument) {
if (!is_object($argument)) {
continue;
}
$parameter = $method->getParameterForCaller($i);
if (!$parameter) {
continue;
}
// If pass-by-reference, make sure the variable exists
// or create it if it doesn't.
if ($parameter->isPassByReference()) {
if ($argument->kind == \ast\AST_VAR) {
// We don't do anything with it; just create it
// if it doesn't exist
$variable = (new ContextNode($this->code_base, $this->context, $argument))->getOrCreateVariable();
} elseif ($argument->kind == \ast\AST_STATIC_PROP || $argument->kind == \ast\AST_PROP) {
$property_name = $argument->children['prop'];
if (is_string($property_name)) {
// We don't do anything with it; just create it
// if it doesn't exist
try {
$property = (new ContextNode($this->code_base, $this->context, $argument))->getOrCreateProperty($argument->children['prop']);
} catch (IssueException $exception) {
Issue::maybeEmitInstance($this->code_base, $this->context, $exception->getIssueInstance());
} catch (\Exception $exception) {
// If we can't figure out what kind of a call
// this is, don't worry about it
}
} else {
// This is stuff like `Class->$foo`. I'm ignoring
// it.
}
}
}
}
// Confirm the argument types are clean
ArgumentType::analyze($method, $node, $this->context, $this->code_base);
// Take another pass over pass-by-reference parameters
// and assign types to passed in variables
foreach ($argument_list->children as $i => $argument) {
if (!is_object($argument)) {
continue;
}
$parameter = $method->getParameterForCaller($i);
if (!$parameter) {
continue;
}
if (Config::get()->dead_code_detection) {
(new ArgumentVisitor($this->code_base, $this->context))($argument);
}
// If the parameter is pass-by-reference and we're
// passing a variable in, see if we should pass
// the parameter and variable types to eachother
$variable = null;
if ($parameter->isPassByReference()) {
if ($argument->kind == \ast\AST_VAR) {
$variable = (new ContextNode($this->code_base, $this->context, $argument))->getOrCreateVariable();
} elseif ($argument->kind == \ast\AST_STATIC_PROP || $argument->kind == \ast\AST_PROP) {
$property_name = $argument->children['prop'];
if (is_string($property_name)) {
// We don't do anything with it; just create it
// if it doesn't exist
try {
$variable = (new ContextNode($this->code_base, $this->context, $argument))->getOrCreateProperty($argument->children['prop']);
} catch (IssueException $exception) {
Issue::maybeEmitInstance($this->code_base, $this->context, $exception->getIssueInstance());
} catch (\Exception $exception) {
// If we can't figure out what kind of a call
// this is, don't worry about it
}
} else {
// This is stuff like `Class->$foo`. I'm ignoring
// it.
}
}
if ($variable) {
$variable->getUnionType()->addUnionType($parameter->getVariadicElementUnionType());
}
}
}
// If we're in quick mode, don't retest methods based on
// parameter types passed in
if (Config::get()->quick_mode) {
return;
//.........这里部分代码省略.........
作者:tpun
项目:pha
/**
* Check to see if signatures match
*
* @return void
*/
public static function analyzeComposition(CodeBase $code_base, Clazz $class)
{
// Get the Class's FQSEN
$fqsen = $class->getFQSEN();
// Get the list of all inherited classes.
$inherited_class_list = $class->getInheritedClassList($code_base);
// No chance of failed composition if we don't inherit from
// lots of stuff.
if (count($inherited_class_list) < 2) {
return;
}
// For each property, find out every inherited class that defines it
// and check to see if the types line up.
foreach ($class->getPropertyList($code_base) as $property) {
try {
$property_union_type = $property->getUnionType();
} catch (IssueException $exception) {
$property_union_type = new UnionType();
}
// Check for that property on each inherited
// class/trait/interface
foreach ($inherited_class_list as $inherited_class) {
// Skip any classes/traits/interfaces not defining that
// property
if (!$inherited_class->hasPropertyWithName($code_base, $property->getName())) {
continue;
}
// We don't call `getProperty` because that will create
// them in some circumstances.
$inherited_property_map = $inherited_class->getPropertyMap($code_base);
if (!isset($inherited_property_map[$property->getName()])) {
continue;
}
// Get the inherited property
$inherited_property = $inherited_property_map[$property->getName()];
// Figure out if this property type can cast to the
// inherited definition's type.
$can_cast = $property_union_type->canCastToExpandedUnionType($inherited_property->getUnionType(), $code_base);
if ($can_cast) {
continue;
}
// Don't emit an issue if the property suppresses the issue
if ($property->hasSuppressIssue(Issue::IncompatibleCompositionProp)) {
continue;
}
Issue::maybeEmit($code_base, $property->getContext(), Issue::IncompatibleCompositionProp, $property->getFileRef()->getLineNumberStart(), (string) $class->getFQSEN(), (string) $inherited_class->getFQSEN(), $property->getName(), (string) $class->getFQSEN(), $class->getFileRef()->getFile(), $class->getFileRef()->getLineNumberStart());
}
}
// TODO: This has too much overlap with PhanParamSignatureMismatch
// and we should figure out how to merge it.
/*
$method_map =
$code_base->getMethodMapByFullyQualifiedClassName($fqsen);
// For each method, find out every inherited class that defines it
// and check to see if the types line up.
foreach ($method_map as $i => $method) {
$method_union_type = $method->getUnionType();
// We don't need to analyze constructors for signature
// compatibility
if ($method->getName() == '__construct') {
continue;
}
// Get the method parameter list
// Check for that method on each inherited
// class/trait/interface
foreach ($inherited_class_list as $inherited_class) {
// Skip anything that doesn't define this method
if (!$inherited_class->hasMethodWithName($code_base, $method->getName())) {
continue;
}
$inherited_method =
$inherited_class->getMethodByName($code_base, $method->getName());
if ($method == $inherited_method) {
continue;
}
// Figure out if this method return type can cast to the
// inherited definition's return type.
$is_compatible =
$method_union_type->canCastToExpandedUnionType(
$inherited_method->getUnionType(),
$code_base
);
$inherited_method_parameter_map =
$inherited_method->getParameterList();
//.........这里部分代码省略.........
作者:tpun
项目:pha
/**
* @param Context $context
* The context in which the node appears
*
* @param CodeBase $code_base
*
* @param Node $node
* An AST node representing a method
*
* @return Method
* A Method representing the AST node in the
* given context
*/
public static function fromNode(Context $context, CodeBase $code_base, Decl $node, FullyQualifiedMethodName $fqsen) : Method
{
// Create the skeleton method object from what
// we know so far
$method = new Method($context, (string) $node->name, new UnionType(), $node->flags ?? 0, $fqsen);
// Parse the comment above the method to get
// extra meta information about the method.
$comment = Comment::fromStringInContext($node->docComment ?? '', $context);
// @var Parameter[]
// The list of parameters specified on the
// method
$parameter_list = Parameter::listFromNode($context, $code_base, $node->children['params']);
// Add each parameter to the scope of the function
foreach ($parameter_list as $parameter) {
$method->getInternalScope()->addVariable($parameter);
}
// If the method is Analyzable, set the node so that
// we can come back to it whenever we like and
// rescan it
$method->setNode($node);
// Set the parameter list on the method
$method->setParameterList($parameter_list);
$method->setNumberOfRequiredParameters(array_reduce($parameter_list, function (int $carry, Parameter $parameter) : int {
return $carry + ($parameter->isRequired() ? 1 : 0);
}, 0));
$method->setNumberOfOptionalParameters(array_reduce($parameter_list, function (int $carry, Parameter $parameter) : int {
return $carry + ($parameter->isOptional() ? 1 : 0);
}, 0));
// Check to see if the comment specifies that the
// method is deprecated
$method->setIsDeprecated($comment->isDeprecated());
$method->setSuppressIssueList($comment->getSuppressIssueList());
if ($method->getIsMagicCall() || $method->getIsMagicCallStatic()) {
$method->setNumberOfOptionalParameters(999);
$method->setNumberOfRequiredParameters(0);
}
// Take a look at method return types
if ($node->children['returnType'] !== null) {
// Get the type of the parameter
$union_type = UnionType::fromNode($context, $code_base, $node->children['returnType']);
$method->getUnionType()->addUnionType($union_type);
}
if ($comment->hasReturnUnionType()) {
// See if we have a return type specified in the comment
$union_type = $comment->getReturnType();
if ($union_type->hasSelfType()) {
// We can't actually figure out 'static' at this
// point, but fill it in regardless. It will be partially
// correct
if ($context->isInClassScope()) {
// n.b.: We're leaving the reference to self, static
// or $this in the type because I'm guessing
// it doesn't really matter. Apologies if it
// ends up being an issue.
$union_type->addUnionType($context->getClassFQSEN()->asUnionType());
}
}
$method->getUnionType()->addUnionType($union_type);
}
// Add params to local scope for user functions
if (!$method->isInternal()) {
$parameter_offset = 0;
foreach ($method->getParameterList() as $i => $parameter) {
if ($parameter->getUnionType()->isEmpty()) {
// If there is no type specified in PHP, check
// for a docComment with @param declarations. We
// assume order in the docComment matches the
// parameter order in the code
if ($comment->hasParameterWithNameOrOffset($parameter->getName(), $parameter_offset)) {
$comment_type = $comment->getParameterWithNameOrOffset($parameter->getName(), $parameter_offset)->getUnionType();
$parameter->getUnionType()->addUnionType($comment_type);
}
}
// If there's a default value on the parameter, check to
// see if the type of the default is cool with the
// specified type.
if ($parameter->hasDefaultValue()) {
$default_type = $parameter->getDefaultValueType();
if (!$default_type->isEqualTo(NullType::instance()->asUnionType())) {
if (!$default_type->isEqualTo(NullType::instance()->asUnionType()) && !$default_type->canCastToUnionType($parameter->getUnionType())) {
Issue::maybeEmit($code_base, $context, Issue::TypeMismatchDefault, $node->lineno ?? 0, (string) $parameter->getUnionType(), $parameter->getName(), (string) $default_type);
}
$parameter->getUnionType()->addUnionType($default_type);
}
// If we have no other type info about a parameter,
// just because it has a default value of null
// doesn't mean that is its type. Any type can default
//.........这里部分代码省略.........
作者:actan
项目:pha
/**
* @param Node $node
* A node to parse
*
* @return Context
* A new or an unchanged context resulting from
* parsing the node
*/
public function visitCatch(Node $node) : Context
{
try {
$union_type = UnionTypeVisitor::unionTypeFromClassNode($this->code_base, $this->context, $node->children['class']);
$class_list = (new ContextNode($this->code_base, $this->context, $node->children['class']))->getClassList();
} catch (CodeBaseException $exception) {
Issue::emit(Issue::UndeclaredClassCatch, $this->context->getFile(), $node->lineno ?? 0, (string) $exception->getFQSEN());
}
$variable_name = (new ContextNode($this->code_base, $this->context, $node->children['var']))->getVariableName();
if (!empty($variable_name)) {
$variable = Variable::fromNodeInContext($node->children['var'], $this->context, $this->code_base, false);
if (!$union_type->isEmpty()) {
$variable->setUnionType($union_type);
}
$this->context->addScopeVariable($variable);
}
return $this->context;
}
作者:Jvbzephi
项目:pha
/**
* @return Parameter[]
* A list of parameters from an AST node.
*
* @see \Phan\Deprecated\Pass1::node_paramlist
* Formerly `function node_paramlist`
*/
public static function listFromNode(Context $context, CodeBase $code_base, Node $node) : array
{
assert($node instanceof Node, "node was not an \\ast\\Node");
$parameter_list = [];
$is_optional_seen = false;
foreach ($node->children ?? [] as $i => $child_node) {
$parameter = Parameter::fromNode($context, $code_base, $child_node);
if (!$parameter->isOptional() && $is_optional_seen) {
Issue::emit(Issue::ParamReqAfterOpt, $context->getFile(), $node->lineno ?? 0);
} else {
if ($parameter->isOptional() && !$is_optional_seen && $parameter->getUnionType()->isEmpty()) {
$is_optional_seen = true;
}
}
$parameter_list[] = $parameter;
}
return $parameter_list;
}