mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 04:45:20 +01:00
Infer literal string from encapsed (interpolated) string.
This commit is contained in:
parent
9b4c8cb53f
commit
450409f045
@ -3,6 +3,7 @@
|
||||
namespace Psalm\Internal\Analyzer\Statements\Expression;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\Node\Scalar\EncapsedStringPart;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Context;
|
||||
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
||||
@ -29,6 +30,8 @@ class EncapsulatedStringAnalyzer
|
||||
|
||||
$all_literals = true;
|
||||
|
||||
$literal_string = "";
|
||||
|
||||
foreach ($stmt->parts as $part) {
|
||||
if ($part instanceof PhpParser\Node\Scalar\EncapsedStringPart
|
||||
&& $part->value
|
||||
@ -42,7 +45,7 @@ class EncapsulatedStringAnalyzer
|
||||
|
||||
$part_type = $statements_analyzer->node_data->getType($part);
|
||||
|
||||
if ($part_type) {
|
||||
if ($part_type !== null) {
|
||||
$casted_part_type = CastAnalyzer::castStringAttempt(
|
||||
$statements_analyzer,
|
||||
$context,
|
||||
@ -54,6 +57,14 @@ class EncapsulatedStringAnalyzer
|
||||
$all_literals = false;
|
||||
}
|
||||
|
||||
if ($literal_string !== null) {
|
||||
if ($casted_part_type->isSingleLiteral()) {
|
||||
$literal_string .= $casted_part_type->getSingleLiteral();
|
||||
} else {
|
||||
$literal_string = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($statements_analyzer->data_flow_graph
|
||||
&& !in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
|
||||
) {
|
||||
@ -82,11 +93,20 @@ class EncapsulatedStringAnalyzer
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($part instanceof EncapsedStringPart) {
|
||||
if ($literal_string !== null) {
|
||||
$literal_string .= $part->value;
|
||||
}
|
||||
} else {
|
||||
$all_literals = false;
|
||||
$literal_string = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($non_empty) {
|
||||
if ($all_literals) {
|
||||
if ($literal_string !== null) {
|
||||
$new_type = Type::getString($literal_string);
|
||||
} elseif ($all_literals) {
|
||||
$new_type = new Union([new TNonEmptyNonspecificLiteralString()]);
|
||||
} else {
|
||||
$new_type = new Union([new TNonEmptyString()]);
|
||||
|
@ -1329,6 +1329,32 @@ class Union implements TypeNode
|
||||
|| isset($this->types['true']);
|
||||
}
|
||||
|
||||
public function isSingleLiteral(): bool
|
||||
{
|
||||
return count($this->types) === 1
|
||||
&& count($this->literal_int_types)
|
||||
+ count($this->literal_string_types)
|
||||
+ count($this->literal_float_types) === 1
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TLiteralInt|TLiteralString|TLiteralFloat
|
||||
*/
|
||||
public function getSingleLiteral()
|
||||
{
|
||||
if (!$this->isSingleLiteral()) {
|
||||
throw new InvalidArgumentException("Not a single literal");
|
||||
}
|
||||
|
||||
return ($literal = reset($this->literal_int_types)) !== false
|
||||
? $literal
|
||||
: (($literal = reset($this->literal_string_types)) !== false
|
||||
? $literal
|
||||
: reset($this->literal_float_types))
|
||||
;
|
||||
}
|
||||
|
||||
public function hasLiteralString(): bool
|
||||
{
|
||||
return count($this->literal_string_types) > 0;
|
||||
|
Loading…
Reference in New Issue
Block a user