mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Emit issue when property type is not defined
This commit is contained in:
parent
7f3a763303
commit
f9f38f283e
@ -525,10 +525,9 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
||||
$property_type = Type::getMixed();
|
||||
}
|
||||
|
||||
// type_location is only set if collect_references is true
|
||||
if ($property->type_location && !$property_type->isMixed()) {
|
||||
$fleshed_out_type = ExpressionChecker::fleshOutTypes(clone $property_type, $this->fq_class_name, null);
|
||||
$fleshed_out_type->check($this, $property->type_location, $this->getSuppressedIssues());
|
||||
$fleshed_out_type->check($this, $property->type_location, $this->getSuppressedIssues(), [], false);
|
||||
}
|
||||
|
||||
if ($property->is_static) {
|
||||
@ -1010,7 +1009,7 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
||||
$property_type = StatementsChecker::getSimpleType($property->default) ?: Type::getMixed();
|
||||
}
|
||||
} else {
|
||||
if ($this->getFileChecker()->project_checker->collect_references && $property_type_line_number) {
|
||||
if ($property_type_line_number) {
|
||||
$property_type_location = new CodeLocation($this, $stmt);
|
||||
$property_type_location->setCommentLine($property_type_line_number);
|
||||
}
|
||||
@ -1128,13 +1127,15 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
||||
* @param FileChecker $file_checker
|
||||
* @param CodeLocation $code_location
|
||||
* @param array<string> $suppressed_issues
|
||||
* @param bool $inferred - whether or not the type was inferred
|
||||
* @return bool|null
|
||||
*/
|
||||
public static function checkFullyQualifiedClassLikeName(
|
||||
$fq_class_name,
|
||||
FileChecker $file_checker,
|
||||
CodeLocation $code_location,
|
||||
array $suppressed_issues
|
||||
array $suppressed_issues,
|
||||
$inferred = true
|
||||
) {
|
||||
if (empty($fq_class_name)) {
|
||||
throw new \InvalidArgumentException('$class cannot be empty');
|
||||
@ -1163,7 +1164,7 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($file_checker->project_checker->collect_references) {
|
||||
if ($file_checker->project_checker->collect_references && !$inferred) {
|
||||
$class_storage = ClassLikeChecker::$storage[strtolower($fq_class_name)];
|
||||
if ($class_storage->referencing_locations === null) {
|
||||
$class_storage->referencing_locations = [];
|
||||
|
@ -353,9 +353,9 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
||||
$substituted_type = clone $param_type;
|
||||
$generic_types = [];
|
||||
$substituted_type->replaceTemplateTypes($template_types, $generic_types, null);
|
||||
$substituted_type->check($this->source, $function_param->location, $this->suppressed_issues);
|
||||
$substituted_type->check($this->source, $function_param->location, $this->suppressed_issues, [], false);
|
||||
} else {
|
||||
$param_type->check($this->source, $function_param->location, $this->suppressed_issues);
|
||||
$param_type->check($this->source, $function_param->location, $this->suppressed_issues, [], false);
|
||||
}
|
||||
|
||||
if ($this->getFileChecker()->project_checker->collect_references) {
|
||||
@ -366,7 +366,9 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
||||
$function_param->signature_type->check(
|
||||
$this->source,
|
||||
$function_param->signature_location,
|
||||
$this->suppressed_issues
|
||||
$this->suppressed_issues,
|
||||
[],
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1087,7 +1089,9 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
||||
$declared_return_type->check(
|
||||
$this,
|
||||
$secondary_return_type_location ?: $return_type_location,
|
||||
$this->getSuppressedIssues()
|
||||
$this->getSuppressedIssues(),
|
||||
[],
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -724,9 +724,11 @@ class CallChecker
|
||||
if ($var_id === '$this') {
|
||||
$does_class_exist = true;
|
||||
} else {
|
||||
$does_class_exist = ClassLikeChecker::classOrInterfaceExists(
|
||||
$does_class_exist = ClassLikeChecker::checkFullyQualifiedClassLikeName(
|
||||
$fq_class_name,
|
||||
$statements_checker->getFileChecker()
|
||||
$statements_checker->getFileChecker(),
|
||||
new CodeLocation($statements_checker->getSource(), $stmt),
|
||||
$statements_checker->getSuppressedIssues()
|
||||
);
|
||||
}
|
||||
|
||||
@ -1058,7 +1060,8 @@ class CallChecker
|
||||
$fq_class_name,
|
||||
$file_checker,
|
||||
new CodeLocation($source, $stmt->class),
|
||||
$statements_checker->getSuppressedIssues()
|
||||
$statements_checker->getSuppressedIssues(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -182,13 +182,15 @@ abstract class Atomic
|
||||
* @param CodeLocation $code_location
|
||||
* @param array<string> $suppressed_issues
|
||||
* @param array<string, bool> $phantom_classes
|
||||
* @param bool $inferred
|
||||
* @return false|null
|
||||
*/
|
||||
public function check(
|
||||
StatementsSource $source,
|
||||
CodeLocation $code_location,
|
||||
array $suppressed_issues,
|
||||
array $phantom_classes = []
|
||||
array $phantom_classes = [],
|
||||
$inferred = true
|
||||
) {
|
||||
if ($this->checked) {
|
||||
return;
|
||||
@ -200,7 +202,8 @@ abstract class Atomic
|
||||
$this->value,
|
||||
$source->getFileChecker(),
|
||||
$code_location,
|
||||
$suppressed_issues
|
||||
$suppressed_issues,
|
||||
$inferred
|
||||
) === false
|
||||
) {
|
||||
return false;
|
||||
@ -208,7 +211,7 @@ abstract class Atomic
|
||||
|
||||
if ($this instanceof Type\Atomic\TArray || $this instanceof Type\Atomic\TGenericObject) {
|
||||
foreach ($this->type_params as $type_param) {
|
||||
$type_param->check($source, $code_location, $suppressed_issues, $phantom_classes);
|
||||
$type_param->check($source, $code_location, $suppressed_issues, $phantom_classes, $inferred);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -342,20 +342,22 @@ class Union
|
||||
* @param CodeLocation $code_location
|
||||
* @param array<string> $suppressed_issues
|
||||
* @param array<string, bool> $phantom_classes
|
||||
* @param bool $inferred
|
||||
* @return void
|
||||
*/
|
||||
public function check(
|
||||
StatementsSource $source,
|
||||
CodeLocation $code_location,
|
||||
array $suppressed_issues,
|
||||
array $phantom_classes = []
|
||||
array $phantom_classes = [],
|
||||
$inferred = true
|
||||
) {
|
||||
if ($this->checked) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->types as $atomic_type) {
|
||||
$atomic_type->check($source, $code_location, $suppressed_issues, $phantom_classes);
|
||||
$atomic_type->check($source, $code_location, $suppressed_issues, $phantom_classes, $inferred);
|
||||
}
|
||||
|
||||
$this->checked = true;
|
||||
|
@ -1054,4 +1054,22 @@ class PropertyTypeTest extends PHPUnit_Framework_TestCase
|
||||
$context = new Context();
|
||||
$file_checker->visitAndAnalyzeMethods($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Psalm\Exception\CodeException
|
||||
* @expectedExceptionMessage UndefinedClass
|
||||
* @return void
|
||||
*/
|
||||
public function testUndefinedPropertyClass()
|
||||
{
|
||||
$stmts = self::$parser->parse('<?php
|
||||
class A {
|
||||
/** @var B */
|
||||
public $foo;
|
||||
}
|
||||
');
|
||||
|
||||
$file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts);
|
||||
$file_checker->visitAndAnalyzeMethods();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user