/** * Check method parameters to make sure they're valid * * @return null */ public static function analyzeParameterTypes(CodeBase $code_base, FunctionInterface $method) { // Look at each parameter to make sure their types // are valid 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; } if ($type instanceof TemplateType) { if ($method instanceof Method) { if ($method->isStatic()) { Issue::maybeEmit($code_base, $method->getContext(), Issue::TemplateTypeStaticMethod, $method->getFileRef()->getLineNumberStart(), (string) $method->getFQSEN()); } } } else { // Make sure the class exists $type_fqsen = $type->asFQSEN(); if (!$code_base->hasClassWithFQSEN($type_fqsen)) { Issue::maybeEmit($code_base, $method->getContext(), Issue::UndeclaredTypeParameter, $method->getFileRef()->getLineNumberStart(), (string) $type_fqsen); } } } } if ($method instanceof Method) { self::analyzeOverrideSignature($code_base, $method); } }
/** * 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); } } } }
/** * 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()); } } }
/** * 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()); } }
private function addMethodWithScopeAndName(FunctionInterface $method, string $scope, string $name) { $this->method_map[$scope][$name] = $method; // If we're doing dead code detection, map the name // directly to the method so we can quickly look up // all methods with that name to add a possible // reference if (Config::get()->dead_code_detection) { $this->method_name_map[strtolower($name)][] = $method; } // Associate the element with the file it was found in $this->getFileByPath($method->getFileRef()->getFile())->addMethodFQSEN($method->getFQSEN()); }
/** * @param CodeBase $code_base * The global code base * * @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 * * @return null * * @see \Phan\Deprecated\Pass2::arglist_type_check * Formerly `function arglist_type_check` */ private static function analyzeParameterList(CodeBase $code_base, FunctionInterface $method, Node $node, Context $context) { foreach ($node->children ?? [] as $i => $argument) { // Get the parameter associated with this argument $parameter = $method->getParameterList()[$i] ?? null; // This issue should be caught elsewhere if (!$parameter) { continue; } // If this is a pass-by-reference parameter, make sure // we're passing an allowable argument if ($parameter->isPassByReference()) { if (!$argument instanceof \ast\Node || $argument->kind != \ast\AST_VAR && $argument->kind != \ast\AST_DIM && $argument->kind != \ast\AST_PROP && $argument->kind != \ast\AST_STATIC_PROP) { Issue::maybeEmit($code_base, $context, Issue::TypeNonVarPassByRef, $node->lineno ?? 0, $i + 1, (string) $method->getFQSEN()); } else { $variable_name = (new ContextNode($code_base, $context, $argument))->getVariableName(); if (Type::isSelfTypeString($variable_name) && !$context->isInClassScope() && $argument->kind == \ast\AST_STATIC_PROP && $argument->kind == \ast\AST_PROP) { Issue::maybeEmit($code_base, $context, Issue::ContextNotObject, $node->lineno ?? 0, "{$variable_name}"); } } } // Get the type of the argument. We'll check it against // the parameter in a moment $argument_type = UnionType::fromNode($context, $code_base, $argument); // Expand it to include all parent types up the chain $argument_type_expanded = $argument_type->asExpandedTypes($code_base); // Check the method to see if it has the correct // parameter types. If not, keep hunting through // alternates of the method until we find one that // takes the correct types $alternate_parameter = null; $alternate_found = false; foreach ($method->alternateGenerator($code_base) as $alternate_id => $alternate_method) { if (empty($alternate_method->getParameterList()[$i])) { continue; } // Get the parameter associated with this argument $alternate_parameter = $alternate_method->getParameterList()[$i] ?? null; // Expand the types to find all parents and traits $alternate_parameter_type_expanded = $alternate_parameter->getUnionType()->asExpandedTypes($code_base); // See if the argument can be cast to the // parameter if ($argument_type_expanded->canCastToUnionType($alternate_parameter_type_expanded)) { $alternate_found = true; break; } } if (!$alternate_found) { $parameter_name = $alternate_parameter ? $alternate_parameter->getName() : 'unknown'; $parameter_type = $alternate_parameter ? $alternate_parameter->getUnionType() : 'unknown'; if ($method->isInternal()) { Issue::maybeEmit($code_base, $context, Issue::TypeMismatchArgumentInternal, $node->lineno ?? 0, $i + 1, $parameter_name, $argument_type_expanded, (string) $method->getFQSEN(), (string) $parameter_type); } else { Issue::maybeEmit($code_base, $context, Issue::TypeMismatchArgument, $node->lineno ?? 0, $i + 1, $parameter_name, $argument_type_expanded, (string) $method->getFQSEN(), (string) $parameter_type, $method->getFileRef()->getFile(), $method->getFileRef()->getLineNumberStart()); } } } }
/** * @param CodeBase $code_base * The global code base * * @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 * * @return null * * @see \Phan\Deprecated\Pass2::arglist_type_check * Formerly `function arglist_type_check` */ private static function analyzeParameterList(CodeBase $code_base, FunctionInterface $method, Node $node, Context $context) { // There's nothing reasonable we can do here if ($method instanceof Method) { if ($method->getIsMagicCall() || $method->getIsMagicCallStatic()) { return; } } foreach ($node->children ?? [] as $i => $argument) { // Get the parameter associated with this argument $parameter = $method->getParameterForCaller($i); // This issue should be caught elsewhere if (!$parameter) { continue; } // If this is a pass-by-reference parameter, make sure // we're passing an allowable argument if ($parameter->isPassByReference()) { if (!$argument instanceof \ast\Node || $argument->kind != \ast\AST_VAR && $argument->kind != \ast\AST_DIM && $argument->kind != \ast\AST_PROP && $argument->kind != \ast\AST_STATIC_PROP) { Issue::maybeEmit($code_base, $context, Issue::TypeNonVarPassByRef, $node->lineno ?? 0, $i + 1, (string) $method->getFQSEN()); } else { $variable_name = (new ContextNode($code_base, $context, $argument))->getVariableName(); if (Type::isSelfTypeString($variable_name) && !$context->isInClassScope() && $argument->kind == \ast\AST_STATIC_PROP && $argument->kind == \ast\AST_PROP) { Issue::maybeEmit($code_base, $context, Issue::ContextNotObject, $node->lineno ?? 0, "{$variable_name}"); } } } // Get the type of the argument. We'll check it against // the parameter in a moment $argument_type = UnionType::fromNode($context, $code_base, $argument); // Expand it to include all parent types up the chain $argument_type_expanded = $argument_type->asExpandedTypes($code_base); // Check the method to see if it has the correct // parameter types. If not, keep hunting through // alternates of the method until we find one that // takes the correct types $alternate_parameter = null; $alternate_found = false; foreach ($method->alternateGenerator($code_base) as $alternate_id => $alternate_method) { // Get the parameter associated with this argument $candidate_alternate_parameter = $alternate_method->getParameterForCaller($i); if (is_null($candidate_alternate_parameter)) { continue; } $alternate_parameter = $candidate_alternate_parameter; // See if the argument can be cast to the // parameter if ($argument_type_expanded->canCastToUnionType($alternate_parameter->getUnionType())) { $alternate_found = true; break; } } if (!$alternate_found) { $parameter_name = $alternate_parameter ? $alternate_parameter->getName() : 'unknown'; $parameter_type = $alternate_parameter ? $alternate_parameter->getUnionType() : 'unknown'; if (is_object($parameter_type) && $parameter_type->hasTemplateType()) { // Don't worry about template types } elseif ($method->isInternal()) { // If we are not in strict mode and we accept a string parameter // and the argument we are passing has a __toString method then it is ok if (!$context->getIsStrictTypes() && $parameter_type->hasType(StringType::instance())) { try { foreach ($argument_type_expanded->asClassList($code_base, $context) as $clazz) { if ($clazz->hasMethodWithName($code_base, "__toString")) { return; } } } catch (CodeBaseException $e) { // Swallow "Cannot find class", go on to emit issue } } Issue::maybeEmit($code_base, $context, Issue::TypeMismatchArgumentInternal, $node->lineno ?? 0, $i + 1, $parameter_name, $argument_type_expanded, (string) $method->getFQSEN(), (string) $parameter_type); } else { Issue::maybeEmit($code_base, $context, Issue::TypeMismatchArgument, $node->lineno ?? 0, $i + 1, $parameter_name, $argument_type_expanded, (string) $method->getFQSEN(), (string) $parameter_type, $method->getFileRef()->getFile(), $method->getFileRef()->getLineNumberStart()); } } } }