1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-11 08:49:52 +01:00

Add debug stuff for code complexity

This commit is contained in:
Matt Brown 2020-11-10 12:49:42 -05:00
parent 81babf2430
commit b731b53d5e
5 changed files with 90 additions and 4 deletions

View File

@ -438,6 +438,21 @@ class Config
*/
public $restrict_return_types = false;
/**
* @var bool
*/
public $limit_method_complexity = false;
/**
* @var int
*/
public $max_graph_size = 200;
/**
* @var int
*/
public $max_avg_path_length = 60;
/**
* @var string[]
*/
@ -841,6 +856,7 @@ class Config
'findUnusedPsalmSuppress' => 'find_unused_psalm_suppress',
'reportInfo' => 'report_info',
'restrictReturnTypes' => 'restrict_return_types',
'limitMethodComplexity' => 'limit_method_complexity',
];
foreach ($booleanAttributes as $xmlName => $internalName) {

View File

@ -1256,7 +1256,9 @@ class ArgumentAnalyzer
$cased_method_id,
$cased_method_id,
$argument_offset,
$function_param->location,
$statements_analyzer->data_flow_graph instanceof TaintFlowGraph
? $function_param->location
: null,
$function_call_location
);
} else {
@ -1264,7 +1266,9 @@ class ArgumentAnalyzer
$cased_method_id,
$cased_method_id,
$argument_offset,
$function_param->location
$statements_analyzer->data_flow_graph instanceof TaintFlowGraph
? $function_param->location
: null
);
if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph && strpos($cased_method_id, '::')) {

View File

@ -172,6 +172,7 @@ class StatementsAnalyzer extends SourceAnalyzer
if ($root_scope
&& !$context->collect_initializations
&& !$context->collect_mutations
&& $codebase->find_unused_variables
&& $context->check_variables
) {
@ -693,6 +694,21 @@ class StatementsAnalyzer extends SourceAnalyzer
$unused_var_remover = new Statements\UnusedAssignmentRemover();
if ($this->data_flow_graph instanceof VariableUseGraph
&& $codebase->config->limit_method_complexity
&& $source instanceof FunctionLikeAnalyzer
&& !$source instanceof ClosureAnalyzer
) {
[$count, $branching, $mean] = $this->data_flow_graph->getEdgeStats();
if ($count > $codebase->config->max_graph_size
&& $mean > $codebase->config->max_avg_path_length
&& $branching > 1.1
) {
echo($source->getId() . ' ' . $count . ' ' . round($mean) . ' ' . number_format($branching, 2). " \n\n");
}
}
foreach ($this->unused_var_locations as [$var_id, $original_location]) {
if (substr($var_id, 0, 2) === '$_') {
continue;

View File

@ -33,7 +33,18 @@ abstract class DataFlowGraph
return;
}
$this->forward_edges[$from_id][$to_id] = new Path($path_type, $added_taints, $removed_taints);
$length = 0;
if ($from->code_location
&& $to->code_location
&& $from->code_location->file_path === $to->code_location->file_path
) {
$to_line = $to->code_location->raw_line_number;
$from_line = $from->code_location->raw_line_number;
$length = \abs($to_line - $from_line);
}
$this->forward_edges[$from_id][$to_id] = new Path($path_type, $length, $added_taints, $removed_taints);
}
/**
@ -83,4 +94,40 @@ abstract class DataFlowGraph
return false;
}
/**
* @return array{int, float, float}
*/
public function getEdgeStats() : array
{
$lengths = 0;
$destination_counts = [];
foreach ($this->forward_edges as $destinations) {
foreach ($destinations as $id => $path) {
if ($path->length === 0) {
continue;
}
$lengths += $path->length;
if (!isset($destination_counts[$id])) {
$destination_counts[$id] = 0;
}
$destination_counts[$id]++;
}
}
$count = array_sum($destination_counts);
if (!$count) {
return [0, 0, 0.0, 0.0];
}
$mean = $lengths / $count;
return [$count, $count / \count($destination_counts), $mean];
}
}

View File

@ -13,13 +13,16 @@ class Path
public $escaped_taints;
public $length;
/**
* @param ?array<string> $unescaped_taints
* @param ?array<string> $escaped_taints
*/
public function __construct(string $type, ?array $unescaped_taints, ?array $escaped_taints)
public function __construct(string $type, int $length, ?array $unescaped_taints, ?array $escaped_taints)
{
$this->type = $type;
$this->length = $length;
$this->unescaped_taints = $unescaped_taints;
$this->escaped_taints = $escaped_taints;
}