本文整理汇总了PHP中Phan\Issue::maybeEmit方法的典型用法代码示例。如果您正苦于以下问题:PHP Issue::maybeEmit方法的具体用法?PHP Issue::maybeEmit怎么用?PHP Issue::maybeEmit使用的例子?那么, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在类Phan\Issue
的用法示例。
在下文中一共展示了Issue::maybeEmit方法的15个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的PHP代码示例。
示例1: analyzeDuplicateFunction
/**
* 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());
}
}
}
示例2: analyzeParentConstructorCalled
/**
* 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::maybeEmit($code_base, $clazz->getContext(), Issue::TypeParentConstructorCalled, $clazz->getFileRef()->getLineNumberStart(), (string) $clazz->getFQSEN(), (string) $parent_clazz->getFQSEN());
}
}
示例3: analyzePropertyTypes
/**
* 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);
}
}
}
}
}
示例4: fqsenExistsForClass
/**
* @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;
}
示例5: visitBinaryAdd
/**
* @param Node $node
* A node to check types on
*
* @return UnionType
* The resulting type(s) of the binary operation
*/
public function visitBinaryAdd(Node $node) : UnionType
{
$left = UnionType::fromNode($this->context, $this->code_base, $node->children['left']);
$right = UnionType::fromNode($this->context, $this->code_base, $node->children['right']);
// fast-track common cases
if ($left->isType(IntType::instance()) && $right->isType(IntType::instance())) {
return IntType::instance()->asUnionType();
}
// If both left and right are arrays, then this is array
// concatenation.
if ($left->isGenericArray() && $right->isGenericArray()) {
if ($left->isEqualTo($right)) {
return $left;
}
return ArrayType::instance()->asUnionType();
}
if (($left->isType(IntType::instance()) || $left->isType(FloatType::instance())) && ($right->isType(IntType::instance()) || $right->isType(FloatType::instance()))) {
return FloatType::instance()->asUnionType();
}
$left_is_array = !empty($left->genericArrayElementTypes()) && empty($left->nonGenericArrayTypes()) || $left->isType(ArrayType::instance());
$right_is_array = !empty($right->genericArrayElementTypes()) && empty($right->nonGenericArrayTypes()) || $right->isType(ArrayType::instance());
if ($left_is_array && !$right->canCastToUnionType(ArrayType::instance()->asUnionType())) {
Issue::maybeEmit($this->code_base, $this->context, Issue::TypeInvalidRightOperand, $node->lineno ?? 0);
return new UnionType();
} elseif ($right_is_array && !$left->canCastToUnionType(ArrayType::instance()->asUnionType())) {
Issue::maybeEmit($this->code_base, $this->context, Issue::TypeInvalidLeftOperand, $node->lineno ?? 0);
return new UnionType();
} elseif ($left_is_array || $right_is_array) {
// If it is a '+' and we know one side is an array
// and the other is unknown, assume array
return ArrayType::instance()->asUnionType();
}
return new UnionType([IntType::instance(), FloatType::instance()]);
}
示例6: analyzeFile
/**
* 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(Config::projectPath($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;
}
return (new BlockAnalysisVisitor($code_base, $context))($node);
}
示例7: fromNode
/**
* @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
//.........这里部分代码省略.........
示例8: analyzeOverrideSignature
//.........这里部分代码省略.........
// If we have a parent type defined, map the method's
// return type and parameter types through it
$type_option = $class->getParentTypeOption();
// Map overridden method parameter types through any
// template type parameters we may have
if ($type_option->isDefined()) {
$o_parameter_list = array_map(function (Parameter $parameter) use($type_option, $code_base) : Parameter {
if (!$parameter->getUnionType()->hasTemplateType()) {
return $parameter;
}
$mapped_parameter = clone $parameter;
$mapped_parameter->setUnionType($mapped_parameter->getUnionType()->withTemplateParameterTypeMap($type_option->get()->getTemplateParameterTypeMap($code_base)));
return $mapped_parameter;
}, $o_parameter_list);
}
// Map overridden method return type through any template
// type parameters we may have
$o_return_union_type = $o_method->getUnionType();
if ($type_option->isDefined() && $o_return_union_type->hasTemplateType()) {
$o_return_union_type = $o_return_union_type->withTemplateParameterTypeMap($type_option->get()->getTemplateParameterTypeMap($code_base));
}
// Determine if the signatures match up
$signatures_match = true;
// Make sure the count of parameters matches
if ($method->getNumberOfRequiredParameters() > $o_method->getNumberOfRequiredParameters()) {
$signatures_match = false;
} else {
if ($method->getNumberOfParameters() < $o_method->getNumberOfParameters()) {
$signatures_match = false;
// If parameter counts match, check their types
} else {
foreach ($method->getParameterList() as $i => $parameter) {
if (!isset($o_parameter_list[$i])) {
continue;
}
$o_parameter = $o_parameter_list[$i];
// Changing pass by reference is not ok
// @see https://3v4l.org/Utuo8
if ($parameter->isPassByReference() != $o_parameter->isPassByReference()) {
$signatures_match = false;
break;
}
// A stricter type on an overriding method is cool
if ($o_parameter->getUnionType()->isEmpty() || $o_parameter->getUnionType()->isType(MixedType::instance())) {
continue;
}
// Its not OK to have a more relaxed type on an
// overriding method
//
// https://3v4l.org/XTm3P
if ($parameter->getUnionType()->isEmpty()) {
$signatures_match = false;
break;
}
// If we have types, make sure they line up
//
// TODO: should we be expanding the types on $o_parameter
// via ->asExpandedTypes($code_base)?
//
// @see https://3v4l.org/ke3kp
if (!$o_parameter->getUnionType()->canCastToUnionType($parameter->getUnionType())) {
$signatures_match = false;
break;
}
}
}
}
// Return types should be mappable
if (!$o_return_union_type->isEmpty()) {
if (!$method->getUnionType()->asExpandedTypes($code_base)->canCastToUnionType($o_return_union_type)) {
$signatures_match = false;
}
}
// Static or non-static should match
if ($method->isStatic() != $o_method->isStatic()) {
if ($o_method->isStatic()) {
Issue::maybeEmit($code_base, $method->getContext(), Issue::AccessStaticToNonStatic, $method->getFileRef()->getLineNumberStart(), $o_method->getFQSEN());
} else {
Issue::maybeEmit($code_base, $method->getContext(), Issue::AccessNonStaticToStatic, $method->getFileRef()->getLineNumberStart(), $o_method->getFQSEN());
}
}
if ($o_method->returnsRef() && !$method->returnsRef()) {
$signatures_match = false;
}
if (!$signatures_match) {
if ($o_method->isInternal()) {
Issue::maybeEmit($code_base, $method->getContext(), Issue::ParamSignatureMismatchInternal, $method->getFileRef()->getLineNumberStart(), $method, $o_method);
} else {
Issue::maybeEmit($code_base, $method->getContext(), Issue::ParamSignatureMismatch, $method->getFileRef()->getLineNumberStart(), $method, $o_method, $o_method->getFileRef()->getFile(), $o_method->getFileRef()->getLineNumberStart());
}
}
// Access must be compatible
if ($o_method->isProtected() && $method->isPrivate() || $o_method->isPublic() && !$method->isPublic()) {
if ($o_method->isInternal()) {
Issue::maybeEmit($code_base, $method->getContext(), Issue::AccessSignatureMismatchInternal, $method->getFileRef()->getLineNumberStart(), $method, $o_method);
} else {
Issue::maybeEmit($code_base, $method->getContext(), Issue::AccessSignatureMismatch, $method->getFileRef()->getLineNumberStart(), $method, $o_method, $o_method->getFileRef()->getFile(), $o_method->getFileRef()->getLineNumberStart());
}
}
}
示例9: analyzeBackwardCompatibility
/**
* 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);
}
}
}
示例10: analyzeComposition
/**
* 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();
//.........这里部分代码省略.........
示例11: fromNode
/**
* @param Context $context
* The context in which the node appears
*
* @param CodeBase $code_base
*
* @param Node $node
* An AST node representing a function
*
* @return Func
* A Func representing the AST node in the
* given context
*/
public static function fromNode(Context $context, CodeBase $code_base, Decl $node) : Func
{
// Parse the comment above the function to get
// extra meta information about the function.
$comment = Comment::fromStringInContext($node->docComment ?? '', $context);
// @var Parameter[]
// The list of parameters specified on the
// function
$parameter_list = Parameter::listFromNode($context, $code_base, $node->children['params']);
// Add each parameter to the scope of the function
foreach ($parameter_list as $parameter) {
$context = $context->withScopeVariable($parameter);
}
// Create the skeleton function object from what
// we know so far
$func = new Func($context, (string) $node->name, new UnionType(), $node->flags ?? 0);
// If the function is Analyzable, set the node so that
// we can come back to it whenever we like and
// rescan it
$func->setNode($node);
// Set the parameter list on the function
$func->setParameterList($parameter_list);
$func->setNumberOfRequiredParameters(array_reduce($parameter_list, function (int $carry, Parameter $parameter) : int {
return $carry + ($parameter->isRequired() ? 1 : 0);
}, 0));
$func->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
// function is deprecated
$func->setIsDeprecated($comment->isDeprecated());
$func->setSuppressIssueList($comment->getSuppressIssueList());
// Take a look at function return types
if ($node->children['returnType'] !== null) {
// Get the type of the parameter
$union_type = UnionType::fromNode($context, $code_base, $node->children['returnType']);
$func->getUnionType()->addUnionType($union_type);
}
if ($comment->hasReturnUnionType()) {
// See if we have a return type specified in the comment
$union_type = $comment->getReturnType();
assert(!$union_type->hasSelfType(), "Function referencing self in {$context}");
$func->getUnionType()->addUnionType($union_type);
}
// Add params to local scope for user functions
if (!$func->isInternal()) {
$parameter_offset = 0;
foreach ($func->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
// to null
if ((string) $default_type === 'null' && !$parameter->getUnionType()->isEmpty()) {
$parameter->getUnionType()->addType(NullType::instance());
}
}
++$parameter_offset;
}
}
return $func;
}
示例12: listFromNode
/**
* @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::maybeEmit($code_base, $context, Issue::ParamReqAfterOpt, $node->lineno ?? 0);
} elseif ($parameter->isOptional() && !$is_optional_seen && $parameter->getVariadicElementUnionType()->isEmpty()) {
$is_optional_seen = true;
}
$parameter_list[] = $parameter;
}
return $parameter_list;
}
示例13: analyzeFile
/**
* 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);
}
示例14: analyzeInternalArgumentType
/**
* Check to see if the given Clazz is a duplicate
*
* @param FunctionInterface $method
* The method we're analyzing arguments for
*
* @param Node $node
* The node holding the method call we're looking at
*
* @param Context $context
* The context in which we see the call
*
* @param CodeBase $code_base
*
* @return null
*
* @see \Phan\Deprecated\Pass2::arg_check
* Formerly `function arg_check`
*/
private static function analyzeInternalArgumentType(FunctionInterface $method, Node $node, Context $context, CodeBase $code_base)
{
$arglist = $node->children['args'];
$argcount = count($arglist->children);
switch ($method->getName()) {
case 'join':
case 'implode':
// (string glue, array pieces),
// (array pieces, string glue) or
// (array pieces)
if ($argcount == 1) {
self::analyzeNodeUnionTypeCast($arglist->children[0], $context, $code_base, ArrayType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method) {
// "arg#1(pieces) is %s but {$method->getFQSEN()}() takes array when passed only 1 arg"
return Issue::fromType(Issue::ParamSpecial2)($context->getFile(), $context->getLineNumberStart(), [1, 'pieces', (string) $method->getFQSEN(), 'string', 'array']);
});
return;
} elseif ($argcount == 2) {
$arg1_type = UnionType::fromNode($context, $code_base, $arglist->children[0]);
$arg2_type = UnionType::fromNode($context, $code_base, $arglist->children[1]);
if ((string) $arg1_type == 'array') {
if (!$arg1_type->canCastToUnionType(StringType::instance()->asUnionType())) {
Issue::maybeEmit($code_base, $context, Issue::ParamSpecial1, $context->getLineNumberStart(), 2, 'glue', (string) $arg2_type, (string) $method->getFQSEN(), 'string', 1, 'array');
}
} elseif ((string) $arg1_type == 'string') {
if (!$arg2_type->canCastToUnionType(ArrayType::instance()->asUnionType())) {
Issue::maybeEmit($code_base, $context, Issue::ParamSpecial1, $context->getLineNumberStart(), 2, 'pieces', (string) $arg2_type, (string) $method->getFQSEN(), 'array', 1, 'string');
}
}
return;
}
// Any other arg counts we will let the regular
// checks handle
break;
case 'array_udiff':
case 'array_diff_uassoc':
case 'array_uintersect_assoc':
case 'array_intersect_ukey':
if ($argcount < 3) {
Issue::maybeEmit($code_base, $context, Issue::ParamTooFewInternal, $context->getLineNumberStart(), $argcount, (string) $method->getFQSEN(), $method->getNumberOfRequiredParameters());
return;
}
self::analyzeNodeUnionTypeCast($arglist->children[$argcount - 1], $context, $code_base, CallableType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method) {
// "The last argument to {$method->getFQSEN()} must be a callable"
return Issue::fromType(Issue::ParamSpecial3)($context->getFile(), $context->getLineNumberStart(), [(string) $method->getFQSEN(), 'callable']);
});
for ($i = 0; $i < $argcount - 1; $i++) {
self::analyzeNodeUnionTypeCast($arglist->children[$i], $context, $code_base, CallableType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method, $i) {
// "arg#".($i+1)." is %s but {$method->getFQSEN()}() takes array"
return Issue::fromType(Issue::ParamTypeMismatch)($context->getFile(), $context->getLineNumberStart(), [$i + 1, (string) $node_type, (string) $method->getFQSEN(), 'array']);
});
}
return;
case 'array_diff_uassoc':
case 'array_uintersect_uassoc':
if ($argcount < 4) {
Issue::maybeEmit($code_base, $context, Issue::ParamTooFewInternal, $context->getLineNumberStart(), $argcount, (string) $method->getFQSEN(), $method->getNumberOfRequiredParameters());
return;
}
// The last 2 arguments must be a callable and there
// can be a variable number of arrays before it
self::analyzeNodeUnionTypeCast($arglist->children[$argcount - 1], $context, $code_base, CallableType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method) {
// "The last argument to {$method->getFQSEN()} must be a callable"
return Issue::fromType(Issue::ParamSpecial3)($context->getFile(), $context->getLineNumberStart(), [(string) $method->getFQSEN(), 'callable']);
});
self::analyzeNodeUnionTypeCast($arglist->children[$argcount - 2], $context, $code_base, CallableType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method) {
// "The second last argument to {$method->getFQSEN()} must be a callable"
return Issue::fromType(Issue::ParamSpecial4)($context->getFile(), $context->getLineNumberStart(), [(string) $method->getFQSEN(), 'callable']);
});
for ($i = 0; $i < $argcount - 2; $i++) {
self::analyzeNodeUnionTypeCast($arglist->children[$i], $context, $code_base, ArrayType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method, $i) {
// "arg#".($i+1)." is %s but {$method->getFQSEN()}() takes array"
return Issue::fromType(Issue::ParamTypeMismatch)($context->getFile(), $context->getLineNumberStart(), [$i + 1, (string) $node_type, (string) $method->getFQSEN(), 'array']);
});
}
return;
case 'strtok':
// (string str, string token) or (string token)
if ($argcount == 1) {
// If we have just one arg it must be a string token
self::analyzeNodeUnionTypeCast($arglist->children[0], $context, $code_base, StringType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method) {
return Issue::fromType(Issue::ParamSpecial2)($context->getFile(), $context->getLineNumberStart(), [1, 'token', (string) $node_type, (string) $method->getFQSEN(), 'string']);
//.........这里部分代码省略.........
示例15: analyzeElementReferenceCounts
/**
* Check to see if the given Clazz is a duplicate
*
* @return null
*/
public static function analyzeElementReferenceCounts(CodeBase $code_base, AddressableElement $element, string $issue_type)
{
// Don't worry about internal elements
if ($element->isInternal()) {
return;
}
// Skip methods that are overrides of other methods
if ($element instanceof ClassElement) {
if ($element->getIsOverride()) {
return;
}
$class_fqsen = $element->getClassFQSEN();
// Don't analyze elements defined in a parent
// class
if ((string) $class_fqsen !== $element->getFQSEN()) {
return;
}
$defining_class = $element->getClass($code_base);
// Don't analyze elements on interfaces or on
// abstract classes, as they're uncallable.
if ($defining_class->isInterface() || $defining_class->isAbstract() || $defining_class->isTrait()) {
return;
}
// Ignore magic methods
if ($element instanceof Method) {
// Doubly nested so that `$element` shows
// up as Method in Phan.
if ($element->getIsMagic()) {
return;
}
}
}
// Skip properties on classes that have a magic
// __get or __set method given that we can't track
// their access
if ($element instanceof Property) {
$defining_class = $element->getClass($code_base);
if ($defining_class->hasGetOrSetMethod($code_base)) {
return;
}
}
/*
print "digraph G {\n";
foreach ($element->getReferenceList() as $file_ref) {
print "\t\"{$file_ref->getFile()}\" -> \"{$element->getFileRef()->getFile()}\";\n";
}
print "}\n";
*/
if ($element->getReferenceCount($code_base) < 1) {
if ($element->hasSuppressIssue($issue_type)) {
return;
}
if ($element instanceof AddressableElement) {
Issue::maybeEmit($code_base, $element->getContext(), $issue_type, $element->getFileRef()->getLineNumberStart(), (string) $element->getFQSEN());
} else {
Issue::maybeEmit($code_base, $element->getContext(), $issue_type, $element->getFileRef()->getLineNumberStart(), (string) $element);
}
}
}