Example #1
0
 /**
  * @param Node $node
  * A node to parse
  *
  * @return Context
  * A new or an unchanged context resulting from
  * parsing the node
  */
 public function visitCall(Node $node) : Context
 {
     $expression = $node->children['expr'];
     (new ContextNode($this->code_base, $this->context, $node))->analyzeBackwardCompatibility();
     foreach ($node->children['args']->children ?? [] as $arg_node) {
         if ($arg_node instanceof Node) {
             (new ContextNode($this->code_base, $this->context, $arg_node))->analyzeBackwardCompatibility();
         }
     }
     if ($expression->kind == \ast\AST_VAR) {
         $variable_name = (new ContextNode($this->code_base, $this->context, $expression))->getVariableName();
         if (empty($variable_name)) {
             return $this->context;
         }
         // $var() - hopefully a closure, otherwise we don't know
         if ($this->context->getScope()->hasVariableWithName($variable_name)) {
             $variable = $this->context->getScope()->getVariableWithName($variable_name);
             $union_type = $variable->getUnionType();
             if ($union_type->isEmpty()) {
                 return $this->context;
             }
             foreach ($union_type->getTypeSet() as $type) {
                 if (!$type instanceof CallableType) {
                     continue;
                 }
                 $closure_fqsen = FullyQualifiedFunctionName::fromFullyQualifiedString((string) $type->asFQSEN());
                 if ($this->code_base->hasMethod($closure_fqsen)) {
                     // Get the closure
                     $method = $this->code_base->getMethod($closure_fqsen);
                     // Check the call for paraemter and argument types
                     $this->analyzeCallToMethod($this->code_base, $method, $node);
                 }
             }
         }
     } else {
         if ($expression->kind == \ast\AST_NAME) {
             try {
                 $method = (new ContextNode($this->code_base, $this->context, $expression))->getFunction($expression->children['name'] ?? $expression->children['method']);
             } catch (IssueException $exception) {
                 $exception->getIssueInstance()();
                 return $this->context;
             }
             // Check the call for paraemter and argument types
             $this->analyzeCallToMethod($this->code_base, $method, $node);
         } else {
             if ($expression->kind == \ast\AST_CALL || $expression->kind == \ast\AST_STATIC_CALL || $expression->kind == \ast\AST_NEW || $expression->kind == \ast\AST_METHOD_CALL) {
                 $class_list = (new ContextNode($this->code_base, $this->context, $expression))->getClassList();
                 foreach ($class_list as $class) {
                     if (!$class->hasMethodWithName($this->code_base, '__invoke')) {
                         continue;
                     }
                     $method = $class->getMethodByNameInContext($this->code_base, '__invoke', $this->context);
                     // Check the call for paraemter and argument types
                     $this->analyzeCallToMethod($this->code_base, $method, $node);
                 }
             }
         }
     }
     return $this->context;
 }
Example #2
0
 /**
  * Visit a node with kind `\ast\AST_STATIC_CALL`
  *
  * @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 visitStaticCall(Node $node) : UnionType
 {
     try {
         $class_name = AST::classNameFromNode($this->context, $this->code_base, $node);
     } catch (TypeException $exception) {
         return new UnionType();
     }
     if (!$class_name) {
         return new UnionType();
     }
     $method_name = $node->children['method'];
     // Give up on any complicated nonsense where the
     // method name is a variable such as in
     // `$variable->$function_name()`.
     if ($method_name instanceof Node) {
         return new UnionType();
     }
     // Method names can some times turn up being
     // other method calls.
     assert(is_string($method_name), "Method name must be a string. Something else given.");
     $method_fqsen = FullyQualifiedMethodName::make(FullyQualifiedClassName::fromStringInContext($class_name, $this->context), $method_name);
     if (!$this->code_base->hasMethod($method_fqsen)) {
         return new UnionType();
     }
     $method = $this->code_base->getMethod($method_fqsen);
     return $method->getUnionType();
 }
Example #3
0
 /**
  * @return Method
  */
 public function getClosure() : Func
 {
     $closure_fqsen = FullyQualifiedFunctionName::fromClosureInContext($this->context);
     if (!$this->code_base->hasMethod($closure_fqsen)) {
         throw new CodeBaseException($closure_fqsen, "Could not find closure {$closure_fqsen}");
     }
     return $this->code_base->getMethod($closure_fqsen);
 }
Example #4
0
 /**
  * @param Node $node
  * A node to parse
  *
  * @return Context
  * A new or an unchanged context resulting from
  * parsing the node
  */
 public function visitCall(Node $node) : Context
 {
     $expression = $node->children['expr'];
     if (Config::get()->backward_compatibility_checks) {
         AST::backwardCompatibilityCheck($this->context, $node);
         foreach ($node->children['args']->children as $arg_node) {
             if ($arg_node instanceof Node) {
                 AST::backwardCompatibilityCheck($this->context, $arg_node);
             }
         }
     }
     if ($expression->kind == \ast\AST_NAME) {
         try {
             $method = AST::functionFromNameInContext($expression->children['name'], $this->context, $this->code_base);
         } catch (CodeBaseException $exception) {
             Log::err(Log::EUNDEF, $exception->getMessage(), $this->context->getFile(), $node->lineno);
             return $this->context;
         }
         // Check the call for paraemter and argument types
         $this->analyzeCallToMethod($this->code_base, $method, $node);
     } else {
         if ($expression->kind == \ast\AST_VAR) {
             $variable_name = AST::variableName($expression);
             if (empty($variable_name)) {
                 return $this->context;
             }
             // $var() - hopefully a closure, otherwise we don't know
             if ($this->context->getScope()->hasVariableWithName($variable_name)) {
                 $variable = $this->context->getScope()->getVariableWithName($variable_name);
                 $union_type = $variable->getUnionType();
                 if ($union_type->isEmpty()) {
                     return $this->context;
                 }
                 $type = $union_type->head();
                 if (!$type instanceof CallableType) {
                     return $this->context;
                 }
                 $closure_fqsen = FullyQualifiedFunctionName::fromFullyQualifiedString((string) $type->asFQSEN());
                 if ($this->code_base->hasMethod($closure_fqsen)) {
                     // Get the closure
                     $method = $this->code_base->getMethod($closure_fqsen);
                     // Check the call for paraemter and argument types
                     $this->analyzeCallToMethod($this->code_base, $method, $node);
                 }
             }
         }
     }
     return $this->context;
 }
 /**
  * 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());
     }
 }
Example #6
0
 /**
  * @param string $function_name
  * The name of the function we'd like to look up
  *
  * @param bool $is_function_declaration
  * This must be set to true if we're getting a function
  * that is being declared and false if we're getting a
  * function being called.
  *
  * @return Method
  * A method with the given name in the given context
  *
  * @throws CodeBaseExtension
  * An exception is thrown if we can't find the given
  * function
  */
 public function getFunction(string $function_name, bool $is_function_declaration = false) : Method
 {
     if ($is_function_declaration) {
         $function_fqsen = FullyQualifiedFunctionName::make($this->context->getNamespace(), $function_name);
     } else {
         $function_fqsen = FullyQualifiedFunctionName::make($this->context->getNamespace(), $function_name);
         // If it doesn't exist in the local namespace, try it
         // in the global namespace
         if (!$this->code_base->hasMethod($function_fqsen)) {
             $function_fqsen = FullyQualifiedFunctionName::fromStringInContext($function_name, $this->context);
         }
     }
     // Make sure the method we're calling actually exists
     if (!$this->code_base->hasMethod($function_fqsen)) {
         throw new CodeBaseException($function_fqsen, "call to undefined function {$function_fqsen}()");
     }
     $method = $this->code_base->getMethod($function_fqsen);
     return $method;
 }
Example #7
0
 /**
  * Check to see if the given Clazz is a duplicate
  *
  * @return null
  */
 public static function analyzeDuplicateFunction(CodeBase $code_base, Method $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 ('internal' === $original_method->getContext()->getFile()) {
         // If its in an conditional and the original is an
         // internal method, presume its all OK.
         if ($method->getContext()->getIsConditional()) {
             return;
         }
         Log::err(Log::EREDEF, "Function {$method_name} defined at {$method->getContext()->getFile()}:{$method->getContext()->getLineNumberStart()} was previously defined internally", $method->getContext()->getFile(), $method->getContext()->getLineNumberStart());
     } else {
         Log::err(Log::EREDEF, "Function {$method_name} defined at {$method->getContext()->getFile()}:{$method->getContext()->getLineNumberStart()} was previously defined at {$original_method->getContext()->getFile()}:{$original_method->getContext()->getLineNumberStart()}", $method->getContext()->getFile(), $method->getContext()->getLineNumberStart());
     }
 }
Example #8
0
 /**
  * @return Method[]
  * The set of all alternates to this method
  */
 public function alternateGenerator(CodeBase $code_base) : \Generator
 {
     $alternate_id = 0;
     $fqsen = $this->getFQSEN();
     while ($code_base->hasMethod($fqsen)) {
         (yield $code_base->getMethod($fqsen));
         $fqsen = $fqsen->withAlternateId(++$alternate_id);
     }
 }
Example #9
0
 /**
  * @return Method
  * The method with the given name
  */
 public function getMethodByNameInContext(CodeBase $code_base, string $name, Context $context) : Method
 {
     $method_fqsen = FullyQualifiedMethodName::make($this->getFQSEN(), $name);
     if (!$code_base->hasMethod($method_fqsen)) {
         if ('__construct' === $name) {
             // Create a default constructor if its requested
             // but doesn't exist yet
             $default_constructor = Method::defaultConstructorForClassInContext($this, $this->getContext()->withClassFQSEN($this->getFQSEN()));
             $this->addMethod($code_base, $default_constructor);
             return $default_constructor;
         }
         throw new CodeBaseException("Method with name {$name} does not exist for class {$this->getFQSEN()}.");
     }
     return $code_base->getMethod($method_fqsen);
 }
Example #10
0
 /**
  * @param string $function_name
  * The name of the function we'd like to look up
  *
  * @param Context $context
  * The context in which we found the reference to the
  * given function name
  *
  * @param CodeBase $code_base
  * The global code base holding all state
  *
  * @param bool $is_function_declaration
  * This must be set to true if we're getting a function
  * that is being declared and false if we're getting a
  * function being called.
  *
  * @return Method
  * A method with the given name in the given context
  *
  * @throws CodeBaseExtension
  * An exception is thrown if we can't find the given
  * function
  */
 public static function functionFromNameInContext(string $function_name, Context $context, CodeBase $code_base, bool $is_function_declaration = false) : Method
 {
     if ($is_function_declaration) {
         $function_fqsen = FullyQualifiedFunctionName::make($context->getNamespace(), $function_name);
     } else {
         $function_fqsen = FullyQualifiedFunctionName::fromStringInContext($function_name, $context);
     }
     // Make sure the method we're calling actually exists
     if (!$code_base->hasMethod($function_fqsen)) {
         throw new CodeBaseException("call to undefined function {$function_fqsen}()");
     }
     $method = $code_base->getMethod($function_fqsen);
     return $method;
 }