1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

Fix #2035 more comprehensively

This commit is contained in:
Matthew Brown 2019-08-19 22:45:24 -04:00
parent f5b63218f1
commit 17e7fe70c1
4 changed files with 86 additions and 5 deletions

View File

@ -434,6 +434,52 @@ class BinaryOpAnalyzer
$t_if_context->vars_in_scope = $t_if_vars_in_scope_reconciled;
}
if (!self::hasArrayDimFetch($stmt->left)) {
// check first if the variable was good
IssueBuffer::startRecording();
if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->left, clone $context) === false) {
return false;
}
IssueBuffer::clearRecordingLevel();
IssueBuffer::stopRecording();
$naive_type = $stmt->left->inferredType ?? null;
if ($naive_type
&& !$naive_type->isMixed()
&& !$naive_type->isNullable()
) {
$var_id = ExpressionAnalyzer::getVarId($stmt->left, $context->self);
if (!$var_id || !\in_array($var_id, $changed_var_ids, true)) {
if ($naive_type->from_docblock) {
if (IssueBuffer::accepts(
new \Psalm\Issue\DocblockTypeContradiction(
$naive_type->getId() . ' does not contain null',
new CodeLocation($statements_analyzer, $stmt->left)
),
$statements_analyzer->getSuppressedIssues()
)) {
// fall through
}
} else {
if (IssueBuffer::accepts(
new \Psalm\Issue\TypeDoesNotContainType(
$naive_type->getId() . ' is always defined and non-null',
new CodeLocation($statements_analyzer, $stmt->left)
),
$statements_analyzer->getSuppressedIssues()
)) {
// fall through
}
}
}
}
}
$t_if_context->inside_isset = true;
if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->left, $t_if_context) === false) {
@ -663,6 +709,21 @@ class BinaryOpAnalyzer
return null;
}
private static function hasArrayDimFetch(PhpParser\Node\Expr $expr) : bool
{
if ($expr instanceof PhpParser\Node\Expr\ArrayDimFetch) {
return true;
}
if ($expr instanceof PhpParser\Node\Expr\PropertyFetch
|| $expr instanceof PhpParser\Node\Expr\MethodCall
) {
return self::hasArrayDimFetch($expr->var);
}
return false;
}
/**
* @param StatementsSource|null $statements_source
* @param PhpParser\Node\Expr $left

View File

@ -79,7 +79,7 @@ class BuildInfoCollector
$this->readEnv['CI_REPO_NAME'] = $slug_parts[1];
}
$pr_slug = (string) $this->env['TRAVIS_PULL_REQUEST_SLUG'] ?? '';
$pr_slug = (string) ($this->env['TRAVIS_PULL_REQUEST_SLUG'] ?? '');
if ($pr_slug) {
$slug_parts = explode('/', $pr_slug);
@ -150,7 +150,7 @@ class BuildInfoCollector
$this->readEnv['APPVEYOR_REPO_BRANCH'] = $this->env['APPVEYOR_REPO_BRANCH'];
$this->readEnv['CI_NAME'] = $this->env['CI_NAME'];
$repo_slug = (string) $this->env['APPVEYOR_REPO_NAME'] ?? '';
$repo_slug = (string) ($this->env['APPVEYOR_REPO_NAME'] ?? '');
if ($repo_slug) {
$slug_parts = explode('/', $repo_slug);
@ -215,7 +215,7 @@ class BuildInfoCollector
// backup
$this->readEnv['CI_NAME'] = 'Scrutinizer';
$repo_slug = (string) $this->env['SCRUTINIZER_PROJECT'] ?? '';
$repo_slug = (string) ($this->env['SCRUTINIZER_PROJECT'] ?? '');
if ($repo_slug) {
$slug_parts = explode('/', $repo_slug);

View File

@ -1237,12 +1237,12 @@ class ConfigTest extends \Psalm\Tests\TestCase
$glob3->func();
}
namespace {
ord($glob1 ?? "str");
ord($glob1 ?: "str");
ord($_GET["str"] ?? "str");
function example4(): void {
global $glob1;
ord($glob1 ?? "str");
ord($glob1 ?: "str");
ord($_GET["str"] ?? "str");
}
}'

View File

@ -1468,6 +1468,19 @@ class TypeReconciliationTest extends TestCase
return null;
}'
],
'nullCoalesceTypedArrayValue' => [
'<?php
/** @param string[] $arr */
function foo(array $arr) : string {
return $arr["b"] ?? "bar";
}',
],
'nullCoalesceTypedValue' => [
'<?php
function foo(?string $s) : string {
return $s ?? "bar";
}',
],
];
}
@ -1752,6 +1765,13 @@ class TypeReconciliationTest extends TestCase
if (false !== firstChar("sdf")) {}',
'error_message' => 'RedundantCondition',
],
'nullCoalesceImpossible' => [
'<?php
function foo(?string $s) : string {
return ((string) $s) ?? "bar";
}',
'error_message' => 'TypeDoesNotContainType'
],
];
}
}