Exemplo n.º 1
0
Arquivo: Type.php Projeto: etsy/phan
 /**
  * @param string $string
  * A string representing a type
  *
  * @param Context $context
  * The context in which the type string was
  * found
  *
  * @return Type
  * Parse a type from the given string
  */
 public static function fromStringInContext(string $string, Context $context) : Type
 {
     assert($string !== '', "Type cannot be empty");
     // Extract the namespace, type and parameter type name list
     $tuple = self::typeStringComponents($string);
     $namespace = $tuple->_0;
     $type_name = $tuple->_1;
     $template_parameter_type_name_list = $tuple->_2;
     // Map the names of the types to actual types in the
     // template parameter type list
     $template_parameter_type_list = array_map(function (string $type_name) use($context) {
         return Type::fromStringInContext($type_name, $context)->asUnionType();
     }, $template_parameter_type_name_list);
     // @var bool
     // True if this type name if of the form 'C[]'
     $is_generic_array_type = self::isGenericArrayString($type_name);
     // If this is a generic array type, get the name of
     // the type of each element
     $non_generic_array_type_name = $type_name;
     if ($is_generic_array_type && false !== ($pos = strrpos($type_name, '[]'))) {
         $non_generic_array_type_name = substr($type_name, 0, $pos);
     }
     // Check to see if the type name is mapped via
     // a using clause.
     //
     // Gotta check this before checking for native types
     // because there are monsters out there that will
     // remap the names via things like `use \Foo\String`.
     $non_generic_partially_qualified_array_type_name = $non_generic_array_type_name;
     if ($namespace) {
         $non_generic_partially_qualified_array_type_name = $namespace . '\\' . $non_generic_partially_qualified_array_type_name;
     }
     if ($context->hasNamespaceMapFor(\ast\flags\USE_NORMAL, $non_generic_partially_qualified_array_type_name)) {
         $fqsen = $context->getNamespaceMapFor(\ast\flags\USE_NORMAL, $non_generic_partially_qualified_array_type_name);
         if ($is_generic_array_type) {
             return GenericArrayType::fromElementType(Type::make($fqsen->getNamespace(), $fqsen->getName(), $template_parameter_type_list));
         }
         return Type::make($fqsen->getNamespace(), $fqsen->getName(), $template_parameter_type_list);
     }
     // If this was a fully qualified type, we're all
     // set
     if (!empty($namespace) && 0 === strpos($namespace, '\\')) {
         return self::make($namespace, $type_name, $template_parameter_type_list);
     }
     if ($is_generic_array_type && self::isNativeTypeString($type_name)) {
         return self::fromInternalTypeName($type_name);
     } else {
         // Check to see if its a builtin type
         switch (strtolower(self::canonicalNameFromName($type_name))) {
             case 'array':
                 return \Phan\Language\Type\ArrayType::instance();
             case 'bool':
                 return \Phan\Language\Type\BoolType::instance();
             case 'callable':
                 return \Phan\Language\Type\CallableType::instance();
             case 'float':
                 return \Phan\Language\Type\FloatType::instance();
             case 'int':
                 return \Phan\Language\Type\IntType::instance();
             case 'mixed':
                 return \Phan\Language\Type\MixedType::instance();
             case 'null':
                 return \Phan\Language\Type\NullType::instance();
             case 'object':
                 return \Phan\Language\Type\ObjectType::instance();
             case 'resource':
                 return \Phan\Language\Type\ResourceType::instance();
             case 'string':
                 return \Phan\Language\Type\StringType::instance();
             case 'void':
                 return \Phan\Language\Type\VoidType::instance();
             case 'static':
                 return \Phan\Language\Type\StaticType::instance();
         }
     }
     // Things like `self[]` or `$this[]`
     if ($is_generic_array_type && self::isSelfTypeString($non_generic_array_type_name) && $context->isInClassScope()) {
         // Callers of this method should be checking on their own
         // to see if this type is a reference to 'parent' and
         // dealing with it there. We don't want to have this
         // method be dependent on the code base
         assert('parent' !== $non_generic_array_type_name, __METHOD__ . " does not know how to handle the type name 'parent'");
         return GenericArrayType::fromElementType(static::fromFullyQualifiedString((string) $context->getClassFQSEN()));
     }
     // If this is a type referencing the current class
     // in scope such as 'self' or 'static', return that.
     if (self::isSelfTypeString($type_name) && $context->isInClassScope()) {
         // Callers of this method should be checking on their own
         // to see if this type is a reference to 'parent' and
         // dealing with it there. We don't want to have this
         // method be dependent on the code base
         assert('parent' !== $type_name, __METHOD__ . " does not know how to handle the type name 'parent'");
         return static::fromFullyQualifiedString((string) $context->getClassFQSEN());
     }
     // Merge the current namespace with the given relative
     // namespace
     if (!empty($context->getNamespace()) && !empty($namespace)) {
         $namespace = $context->getNamespace() . '\\' . $namespace;
     } else {
         if (!empty($context->getNamespace())) {
             $namespace = $context->getNamespace();
         } else {
             $namespace = '\\' . $namespace;
         }
     }
     // Attach the context's namespace to the type name
     return self::make($namespace, $type_name, $template_parameter_type_list);
 }
Exemplo n.º 2
0
 /**
  * @param string $string
  * A string representing a type
  *
  * @param Context $context
  * The context in which the type string was
  * found
  *
  * @return Type
  * Parse a type from the given string
  */
 public static function fromStringInContext(string $string, Context $context) : Type
 {
     assert($string !== '', "Type cannot be empty in {$context}");
     $namespace = null;
     // Extract the namespace if the type string is
     // fully-qualified
     if ('\\' === $string[0]) {
         list($namespace, $string) = self::namespaceAndTypeFromString($string);
     }
     $type_name = $string;
     // @var bool
     // True if this type name if of the form 'C[]'
     $is_generic_array_type = self::isGenericArrayString($type_name);
     // If this is a generic array type, get the name of
     // the type of each element
     $non_generic_array_type_name = $type_name;
     if ($is_generic_array_type && false !== ($pos = strpos($type_name, '[]'))) {
         $non_generic_array_type_name = substr($type_name, 0, $pos);
     }
     // Check to see if the type name is mapped via
     // a using clause.
     //
     // Gotta check this before checking for native types
     // because there are monsters out there that will
     // remap the names via things like `use \Foo\String`.
     if ($context->hasNamespaceMapFor(T_CLASS, $non_generic_array_type_name)) {
         $fqsen = $context->getNamespaceMapFor(T_CLASS, $non_generic_array_type_name);
         if ($is_generic_array_type) {
             return GenericArrayType::fromElementType(Type::make($fqsen->getNamespace(), $fqsen->getName()));
         }
         return Type::make($fqsen->getNamespace(), $fqsen->getName());
     }
     // If this was a fully qualified type, we're all
     // set
     if (!empty($namespace)) {
         return self::fromNamespaceAndName($namespace, $type_name);
     }
     if ($is_generic_array_type && self::isNativeTypeString($type_name)) {
         return self::fromInternalTypeName($type_name);
     } else {
         // Check to see if its a builtin type
         switch (self::canonicalNameFromName($type_name)) {
             case 'array':
                 return \Phan\Language\Type\ArrayType::instance();
             case 'bool':
                 return \Phan\Language\Type\BoolType::instance();
             case 'callable':
                 return \Phan\Language\Type\CallableType::instance();
             case 'float':
                 return \Phan\Language\Type\FloatType::instance();
             case 'int':
                 return \Phan\Language\Type\IntType::instance();
             case 'mixed':
                 return \Phan\Language\Type\MixedType::instance();
             case 'null':
                 return \Phan\Language\Type\NullType::instance();
             case 'object':
                 return \Phan\Language\Type\ObjectType::instance();
             case 'resource':
                 return \Phan\Language\Type\ResourceType::instance();
             case 'string':
                 return \Phan\Language\Type\StringType::instance();
             case 'void':
                 return \Phan\Language\Type\VoidType::instance();
         }
     }
     // Things like `self[]` or `$this[]`
     if ($is_generic_array_type && self::isSelfTypeString($non_generic_array_type_name) && $context->isInClassScope()) {
         // Callers of this method should be checking on their own
         // to see if this type is a reference to 'parent' and
         // dealing with it there. We don't want to have this
         // method be dependent on the code base
         assert('parent' !== $non_generic_array_type_name, __METHOD__ . " does not know how to handle the type name 'parent' in {$context}");
         return GenericArrayType::fromElementType(static::fromFullyQualifiedString((string) $context->getClassFQSEN()));
     }
     // If this is a type referencing the current class
     // in scope such as 'self' or 'static', return that.
     if (self::isSelfTypeString($type_name) && $context->isInClassScope()) {
         // Callers of this method should be checking on their own
         // to see if this type is a reference to 'parent' and
         // dealing with it there. We don't want to have this
         // method be dependent on the code base
         assert('parent' !== $type_name, __METHOD__ . " does not know how to handle the type name 'parent' in {$context}");
         return static::fromFullyQualifiedString((string) $context->getClassFQSEN());
     }
     // Attach the context's namespace to the type name
     return self::fromNamespaceAndName($context->getNamespace() ?: '\\', $type_name);
 }
Exemplo n.º 3
0
 /**
  * @param string $string
  * A string representing a type
  *
  * @param Context $context
  * The context in which the type string was
  * found
  *
  * @return Type
  * Parse a type from the given string
  */
 public static function fromStringInContext(string $string, Context $context) : Type
 {
     assert($string !== '', "Type cannot be empty in {$context}");
     $namespace = null;
     // Extract the namespace if the type string is
     // fully-qualified
     if ('\\' === $string[0]) {
         list($namespace, $string) = self::namespaceAndTypeFromString($string);
     }
     $type_name = strtolower($string);
     // Check to see if the type name is mapped via
     // a using clause.
     //
     // Gotta check this before checking for native types
     // because there are monsters out there that will
     // remap the names via things like `use \Foo\String`.
     if ($context->hasNamespaceMapFor(T_CLASS, $type_name)) {
         $fqsen = $context->getNamespaceMapFor(T_CLASS, $type_name);
         return new Type($fqsen->getNamespace(), $fqsen->getName());
     }
     // If this was a fully qualified type, we're all
     // set
     if (!empty($namespace)) {
         return self::fromNamespaceAndName($namespace, $type_name);
     }
     // Check to see if its a builtin type
     switch (self::canonicalNameFromName($type_name)) {
         case 'array':
             return \Phan\Language\Type\ArrayType::instance();
         case 'bool':
             return \Phan\Language\Type\BoolType::instance();
         case 'callable':
             return \Phan\Language\Type\CallableType::instance();
         case 'float':
             return \Phan\Language\Type\FloatType::instance();
         case 'int':
             return \Phan\Language\Type\IntType::instance();
         case 'mixed':
             return \Phan\Language\Type\MixedType::instance();
         case 'null':
             return \Phan\Language\Type\NullType::instance();
         case 'object':
             return \Phan\Language\Type\ObjectType::instance();
         case 'resource':
             return \Phan\Language\Type\ResourceType::instance();
         case 'string':
             return \Phan\Language\Type\StringType::instance();
         case 'void':
             return \Phan\Language\Type\VoidType::instance();
     }
     // If this is a type referencing the current class
     // in scope such as 'self' or 'static', return that.
     if (self::isSelfTypeString($type_name) && $context->isInClassScope()) {
         return static::fromFullyQualifiedString((string) $context->getClassFQSEN());
     }
     // Attach the context's namespace to the type name
     return self::fromNamespaceAndName($context->getNamespace() ?: '\\', $type_name);
 }
 /**
  * @param Node $node
  * A node to parse
  *
  * @return Context
  * A new or an unchanged context resulting from
  * parsing the node
  */
 public function visitMethod(Decl $node) : Context
 {
     $method = $this->context->getMethodInScope($this->code_base);
     $return_type = $method->getUnionType();
     $has_interface_class = false;
     if ($method->getFQSEN() instanceof FullyQualifiedMethodName) {
         try {
             $class = $method->getDefiningClass($this->code_base);
             $has_interface_class = $class->isInterface();
         } catch (\Exception $exception) {
         }
     }
     if (!$method->isAbstract() && !$has_interface_class && !$return_type->isEmpty() && !$method->getHasReturn() && !$return_type->hasType(VoidType::instance()) && !$return_type->hasType(NullType::instance())) {
         Log::err(Log::ETYPE, "Method {$method->getFQSEN()} is declared to return {$return_type} but has no return value", $this->context->getFile(), $node->lineno);
     }
     return $this->context;
 }
Exemplo n.º 5
0
 /**
  * Visit a node with kind `\ast\AST_FUNC_DECL`
  *
  * @param Node $node
  * A node to parse
  *
  * @return Context
  * A new or an unchanged context resulting from
  * parsing the node
  */
 public function visitFuncDecl(Decl $node) : Context
 {
     $method = $this->context->getFunctionLikeInScope($this->code_base);
     $return_type = $method->getUnionType();
     if (!$return_type->isEmpty() && !$method->getHasReturn() && !$this->declOnlyThrows($node) && !$return_type->hasType(VoidType::instance()) && !$return_type->hasType(NullType::instance())) {
         $this->emitIssue(Issue::TypeMissingReturn, $node->lineno ?? 0, (string) $method->getFQSEN(), (string) $return_type);
     }
     $parameters_seen = [];
     foreach ($method->getParameterList() as $i => $parameter) {
         if (isset($parameters_seen[$parameter->getName()])) {
             $this->emitIssue(Issue::ParamRedefined, $node->lineno ?? 0, '$' . $parameter->getName());
         } else {
             $parameters_seen[$parameter->getName()] = $i;
         }
     }
     return $this->context;
 }
Exemplo n.º 6
0
 /**
  * @param Node $node
  * A node to parse
  *
  * @return Context
  * A new or an unchanged context resulting from
  * parsing the node
  */
 public function visitMethod(Decl $node) : Context
 {
     $method = $this->context->getMethodInScope($this->code_base);
     $return_type = $method->getUnionType();
     $has_interface_class = false;
     if ($method->getFQSEN() instanceof FullyQualifiedMethodName) {
         try {
             $class = $method->getDefiningClass($this->code_base);
             $has_interface_class = $class->isInterface();
         } catch (\Exception $exception) {
         }
     }
     if (!$method->isAbstract() && !$has_interface_class && !$return_type->isEmpty() && !$method->getHasReturn() && !$return_type->hasType(VoidType::instance()) && !$return_type->hasType(NullType::instance())) {
         Issue::emit(Issue::TypeMissingReturn, $this->context->getFile(), $node->lineno ?? 0, $method->getFQSEN(), (string) $return_type);
     }
     return $this->context;
 }
Exemplo n.º 7
0
 /**
  * Visit a node with kind `\ast\AST_FUNC_DECL`
  *
  * @param Node $node
  * A node to parse
  *
  * @return Context
  * A new or an unchanged context resulting from
  * parsing the node
  */
 public function visitFuncDecl(Decl $node) : Context
 {
     $method = $this->context->getMethodInScope($this->code_base);
     $return_type = $method->getUnionType();
     if (!$return_type->isEmpty() && !$method->getHasReturn() && !$return_type->hasType(VoidType::instance()) && !$return_type->hasType(NullType::instance())) {
         $this->emitIssue(Issue::TypeMissingReturn, $node->lineno ?? 0, $method->getFQSEN(), (string) $return_type);
     }
     return $this->context;
 }