In order to understand internal classes, interfaces, traits and functions, a CodeBase needs to be initialized with the list of those elements begotten before any classes are loaded. # Example Grab these before we define our own classes $internal_class_name_list = get_declared_classes(); $internal_interface_name_list = get_declared_interfaces(); $internal_trait_name_list = get_declared_traits(); $internal_function_name_list = get_defined_functions()['internal']; Load any required code ... $code_base = new CodeBase( $internal_class_name_list, $internal_interface_name_list, $internal_trait_name_list, $internal_function_name_list ); Do stuff ...
예제 #1
0
 /**
  * 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()) {
         Log::err(Log::EREDEF, "{$clazz} defined at " . "{$clazz->getContext()->getFile()}:{$clazz->getContext()->getLineNumberStart()} " . "was previously defined as {$original_class} internally", $clazz->getContext()->getFile(), $clazz->getContext()->getLineNumberStart());
         // Otherwise, print the coordinates of the original
         // definition
     } else {
         Log::err(Log::EREDEF, "{$clazz} defined at " . "{$clazz->getContext()->getFile()}:{$clazz->getContext()->getLineNumberStart()} " . "was previously defined as {$original_class} at " . "{$original_class->getContext()->getFile()}:{$original_class->getContext()->getLineNumberStart()}", $clazz->getContext()->getFile(), $clazz->getContext()->getLineNumberStart());
     }
     return;
 }
예제 #2
0
 /**
  * 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;
 }
예제 #3
0
 /**
  * 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());
     }
 }
예제 #4
0
 /**
  * @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) : bool
 {
     if (!$code_base->hasClassWithFQSEN($fqsen)) {
         Log::err(Log::EUNDEF, "Trying to inherit from unknown class {$fqsen}", $clazz->getContext()->getFile(), $clazz->getContext()->getLineNumberStart());
         return false;
     }
     return true;
 }
예제 #5
0
 /**
  * @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 $message_template) : bool
 {
     if (!$code_base->hasClassWithFQSEN($fqsen)) {
         Log::err(Log::EUNDEF, sprintf($message_template, $fqsen), $clazz->getContext()->getFile(), $clazz->getContext()->getLineNumberStart());
         return false;
     }
     return true;
 }
예제 #6
0
 /**
  * @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;
 }
예제 #7
0
 /**
  * @return Clazz
  * The class that defined this element
  *
  * @throws CodeBaseException
  * An exception may be thrown if we can't find the
  * class
  */
 public function getDefiningClass(CodeBase $code_base) : Clazz
 {
     $class_fqsen = $this->getDefiningClassFQSEN();
     if (!$code_base->hasClassWithFQSEN($class_fqsen)) {
         throw new CodeBaseException($class_fqsen, "Defining class {$class_fqsen} for {$this->getFQSEN()} not found");
     }
     return $code_base->getClassByFQSEN($class_fqsen);
 }
예제 #8
0
 public function testMethodInCodeBase()
 {
     $context = $this->contextForCode("\n                namespace A;\n                Class B {\n                    public function c() {\n                        return 42;\n                    }\n                }\n            ");
     $class_fqsen = FullyQualifiedClassName::fromFullyQualifiedString('\\A\\b');
     self::assertTrue($this->code_base->hasClassWithFQSEN($class_fqsen), "Class with FQSEN {$class_fqsen} not found");
     $clazz = $this->code_base->getClassByFQSEN($class_fqsen);
     self::assertTrue($clazz->hasMethodWithName($this->code_base, 'c'), "Method with FQSEN not found");
 }
예제 #9
0
 /**
  * @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 string
  * The class name represented by the given call
  */
 public function visitProp(Node $node) : string
 {
     if (!($node->children['expr']->kind == \ast\AST_VAR && !$node->children['expr']->children['name'] instanceof Node)) {
         return '';
     }
     // $var->prop->method()
     $var = $node->children['expr'];
     $class = null;
     if ($var->children['name'] == 'this') {
         // If we're not in a class scope, 'this' won't work
         if (!$this->context->isInClassScope()) {
             Log::err(Log::ESTATIC, 'Using $this when not in object context', $this->context->getFile(), $node->lineno);
             return '';
         }
         // $this->$node->method()
         if ($node->children['prop'] instanceof Node) {
             // Too hard. Giving up.
             return '';
         }
         $class = $this->context->getClassInScope($this->code_base);
     } else {
         // Get the list of viable class types for the
         // variable
         $union_type = AST::varUnionType($this->context, $var)->nonNativeTypes()->nonGenericArrayTypes();
         if ($union_type->isEmpty()) {
             return '';
         }
         $class_fqsen = $union_type->head()->asFQSEN();
         if (!$this->code_base->hasClassWithFQSEN($class_fqsen)) {
             return '';
         }
         $class = $this->code_base->getClassByFQSEN($class_fqsen);
     }
     $property_name = $node->children['prop'];
     if (!$class->hasPropertyWithName($this->code_base, $property_name)) {
         // If we can't find the property, there's
         // no type. Thie issue should be caught
         // elsewhere.
         return '';
     }
     try {
         $property = $class->getPropertyByNameInContext($this->code_base, $property_name, $this->context);
     } catch (AccessException $exception) {
         Log::err(Log::EACCESS, $exception->getMessage(), $this->context->getFile(), $node->lineno);
         return '';
     }
     $union_type = $property->getUnionType()->nonNativeTypes();
     if ($union_type->isEmpty()) {
         // If we don't have a type on the property we
         // can't figure out the class type.
         return '';
     } else {
         // Return the first type on the property
         // that could be a reference to a class
         return (string) $union_type->head()->asFQSEN();
     }
     // No such property was found, or none were classes
     // that could be found
     return '';
 }
예제 #10
0
 /**
  * Visit a node with kind `\ast\AST_METHOD_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 visitMethodCall(Node $node) : UnionType
 {
     $class_name = AST::classNameFromNode($this->context, $this->code_base, $node);
     if (empty($class_name)) {
         return new UnionType();
     }
     $class_fqsen = FullyQualifiedClassName::fromstringInContext($class_name, $this->context);
     assert($this->code_base->hasClassWithFQSEN($class_fqsen), "Class {$class_fqsen} must exist");
     $clazz = $this->code_base->getClassByFQSEN($class_fqsen);
     $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.");
     if (!$clazz->hasMethodWithName($this->code_base, $method_name)) {
         Log::err(Log::EUNDEF, "call to undeclared method {$class_fqsen}->{$method_name}()", $this->context->getFile(), $node->lineno);
         return new UnionType();
     }
     $method = $clazz->getMethodByNameInContext($this->code_base, $method_name, $this->context);
     return $method->getUnionType();
 }
예제 #11
0
 /**
  * @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 string
  * The class name represented by the given call
  */
 public function visitVar(Node $node) : string
 {
     // $$var->method()
     if ($node->children['name'] instanceof Node) {
         return '';
     }
     // $this->method()
     if ($node->children['name'] == 'this') {
         if (!$this->context->isInClassScope()) {
             Log::err(Log::ESTATIC, 'Using $this when not in object context', $this->context->getFile(), $node->lineno);
             return '';
         }
         return (string) $this->context->getClassFQSEN();
     }
     $variable_name = $node->children['name'];
     if (!$this->context->getScope()->hasVariableWithName($variable_name)) {
         // Got lost, couldn't find the variable in the current scope
         // If it really isn't defined, it will be caught by the
         // undefined var error
         return '';
     }
     $variable = $this->context->getScope()->getVariableWithName($variable_name);
     // Hack - loop through the possible types of the var and assume
     // first found class is correct
     foreach ($variable->getUnionType()->nonGenericArrayTypes()->getTypeList() as $type) {
         $child_class_fqsen = FullyQualifiedClassName::fromStringInContext((string) $type, $this->context);
         if ($this->code_base->hasClassWithFQSEN($child_class_fqsen)) {
             return (string) FullyQualifiedClassName::fromStringInContext((string) $type, $this->context);
         }
     }
     // Could not find name
     return '';
 }
예제 #12
0
 /**
  * @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) {
         $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) {
             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;
 }
예제 #13
0
 /**
  * @return Method
  */
 public function getClosure() : Func
 {
     $closure_fqsen = FullyQualifiedFunctionName::fromClosureInContext($this->context);
     if (!$this->code_base->hasFunctionWithFQSEN($closure_fqsen)) {
         throw new CodeBaseException($closure_fqsen, "Could not find closure {$closure_fqsen}");
     }
     return $this->code_base->getFunctionByFQSEN($closure_fqsen);
 }
 /**
  * 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());
     }
 }
예제 #15
0
 /**
  * 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);
             }
         }
     }
 }
예제 #16
0
 /**
  * Check to see if the given Clazz is a duplicate
  *
  * @return null
  */
 public static function analyzeParameterTypes(CodeBase $code_base, Method $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->getTypeList() 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)) {
                 Log::err(Log::EUNDEF, "parameter of undeclared type {$type_fqsen}", $method->getContext()->getFile(), $method->getContext()->getLineNumberStart());
             }
         }
     }
 }
예제 #17
0
 /**
  * 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());
         }
     }
 }
예제 #18
0
 /**
  * @return Constant
  * Get the (non-class) constant associated with this node
  * in this context
  *
  * @throws NodeException
  * An exception is thrown if we can't understand the node
  *
  * @throws CodeBaseExtension
  * An exception is thrown if we can't find the given
  * class
  */
 public function getConst() : Constant
 {
     assert($this->node->kind === \ast\AST_CONST, "Node must be of type \\ast\\AST_CONST");
     if ($this->node->children['name']->kind !== \ast\AST_NAME) {
         throw new NodeException($this->node, "Can't determine constant name");
     }
     // Get an FQSEN for the root namespace
     $fqsen = null;
     $constant_name = $this->node->children['name']->children['name'];
     if (!$this->code_base->hasConstant($fqsen, $constant_name)) {
         throw new CodeBaseException($fqsen, "Cannot find constant with name {$constant_name}");
     }
     return $this->code_base->getConstant($fqsen, $constant_name);
 }
예제 #19
0
 /**
  * Take a look at all globally accessible elements and see if
  * we can find any dead code that is never referenced
  *
  * @return void
  */
 public static function analyzeReferenceCounts(CodeBase $code_base)
 {
     // Check to see if dead code detection is enabled. Keep
     // in mind that the results here are just a guess and
     // we can't tell with certainty that anything is
     // definitely unreferenced.
     if (!Config::get()->dead_code_detection) {
         return;
     }
     // Get the count of all known elements
     $total_count = $code_base->totalElementCount();
     $i = 0;
     // Functions
     self::analyzeElementListReferenceCounts($code_base, $code_base->getFunctionMap(), Issue::UnreferencedMethod, $total_count, $i);
     // Constants
     self::analyzeElementListReferenceCounts($code_base, $code_base->getGlobalConstantMap(), Issue::UnreferencedConstant, $total_count, $i);
     // Classes
     self::analyzeElementListReferenceCounts($code_base, $code_base->getClassMap(), Issue::UnreferencedClass, $total_count, $i);
     // Class Maps
     foreach ($code_base->getClassMapMap() as $class_map) {
         self::analyzeClassMapReferenceCounts($code_base, $class_map, $total_count, $i);
     }
 }
예제 #20
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());
     }
 }
예제 #21
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;
 }
예제 #22
0
 /**
  * @return Clazz[]
  * A list of classes associated with the given node
  *
  * @throws CodeBaseException
  * 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()->getTypeList() 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 CodeBaseException($class_fqsen, "reference to undeclared class {$class_fqsen}");
         }
         (yield $this->code_base->getClassByFQSEN($class_fqsen));
     }
 }
예제 #23
0
 /**
  * @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));
     }
 }
예제 #24
0
 /**
  * Take a look at all globally accessible elements and see if
  * we can find any dead code that is never referenced
  *
  * @return void
  */
 public static function analyzeReferenceCounts(CodeBase $code_base)
 {
     // Check to see if dead code detection is enabled. Keep
     // in mind that the results here are just a guess and
     // we can't tell with certainty that anything is
     // definitely unreferenced.
     if (!Config::get()->dead_code_detection) {
         return;
     }
     // Get the count of all known elements
     $total_count = count($code_base->getMethodMap(), COUNT_RECURSIVE) + count($code_base->getPropertyMap(), COUNT_RECURSIVE) + count($code_base->getConstantMap(), COUNT_RECURSIVE) + count($code_base->getClassMap(), COUNT_RECURSIVE);
     $i = 0;
     $analyze_list = function ($list) use($code_base, &$i, $total_count) {
         foreach ($list as $name => $element) {
             CLI::progress('dead code', ++$i / $total_count);
             self::analyzeElementReferenceCounts($code_base, $element);
         }
     };
     $analyze_map = function ($map) use($code_base, &$i, $total_count) {
         foreach ($map as $fqsen_string => $list) {
             foreach ($list as $name => $element) {
                 CLI::progress('dead code', ++$i / $total_count);
                 // Don't worry about internal elements
                 if ($element->getContext()->isInternal()) {
                     continue;
                 }
                 $element_fqsen = $element->getFQSEN();
                 if ($element_fqsen instanceof FullyQualifiedClassElement) {
                     $class_fqsen = $element->getDefiningClassFQSEN();
                     // Don't analyze elements defined in a parent
                     // class
                     if ((string) $class_fqsen !== $fqsen_string) {
                         continue;
                     }
                     $defining_class = $element->getDefiningClass($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()) {
                         continue;
                     }
                     // Ignore magic methods
                     if ($element instanceof Method && $element->getIsMagic()) {
                         continue;
                     }
                 }
                 self::analyzeElementReferenceCounts($code_base, $element);
             }
         }
     };
     $analyze_map($code_base->getMethodMap());
     $analyze_map($code_base->getPropertyMap());
     $analyze_map($code_base->getConstantMap());
     $analyze_list($code_base->getClassMap());
 }
예제 #25
0
파일: Type.php 프로젝트: etsy/phan
 public function isSubclassOf(CodeBase $code_base, Type $parent)
 {
     $fqsen = $this->asFQSEN();
     assert($fqsen instanceof FullyQualifiedClassName);
     $this_clazz = $code_base->getClassByFQSEN($fqsen);
     $parent_fqsen = $parent->asFQSEN();
     assert($parent_fqsen instanceof FullyQualifiedClassName);
     $parent_clazz = $code_base->getClassByFQSEN($parent_fqsen);
     return $this_clazz->isSubclassOf($code_base, $parent_clazz);
 }
예제 #26
0
 /**
  * @return Method
  * Get the closure in this scope or fail real hard
  */
 public function getClosureInScope(CodeBase $code_base) : Method
 {
     assert($this->isClosureScope(), "Must be in closure scope to get closure. Actually in {$this}");
     return $code_base->getMethod($this->getClosureFQSEN());
 }
예제 #27
0
파일: Analysis.php 프로젝트: etsy/phan
 /**
  * Take a pass over all functions verifying various
  * states.
  *
  * @return null
  */
 public static function analyzeFunctions(CodeBase $code_base)
 {
     $function_count = count($code_base->getFunctionAndMethodSet());
     $i = 0;
     foreach ($code_base->getFunctionAndMethodSet() as $function_or_method) {
         CLI::progress('method', ++$i / $function_count);
         if ($function_or_method->isInternal()) {
             continue;
         }
         DuplicateFunctionAnalyzer::analyzeDuplicateFunction($code_base, $function_or_method);
         ParameterTypesAnalyzer::analyzeParameterTypes($code_base, $function_or_method);
         // Let any plugins analyze the methods or functions
         if ($function_or_method instanceof Func) {
             ConfigPluginSet::instance()->analyzeFunction($code_base, $function_or_method);
         } else {
             if ($function_or_method instanceof Method) {
                 ConfigPluginSet::instance()->analyzeMethod($code_base, $function_or_method);
             }
         }
     }
 }
예제 #28
0
파일: Method.php 프로젝트: tpunt/phan
 /**
  * @return Method[]|\Generator
  * The set of all alternates to this method
  */
 public function alternateGenerator(CodeBase $code_base) : \Generator
 {
     $alternate_id = 0;
     $fqsen = $this->getFQSEN();
     while ($code_base->hasMethodWithFQSEN($fqsen)) {
         (yield $code_base->getMethodByFQSEN($fqsen));
         $fqsen = $fqsen->withAlternateId(++$alternate_id);
     }
 }
예제 #29
0
파일: Type.php 프로젝트: ablyler/phan
 /**
  * @param CodeBase
  * The code base to use in order to find super classes, etc.
  *
  * @param $recursion_depth
  * This thing has a tendency to run-away on me. This tracks
  * how bad I messed up by seeing how far the expanded types
  * go
  *
  * @return UnionType
  * Expands class types to all inherited classes returning
  * a superset of this type.
  */
 public function asExpandedTypes(CodeBase $code_base, int $recursion_depth = 0) : UnionType
 {
     return $this->memoize(__METHOD__, function () use($code_base, $recursion_depth) : UnionType {
         // We're going to assume that if the type hierarchy
         // is taller than some value we probably messed up
         // and should bail out.
         assert($recursion_depth < 20, "Recursion has gotten out of hand for type {$this}");
         if ($this->isNativeType()) {
             return $this->asUnionType();
         }
         $union_type = $this->asUnionType();
         $class_fqsen = $this->isGenericArray() ? $this->genericArrayElementType()->asFQSEN() : $this->asFQSEN();
         if (!$code_base->hasClassWithFQSEN($class_fqsen)) {
             return $union_type;
         }
         $clazz = $code_base->getClassByFQSEN($class_fqsen);
         $union_type->addUnionType($this->isGenericArray() ? $clazz->getUnionType()->asGenericArrayTypes() : $clazz->getUnionType());
         // Resurse up the tree to include all types
         $recursive_union_type = new UnionType();
         foreach ($union_type->getTypeSet() as $clazz_type) {
             if ((string) $clazz_type != (string) $this) {
                 $recursive_union_type->addUnionType($clazz_type->asExpandedTypes($code_base, $recursion_depth + 1));
             } else {
                 $recursive_union_type->addType($clazz_type);
             }
         }
         return $recursive_union_type;
     });
 }
예제 #30
0
파일: Analysis.php 프로젝트: Jvbzephir/phan
 /**
  * Take a pass over all functions verifying various
  * states.
  *
  * @return null
  */
 public static function analyzeFunctions(CodeBase $code_base)
 {
     $function_count = count($code_base->getMethodMap(), COUNT_RECURSIVE);
     $i = 0;
     foreach ($code_base->getMethodMap() as $fqsen_string => $method_map) {
         foreach ($method_map as $name => $method) {
             CLI::progress('method', ++$i / $function_count);
             if ($method->getContext()->isInternal()) {
                 continue;
             }
             DuplicateFunctionAnalyzer::analyzeDuplicateFunction($code_base, $method);
             ParameterTypesAnalyzer::analyzeParameterTypes($code_base, $method);
         }
     }
 }