From 472ab29d74d42a6964397db3c017edaa3aa003e9 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Sun, 17 Mar 2019 17:10:51 -0400 Subject: [PATCH] Add PossiblyInvalidCast for more refined checks --- assets/config_levels/5.xml | 1 + assets/config_levels/6.xml | 1 + assets/config_levels/7.xml | 1 + assets/config_levels/8.xml | 1 + config.xsd | 1 + docs/issues.md | 14 +++++++++++ src/Psalm/Config.php | 2 +- .../Statements/ExpressionAnalyzer.php | 24 ++++++++++++++++++- src/Psalm/Issue/PossiblyInvalidCast.php | 6 +++++ tests/ToStringTest.php | 2 +- 10 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 src/Psalm/Issue/PossiblyInvalidCast.php diff --git a/assets/config_levels/5.xml b/assets/config_levels/5.xml index 33463bacb..2abd6f9a1 100644 --- a/assets/config_levels/5.xml +++ b/assets/config_levels/5.xml @@ -65,6 +65,7 @@ + diff --git a/assets/config_levels/6.xml b/assets/config_levels/6.xml index a7c3875f3..31998d1a5 100644 --- a/assets/config_levels/6.xml +++ b/assets/config_levels/6.xml @@ -65,6 +65,7 @@ + diff --git a/assets/config_levels/7.xml b/assets/config_levels/7.xml index b4136d1d8..2c4371395 100644 --- a/assets/config_levels/7.xml +++ b/assets/config_levels/7.xml @@ -65,6 +65,7 @@ + diff --git a/assets/config_levels/8.xml b/assets/config_levels/8.xml index 66a8cb924..e5656de2a 100644 --- a/assets/config_levels/8.xml +++ b/assets/config_levels/8.xml @@ -65,6 +65,7 @@ + diff --git a/config.xsd b/config.xsd index 587018326..e726c72ad 100644 --- a/config.xsd +++ b/config.xsd @@ -261,6 +261,7 @@ + diff --git a/docs/issues.md b/docs/issues.md index 2a68991bb..631ba93a6 100644 --- a/docs/issues.md +++ b/docs/issues.md @@ -1464,6 +1464,20 @@ $arr = rand(0, 1) ? 5 : [4, 3, 2, 1]; $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 Emitted when it’s possible that the array offset is not applicable to the value you’re trying to access. diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index 1a3586dc7..645a9a755 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -990,7 +990,7 @@ class Config $fq_class_name = reset($declared_classes); - if (!$codebase->classExtends( + if (!$codebase->classlikes->classExtends( $fq_class_name, $must_extend ) diff --git a/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php index 2d962c7e0..31e6dbbd4 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php @@ -35,6 +35,7 @@ use Psalm\Issue\ForbiddenCode; use Psalm\Issue\InvalidCast; use Psalm\Issue\InvalidClone; use Psalm\Issue\InvalidDocblock; +use Psalm\Issue\PossiblyInvalidCast; use Psalm\Issue\PossiblyUndefinedVariable; use Psalm\Issue\UndefinedConstant; use Psalm\Issue\UndefinedVariable; @@ -1473,6 +1474,9 @@ class ExpressionAnalyzer return; } + $has_valid_cast = false; + $invalid_casts = []; + foreach ($stmt->inferredType->getTypes() as $atomic_type) { if (!$atomic_type instanceof TMixed && !$atomic_type instanceof Type\Atomic\TResource @@ -1487,9 +1491,27 @@ class ExpressionAnalyzer ) && !$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( new InvalidCast( - $atomic_type->getId() . ' cannot be cast to string', + $invalid_casts[0] . ' cannot be cast to string', new CodeLocation($statements_analyzer->getSource(), $stmt) ), $statements_analyzer->getSuppressedIssues() diff --git a/src/Psalm/Issue/PossiblyInvalidCast.php b/src/Psalm/Issue/PossiblyInvalidCast.php new file mode 100644 index 000000000..637a54bec --- /dev/null +++ b/src/Psalm/Issue/PossiblyInvalidCast.php @@ -0,0 +1,6 @@ + 'InvalidCast', + 'error_message' => 'PossiblyInvalidCast', ], 'cannotCastInsideString' => [ '