diff --git a/src/Psalm/Checker/ClassLikeChecker.php b/src/Psalm/Checker/ClassLikeChecker.php index 2f2d87e83..6f3a0b620 100644 --- a/src/Psalm/Checker/ClassLikeChecker.php +++ b/src/Psalm/Checker/ClassLikeChecker.php @@ -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 $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 = []; diff --git a/src/Psalm/Checker/FunctionLikeChecker.php b/src/Psalm/Checker/FunctionLikeChecker.php index 9f21311ef..1af4753cf 100644 --- a/src/Psalm/Checker/FunctionLikeChecker.php +++ b/src/Psalm/Checker/FunctionLikeChecker.php @@ -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 ); } diff --git a/src/Psalm/Checker/Statements/Expression/CallChecker.php b/src/Psalm/Checker/Statements/Expression/CallChecker.php index 23352b3c6..9a538ce48 100644 --- a/src/Psalm/Checker/Statements/Expression/CallChecker.php +++ b/src/Psalm/Checker/Statements/Expression/CallChecker.php @@ -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 ); } diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index ee1d98d12..1a1b50017 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -182,13 +182,15 @@ abstract class Atomic * @param CodeLocation $code_location * @param array $suppressed_issues * @param array $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); } } diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index 213887479..b6591f3fa 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -342,20 +342,22 @@ class Union * @param CodeLocation $code_location * @param array $suppressed_issues * @param array $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; diff --git a/tests/PropertyTypeTest.php b/tests/PropertyTypeTest.php index 606bdd799..aa544922d 100644 --- a/tests/PropertyTypeTest.php +++ b/tests/PropertyTypeTest.php @@ -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('project_checker, $stmts); + $file_checker->visitAndAnalyzeMethods(); + } }