/** * 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) { Phan::getIssueCollector()->collectIssue($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; } // Otherwise, make sure the class exists $type_fqsen = $type->asFQSEN(); if (!$code_base->hasClassWithFQSEN($type_fqsen)) { Issue::emit(Issue::UndeclaredTypeProperty, $property->getFileRef()->getFile(), $property->getFileRef()->getLineNumberStart(), (string) $type_fqsen); } } } }
/** * Visit a node with kind `\ast\AST_PROP` * * @param Node $node * A node of the type indicated by the method name that we'd * like to figure out the type that it produces. * * @return UnionType * The set of types that are possibly produced by the * given node */ public function visitProp(Node $node) : UnionType { try { $property = (new ContextNode($this->code_base, $this->context, $node))->getProperty($node->children['prop']); return $property->getUnionType(); } catch (IssueException $exception) { Phan::getIssueCollector()->collectIssue($exception->getIssueInstance()); } catch (CodeBaseException $exception) { $property_name = $node->children['prop']; Issue::emit(Issue::UndeclaredProperty, $this->context->getFile(), $node->lineno ?? 0, "{$exception->getFQSEN()}->{$property_name}"); } catch (UnanalyzableException $exception) { // Swallow it. There are some constructs that we // just can't figure out. } catch (NodeException $exception) { // Swallow it. There are some constructs that we // just can't figure out. } return new UnionType(); }
/** * @param IssueInstance $issue_instance * An issue instance to emit * * @return void */ public static function emitInstance(IssueInstance $issue_instance) { Phan::getIssueCollector()->collectIssue($issue_instance); }
/** * @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) { Phan::getIssueCollector()->collectIssue($exception->getIssueInstance()); return $this->context; } if (!$this->right_type->canCastToExpandedUnionType($property->getUnionType(), $this->code_base)) { Issue::emit(Issue::TypeMismatchProperty, $this->context->getFile(), $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)) { Issue::emit(Issue::UndeclaredProperty, $this->context->getFile(), $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; }
/** * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitMethodCall(Node $node) : Context { $method_name = $node->children['method']; if (!is_string($method_name)) { return $this->context; } try { $method = (new ContextNode($this->code_base, $this->context, $node))->getMethod($method_name, false); } catch (IssueException $exception) { Phan::getIssueCollector()->collectIssue($exception->getIssueInstance()); return $this->context; } catch (NodeException $exception) { // If we can't figure out the class for this method // call, cry YOLO and mark every method with that // name with a reference. if (Config::get()->dead_code_detection && Config::get()->dead_code_detection_prefer_false_negative) { foreach ($this->code_base->getMethodListByName($method_name) as $method) { $method->addReference($this->context); } } // Swallow it return $this->context; } // Check the call for paraemter and argument types $this->analyzeCallToMethod($this->code_base, $method, $node); return $this->context; }
/** * Emit a log message if the type of the given * node cannot be cast to the given type * * @param Node|null|string|int $node * A node or whatever php-ast feels like returning * * @return bool * True if the cast is possible, else false */ private static function analyzeNodeUnionTypeCast($node, Context $context, CodeBase $code_base, UnionType $cast_type, \Closure $issue_instance) : bool { // Get the type of the node $node_type = UnionType::fromNode($context, $code_base, $node); // See if it can be cast to the given type $can_cast = $node_type->canCastToUnionType($cast_type); // If it can't, emit the log message if (!$can_cast) { Phan::getIssueCollector()->collectIssue($issue_instance($node_type)); } return $can_cast; }