1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Fix #1236 - forbid bad class names in @extends

This commit is contained in:
Brown 2019-01-24 16:43:22 -05:00
parent 5352084188
commit 650c2b70f8
2 changed files with 116 additions and 68 deletions

View File

@ -233,6 +233,23 @@ class ClassAnalyzer extends ClassLikeAnalyzer
}
}
if ($storage->template_type_extends) {
foreach ($storage->template_type_extends as $type_map) {
foreach ($type_map as $atomic_type) {
$atomic_type->check(
$this,
new CodeLocation(
$this,
$class->name ?: $class,
null,
true
),
$this->getSuppressedIssues()
);
}
}
}
if ($storage->invalid_dependencies) {
return;
}
@ -1193,76 +1210,74 @@ class ClassAnalyzer extends ClassLikeAnalyzer
$actual_method_storage = $codebase->methods->getStorage($actual_method_id);
if (!$actual_method_storage->has_template_return_type) {
if ($actual_method_id) {
$return_type_location = $codebase->methods->getMethodReturnTypeLocation(
$actual_method_id,
$secondary_return_type_location
);
}
$self_class = $class_context->self;
$return_type = $codebase->methods->getMethodReturnType($analyzed_method_id, $self_class);
if ($return_type && $class_storage->template_type_extends) {
$generic_params = [];
$class_template_params = MethodCallAnalyzer::getClassTemplateParams(
$codebase,
$class_storage,
$class_context->self,
strtolower($stmt->name->name)
) ?: [];
$return_type->replaceTemplateTypesWithStandins($class_template_params, $generic_params);
}
$overridden_method_ids = isset($class_storage->overridden_method_ids[strtolower($stmt->name->name)])
? $class_storage->overridden_method_ids[strtolower($stmt->name->name)]
: [];
if ($actual_method_storage->overridden_downstream) {
$overridden_method_ids[] = 'overridden::downstream';
}
if (!$return_type && isset($class_storage->interface_method_ids[strtolower($stmt->name->name)])) {
$interface_method_ids = $class_storage->interface_method_ids[strtolower($stmt->name->name)];
foreach ($interface_method_ids as $interface_method_id) {
list($interface_class) = explode('::', $interface_method_id);
$interface_return_type = $codebase->methods->getMethodReturnType(
$interface_method_id,
$interface_class
);
$interface_return_type_location = $codebase->methods->getMethodReturnTypeLocation(
$interface_method_id
);
ReturnTypeAnalyzer::verifyReturnType(
$stmt,
$source,
$method_analyzer,
$interface_return_type,
$interface_class,
$interface_return_type_location,
[$analyzed_method_id]
);
}
}
ReturnTypeAnalyzer::verifyReturnType(
$stmt,
$source,
$method_analyzer,
$return_type,
$self_class,
$return_type_location,
$overridden_method_ids
if ($actual_method_id) {
$return_type_location = $codebase->methods->getMethodReturnTypeLocation(
$actual_method_id,
$secondary_return_type_location
);
}
$self_class = $class_context->self;
$return_type = $codebase->methods->getMethodReturnType($analyzed_method_id, $self_class);
if ($return_type && $class_storage->template_type_extends) {
$generic_params = [];
$class_template_params = MethodCallAnalyzer::getClassTemplateParams(
$codebase,
$class_storage,
$class_context->self,
strtolower($stmt->name->name)
) ?: [];
$return_type->replaceTemplateTypesWithStandins($class_template_params, $generic_params);
}
$overridden_method_ids = isset($class_storage->overridden_method_ids[strtolower($stmt->name->name)])
? $class_storage->overridden_method_ids[strtolower($stmt->name->name)]
: [];
if ($actual_method_storage->overridden_downstream) {
$overridden_method_ids[] = 'overridden::downstream';
}
if (!$return_type && isset($class_storage->interface_method_ids[strtolower($stmt->name->name)])) {
$interface_method_ids = $class_storage->interface_method_ids[strtolower($stmt->name->name)];
foreach ($interface_method_ids as $interface_method_id) {
list($interface_class) = explode('::', $interface_method_id);
$interface_return_type = $codebase->methods->getMethodReturnType(
$interface_method_id,
$interface_class
);
$interface_return_type_location = $codebase->methods->getMethodReturnTypeLocation(
$interface_method_id
);
ReturnTypeAnalyzer::verifyReturnType(
$stmt,
$source,
$method_analyzer,
$interface_return_type,
$interface_class,
$interface_return_type_location,
[$analyzed_method_id]
);
}
}
ReturnTypeAnalyzer::verifyReturnType(
$stmt,
$source,
$method_analyzer,
$return_type,
$self_class,
$return_type_location,
$overridden_method_ids
);
}
if (!$method_already_analyzed

View File

@ -2675,6 +2675,39 @@ class TemplateTest extends TestCase
}',
'error_message' => 'InvalidReturnType',
],
'templateWithNoReturn' => [
'<?php
/**
* @template T
*/
class A {
/** @return T */
public function foo() {}
}',
'error_message' => 'InvalidReturnType',
],
'badTemplateExtends' => [
'<?php
/**
* @template T
*/
class A {
/** @var T */
public $t;
/** @param T $t */
public function __construct($t) {
$this->t = $t;
}
}
/**
* @template TT
* @template-extends A<Z>
*/
class B extends A {}',
'error_message' => 'UndefinedClass'
],
];
}
}