1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Add PossiblyInvalidCast for more refined checks

This commit is contained in:
Matthew Brown 2019-03-17 17:10:51 -04:00
parent e3d8ebf612
commit 472ab29d74
10 changed files with 50 additions and 3 deletions

View File

@ -65,6 +65,7 @@
<PossiblyInvalidArrayAccess errorLevel="info" /> <PossiblyInvalidArrayAccess errorLevel="info" />
<PossiblyInvalidArrayAssignment errorLevel="info" /> <PossiblyInvalidArrayAssignment errorLevel="info" />
<PossiblyInvalidArrayOffset errorLevel="info" /> <PossiblyInvalidArrayOffset errorLevel="info" />
<PossiblyInvalidCast errorLevel="info" />
<PossiblyInvalidFunctionCall errorLevel="info" /> <PossiblyInvalidFunctionCall errorLevel="info" />
<PossiblyInvalidIterator errorLevel="info" /> <PossiblyInvalidIterator errorLevel="info" />
<PossiblyInvalidMethodCall errorLevel="info" /> <PossiblyInvalidMethodCall errorLevel="info" />

View File

@ -65,6 +65,7 @@
<PossiblyInvalidArrayAccess errorLevel="info" /> <PossiblyInvalidArrayAccess errorLevel="info" />
<PossiblyInvalidArrayAssignment errorLevel="info" /> <PossiblyInvalidArrayAssignment errorLevel="info" />
<PossiblyInvalidArrayOffset errorLevel="info" /> <PossiblyInvalidArrayOffset errorLevel="info" />
<PossiblyInvalidCast errorLevel="info" />
<PossiblyInvalidFunctionCall errorLevel="info" /> <PossiblyInvalidFunctionCall errorLevel="info" />
<PossiblyInvalidIterator errorLevel="info" /> <PossiblyInvalidIterator errorLevel="info" />
<PossiblyInvalidMethodCall errorLevel="info" /> <PossiblyInvalidMethodCall errorLevel="info" />

View File

@ -65,6 +65,7 @@
<PossiblyInvalidArrayAccess errorLevel="info" /> <PossiblyInvalidArrayAccess errorLevel="info" />
<PossiblyInvalidArrayAssignment errorLevel="info" /> <PossiblyInvalidArrayAssignment errorLevel="info" />
<PossiblyInvalidArrayOffset errorLevel="info" /> <PossiblyInvalidArrayOffset errorLevel="info" />
<PossiblyInvalidCast errorLevel="info" />
<PossiblyInvalidFunctionCall errorLevel="info" /> <PossiblyInvalidFunctionCall errorLevel="info" />
<PossiblyInvalidIterator errorLevel="info" /> <PossiblyInvalidIterator errorLevel="info" />
<PossiblyInvalidMethodCall errorLevel="info" /> <PossiblyInvalidMethodCall errorLevel="info" />

View File

@ -65,6 +65,7 @@
<PossiblyInvalidArrayAccess errorLevel="info" /> <PossiblyInvalidArrayAccess errorLevel="info" />
<PossiblyInvalidArrayAssignment errorLevel="info" /> <PossiblyInvalidArrayAssignment errorLevel="info" />
<PossiblyInvalidArrayOffset errorLevel="info" /> <PossiblyInvalidArrayOffset errorLevel="info" />
<PossiblyInvalidCast errorLevel="info" />
<PossiblyInvalidFunctionCall errorLevel="info" /> <PossiblyInvalidFunctionCall errorLevel="info" />
<PossiblyInvalidIterator errorLevel="info" /> <PossiblyInvalidIterator errorLevel="info" />
<PossiblyInvalidMethodCall errorLevel="info" /> <PossiblyInvalidMethodCall errorLevel="info" />

View File

@ -261,6 +261,7 @@
<xs:element name="PossiblyInvalidArrayAccess" type="IssueHandlerType" minOccurs="0" /> <xs:element name="PossiblyInvalidArrayAccess" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidArrayAssignment" type="IssueHandlerType" minOccurs="0" /> <xs:element name="PossiblyInvalidArrayAssignment" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidArrayOffset" type="IssueHandlerType" minOccurs="0" /> <xs:element name="PossiblyInvalidArrayOffset" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidCast" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidFunctionCall" type="IssueHandlerType" minOccurs="0" /> <xs:element name="PossiblyInvalidFunctionCall" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidIterator" type="IssueHandlerType" minOccurs="0" /> <xs:element name="PossiblyInvalidIterator" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidMethodCall" type="IssueHandlerType" minOccurs="0" /> <xs:element name="PossiblyInvalidMethodCall" type="IssueHandlerType" minOccurs="0" />

View File

@ -1464,6 +1464,20 @@ $arr = rand(0, 1) ? 5 : [4, 3, 2, 1];
$arr[0] = "hello"; $arr[0] = "hello";
``` ```
### PossiblyInvalidCast
Emitted when attempting to cast a value that may not be castable
```php
class A {}
class B {
public function __toString() {
return 'hello';
}
}
$c = (string) (rand(0, 1) ? new A() : new B());
```
### PossiblyInvalidArrayOffset ### PossiblyInvalidArrayOffset
Emitted when its possible that the array offset is not applicable to the value youre trying to access. Emitted when its possible that the array offset is not applicable to the value youre trying to access.

View File

@ -990,7 +990,7 @@ class Config
$fq_class_name = reset($declared_classes); $fq_class_name = reset($declared_classes);
if (!$codebase->classExtends( if (!$codebase->classlikes->classExtends(
$fq_class_name, $fq_class_name,
$must_extend $must_extend
) )

View File

@ -35,6 +35,7 @@ use Psalm\Issue\ForbiddenCode;
use Psalm\Issue\InvalidCast; use Psalm\Issue\InvalidCast;
use Psalm\Issue\InvalidClone; use Psalm\Issue\InvalidClone;
use Psalm\Issue\InvalidDocblock; use Psalm\Issue\InvalidDocblock;
use Psalm\Issue\PossiblyInvalidCast;
use Psalm\Issue\PossiblyUndefinedVariable; use Psalm\Issue\PossiblyUndefinedVariable;
use Psalm\Issue\UndefinedConstant; use Psalm\Issue\UndefinedConstant;
use Psalm\Issue\UndefinedVariable; use Psalm\Issue\UndefinedVariable;
@ -1473,6 +1474,9 @@ class ExpressionAnalyzer
return; return;
} }
$has_valid_cast = false;
$invalid_casts = [];
foreach ($stmt->inferredType->getTypes() as $atomic_type) { foreach ($stmt->inferredType->getTypes() as $atomic_type) {
if (!$atomic_type instanceof TMixed if (!$atomic_type instanceof TMixed
&& !$atomic_type instanceof Type\Atomic\TResource && !$atomic_type instanceof Type\Atomic\TResource
@ -1487,9 +1491,27 @@ class ExpressionAnalyzer
) )
&& !$has_scalar_match && !$has_scalar_match
) { ) {
$invalid_casts[] = $atomic_type->getId();
} else {
$has_valid_cast = true;
}
}
if ($invalid_casts) {
if ($has_valid_cast) {
if (IssueBuffer::accepts(
new PossiblyInvalidCast(
$invalid_casts[0] . ' cannot be cast to string',
new CodeLocation($statements_analyzer->getSource(), $stmt)
),
$statements_analyzer->getSuppressedIssues()
)) {
// fall through
}
} else {
if (IssueBuffer::accepts( if (IssueBuffer::accepts(
new InvalidCast( new InvalidCast(
$atomic_type->getId() . ' cannot be cast to string', $invalid_casts[0] . ' cannot be cast to string',
new CodeLocation($statements_analyzer->getSource(), $stmt) new CodeLocation($statements_analyzer->getSource(), $stmt)
), ),
$statements_analyzer->getSuppressedIssues() $statements_analyzer->getSuppressedIssues()

View File

@ -0,0 +1,6 @@
<?php
namespace Psalm\Issue;
class PossiblyInvalidCast extends CodeIssue
{
}

View File

@ -193,7 +193,7 @@ class ToStringTest extends TestCase
$a = []; $a = [];
} }
$b = (string) $a;', $b = (string) $a;',
'error_message' => 'InvalidCast', 'error_message' => 'PossiblyInvalidCast',
], ],
'cannotCastInsideString' => [ 'cannotCastInsideString' => [
'<?php '<?php