mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
Add separate issue type for undefined interface method
This commit is contained in:
parent
0cca0d76cb
commit
bd9f2ec546
@ -115,6 +115,7 @@
|
||||
<InvalidReturnStatement errorLevel="info" />
|
||||
<InvalidReturnType errorLevel="info" />
|
||||
<UndefinedThisPropertyAssignment errorLevel="info" />
|
||||
<UndefinedInterfaceMethod errorLevel="info" />
|
||||
|
||||
</issueHandlers>
|
||||
</psalm>
|
||||
|
@ -115,6 +115,7 @@
|
||||
<InvalidReturnStatement errorLevel="info" />
|
||||
<InvalidReturnType errorLevel="info" />
|
||||
<UndefinedThisPropertyAssignment errorLevel="info" />
|
||||
<UndefinedInterfaceMethod errorLevel="info" />
|
||||
|
||||
<!-- level 7 issues - even worse -->
|
||||
|
||||
|
@ -115,6 +115,7 @@
|
||||
<InvalidReturnStatement errorLevel="info" />
|
||||
<InvalidReturnType errorLevel="info" />
|
||||
<UndefinedThisPropertyAssignment errorLevel="info" />
|
||||
<UndefinedInterfaceMethod errorLevel="info" />
|
||||
|
||||
<!-- level 7 issues - even worse -->
|
||||
|
||||
|
@ -290,6 +290,7 @@
|
||||
<xs:element name="UndefinedClass" type="ClassIssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="UndefinedConstant" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="UndefinedFunction" type="FunctionIssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="UndefinedInterfaceMethod" type="MethodIssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="UndefinedMethod" type="MethodIssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="UndefinedPropertyAssignment" type="PropertyIssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="UndefinedPropertyFetch" type="PropertyIssueHandlerType" minOccurs="0" />
|
||||
|
@ -1913,6 +1913,18 @@ class A {}
|
||||
A::foo();
|
||||
```
|
||||
|
||||
### UndefinedInterfaceMethod
|
||||
|
||||
Emitted when calling a method that doesn’t exist on an interface
|
||||
|
||||
```php
|
||||
interface I {}
|
||||
|
||||
function foo(I $i) {
|
||||
$i->bar();
|
||||
}
|
||||
```
|
||||
|
||||
### UndefinedPropertyAssignment
|
||||
|
||||
Emitted when assigning a property on an object that doesn’t have that property defined
|
||||
|
@ -26,6 +26,7 @@ use Psalm\Issue\PossiblyInvalidPropertyAssignmentValue;
|
||||
use Psalm\Issue\PossiblyNullReference;
|
||||
use Psalm\Issue\PossiblyUndefinedMethod;
|
||||
use Psalm\Issue\TypeCoercion;
|
||||
use Psalm\Issue\UndefinedInterfaceMethod;
|
||||
use Psalm\Issue\UndefinedMethod;
|
||||
use Psalm\Issue\UndefinedThisPropertyAssignment;
|
||||
use Psalm\Issue\UndefinedThisPropertyFetch;
|
||||
@ -159,7 +160,8 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
$codebase = $statements_analyzer->getCodebase();
|
||||
$config = $codebase->config;
|
||||
|
||||
$non_existent_method_ids = [];
|
||||
$non_existent_class_method_ids = [];
|
||||
$non_existent_interface_method_ids = [];
|
||||
$existent_method_ids = [];
|
||||
$has_mixed_method_call = false;
|
||||
|
||||
@ -192,7 +194,8 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
$has_mixed_method_call,
|
||||
$invalid_method_call_types,
|
||||
$existent_method_ids,
|
||||
$non_existent_method_ids
|
||||
$non_existent_class_method_ids,
|
||||
$non_existent_interface_method_ids
|
||||
);
|
||||
|
||||
if ($result === false) {
|
||||
@ -230,14 +233,14 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
}
|
||||
}
|
||||
|
||||
if ($non_existent_method_ids) {
|
||||
if ($non_existent_class_method_ids) {
|
||||
if ($context->check_methods) {
|
||||
if ($existent_method_ids || $has_mixed_method_call) {
|
||||
if (IssueBuffer::accepts(
|
||||
new PossiblyUndefinedMethod(
|
||||
'Method ' . $non_existent_method_ids[0] . ' does not exist',
|
||||
'Method ' . $non_existent_class_method_ids[0] . ' does not exist',
|
||||
new CodeLocation($source, $stmt->name),
|
||||
$non_existent_method_ids[0]
|
||||
$non_existent_class_method_ids[0]
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues()
|
||||
)) {
|
||||
@ -246,9 +249,39 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
} else {
|
||||
if (IssueBuffer::accepts(
|
||||
new UndefinedMethod(
|
||||
'Method ' . $non_existent_method_ids[0] . ' does not exist',
|
||||
'Method ' . $non_existent_class_method_ids[0] . ' does not exist',
|
||||
new CodeLocation($source, $stmt->name),
|
||||
$non_existent_method_ids[0]
|
||||
$non_existent_class_method_ids[0]
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues()
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($non_existent_interface_method_ids) {
|
||||
if ($context->check_methods) {
|
||||
if ($existent_method_ids || $has_mixed_method_call) {
|
||||
if (IssueBuffer::accepts(
|
||||
new PossiblyUndefinedMethod(
|
||||
'Method ' . $non_existent_interface_method_ids[0] . ' does not exist',
|
||||
new CodeLocation($source, $stmt->name),
|
||||
$non_existent_interface_method_ids[0]
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues()
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (IssueBuffer::accepts(
|
||||
new UndefinedInterfaceMethod(
|
||||
'Method ' . $non_existent_interface_method_ids[0] . ' does not exist',
|
||||
new CodeLocation($source, $stmt->name),
|
||||
$non_existent_interface_method_ids[0]
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues()
|
||||
)) {
|
||||
@ -345,7 +378,8 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
* @param bool &$has_mixed_method_call
|
||||
* @param array<string> &$invalid_method_call_types
|
||||
* @param array<string> &$existent_method_ids
|
||||
* @param array<string> &$non_existent_method_ids
|
||||
* @param array<string> &$non_existent_class_method_ids
|
||||
* @param array<string> &$non_existent_interface_method_ids
|
||||
* @return null|bool
|
||||
*/
|
||||
private static function analyzeAtomicCall(
|
||||
@ -362,7 +396,8 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
&$has_mixed_method_call,
|
||||
&$invalid_method_call_types,
|
||||
&$existent_method_ids,
|
||||
&$non_existent_method_ids
|
||||
&$non_existent_class_method_ids,
|
||||
&$non_existent_interface_method_ids
|
||||
) {
|
||||
$config = $codebase->config;
|
||||
|
||||
@ -582,7 +617,7 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
}
|
||||
|
||||
if ($class_storage->sealed_methods) {
|
||||
$non_existent_method_ids[] = $method_id;
|
||||
$non_existent_class_method_ids[] = $method_id;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -641,8 +676,14 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
}
|
||||
}
|
||||
|
||||
$is_interface = false;
|
||||
|
||||
if ($codebase->interfaceExists($fq_class_name)) {
|
||||
$is_interface = true;
|
||||
}
|
||||
|
||||
if ($intersection_types && !$codebase->methodExists($method_id)) {
|
||||
if ($codebase->interfaceExists($fq_class_name)) {
|
||||
if ($is_interface) {
|
||||
$interface_storage = $codebase->classlike_storage_provider->get($fq_class_name);
|
||||
|
||||
$check_visibility = $check_visibility && !$interface_storage->override_method_visibility;
|
||||
@ -684,6 +725,8 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
}
|
||||
|
||||
if ($codebase->methodExists($method_id)) {
|
||||
$is_interface = $codebase->interfaceExists($fq_class_name);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -751,7 +794,12 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
}
|
||||
}
|
||||
|
||||
$non_existent_method_ids[] = $intersection_method_id ?: $method_id;
|
||||
if ($is_interface) {
|
||||
$non_existent_interface_method_ids[] = $intersection_method_id ?: $method_id;
|
||||
} else {
|
||||
$non_existent_class_method_ids[] = $intersection_method_id ?: $method_id;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
6
src/Psalm/Issue/UndefinedInterfaceMethod.php
Normal file
6
src/Psalm/Issue/UndefinedInterfaceMethod.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace Psalm\Issue;
|
||||
|
||||
class UndefinedInterfaceMethod extends MethodIssue
|
||||
{
|
||||
}
|
@ -126,6 +126,10 @@ class IssueBuffer
|
||||
return preg_replace('/^(False|Null)/', 'Invalid', $issue_type);
|
||||
}
|
||||
|
||||
if ($issue_type === 'UndefinedInterfaceMethod') {
|
||||
return 'UndefinedMethod';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ abstract class Atomic
|
||||
return new TNamedObject('static');
|
||||
|
||||
default:
|
||||
if (strpos($value, '-')) {
|
||||
if (strpos($value, '-') && substr($value, 0, 4) !== 'OCI-') {
|
||||
throw new \Psalm\Exception\TypeParseTreeException('no hyphens allowed');
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user