Skip to content
Permalink
Browse files

Add separate issue for undefined classes in docblocks

  • Loading branch information...
muglug committed May 15, 2019
1 parent 02498a3 commit 4a434d9a2f8fa15dd0616243d1668e505972ccbf
@@ -313,6 +313,7 @@
<xs:element name="UncaughtThrowInGlobalScope" type="IssueHandlerType" minOccurs="0" />
<xs:element name="UndefinedClass" type="ClassIssueHandlerType" minOccurs="0" />
<xs:element name="UndefinedConstant" type="IssueHandlerType" minOccurs="0" />
<xs:element name="UndefinedDocblockClass" type="ClassIssueHandlerType" minOccurs="0" />
<xs:element name="UndefinedFunction" type="FunctionIssueHandlerType" minOccurs="0" />
<xs:element name="UndefinedInterface" type="ClassIssueHandlerType" minOccurs="0" />
<xs:element name="UndefinedInterfaceMethod" type="MethodIssueHandlerType" minOccurs="0" />
@@ -2084,6 +2084,17 @@ Emitted when referencing a constant that doesn’t exist
echo FOO_BAR;
```

### UndefinedDocblockClass

Emitted when referencing a class that doesn’t exist from a docblock

```php
/**
* @param DoesNotExist $a
*/
function foo($a) : void {}
```

### UndefinedFunction

Emitted when referencing a function that doesn't exist
@@ -1252,6 +1252,10 @@ private static function getParentIssueType($issue_type)
return 'MoreSpecificImplementedParamType';
}
if ($issue_type === 'UndefinedDocblockClass') {
return 'UndefinedClass';
}
if ($issue_type === 'MixedArgumentTypeCoercion'
|| $issue_type === 'MixedPropertyTypeCoercion'
|| $issue_type === 'MixedReturnTypeCoercion'
@@ -12,6 +12,7 @@
use Psalm\Issue\MissingDependency;
use Psalm\Issue\ReservedWord;
use Psalm\Issue\UndefinedClass;
use Psalm\Issue\UndefinedDocblockClass;
use Psalm\IssueBuffer;
use Psalm\StatementsSource;
use Psalm\Storage\ClassLikeStorage;
@@ -183,9 +184,10 @@ public static function checkFullyQualifiedClassLikeName(
$fq_class_name,
CodeLocation $code_location,
array $suppressed_issues,
$inferred = true,
bool $inferred = true,
bool $allow_trait = false,
bool $allow_interface = true
bool $allow_interface = true,
bool $from_docblock = false
) {
$codebase = $statements_source->getCodebase();
if (empty($fq_class_name)) {
@@ -242,15 +244,28 @@ public static function checkFullyQualifiedClassLikeName(
if (!$class_exists && !$interface_exists) {
if (!$allow_trait || !$codebase->classlikes->traitExists($fq_class_name, $code_location)) {
if (IssueBuffer::accepts(
new UndefinedClass(
'Class or interface ' . $fq_class_name . ' does not exist',
$code_location,
$fq_class_name
),
$suppressed_issues
)) {
return false;
if ($from_docblock) {
if (IssueBuffer::accepts(
new UndefinedDocblockClass(
'Docblock-defined class or interface ' . $fq_class_name . ' does not exist',
$code_location,
$fq_class_name
),
$suppressed_issues
)) {
return false;
}
} else {
if (IssueBuffer::accepts(
new UndefinedClass(
'Class or interface ' . $fq_class_name . ' does not exist',
$code_location,
$fq_class_name
),
$suppressed_issues
)) {
return false;
}
}
}
@@ -554,12 +554,17 @@ private static function analyzeAtomicCall(
$statements_analyzer,
$fq_class_name,
new CodeLocation($source, $stmt->var),
$statements_analyzer->getSuppressedIssues()
$statements_analyzer->getSuppressedIssues(),
true,
false,
true,
$lhs_type_part->from_docblock
);
}
if (!$does_class_exist) {
return $does_class_exist;
$non_existent_class_method_ids[] = $fq_class_name . '::*';
return false;
}
if (!$stmt->name instanceof PhpParser\Node\Identifier) {
@@ -782,7 +787,11 @@ function (PhpParser\Node\Arg $arg) {
$statements_analyzer,
$fq_class_name,
new CodeLocation($source, $stmt->var),
$statements_analyzer->getSuppressedIssues()
$statements_analyzer->getSuppressedIssues(),
false,
false,
true,
$intersection_type->from_docblock
);
if (!$does_class_exist) {
@@ -2905,6 +2905,18 @@ protected static function applyAssertionsToContext(
foreach ($changed_vars as $changed_var) {
if (isset($op_vars_in_scope[$changed_var])) {
$op_vars_in_scope[$changed_var]->from_docblock = true;
foreach ($op_vars_in_scope[$changed_var]->getTypes() as $changed_atomic_type) {
$changed_atomic_type->from_docblock = true;
if ($changed_atomic_type instanceof Type\Atomic\TNamedObject
&& $changed_atomic_type->extra_types
) {
foreach ($changed_atomic_type->extra_types as $extra_type) {
$extra_type->from_docblock = true;
}
}
}
}
}
@@ -111,7 +111,7 @@ function (Message $msg) {
// Invoke the method handler to get a result
/**
* @var Promise
* @psalm-suppress UndefinedClass
* @psalm-suppress UndefinedDocblockClass
*/
$dispatched = $this->dispatch($msg->body);
$result = yield $dispatched;
@@ -1050,6 +1050,8 @@ private function extendTemplatedType(
}
}
$extended_union_type->setFromDocblock();
foreach ($extended_union_type->getTypes() as $atomic_type) {
if (!$atomic_type instanceof Type\Atomic\TGenericObject) {
if (IssueBuffer::accepts(
@@ -1134,6 +1136,8 @@ private function implementTemplatedType(
}
}
$implemented_union_type->setFromDocblock();
foreach ($implemented_union_type->getTypes() as $atomic_type) {
if (!$atomic_type instanceof Type\Atomic\TGenericObject) {
if (IssueBuffer::accepts(
@@ -1216,6 +1220,8 @@ private function useTemplatedType(
}
}
$used_union_type->setFromDocblock();
foreach ($used_union_type->getTypes() as $atomic_type) {
if (!$atomic_type instanceof Type\Atomic\TGenericObject) {
if (IssueBuffer::accepts(
@@ -0,0 +1,6 @@
<?php
namespace Psalm\Issue;
class UndefinedDocblockClass extends ClassIssue
{
}
@@ -294,7 +294,10 @@ public function check(
$this->value,
$code_location,
$suppressed_issues,
$inferred
$inferred,
false,
true,
$this->from_docblock
) === false
) {
return false;
@@ -312,7 +315,10 @@ public function check(
$extra_type->value,
$code_location,
$suppressed_issues,
$inferred
$inferred,
false,
true,
$this->from_docblock
) === false
) {
return false;
@@ -327,7 +333,10 @@ public function check(
$this->value,
$code_location,
$suppressed_issues,
$inferred
$inferred,
false,
true,
$this->from_docblock
) === false
) {
return false;
@@ -351,7 +360,10 @@ public function check(
$this->as,
$code_location,
$suppressed_issues,
$inferred
$inferred,
false,
true,
$this->from_docblock
) === false
) {
return false;
@@ -403,7 +415,10 @@ public function check(
$fq_classlike_name,
$code_location,
$suppressed_issues,
$inferred
$inferred,
false,
true,
$this->from_docblock
) === false
) {
return false;
@@ -1177,6 +1177,26 @@ function foo(): boolean {
}',
'error_message' => 'UndefinedClass',
],
'undefinedDocblockClassCall' => [
'<?php
class B {
/**
* @return A
* @psalm-suppress UndefinedDocblockClass
* @psalm-suppress InvalidReturnStatement
* @psalm-suppress InvalidReturnType
*/
public function foo() {
return new stdClass();
}
public function bar() {
$this->foo()->bar();
}
}
',
'error_message' => 'UndefinedDocblockClass',
],
'preventBadObjectLikeFormat' => [
'<?php
/**
@@ -1321,7 +1341,7 @@ class A {}
'<?php
/** @var Foo */
$a = $_GET["foo"];',
'error_message' => 'UndefinedClass',
'error_message' => 'UndefinedDocblockClass',
],
'badPsalmType' => [
'<?php
@@ -854,7 +854,7 @@ function getImplementationOfFoo(): Foo {
assertInstanceOf($bar, Bar::class);
$bar->sayHello();',
'error_message' => 'UndefinedClass',
'error_message' => 'UndefinedDocblockClass',
],
'detectRedundantCondition' => [
'<?php
@@ -165,7 +165,7 @@ function foo($s) : void {}
/** @psalm-param "foo"|"bar"|C::A|C::B $s */
function foo($s) : void {}',
'error_message' => 'UndefinedClass',
'error_message' => 'UndefinedDocblockClass',
],
'selfClassConstBadValue' => [
'<?php
@@ -221,7 +221,7 @@ function getVal()
{
return 5;
}',
'error_message' => 'UndefinedClass',
'error_message' => 'UndefinedDocblockClass',
],
'nonExistentClassConstant' => [
'<?php
@@ -2106,7 +2106,7 @@ class A {
/** @var B */
public $foo;
}',
'error_message' => 'UndefinedClass',
'error_message' => 'UndefinedDocblockClass',
],
'abstractClassWithNoConstructorButChild' => [
'<?php
@@ -2078,7 +2078,7 @@ public function __construct($t) {
* @template-extends A<Z>
*/
class B extends A {}',
'error_message' => 'UndefinedClass',
'error_message' => 'UndefinedDocblockClass',
],
'badTemplateExtendsInt' => [
'<?php
@@ -2161,7 +2161,7 @@ public function __construct($t);
* @template-implements I<Z>
*/
class B implements I {}',
'error_message' => 'UndefinedClass',
'error_message' => 'UndefinedDocblockClass',
],
'badTemplateImplementsInt' => [
'<?php
@@ -2238,7 +2238,7 @@ class B {
*/
use T;
}',
'error_message' => 'UndefinedClass',
'error_message' => 'UndefinedDocblockClass',
],
'badTemplateUseBadFormat' => [
'<?php
@@ -1047,7 +1047,7 @@ class X {
/** @var T|null */
public $hm;
}',
'error_message' => 'UndefinedClass',
'error_message' => 'UndefinedDocblockClass',
],
];
}

0 comments on commit 4a434d9

Please sign in to comment.
You can’t perform that action at this time.