mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Fix #510 - check that constructor parent classes and interfaces exist
This commit is contained in:
parent
d0a2258806
commit
be75c143d4
@ -76,6 +76,8 @@ class ClassChecker extends ClassLikeChecker
|
||||
|
||||
$fq_class_name = $class_context && $class_context->self ? $class_context->self : $this->fq_class_name;
|
||||
|
||||
$storage = $this->storage;
|
||||
|
||||
if (preg_match(
|
||||
'/(^|\\\)(int|float|bool|string|void|null|false|true|resource|object|numeric|mixed)$/i',
|
||||
$fq_class_name
|
||||
@ -94,7 +96,7 @@ class ClassChecker extends ClassLikeChecker
|
||||
true
|
||||
)
|
||||
),
|
||||
$this->source->getSuppressedIssues()
|
||||
array_merge($storage->suppressed_issues, $this->source->getSuppressedIssues())
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
@ -102,8 +104,6 @@ class ClassChecker extends ClassLikeChecker
|
||||
return null;
|
||||
}
|
||||
|
||||
$storage = $this->storage;
|
||||
|
||||
$project_checker = $this->file_checker->project_checker;
|
||||
$codebase = $project_checker->codebase;
|
||||
|
||||
@ -120,7 +120,7 @@ class ClassChecker extends ClassLikeChecker
|
||||
$this,
|
||||
$this->parent_fq_class_name,
|
||||
$parent_reference_location,
|
||||
$this->getSuppressedIssues(),
|
||||
array_merge($storage->suppressed_issues, $this->getSuppressedIssues()),
|
||||
false
|
||||
) === false) {
|
||||
return false;
|
||||
@ -142,7 +142,7 @@ class ClassChecker extends ClassLikeChecker
|
||||
$this->parent_fq_class_name . ' is marked deprecated',
|
||||
$code_location
|
||||
),
|
||||
$this->source->getSuppressedIssues()
|
||||
array_merge($storage->suppressed_issues, $this->getSuppressedIssues())
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
@ -196,7 +196,7 @@ class ClassChecker extends ClassLikeChecker
|
||||
$interface_name . ' is marked deprecated',
|
||||
$code_location
|
||||
),
|
||||
$this->source->getSuppressedIssues()
|
||||
array_merge($storage->suppressed_issues, $this->getSuppressedIssues())
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
@ -229,7 +229,7 @@ class ClassChecker extends ClassLikeChecker
|
||||
$storage->name,
|
||||
$code_location
|
||||
),
|
||||
$this->source->getSuppressedIssues()
|
||||
array_merge($storage->suppressed_issues, $this->getSuppressedIssues())
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
@ -244,7 +244,7 @@ class ClassChecker extends ClassLikeChecker
|
||||
. ' must be public in ' . $storage->name,
|
||||
$code_location
|
||||
),
|
||||
$this->source->getSuppressedIssues()
|
||||
array_merge($storage->suppressed_issues, $this->getSuppressedIssues())
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
@ -294,7 +294,7 @@ class ClassChecker extends ClassLikeChecker
|
||||
true
|
||||
)
|
||||
),
|
||||
$this->source->getSuppressedIssues()
|
||||
array_merge($storage->suppressed_issues, $this->getSuppressedIssues())
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
@ -377,7 +377,7 @@ class ClassChecker extends ClassLikeChecker
|
||||
'Trait ' . $fq_trait_name . ' does not exist',
|
||||
new CodeLocation($this, $trait)
|
||||
),
|
||||
$this->source->getSuppressedIssues()
|
||||
array_merge($storage->suppressed_issues, $this->getSuppressedIssues())
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
@ -388,7 +388,7 @@ class ClassChecker extends ClassLikeChecker
|
||||
'Trait ' . $fq_trait_name . ' has wrong casing',
|
||||
new CodeLocation($this, $trait)
|
||||
),
|
||||
$this->source->getSuppressedIssues()
|
||||
array_merge($storage->suppressed_issues, $this->getSuppressedIssues())
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
@ -614,7 +614,7 @@ class ClassChecker extends ClassLikeChecker
|
||||
', but no constructor',
|
||||
$first_uninitialized_property->location
|
||||
),
|
||||
$this->source->getSuppressedIssues()
|
||||
array_merge($storage->suppressed_issues, $this->getSuppressedIssues())
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ use Psalm\Context;
|
||||
use Psalm\Issue\AbstractInstantiation;
|
||||
use Psalm\Issue\DeprecatedClass;
|
||||
use Psalm\Issue\TooManyArguments;
|
||||
use Psalm\Issue\UndefinedClass;
|
||||
use Psalm\IssueBuffer;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TNamedObject;
|
||||
@ -110,6 +111,34 @@ class NewChecker extends \Psalm\Checker\Statements\Expression\CallChecker
|
||||
) {
|
||||
$storage = $project_checker->classlike_storage_provider->get($fq_class_name);
|
||||
|
||||
foreach ($storage->parent_classes as $parent_class_name) {
|
||||
if (!$codebase->classlikes->classExists($parent_class_name)) {
|
||||
if (IssueBuffer::accepts(
|
||||
new UndefinedClass(
|
||||
'Parent class ' . $parent_class_name . ' does not exist',
|
||||
new CodeLocation($statements_checker->getSource(), $stmt)
|
||||
),
|
||||
$statements_checker->getSuppressedIssues()
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($storage->class_implements as $interface_name) {
|
||||
if (!$codebase->classlikes->interfaceExists($interface_name)) {
|
||||
if (IssueBuffer::accepts(
|
||||
new UndefinedClass(
|
||||
'Implemented interface ' . $interface_name . ' does not exist',
|
||||
new CodeLocation($statements_checker->getSource(), $stmt)
|
||||
),
|
||||
$statements_checker->getSuppressedIssues()
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we're not calling this constructor via new static()
|
||||
if ($storage->abstract && !$late_static) {
|
||||
if (IssueBuffer::accepts(
|
||||
|
@ -236,6 +236,16 @@ class MethodCallTest extends TestCase
|
||||
$a->bar();',
|
||||
'error_message' => 'PossiblyFalseReference',
|
||||
],
|
||||
'undefinedParentClass' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-suppress UndefinedClass
|
||||
*/
|
||||
class B extends A {}
|
||||
|
||||
$b = new B();',
|
||||
'error_message' => 'UndefinedClass - src/somefile.php:7',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user