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:
parent
48d624e305
commit
feff357a75
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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) . '::'
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
31
src/Psalm/Output/JsonSummary.php
Normal file
31
src/Psalm/Output/JsonSummary.php
Normal 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";
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user