1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-13 17:57:37 +01:00
psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php

891 lines
31 KiB
PHP
Raw Normal View History

<?php
2018-11-06 03:57:36 +01:00
namespace Psalm\Internal\Analyzer;
2016-02-04 15:22:46 +01:00
use PhpParser;
2018-11-06 03:57:36 +01:00
use Psalm\Internal\Analyzer\Statements\Block\DoAnalyzer;
use Psalm\Internal\Analyzer\Statements\Block\ForAnalyzer;
use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
use Psalm\Internal\Analyzer\Statements\Block\IfAnalyzer;
use Psalm\Internal\Analyzer\Statements\Block\SwitchAnalyzer;
use Psalm\Internal\Analyzer\Statements\Block\TryAnalyzer;
use Psalm\Internal\Analyzer\Statements\Block\WhileAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\Assignment\InstancePropertyAssignmentAnalyzer;
2020-05-19 18:56:23 +02:00
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ClassConstFetchAnalyzer;
2018-11-06 03:57:36 +01:00
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ConstFetchAnalyzer;
2020-05-19 18:56:23 +02:00
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\VariableFetchAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\SimpleTypeInferer;
2018-11-06 03:57:36 +01:00
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
use Psalm\Internal\Analyzer\Statements\ReturnAnalyzer;
use Psalm\Internal\Analyzer\Statements\ThrowAnalyzer;
2020-05-29 04:14:41 +02:00
use Psalm\Internal\Scanner\ParsedDocblock;
2018-11-06 03:57:36 +01:00
use Psalm\Codebase;
use Psalm\CodeLocation;
2016-11-02 07:29:00 +01:00
use Psalm\Context;
2018-11-06 03:57:36 +01:00
use Psalm\DocComment;
use Psalm\Exception\DocblockParseException;
use Psalm\FileManipulation;
use Psalm\Internal\FileManipulation\FileManipulationBuffer;
use Psalm\Issue\InvalidDocblock;
use Psalm\Issue\MissingDocblockType;
use Psalm\Issue\Trace;
use Psalm\Issue\UndefinedTrace;
use Psalm\Issue\UnevaluatedCode;
2016-11-06 01:53:39 +01:00
use Psalm\Issue\UnrecognizedStatement;
use Psalm\Issue\UnusedVariable;
2016-11-02 07:29:00 +01:00
use Psalm\IssueBuffer;
use Psalm\StatementsSource;
2016-10-22 19:23:18 +02:00
use Psalm\Type;
use function strtolower;
use function fwrite;
use const STDERR;
use function array_filter;
use function array_map;
use function array_merge;
use function preg_split;
use function get_class;
use function strrpos;
use function strlen;
use function substr;
use function array_key_exists;
use function array_change_key_case;
use function array_reverse;
use function trim;
use function array_column;
use function array_combine;
/**
* @internal
*/
2018-11-06 03:57:36 +01:00
class StatementsAnalyzer extends SourceAnalyzer implements StatementsSource
{
/**
2018-11-06 03:57:36 +01:00
* @var SourceAnalyzer
*/
2016-08-11 00:10:12 +02:00
protected $source;
/**
2018-11-06 03:57:36 +01:00
* @var FileAnalyzer
*/
2018-11-11 18:01:14 +01:00
protected $file_analyzer;
2018-11-06 03:57:36 +01:00
/**
* @var Codebase
*/
protected $codebase;
2016-11-01 05:39:41 +01:00
/**
* @var array<string, CodeLocation>
2016-11-01 05:39:41 +01:00
*/
private $all_vars = [];
2016-11-01 05:39:41 +01:00
/**
* @var array<string, int>
*/
private $var_branch_points = [];
/**
* Possibly undefined variables should be initialised if we're altering code
*
* @var array<string, int>|null
*/
private $vars_to_initialize;
/**
2018-11-06 03:57:36 +01:00
* @var array<string, FunctionAnalyzer>
*/
2018-11-11 18:01:14 +01:00
private $function_analyzers = [];
/**
* @var array<string, array{0: string, 1: CodeLocation}>
*/
private $unused_var_locations = [];
2018-06-17 02:01:33 +02:00
/**
* @var array<string, bool>
*/
private $used_var_locations = [];
/**
* @var ?array<string, bool>
*/
2020-05-19 18:56:23 +02:00
public $byref_uses;
/**
2020-05-29 04:14:41 +02:00
* @var ParsedDocblock|null
*/
private $parsed_docblock = null;
/**
* @var ?string
*/
private $fake_this_class = null;
/** @var \Psalm\Internal\Provider\NodeDataProvider */
public $node_data;
public function __construct(SourceAnalyzer $source, \Psalm\Internal\Provider\NodeDataProvider $node_data)
{
2016-08-11 00:10:12 +02:00
$this->source = $source;
2018-11-11 18:01:14 +01:00
$this->file_analyzer = $source->getFileAnalyzer();
2018-11-06 03:57:36 +01:00
$this->codebase = $source->getCodebase();
$this->node_data = $node_data;
}
2016-06-18 20:45:55 +02:00
/**
* Checks an array of statements for validity
*
* @param array<PhpParser\Node\Stmt> $stmts
2017-05-27 02:16:18 +02:00
*
2016-06-18 20:45:55 +02:00
* @return null|false
*/
public function analyze(
2017-01-07 20:35:07 +01:00
array $stmts,
Context $context,
?Context $global_context = null,
bool $root_scope = false
2017-01-07 20:35:07 +01:00
) {
if (!$stmts) {
return;
}
2016-08-15 06:58:30 +02:00
// hoist functions to the top
2020-05-19 18:56:23 +02:00
$this->hoistFunctions($stmts);
2016-08-15 06:58:30 +02:00
2018-11-11 18:01:14 +01:00
$project_analyzer = $this->getFileAnalyzer()->project_analyzer;
$codebase = $project_analyzer->getCodebase();
if ($codebase->config->hoist_constants) {
2020-05-19 18:56:23 +02:00
self::hoistConstants($this, $stmts, $context);
2017-12-03 00:28:18 +01:00
}
foreach ($stmts as $stmt) {
2020-05-19 18:56:23 +02:00
if (self::analyzeStatement($this, $stmt, $context, $global_context) === false) {
return false;
}
}
2016-11-02 07:29:00 +01:00
if ($root_scope
&& !$context->collect_initializations
&& $codebase->find_unused_variables
&& $context->check_variables
) {
$this->checkUnreferencedVars($stmts);
}
2018-11-06 03:57:36 +01:00
if ($codebase->alter_code && $root_scope && $this->vars_to_initialize) {
$file_contents = $codebase->getFileContents($this->getFilePath());
foreach ($this->vars_to_initialize as $var_id => $branch_point) {
$newline_pos = (int)strrpos($file_contents, "\n", $branch_point - strlen($file_contents)) + 1;
$indentation = substr($file_contents, $newline_pos, $branch_point - $newline_pos);
FileManipulationBuffer::add($this->getFilePath(), [
new FileManipulation($branch_point, $branch_point, $var_id . ' = null;' . "\n" . $indentation),
]);
}
}
2016-11-02 07:29:00 +01:00
return null;
}
/**
* @param array<PhpParser\Node\Stmt> $stmts
2018-12-20 22:03:21 +01:00
*/
2020-05-19 18:56:23 +02:00
private function hoistFunctions(array $stmts) : void
2018-12-20 22:03:21 +01:00
{
2020-05-19 18:56:23 +02:00
foreach ($stmts as $stmt) {
if ($stmt instanceof PhpParser\Node\Stmt\Function_) {
$function_name = strtolower($stmt->name->name);
2018-12-20 22:03:21 +01:00
2020-05-19 18:56:23 +02:00
if ($ns = $this->getNamespace()) {
$fq_function_name = strtolower($ns) . '\\' . $function_name;
} else {
$fq_function_name = $function_name;
2018-12-20 22:03:21 +01:00
}
try {
2020-05-19 18:56:23 +02:00
$function_analyzer = new FunctionAnalyzer($stmt, $this->source);
$this->function_analyzers[$fq_function_name] = $function_analyzer;
} catch (\UnexpectedValueException $e) {
// do nothing
}
2016-10-22 19:23:18 +02:00
}
2020-05-19 18:56:23 +02:00
}
2016-10-22 19:23:18 +02:00
}
2016-10-22 19:23:18 +02:00
/**
2020-05-19 18:56:23 +02:00
* @param array<PhpParser\Node\Stmt> $stmts
2016-10-22 19:23:18 +02:00
*/
2020-05-19 18:56:23 +02:00
private static function hoistConstants(
StatementsAnalyzer $statements_analyzer,
array $stmts,
Context $context
) : void {
$codebase = $statements_analyzer->getCodebase();
2020-05-19 18:56:23 +02:00
foreach ($stmts as $stmt) {
if ($stmt instanceof PhpParser\Node\Stmt\Const_) {
foreach ($stmt->consts as $const) {
ConstFetchAnalyzer::setConstType(
$statements_analyzer,
$const->name->name,
SimpleTypeInferer::infer(
$codebase,
$statements_analyzer->node_data,
$const->value,
$statements_analyzer->getAliases(),
$statements_analyzer
) ?: Type::getMixed(),
$context
);
}
2020-05-19 18:56:23 +02:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Expression
&& $stmt->expr instanceof PhpParser\Node\Expr\FuncCall
&& $stmt->expr->name instanceof PhpParser\Node\Name
&& $stmt->expr->name->parts === ['define']
&& isset($stmt->expr->args[1])
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
) {
2020-05-19 18:56:23 +02:00
$const_name = ConstFetchAnalyzer::getConstName(
$stmt->expr->args[0]->value,
$statements_analyzer->node_data,
$codebase,
$statements_analyzer->getAliases()
);
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
2020-05-19 18:56:23 +02:00
if ($const_name !== null) {
ConstFetchAnalyzer::setConstType(
$statements_analyzer,
$const_name,
Statements\Expression\SimpleTypeInferer::infer(
$codebase,
$statements_analyzer->node_data,
$stmt->expr->args[1]->value,
$statements_analyzer->getAliases(),
$statements_analyzer
) ?: Type::getMixed(),
$context
);
}
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
2020-05-19 18:56:23 +02:00
}
}
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
2020-05-19 18:56:23 +02:00
/**
* @psalm-return false|null
*/
private static function analyzeStatement(
StatementsAnalyzer $statements_analyzer,
PhpParser\Node\Stmt $stmt,
Context $context,
?Context $global_context
) {
$ignore_variable_property = false;
$ignore_variable_method = false;
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
2020-05-19 18:56:23 +02:00
$codebase = $statements_analyzer->getCodebase();
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
2020-05-19 18:56:23 +02:00
if ($context->has_returned
&& !$context->collect_initializations
&& !$context->collect_mutations
&& !($stmt instanceof PhpParser\Node\Stmt\Nop)
&& !($stmt instanceof PhpParser\Node\Stmt\InlineHTML)
) {
if ($codebase->find_unused_variables) {
if (IssueBuffer::accepts(
new UnevaluatedCode(
'Expressions after return/throw/continue',
new CodeLocation($statements_analyzer->source, $stmt)
),
$statements_analyzer->source->getSuppressedIssues()
)) {
return false;
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
}
2020-05-19 18:56:23 +02:00
return;
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
2020-05-19 18:56:23 +02:00
if ($statements_analyzer->getProjectAnalyzer()->debug_lines) {
fwrite(STDERR, $statements_analyzer->getFilePath() . ':' . $stmt->getLine() . "\n");
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
2020-05-19 18:56:23 +02:00
/*
if (isset($context->vars_in_scope['$array']) && !$stmt instanceof PhpParser\Node\Stmt\Nop) {
var_dump($stmt->getLine(), $context->vars_in_scope['$array']);
}
2020-05-19 18:56:23 +02:00
*/
2020-05-19 18:56:23 +02:00
$new_issues = null;
$traced_variables = [];
2020-05-19 18:56:23 +02:00
if ($docblock = $stmt->getDocComment()) {
$statements_analyzer->parseStatementDocblock($docblock, $stmt, $context);
2020-05-29 04:14:41 +02:00
if (isset($statements_analyzer->parsed_docblock->tags['psalm-trace'])) {
foreach ($statements_analyzer->parsed_docblock->tags['psalm-trace'] as $traced_variable_line) {
2020-05-19 18:56:23 +02:00
$possible_traced_variable_names = preg_split('/[\s]+/', $traced_variable_line);
if ($possible_traced_variable_names) {
$traced_variables = array_merge(
$traced_variables,
array_filter($possible_traced_variable_names)
2018-06-28 03:53:25 +02:00
);
}
}
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
2020-05-29 04:14:41 +02:00
if (isset($statements_analyzer->parsed_docblock->tags['psalm-ignore-variable-method'])) {
2020-05-19 18:56:23 +02:00
$context->ignore_variable_method = $ignore_variable_method = true;
}
2020-05-29 04:14:41 +02:00
if (isset($statements_analyzer->parsed_docblock->tags['psalm-ignore-variable-property'])) {
2020-05-19 18:56:23 +02:00
$context->ignore_variable_property = $ignore_variable_property = true;
}
2020-05-29 04:14:41 +02:00
if (isset($statements_analyzer->parsed_docblock->tags['psalm-suppress'])) {
2020-05-19 18:56:23 +02:00
$suppressed = array_filter(
array_map(
/**
* @param string $line
*
* @return string
*/
function ($line): string {
2020-05-19 18:56:23 +02:00
return preg_split('/[\s]+/', $line)[0];
},
2020-05-29 04:14:41 +02:00
$statements_analyzer->parsed_docblock->tags['psalm-suppress']
2020-05-19 18:56:23 +02:00
)
);
2017-12-19 15:48:01 +01:00
2020-05-19 18:56:23 +02:00
if ($suppressed) {
$new_issues = [];
2020-05-19 18:56:23 +02:00
foreach ($suppressed as $offset => $issue_type) {
$offset += $docblock->getFilePos();
$new_issues[$offset] = $issue_type;
2018-01-14 00:33:32 +01:00
2020-05-19 18:56:23 +02:00
if ($issue_type === 'InaccessibleMethod') {
continue;
}
2017-12-19 15:48:01 +01:00
2020-05-19 18:56:23 +02:00
if ($codebase->track_unused_suppressions) {
IssueBuffer::addUnusedSuppression(
$statements_analyzer->getFilePath(),
$offset,
$issue_type
);
2017-12-19 15:48:01 +01:00
}
}
2020-05-19 18:56:23 +02:00
$statements_analyzer->addSuppressedIssues($new_issues);
2018-06-28 03:53:25 +02:00
}
2020-05-19 18:56:23 +02:00
}
if (isset($statements_analyzer->parsed_docblock->combined_tags['var'])
&& !($stmt instanceof PhpParser\Node\Stmt\Expression
&& $stmt->expr instanceof PhpParser\Node\Expr\Assign)
&& !$stmt instanceof PhpParser\Node\Stmt\Foreach_
&& !$stmt instanceof PhpParser\Node\Stmt\Return_
) {
$file_path = $statements_analyzer->getRootFilePath();
$file_storage_provider = $codebase->file_storage_provider;
$file_storage = $file_storage_provider->get($file_path);
$template_type_map = $statements_analyzer->getTemplateTypeMap();
$var_comments = [];
try {
$var_comments = CommentAnalyzer::arrayToDocblocks(
$docblock,
$statements_analyzer->parsed_docblock,
$statements_analyzer->getSource(),
$statements_analyzer->getAliases(),
$template_type_map,
$file_storage->type_aliases
);
} catch (\Psalm\Exception\IncorrectDocblockException $e) {
if (IssueBuffer::accepts(
new MissingDocblockType(
(string)$e->getMessage(),
new CodeLocation($statements_analyzer->getSource(), $stmt)
)
)) {
// fall through
}
} catch (\Psalm\Exception\DocblockParseException $e) {
if (IssueBuffer::accepts(
new InvalidDocblock(
(string)$e->getMessage(),
new CodeLocation($statements_analyzer->getSource(), $stmt)
)
)) {
// fall through
}
}
foreach ($var_comments as $var_comment) {
AssignmentAnalyzer::assignTypeFromVarDocblock(
$statements_analyzer,
$stmt,
$var_comment,
$context
);
}
}
2020-05-19 18:56:23 +02:00
} else {
$statements_analyzer->parsed_docblock = null;
}
if ($stmt instanceof PhpParser\Node\Stmt\If_) {
if (IfAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
return false;
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\TryCatch) {
if (TryAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
return false;
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\For_) {
if (ForAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
return false;
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\Foreach_) {
if (ForeachAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
return false;
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\While_) {
if (WhileAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
return false;
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\Do_) {
DoAnalyzer::analyze($statements_analyzer, $stmt, $context);
} elseif ($stmt instanceof PhpParser\Node\Stmt\Const_) {
ConstFetchAnalyzer::analyzeConstAssignment($statements_analyzer, $stmt, $context);
} elseif ($stmt instanceof PhpParser\Node\Stmt\Unset_) {
Statements\UnsetAnalyzer::analyze($statements_analyzer, $stmt, $context);
} elseif ($stmt instanceof PhpParser\Node\Stmt\Return_) {
ReturnAnalyzer::analyze($statements_analyzer, $stmt, $context);
$context->has_returned = true;
} elseif ($stmt instanceof PhpParser\Node\Stmt\Throw_) {
ThrowAnalyzer::analyze($statements_analyzer, $stmt, $context);
$context->has_returned = true;
} elseif ($stmt instanceof PhpParser\Node\Stmt\Switch_) {
SwitchAnalyzer::analyze($statements_analyzer, $stmt, $context);
} elseif ($stmt instanceof PhpParser\Node\Stmt\Break_) {
Statements\BreakAnalyzer::analyze($statements_analyzer, $stmt, $context);
} elseif ($stmt instanceof PhpParser\Node\Stmt\Continue_) {
Statements\ContinueAnalyzer::analyze($statements_analyzer, $stmt, $context);
} elseif ($stmt instanceof PhpParser\Node\Stmt\Static_) {
Statements\StaticAnalyzer::analyze($statements_analyzer, $stmt, $context);
} elseif ($stmt instanceof PhpParser\Node\Stmt\Echo_) {
if (Statements\EchoAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
return false;
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\Function_) {
FunctionAnalyzer::analyzeStatement($statements_analyzer, $stmt, $context);
} elseif ($stmt instanceof PhpParser\Node\Stmt\Expression) {
if (ExpressionAnalyzer::analyze(
$statements_analyzer,
$stmt->expr,
$context,
false,
$global_context,
true
) === false) {
return false;
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\InlineHTML) {
// do nothing
} elseif ($stmt instanceof PhpParser\Node\Stmt\Global_) {
Statements\GlobalAnalyzer::analyze($statements_analyzer, $stmt, $context, $global_context);
} elseif ($stmt instanceof PhpParser\Node\Stmt\Property) {
InstancePropertyAssignmentAnalyzer::analyzeStatement($statements_analyzer, $stmt, $context);
2020-05-19 18:56:23 +02:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\ClassConst) {
ClassConstFetchAnalyzer::analyzeClassConstAssignment($statements_analyzer, $stmt, $context);
} elseif ($stmt instanceof PhpParser\Node\Stmt\Class_) {
try {
$class_analyzer = new ClassAnalyzer(
$stmt,
$statements_analyzer->source,
$stmt->name ? $stmt->name->name : null
);
2018-06-28 03:53:25 +02:00
2020-05-19 18:56:23 +02:00
$class_analyzer->analyze(null, $global_context);
} catch (\InvalidArgumentException $e) {
// disregard this exception, we'll likely see it elsewhere in the form
// of an issue
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\Nop) {
// do nothing
2020-05-19 18:56:23 +02:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Goto_) {
// do nothing
} elseif ($stmt instanceof PhpParser\Node\Stmt\Label) {
// do nothing
} elseif ($stmt instanceof PhpParser\Node\Stmt\Declare_) {
foreach ($stmt->declares as $declaration) {
if ((string) $declaration->key === 'strict_types'
&& $declaration->value instanceof PhpParser\Node\Scalar\LNumber
&& $declaration->value->value === 1
2018-06-28 03:53:25 +02:00
) {
2020-05-19 18:56:23 +02:00
$context->strict_types = true;
}
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\HaltCompiler) {
$context->has_returned = true;
} else {
if (IssueBuffer::accepts(
new UnrecognizedStatement(
'Psalm does not understand ' . get_class($stmt),
new CodeLocation($statements_analyzer->source, $stmt)
),
$statements_analyzer->getSuppressedIssues()
)) {
return false;
}
}
2020-05-19 18:56:23 +02:00
$codebase = $statements_analyzer->getCodebase();
2018-06-28 03:53:25 +02:00
2020-05-19 18:56:23 +02:00
$plugin_classes = $codebase->config->after_statement_checks;
2018-08-21 23:59:06 +02:00
2020-05-19 18:56:23 +02:00
if ($plugin_classes) {
$file_manipulations = [];
2017-12-19 15:48:01 +01:00
2020-05-19 18:56:23 +02:00
foreach ($plugin_classes as $plugin_fq_class_name) {
if ($plugin_fq_class_name::afterStatementAnalysis(
$stmt,
$context,
$statements_analyzer,
$codebase,
$file_manipulations
) === false) {
return false;
2017-12-19 15:48:01 +01:00
}
}
2020-05-19 18:56:23 +02:00
if ($file_manipulations) {
FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations);
2018-06-28 03:53:25 +02:00
}
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
2020-05-19 18:56:23 +02:00
if ($new_issues) {
$statements_analyzer->removeSuppressedIssues($new_issues);
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
2020-05-19 18:56:23 +02:00
if ($ignore_variable_property) {
$context->ignore_variable_property = false;
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
2020-05-19 18:56:23 +02:00
if ($ignore_variable_method) {
$context->ignore_variable_method = false;
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
2020-05-19 18:56:23 +02:00
foreach ($traced_variables as $traced_variable) {
if (isset($context->vars_in_scope[$traced_variable])) {
if (IssueBuffer::accepts(
new Trace(
$traced_variable . ': ' . $context->vars_in_scope[$traced_variable]->getId(),
new CodeLocation($statements_analyzer->source, $stmt)
),
$statements_analyzer->getSuppressedIssues()
)) {
// fall through
}
} else {
if (IssueBuffer::accepts(
new UndefinedTrace(
'Attempt to trace undefined variable ' . $traced_variable,
new CodeLocation($statements_analyzer->source, $stmt)
),
$statements_analyzer->getSuppressedIssues()
)) {
// fall through
}
}
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
2020-05-19 18:56:23 +02:00
}
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
2020-05-19 18:56:23 +02:00
private function parseStatementDocblock(
PhpParser\Comment\Doc $docblock,
PhpParser\Node\Stmt $stmt,
Context $context
) : void {
$codebase = $this->getCodebase();
2020-05-19 18:56:23 +02:00
try {
$this->parsed_docblock = DocComment::parsePreservingLength($docblock);
} catch (DocblockParseException $e) {
if (IssueBuffer::accepts(
new InvalidDocblock(
(string)$e->getMessage(),
new CodeLocation($this->getSource(), $stmt, null, true)
)
)) {
// fall through
}
2020-05-19 18:56:23 +02:00
$this->parsed_docblock = null;
2016-06-28 21:28:05 +02:00
}
2016-10-30 17:46:18 +01:00
2020-05-19 18:56:23 +02:00
$comments = $this->parsed_docblock;
2020-05-29 04:14:41 +02:00
if (isset($comments->tags['psalm-scope-this'])) {
$trimmed = trim(\reset($comments->tags['psalm-scope-this']));
2020-05-19 18:56:23 +02:00
if (!$codebase->classExists($trimmed)) {
if (IssueBuffer::accepts(
new \Psalm\Issue\UndefinedDocblockClass(
'Scope class ' . $trimmed . ' does not exist',
new CodeLocation($this->getSource(), $stmt, null, true),
$trimmed
)
)) {
// fall through
}
2020-05-19 18:56:23 +02:00
} else {
$this_type = Type::parseString($trimmed);
$context->self = $trimmed;
$context->vars_in_scope['$this'] = $this_type;
$this->setFQCLN($trimmed);
}
}
2016-10-22 19:23:18 +02:00
}
2016-11-02 07:29:00 +01:00
/**
2020-05-19 18:56:23 +02:00
* @param array<PhpParser\Node\Stmt> $stmts
* @return void
2016-11-02 07:29:00 +01:00
*/
2020-05-19 18:56:23 +02:00
public function checkUnreferencedVars(array $stmts)
2016-10-22 19:23:18 +02:00
{
2020-05-19 18:56:23 +02:00
$source = $this->getSource();
$codebase = $source->getCodebase();
$function_storage = $source instanceof FunctionLikeAnalyzer ? $source->getFunctionLikeStorage($this) : null;
if ($codebase->alter_code) {
// Reverse array to deal with chain of assignments
$this->unused_var_locations = array_reverse($this->unused_var_locations, true);
2016-08-11 23:36:22 +02:00
}
2020-05-19 18:56:23 +02:00
$var_list = array_column($this->unused_var_locations, 0);
$loc_list = array_column($this->unused_var_locations, 1);
2016-08-11 23:36:22 +02:00
2020-05-19 18:56:23 +02:00
$project_analyzer = $this->getProjectAnalyzer();
2017-01-07 20:35:07 +01:00
2020-05-19 18:56:23 +02:00
$unused_var_remover = new Statements\UnusedAssignmentRemover();
2016-11-21 03:49:06 +01:00
foreach ($this->unused_var_locations as $hash => [$var_id, $original_location]) {
2020-05-19 18:56:23 +02:00
if (substr($var_id, 0, 2) === '$_' || isset($this->used_var_locations[$hash])) {
continue;
2016-11-21 03:49:06 +01:00
}
2020-05-19 18:56:23 +02:00
if ((!$function_storage
2020-06-24 17:48:27 +02:00
|| !array_key_exists(substr($var_id, 1), $function_storage->param_lookup))
2020-05-19 18:56:23 +02:00
&& !isset($this->byref_uses[$var_id])
&& !VariableFetchAnalyzer::isSuperGlobal($var_id)
) {
$issue = new UnusedVariable(
'Variable ' . $var_id . ' is never referenced',
$original_location
);
2020-05-19 18:56:23 +02:00
if ($codebase->alter_code
&& !$unused_var_remover->checkIfVarRemoved($var_id, $original_location)
&& isset($project_analyzer->getIssuesToFix()['UnusedVariable'])
&& !IssueBuffer::isSuppressed($issue, $this->getSuppressedIssues())
) {
$unused_var_remover->findUnusedAssignment(
$this->getCodebase(),
$stmts,
array_combine($var_list, $loc_list),
$var_id,
$original_location
);
}
2020-05-19 18:56:23 +02:00
if (IssueBuffer::accepts(
$issue,
$this->getSuppressedIssues(),
true
)) {
// fall through
}
}
2016-11-21 03:49:06 +01:00
}
}
public function hasVariable(string $var_name): bool
{
return isset($this->all_vars[$var_name]);
}
/**
* @return void
*/
public function registerVariable(string $var_id, CodeLocation $location, ?int $branch_point)
{
$this->all_vars[$var_id] = $location;
if ($branch_point) {
$this->var_branch_points[$var_id] = $branch_point;
}
$this->registerVariableAssignment($var_id, $location);
}
/**
* @return void
*/
public function registerVariableAssignment(string $var_id, CodeLocation $location)
{
2018-06-17 02:01:33 +02:00
$this->unused_var_locations[$location->getHash()] = [$var_id, $location];
}
/**
2018-06-17 02:01:33 +02:00
* @param array<string, CodeLocation> $locations
* @return void
*/
2018-06-17 02:01:33 +02:00
public function registerVariableUses(array $locations)
{
2018-06-17 02:01:33 +02:00
foreach ($locations as $hash => $_) {
unset($this->unused_var_locations[$hash]);
$this->used_var_locations[$hash] = true;
}
}
/**
* @return array<string, array{0: string, 1: CodeLocation}>
*/
public function getUnusedVarLocations(): array
{
2020-03-14 01:28:32 +01:00
return \array_diff_key($this->unused_var_locations, $this->used_var_locations);
}
2016-10-22 19:23:18 +02:00
/**
* The first appearance of the variable in this set of statements being evaluated
*/
public function getFirstAppearance(string $var_id): ?CodeLocation
{
return isset($this->all_vars[$var_id]) ? $this->all_vars[$var_id] : null;
}
public function getBranchPoint(string $var_id): ?int
{
return isset($this->var_branch_points[$var_id]) ? $this->var_branch_points[$var_id] : null;
}
/**
* @return void
*/
public function addVariableInitialization(string $var_id, int $branch_point)
2016-10-02 19:05:49 +02:00
{
$this->vars_to_initialize[$var_id] = $branch_point;
2016-10-02 19:05:49 +02:00
}
2016-10-21 00:16:17 +02:00
2018-11-06 03:57:36 +01:00
public function getFileAnalyzer() : FileAnalyzer
{
2018-11-11 18:01:14 +01:00
return $this->file_analyzer;
}
2018-11-06 03:57:36 +01:00
public function getCodebase() : Codebase
{
return $this->codebase;
}
/**
2018-11-06 03:57:36 +01:00
* @return array<string, FunctionAnalyzer>
*/
public function getFunctionAnalyzers(): array
{
2018-11-11 18:01:14 +01:00
return $this->function_analyzers;
}
2019-02-18 19:17:08 +01:00
/**
* @param array<string, bool> $byref_uses
* @return void
*/
public function setByRefUses(array $byref_uses)
{
$this->byref_uses = $byref_uses;
}
/**
2019-04-03 01:42:23 +02:00
* @return array<string, array<array-key, CodeLocation>>
*/
public function getUncaughtThrows(Context $context): array
{
$uncaught_throws = [];
if ($context->collect_exceptions) {
if ($context->possibly_thrown_exceptions) {
$config = $this->codebase->config;
$ignored_exceptions = array_change_key_case(
$context->is_global ?
$config->ignored_exceptions_in_global_scope :
$config->ignored_exceptions
);
$ignored_exceptions_and_descendants = array_change_key_case(
$context->is_global ?
$config->ignored_exceptions_and_descendants_in_global_scope :
$config->ignored_exceptions_and_descendants
);
foreach ($context->possibly_thrown_exceptions as $possibly_thrown_exception => $codelocations) {
if (isset($ignored_exceptions[strtolower($possibly_thrown_exception)])) {
continue;
}
$is_expected = false;
foreach ($ignored_exceptions_and_descendants as $expected_exception => $_) {
try {
if ($expected_exception === strtolower($possibly_thrown_exception)
|| $this->codebase->classExtends($possibly_thrown_exception, $expected_exception)
) {
$is_expected = true;
break;
}
} catch (\InvalidArgumentException $e) {
$is_expected = true;
break;
}
}
if (!$is_expected) {
$uncaught_throws[$possibly_thrown_exception] = $codelocations;
}
}
}
}
return $uncaught_throws;
}
2020-05-19 18:56:23 +02:00
public function getFunctionAnalyzer(string $function_id) : ?FunctionAnalyzer
{
return $this->function_analyzers[$function_id] ?? null;
}
2020-05-29 04:14:41 +02:00
public function getParsedDocblock() : ?ParsedDocblock
{
return $this->parsed_docblock;
}
public function getFQCLN(): ?string
{
if ($this->fake_this_class) {
return $this->fake_this_class;
}
return parent::getFQCLN();
}
public function setFQCLN(string $fake_this_class) : void
{
$this->fake_this_class = $fake_this_class;
}
public function getNodeTypeProvider() : \Psalm\NodeTypeProvider
{
return $this->node_data;
}
}