1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-27 04:45:20 +01:00

Be smarter about discovering files

This commit is contained in:
Brown 2019-05-10 18:07:13 -04:00
parent 48d624e305
commit feff357a75
6 changed files with 141 additions and 38 deletions

View File

@ -43,11 +43,11 @@ class Creator
throw new ConfigCreationException('Invalid composer.json at ' . $composer_json_location);
}
$replacements = self::getPsr4Paths($current_dir, $composer_json);
$replacements = self::getPsr4Or0Paths($current_dir, $composer_json);
if (!$replacements) {
throw new ConfigCreationException(
'Could not located any PSR-4-compatible paths in ' . $composer_json_location
'Could not located any PSR-0 or PSR-4-compatible paths in ' . $composer_json_location
);
}
}
@ -73,17 +73,23 @@ class Creator
* @return string[]
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedOperand
* @psalm-suppress MixedArgument
*/
private static function getPsr4Paths(string $current_dir, array $composer_json) : array
private static function getPsr4Or0Paths(string $current_dir, array $composer_json) : array
{
if (!isset($composer_json['autoload']['psr-4'])) {
return [];
$psr_paths = array_merge(
$composer_json['autoload']['psr-4'] ?? [],
$composer_json['autoload']['psr-0'] ?? []
);
if (!$psr_paths) {
return self::guessPhpFileDirs($current_dir);
}
$nodes = [];
/** @var string|string[] $path */
foreach ($composer_json['autoload']['psr-4'] as $paths) {
foreach ($psr_paths as $paths) {
if (!is_array($paths)) {
$paths = [$paths];
}
@ -91,33 +97,11 @@ class Creator
/** @var string $path */
foreach ($paths as $path) {
if ($path === '') {
/** @var string[] */
$php_files = array_merge(
glob($current_dir . DIRECTORY_SEPARATOR . '*.php'),
glob($current_dir . DIRECTORY_SEPARATOR . '**/*.php'),
glob($current_dir . DIRECTORY_SEPARATOR . '**/**/*.php')
$nodes = array_merge(
$nodes,
self::guessPhpFileDirs($current_dir)
);
foreach ($php_files as $php_file) {
$php_file = str_replace($current_dir . DIRECTORY_SEPARATOR, '', $php_file);
$parts = explode(DIRECTORY_SEPARATOR, $php_file);
if (!$parts[0]) {
array_shift($parts);
}
if ($parts[0] === 'vendor' || $parts[0] === 'tests') {
continue;
}
if (count($parts) === 1) {
$nodes[] = '<file name="' . $php_file . '" />';
} else {
$nodes[] = '<file name="' . $parts[0] . '" />';
}
}
continue;
}
@ -135,4 +119,43 @@ class Creator
return $nodes;
}
/**
* @return string[]
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedOperand
*/
private static function guessPhpFileDirs(string $current_dir) : array
{
$nodes = [];
/** @var string[] */
$php_files = array_merge(
glob($current_dir . DIRECTORY_SEPARATOR . '*.php'),
glob($current_dir . DIRECTORY_SEPARATOR . '**/*.php'),
glob($current_dir . DIRECTORY_SEPARATOR . '**/**/*.php')
);
foreach ($php_files as $php_file) {
$php_file = str_replace($current_dir . DIRECTORY_SEPARATOR, '', $php_file);
$parts = explode(DIRECTORY_SEPARATOR, $php_file);
if (!$parts[0]) {
array_shift($parts);
}
if ($parts[0] === 'vendor' || $parts[0] === 'tests') {
continue;
}
if (count($parts) === 1) {
$nodes[] = '<file name="' . $php_file . '" />';
} else {
$nodes[] = '<file name="' . $parts[0] . '" />';
}
}
return $nodes;
}
}

View File

@ -136,6 +136,7 @@ class ProjectAnalyzer
const TYPE_CONSOLE = 'console';
const TYPE_PYLINT = 'pylint';
const TYPE_JSON = 'json';
const TYPE_JSON_SUMMARY = 'json-summary';
const TYPE_EMACS = 'emacs';
const TYPE_XML = 'xml';
const TYPE_CHECKSTYLE = 'checkstyle';
@ -146,6 +147,7 @@ class ProjectAnalyzer
self::TYPE_CONSOLE,
self::TYPE_PYLINT,
self::TYPE_JSON,
self::TYPE_JSON_SUMMARY,
self::TYPE_EMACS,
self::TYPE_XML,
self::TYPE_CHECKSTYLE,
@ -197,6 +199,7 @@ class ProjectAnalyzer
if ($reports) {
$mapping = [
'checkstyle.xml' => self::TYPE_CHECKSTYLE,
'summary.json' => self::TYPE_JSON_SUMMARY,
'.xml' => self::TYPE_XML,
'.json' => self::TYPE_JSON,
'.txt' => self::TYPE_TEXT,

View File

@ -837,7 +837,11 @@ class ClassLikes
}
foreach ($classlike_storage->class_implements as $fq_interface_name) {
$interface_storage = $this->classlike_storage_provider->get($fq_interface_name);
try {
$interface_storage = $this->classlike_storage_provider->get($fq_interface_name);
} catch (\InvalidArgumentException $e) {
continue;
}
if ($codebase->analyzer->hasMixedMemberName(
strtolower($fq_interface_name) . '::'

View File

@ -8,6 +8,7 @@ use Psalm\Output\Compact;
use Psalm\Output\Console;
use Psalm\Output\Emacs;
use Psalm\Output\Json;
use Psalm\Output\JsonSummary;
use Psalm\Output\Pylint;
use Psalm\Output\Text;
use Psalm\Output\Xml;
@ -270,7 +271,8 @@ class IssueBuffer
$project_analyzer->output_format,
$project_analyzer->use_color,
$project_analyzer->show_snippet,
$project_analyzer->show_info
$project_analyzer->show_info,
$codebase->analyzer->getTotalTypeCoverage($codebase)
);
}
@ -299,7 +301,13 @@ class IssueBuffer
foreach ($project_analyzer->reports as $format => $path) {
file_put_contents(
$path,
self::getOutput($format, $project_analyzer->use_color)
self::getOutput(
$format,
$project_analyzer->use_color,
true,
true,
$codebase->analyzer->getTotalTypeCoverage($codebase)
)
);
}
@ -360,11 +368,20 @@ class IssueBuffer
* @param bool $use_color
* @param bool $show_snippet
* @param bool $show_info
* @param array{int, int} $mixed_counts
*
* @return string
*/
public static function getOutput($format, $use_color, $show_snippet = true, $show_info = true)
{
public static function getOutput(
string $format,
bool $use_color,
bool $show_snippet = true,
bool $show_info = true,
array $mixed_counts = [0, 0]
) {
$total_expression_count = $mixed_counts[0] + $mixed_counts[1];
$mixed_expression_count = $mixed_counts[0];
switch ($format) {
case ProjectAnalyzer::TYPE_COMPACT:
$output = new Compact(self::$issues_data, $use_color, $show_snippet, $show_info);
@ -382,6 +399,17 @@ class IssueBuffer
$output = new Json(self::$issues_data, $use_color, $show_snippet, $show_info);
break;
case ProjectAnalyzer::TYPE_JSON_SUMMARY:
$output = new JsonSummary(
self::$issues_data,
$use_color,
$show_snippet,
$show_info,
$mixed_expression_count,
$total_expression_count
);
break;
case ProjectAnalyzer::TYPE_PYLINT:
$output = new Pylint(self::$issues_data, $use_color, $show_snippet, $show_info);
break;

View File

@ -19,6 +19,12 @@ abstract class Output
/** @var bool */
protected $show_info;
/** @var int */
protected $mixed_expression_count;
/** @var int */
protected $total_expression_count;
/**
* @param array<int, array{severity: string, line_from: int, line_to: int, type: string, message: string,
* file_name: string, file_path: string, snippet: string, from: int, to: int,
@ -27,12 +33,20 @@ abstract class Output
* @param bool $show_snippet
* @param bool $show_info
*/
public function __construct(array $issues_data, bool $use_color, bool $show_snippet = true, bool $show_info = true)
{
public function __construct(
array $issues_data,
bool $use_color,
bool $show_snippet = true,
bool $show_info = true,
int $mixed_expression_count = 1,
int $total_expression_count = 1
) {
$this->issues_data = $issues_data;
$this->use_color = $use_color;
$this->show_snippet = $show_snippet;
$this->show_info = $show_info;
$this->mixed_expression_count = $mixed_expression_count;
$this->total_expression_count = $total_expression_count;
}
/**

View File

@ -0,0 +1,31 @@
<?php
namespace Psalm\Output;
use Psalm\Output;
class JsonSummary extends Output
{
/**
* {{@inheritdoc}}
*/
public function create(): string
{
$type_counts = [];
foreach ($this->issues_data as $issue_data) {
$type = $issue_data['type'];
if (!isset($type_counts[$type])) {
$type_counts[$type] = 0;
}
$type_counts[$type]++;
}
return json_encode([
'issue_counts' => $type_counts,
'mixed_expression_count' => $this->mixed_expression_count,
'total_expression_count' => $this->total_expression_count
]) . "\n";
}
}