1
0
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:
Matt Brown 2020-11-27 17:48:39 -05:00 committed by Daniil Gentili
parent 65947e5808
commit 3005d2e1d7
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
5 changed files with 91 additions and 67 deletions

View File

@ -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[]

View File

@ -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>
*/ */

View File

@ -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 methods complexity is greater than the project limit' 'This methods 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) . ')',

View File

@ -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];
} }
} }

View File

@ -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
{ {