mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Simplify more things
This commit is contained in:
parent
65947e5808
commit
3005d2e1d7
@ -452,7 +452,7 @@ class Config
|
|||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
public $max_avg_path_length = 60;
|
public $max_avg_path_length = 70;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string[]
|
* @var string[]
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
namespace Psalm\Internal\Analyzer\FunctionLike;
|
namespace Psalm\Internal\Analyzer\FunctionLike;
|
||||||
|
|
||||||
use PhpParser;
|
use PhpParser;
|
||||||
|
use Psalm\Codebase;
|
||||||
use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
|
use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
|
||||||
use Psalm\Type;
|
use Psalm\Type;
|
||||||
use Psalm\Type\Atomic;
|
use Psalm\Type\Atomic;
|
||||||
@ -21,7 +22,7 @@ class ReturnTypeCollector
|
|||||||
* @return list<Type\Union> a list of return types
|
* @return list<Type\Union> a list of return types
|
||||||
*/
|
*/
|
||||||
public static function getReturnTypes(
|
public static function getReturnTypes(
|
||||||
\Psalm\Codebase $codebase,
|
Codebase $codebase,
|
||||||
\Psalm\Internal\Provider\NodeDataProvider $nodes,
|
\Psalm\Internal\Provider\NodeDataProvider $nodes,
|
||||||
array $stmts,
|
array $stmts,
|
||||||
array &$yield_types,
|
array &$yield_types,
|
||||||
@ -193,65 +194,78 @@ class ReturnTypeCollector
|
|||||||
if ($collapse_types) {
|
if ($collapse_types) {
|
||||||
// if it's a generator, boil everything down to a single generator return type
|
// if it's a generator, boil everything down to a single generator return type
|
||||||
if ($yield_types) {
|
if ($yield_types) {
|
||||||
$key_type = null;
|
$yield_types = self::processYieldTypes($codebase, $return_types, $yield_types);
|
||||||
$value_type = null;
|
|
||||||
|
|
||||||
$yield_type = Type::combineUnionTypeArray($yield_types, null);
|
|
||||||
|
|
||||||
foreach ($yield_type->getAtomicTypes() as $type) {
|
|
||||||
if ($type instanceof Type\Atomic\TKeyedArray) {
|
|
||||||
$type = $type->getGenericArrayType();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($type instanceof Type\Atomic\TList) {
|
|
||||||
$type = new Type\Atomic\TArray([Type::getInt(), $type->type_param]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($type instanceof Type\Atomic\TArray) {
|
|
||||||
[$key_type_param, $value_type_param] = $type->type_params;
|
|
||||||
|
|
||||||
if (!$key_type) {
|
|
||||||
$key_type = clone $key_type_param;
|
|
||||||
} else {
|
|
||||||
$key_type = Type::combineUnionTypes($key_type_param, $key_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$value_type) {
|
|
||||||
$value_type = clone $value_type_param;
|
|
||||||
} else {
|
|
||||||
$value_type = Type::combineUnionTypes($value_type_param, $value_type);
|
|
||||||
}
|
|
||||||
} elseif ($type instanceof Type\Atomic\TIterable
|
|
||||||
|| $type instanceof Type\Atomic\TNamedObject
|
|
||||||
) {
|
|
||||||
ForeachAnalyzer::getKeyValueParamsForTraversableObject(
|
|
||||||
$type,
|
|
||||||
$codebase,
|
|
||||||
$key_type,
|
|
||||||
$value_type
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$yield_types = [
|
|
||||||
new Type\Union([
|
|
||||||
new Atomic\TGenericObject(
|
|
||||||
'Generator',
|
|
||||||
[
|
|
||||||
$key_type ?: Type::getMixed(),
|
|
||||||
$value_type ?: Type::getMixed(),
|
|
||||||
Type::getMixed(),
|
|
||||||
$return_types ? Type::combineUnionTypeArray($return_types, null) : Type::getVoid()
|
|
||||||
]
|
|
||||||
),
|
|
||||||
])
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $return_types;
|
return $return_types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param list<Type\Union> $return_types
|
||||||
|
* @param non-empty-list<Type\Union> $yield_types
|
||||||
|
* @return non-empty-list<Type\Union>
|
||||||
|
*/
|
||||||
|
private static function processYieldTypes(
|
||||||
|
Codebase $codebase,
|
||||||
|
array $return_types,
|
||||||
|
array $yield_types
|
||||||
|
) : array {
|
||||||
|
$key_type = null;
|
||||||
|
$value_type = null;
|
||||||
|
|
||||||
|
$yield_type = Type::combineUnionTypeArray($yield_types, null);
|
||||||
|
|
||||||
|
foreach ($yield_type->getAtomicTypes() as $type) {
|
||||||
|
if ($type instanceof Type\Atomic\TKeyedArray) {
|
||||||
|
$type = $type->getGenericArrayType();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type instanceof Type\Atomic\TList) {
|
||||||
|
$type = new Type\Atomic\TArray([Type::getInt(), $type->type_param]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type instanceof Type\Atomic\TArray) {
|
||||||
|
[$key_type_param, $value_type_param] = $type->type_params;
|
||||||
|
|
||||||
|
if (!$key_type) {
|
||||||
|
$key_type = clone $key_type_param;
|
||||||
|
} else {
|
||||||
|
$key_type = Type::combineUnionTypes($key_type_param, $key_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$value_type) {
|
||||||
|
$value_type = clone $value_type_param;
|
||||||
|
} else {
|
||||||
|
$value_type = Type::combineUnionTypes($value_type_param, $value_type);
|
||||||
|
}
|
||||||
|
} elseif ($type instanceof Type\Atomic\TIterable
|
||||||
|
|| $type instanceof Type\Atomic\TNamedObject
|
||||||
|
) {
|
||||||
|
ForeachAnalyzer::getKeyValueParamsForTraversableObject(
|
||||||
|
$type,
|
||||||
|
$codebase,
|
||||||
|
$key_type,
|
||||||
|
$value_type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
new Type\Union([
|
||||||
|
new Atomic\TGenericObject(
|
||||||
|
'Generator',
|
||||||
|
[
|
||||||
|
$key_type ?: Type::getMixed(),
|
||||||
|
$value_type ?: Type::getMixed(),
|
||||||
|
Type::getMixed(),
|
||||||
|
$return_types ? Type::combineUnionTypeArray($return_types, null) : Type::getVoid()
|
||||||
|
]
|
||||||
|
),
|
||||||
|
])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return list<Type\Union>
|
* @return list<Type\Union>
|
||||||
*/
|
*/
|
||||||
|
@ -704,11 +704,13 @@ class StatementsAnalyzer extends SourceAnalyzer
|
|||||||
&& $function_storage
|
&& $function_storage
|
||||||
&& $function_storage->location
|
&& $function_storage->location
|
||||||
) {
|
) {
|
||||||
[$count, $branching, $mean] = $this->data_flow_graph->getEdgeStats();
|
[$count, , $unique_destinations, $mean] = $this->data_flow_graph->getEdgeStats();
|
||||||
|
|
||||||
|
$average_destination_branches_converging = $unique_destinations > 0 ? $count / $unique_destinations : 0;
|
||||||
|
|
||||||
if ($count > $codebase->config->max_graph_size
|
if ($count > $codebase->config->max_graph_size
|
||||||
&& $mean > $codebase->config->max_avg_path_length
|
&& $mean > $codebase->config->max_avg_path_length
|
||||||
&& $branching > 1.1
|
&& $average_destination_branches_converging > 1.1
|
||||||
) {
|
) {
|
||||||
if ($source instanceof FunctionAnalyzer) {
|
if ($source instanceof FunctionAnalyzer) {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
@ -723,7 +725,7 @@ class StatementsAnalyzer extends SourceAnalyzer
|
|||||||
// fall through
|
// fall through
|
||||||
}
|
}
|
||||||
} elseif ($source instanceof MethodAnalyzer) {
|
} elseif ($source instanceof MethodAnalyzer) {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new ComplexMethod(
|
new ComplexMethod(
|
||||||
'This method’s complexity is greater than the project limit'
|
'This method’s complexity is greater than the project limit'
|
||||||
. ' (method graph size = ' . $count .', average path length = ' . round($mean) . ')',
|
. ' (method graph size = ' . $count .', average path length = ' . round($mean) . ')',
|
||||||
|
@ -97,38 +97,41 @@ abstract class DataFlowGraph
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array{int, float, float}
|
* @return array{int, int, int, float}
|
||||||
*/
|
*/
|
||||||
public function getEdgeStats() : array
|
public function getEdgeStats() : array
|
||||||
{
|
{
|
||||||
$lengths = 0;
|
$lengths = 0;
|
||||||
|
|
||||||
$destination_counts = [];
|
$destination_counts = [];
|
||||||
|
$origin_counts = [];
|
||||||
|
|
||||||
foreach ($this->forward_edges as $destinations) {
|
foreach ($this->forward_edges as $from_id => $destinations) {
|
||||||
foreach ($destinations as $id => $path) {
|
foreach ($destinations as $to_id => $path) {
|
||||||
if ($path->length === 0) {
|
if ($path->length === 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$lengths += $path->length;
|
$lengths += $path->length;
|
||||||
|
|
||||||
if (!isset($destination_counts[$id])) {
|
if (!isset($destination_counts[$to_id])) {
|
||||||
$destination_counts[$id] = 0;
|
$destination_counts[$to_id] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$destination_counts[$id]++;
|
$destination_counts[$to_id]++;
|
||||||
|
|
||||||
|
$origin_counts[$from_id] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$count = array_sum($destination_counts);
|
$count = array_sum($destination_counts);
|
||||||
|
|
||||||
if (!$count) {
|
if (!$count) {
|
||||||
return [0, 0, 0.0, 0.0];
|
return [0, 0, 0, 0.0];
|
||||||
}
|
}
|
||||||
|
|
||||||
$mean = $lengths / $count;
|
$mean = $lengths / $count;
|
||||||
|
|
||||||
return [$count, $count / \count($destination_counts), $mean];
|
return [$count, \count($origin_counts), \count($destination_counts), $mean];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,12 @@ class TypeTokenizer
|
|||||||
private static $memoized_tokens = [];
|
private static $memoized_tokens = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return list<array{0: string, 1: int}>
|
* Tokenises a type string into an array of tuples where the first element
|
||||||
|
* contains the string token and the second element contains its offset,
|
||||||
|
*
|
||||||
|
* @return list<array{string, int}>
|
||||||
|
*
|
||||||
|
* @psalm-suppress ComplexMethod
|
||||||
*/
|
*/
|
||||||
public static function tokenize(string $string_type, bool $ignore_space = true): array
|
public static function tokenize(string $string_type, bool $ignore_space = true): array
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user