mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Read more from config and fix switch snafu
This commit is contained in:
parent
4edd11cd44
commit
46005ddd29
7
src/CodeInspector/CodeException.php
Normal file
7
src/CodeInspector/CodeException.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace CodeInspector;
|
||||||
|
|
||||||
|
class CodeException extends \Exception {
|
||||||
|
|
||||||
|
}
|
@ -11,49 +11,66 @@ class Config
|
|||||||
{
|
{
|
||||||
protected static $_config;
|
protected static $_config;
|
||||||
|
|
||||||
public $stopOnError = true;
|
public $stop_on_error = true;
|
||||||
public $useDocblockReturnType = false;
|
public $use_docblock_return_type = false;
|
||||||
|
|
||||||
protected $errorHandlers;
|
protected $inspect_files;
|
||||||
|
|
||||||
protected $inspectFiles;
|
protected $base_dir;
|
||||||
|
|
||||||
protected $fileExtensions = ['php'];
|
protected $file_extensions = ['php'];
|
||||||
|
|
||||||
|
protected $issue_handlers = [];
|
||||||
|
|
||||||
|
protected $mock_classes = [];
|
||||||
|
|
||||||
private function __construct()
|
private function __construct()
|
||||||
{
|
{
|
||||||
self::$_config = $this;
|
self::$_config = $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function loadFromXML($file_contents)
|
public static function loadFromXML($file_name)
|
||||||
{
|
{
|
||||||
$config = new self();
|
$config = new self();
|
||||||
|
|
||||||
|
$file_contents = file_get_contents($file_name);
|
||||||
|
|
||||||
|
$config->base_dir = dirname($file_name) . '/';
|
||||||
|
|
||||||
$config_xml = new SimpleXMLElement($file_contents);
|
$config_xml = new SimpleXMLElement($file_contents);
|
||||||
|
|
||||||
if (isset($config_xml['stopOnError'])) {
|
if (isset($config_xml['stopOnError'])) {
|
||||||
$config->stopOnError = (bool) $config_xml['stopOnError'];
|
$config->stop_on_error = (bool) $config_xml['stopOnError'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($config_xml['useDocblockReturnType'])) {
|
if (isset($config_xml['useDocblockReturnType'])) {
|
||||||
$config->stopOnError = (bool) $config_xml['useDocblockReturnType'];
|
$config->use_docblock_return_type = (bool) $config_xml['useDocblockReturnType'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($config_xml->inspectFiles)) {
|
if (isset($config_xml->inspectFiles)) {
|
||||||
$config->inspectFiles = new FileFilter($config_xml->inspectFiles);
|
$config->inspect_files = FileFilter::loadFromXML($config_xml->inspectFiles, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($config_xml->fileExtensions)) {
|
if (isset($config_xml->fileExtensions)) {
|
||||||
$config->fileExtensions = [];
|
$config->file_extensions = [];
|
||||||
if ($config_xml->fileExtensions->extension instanceof SimpleXMLElement) {
|
|
||||||
$config->fileExtensions[] = preg_replace('/^.?/', '', $config_xml->fileExtensions->extension);
|
foreach ($config_xml->fileExtensions->extension as $extension) {
|
||||||
|
$config->file_extensions[] = preg_replace('/^\.?/', '', $extension['name']);
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
foreach ($config_xml->fileExtensions->extension as $extension) {
|
|
||||||
$config->fileExtensions[] = preg_replace('/^.?/', '', $extension);
|
if (isset($config_xml->mockClasses) && isset($config_xml->mockClasses->class)) {
|
||||||
|
foreach ($config_xml->mockClasses->class as $mock_class) {
|
||||||
|
$config->mock_classes[] = $mock_class['name'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($config_xml->issueHandler)) {
|
||||||
|
foreach ($config_xml->issueHandler->children() as $key => $issue_handler) {
|
||||||
|
if (isset($issue_handler->excludeFiles)) {
|
||||||
|
$config->issue_handlers[$key] = FileFilter::loadFromXML($issue_handler->excludeFiles, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$config->inspectFiles = new FileFilter($config_xml->inspectFiles);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +88,14 @@ class Config
|
|||||||
|
|
||||||
public function excludeIssueInFile($issue_type, $file_name)
|
public function excludeIssueInFile($issue_type, $file_name)
|
||||||
{
|
{
|
||||||
|
$issue_type = array_pop(explode('\\', $issue_type));
|
||||||
|
$file_name = preg_replace('/^' . preg_quote($this->base_dir, '/') . '/', '', $file_name);
|
||||||
|
|
||||||
|
if (!isset($this->issue_handlers[$issue_type])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !$this->issue_handlers[$issue_type]->allows($file_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doesInheritVariables($file_name)
|
public function doesInheritVariables($file_name)
|
||||||
@ -81,16 +105,16 @@ class Config
|
|||||||
|
|
||||||
public function getFilesToCheck()
|
public function getFilesToCheck()
|
||||||
{
|
{
|
||||||
$files = $this->inspectFiles->getIncludeFiles();
|
$files = $this->inspect_files->getIncludeFiles();
|
||||||
|
|
||||||
foreach ($this->inspectFiles->getIncludeFolders() as $folder) {
|
foreach ($this->inspect_files->getIncludeDirs() as $dir) {
|
||||||
/** @var RecursiveDirectoryIterator */
|
/** @var RecursiveDirectoryIterator */
|
||||||
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($folder));
|
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->base_dir . '/' . $dir));
|
||||||
$iterator->rewind();
|
$iterator->rewind();
|
||||||
|
|
||||||
while ($iterator->valid()) {
|
while ($iterator->valid()) {
|
||||||
if (!$iterator->isDot()) {
|
if (!$iterator->isDot()) {
|
||||||
if (in_array($iterator->getExtension(), $this->extensions)) {
|
if (in_array($iterator->getExtension(), $this->file_extensions)) {
|
||||||
$files[] = $iterator->getRealPath();
|
$files[] = $iterator->getRealPath();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,4 +125,9 @@ class Config
|
|||||||
|
|
||||||
return $files;
|
return $files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMockClasses()
|
||||||
|
{
|
||||||
|
return $this->mock_classes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,47 +39,27 @@ class FileFilter
|
|||||||
$filter->inclusive = true;
|
$filter->inclusive = true;
|
||||||
|
|
||||||
if ($e->directory) {
|
if ($e->directory) {
|
||||||
if ($e->directory instanceof \SimpleXMLElement) {
|
foreach ($e->directory as $directory) {
|
||||||
$filter->include_dirs[] = self::slashify($e->directory['name']);
|
$filter->include_dirs[] = self::slashify($directory['name']);
|
||||||
}
|
|
||||||
else {
|
|
||||||
foreach ($e->directory as $directory) {
|
|
||||||
$filter->include_dirs[] = self::slashify($directory['name']);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($e->file) {
|
if ($e->file) {
|
||||||
if ($e->file instanceof \SimpleXMLElement) {
|
foreach ($e->file as $file) {
|
||||||
$filter->include_files[] = $e->file['name'];
|
$filter->include_files[] = $file['name'];
|
||||||
}
|
|
||||||
else {
|
|
||||||
foreach ($e->file as $file) {
|
|
||||||
$filter->include_files[] = $file['name'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ($e->directory) {
|
if ($e->directory) {
|
||||||
if ($e->directory instanceof \SimpleXMLElement) {
|
foreach ($e->directory as $directory) {
|
||||||
$filter->exclude_dirs[] = self::slashify($e->directory['name']);
|
$filter->exclude_dirs[] = self::slashify($directory['name']);
|
||||||
}
|
|
||||||
else {
|
|
||||||
foreach ($e->directory as $directory) {
|
|
||||||
$filter->exclude_dirs[] = self::slashify($directory['name']);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($e->file) {
|
if ($e->file) {
|
||||||
if ($e->file instanceof \SimpleXMLElement) {
|
foreach ($e->file as $file) {
|
||||||
$filter->exclude_files[] = $e->file['name'];
|
$filter->exclude_files[] = $file['name'];
|
||||||
}
|
|
||||||
else {
|
|
||||||
foreach ($e->file as $file) {
|
|
||||||
$filter->exclude_files[] = $file['name'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,7 +76,7 @@ class FileFilter
|
|||||||
{
|
{
|
||||||
if ($this->inclusive) {
|
if ($this->inclusive) {
|
||||||
foreach ($this->include_dirs as $include_dir) {
|
foreach ($this->include_dirs as $include_dir) {
|
||||||
if (strpos($file_name, $include_dir) !== false) {
|
if (strpos($file_name, $include_dir) === 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,7 +90,7 @@ class FileFilter
|
|||||||
|
|
||||||
// exclusive
|
// exclusive
|
||||||
foreach ($this->exclude_dirs as $exclude_dir) {
|
foreach ($this->exclude_dirs as $exclude_dir) {
|
||||||
if (strpos($file_name, $exclude_dir) !== false) {
|
if (strpos($file_name, $exclude_dir) === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,4 +101,24 @@ class FileFilter
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getIncludeDirs()
|
||||||
|
{
|
||||||
|
return $this->include_dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExcludeDirs()
|
||||||
|
{
|
||||||
|
return $this->exclude_dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIncludeFiles()
|
||||||
|
{
|
||||||
|
return $this->include_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExcludeFiles()
|
||||||
|
{
|
||||||
|
return $this->exclude_files;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,14 @@ class ExceptionHandler
|
|||||||
{
|
{
|
||||||
$config = Config::getInstance();
|
$config = Config::getInstance();
|
||||||
|
|
||||||
if ($config->stopOnError) {
|
|
||||||
die($e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($config->excludeIssueInFile(get_class($e), $e->getFileName())) {
|
if ($config->excludeIssueInFile(get_class($e), $e->getFileName())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($config->stop_on_error) {
|
||||||
|
throw new CodeException($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
namespace CodeInspector\Issue;
|
namespace CodeInspector\Issue;
|
||||||
|
|
||||||
class PossiblyUndefinedVariableError extends CodeError
|
class PossiblyUndefinedVariableNotice extends CodeIssue
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -13,26 +13,29 @@ class ScopeChecker
|
|||||||
* @param bool $check_continue - also looks for a continue
|
* @param bool $check_continue - also looks for a continue
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function doesLeaveBlock(array $stmts, $check_continue = true)
|
public static function doesLeaveBlock(array $stmts, $check_continue = true, $check_break = true)
|
||||||
{
|
{
|
||||||
for ($i = count($stmts) - 1; $i >= 0; $i--) {
|
for ($i = count($stmts) - 1; $i >= 0; $i--) {
|
||||||
$stmt = $stmts[$i];
|
$stmt = $stmts[$i];
|
||||||
|
|
||||||
if ($stmt instanceof PhpParser\Node\Stmt\Return_ ||
|
if ($stmt instanceof PhpParser\Node\Stmt\Return_ ||
|
||||||
$stmt instanceof PhpParser\Node\Stmt\Throw_ ||
|
$stmt instanceof PhpParser\Node\Stmt\Throw_ ||
|
||||||
($check_continue && ($stmt instanceof PhpParser\Node\Stmt\Continue_ || $stmt instanceof PhpParser\Node\Stmt\Break_))) {
|
($check_continue && $stmt instanceof PhpParser\Node\Stmt\Continue_) ||
|
||||||
|
($check_break && $stmt instanceof PhpParser\Node\Stmt\Break_)) {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($stmt instanceof PhpParser\Node\Stmt\If_) {
|
if ($stmt instanceof PhpParser\Node\Stmt\If_) {
|
||||||
if ($stmt->else && self::doesLeaveBlock($stmt->stmts, $check_continue) && self::doesLeaveBlock($stmt->else->stmts, $check_continue)) {
|
if ($stmt->else && self::doesLeaveBlock($stmt->stmts, $check_continue, $check_break) &&
|
||||||
|
self::doesLeaveBlock($stmt->else->stmts, $check_continue, $check_break)) {
|
||||||
|
|
||||||
if (empty($stmt->elseifs)) {
|
if (empty($stmt->elseifs)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($stmt->elseifs as $elseif) {
|
foreach ($stmt->elseifs as $elseif) {
|
||||||
if (!self::doesLeaveBlock($elseif->stmts, $check_continue)) {
|
if (!self::doesLeaveBlock($elseif->stmts, $check_continue, $check_break)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ class StatementsChecker
|
|||||||
$this->_checkThrow($stmt, $vars_in_scope, $vars_possibly_in_scope);
|
$this->_checkThrow($stmt, $vars_in_scope, $vars_possibly_in_scope);
|
||||||
|
|
||||||
} elseif ($stmt instanceof PhpParser\Node\Stmt\Switch_) {
|
} elseif ($stmt instanceof PhpParser\Node\Stmt\Switch_) {
|
||||||
$this->_checkSwitch($stmt, $vars_in_scope, $vars_possibly_in_scope);
|
$this->_checkSwitch($stmt, $vars_in_scope, $vars_possibly_in_scope, $for_vars_possibly_in_scope);
|
||||||
|
|
||||||
} elseif ($stmt instanceof PhpParser\Node\Stmt\Break_) {
|
} elseif ($stmt instanceof PhpParser\Node\Stmt\Break_) {
|
||||||
// do nothing
|
// do nothing
|
||||||
@ -252,7 +252,7 @@ class StatementsChecker
|
|||||||
$post_type_assertions = [];
|
$post_type_assertions = [];
|
||||||
|
|
||||||
if (count($stmt->stmts)) {
|
if (count($stmt->stmts)) {
|
||||||
$has_leaving_statments = ScopeChecker::doesLeaveBlock($stmt->stmts, true);
|
$has_leaving_statments = ScopeChecker::doesLeaveBlock($stmt->stmts, true, true);
|
||||||
|
|
||||||
if (!$has_leaving_statments) {
|
if (!$has_leaving_statments) {
|
||||||
$new_vars = array_diff_key($if_vars, $vars_in_scope);
|
$new_vars = array_diff_key($if_vars, $vars_in_scope);
|
||||||
@ -278,7 +278,7 @@ class StatementsChecker
|
|||||||
$post_type_assertions = $negated_types;
|
$post_type_assertions = $negated_types;
|
||||||
}
|
}
|
||||||
|
|
||||||
$has_ending_statments = ScopeChecker::doesLeaveBlock($stmt->stmts, false);
|
$has_ending_statments = ScopeChecker::doesLeaveBlock($stmt->stmts, false, false);
|
||||||
|
|
||||||
if (!$has_ending_statments) {
|
if (!$has_ending_statments) {
|
||||||
$vars = array_diff_key($if_vars_possibly_in_scope, $vars_possibly_in_scope);
|
$vars = array_diff_key($if_vars_possibly_in_scope, $vars_possibly_in_scope);
|
||||||
@ -327,7 +327,7 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (count($elseif->stmts)) {
|
if (count($elseif->stmts)) {
|
||||||
$has_leaving_statements = ScopeChecker::doesLeaveBlock($elseif->stmts, true);
|
$has_leaving_statements = ScopeChecker::doesLeaveBlock($elseif->stmts, true, true);
|
||||||
|
|
||||||
if (!$has_leaving_statements) {
|
if (!$has_leaving_statements) {
|
||||||
$elseif_redefined_vars = [];
|
$elseif_redefined_vars = [];
|
||||||
@ -384,7 +384,7 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
// has a return/throw at end
|
// has a return/throw at end
|
||||||
$has_ending_statments = ScopeChecker::doesLeaveBlock($elseif->stmts, false);
|
$has_ending_statments = ScopeChecker::doesLeaveBlock($elseif->stmts, false, false);
|
||||||
|
|
||||||
if (!$has_ending_statments) {
|
if (!$has_ending_statments) {
|
||||||
$vars = array_diff_key($elseif_vars_possibly_in_scope, $vars_possibly_in_scope);
|
$vars = array_diff_key($elseif_vars_possibly_in_scope, $vars_possibly_in_scope);
|
||||||
@ -421,7 +421,7 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (count($stmt->else->stmts)) {
|
if (count($stmt->else->stmts)) {
|
||||||
$has_leaving_statements = ScopeChecker::doesLeaveBlock($stmt->else->stmts, true);
|
$has_leaving_statements = ScopeChecker::doesLeaveBlock($stmt->else->stmts, true, true);
|
||||||
|
|
||||||
// if it doesn't end in a return
|
// if it doesn't end in a return
|
||||||
if (!$has_leaving_statements) {
|
if (!$has_leaving_statements) {
|
||||||
@ -470,7 +470,7 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
// has a return/throw at end
|
// has a return/throw at end
|
||||||
$has_ending_statments = ScopeChecker::doesLeaveBlock($stmt->else->stmts, false);
|
$has_ending_statments = ScopeChecker::doesLeaveBlock($stmt->else->stmts, false, false);
|
||||||
|
|
||||||
if (!$has_ending_statments) {
|
if (!$has_ending_statments) {
|
||||||
$vars = array_diff_key($else_vars_possibly_in_scope, $vars_possibly_in_scope);
|
$vars = array_diff_key($else_vars_possibly_in_scope, $vars_possibly_in_scope);
|
||||||
@ -502,7 +502,7 @@ class StatementsChecker
|
|||||||
* let's get the type assertions from the condition if it's a terminator
|
* let's get the type assertions from the condition if it's a terminator
|
||||||
* so that we can negate them going forward
|
* so that we can negate them going forward
|
||||||
*/
|
*/
|
||||||
if (ScopeChecker::doesLeaveBlock($stmt->stmts, false) && $negated_if_types) {
|
if (ScopeChecker::doesLeaveBlock($stmt->stmts, false, false) && $negated_if_types) {
|
||||||
$vars_in_scope_reconciled = TypeChecker::reconcileTypes($negated_if_types, $vars_in_scope, true, $this->_file_name, $stmt->getLine());
|
$vars_in_scope_reconciled = TypeChecker::reconcileTypes($negated_if_types, $vars_in_scope, true, $this->_file_name, $stmt->getLine());
|
||||||
|
|
||||||
if ($vars_in_scope_reconciled === false) {
|
if ($vars_in_scope_reconciled === false) {
|
||||||
@ -620,7 +620,7 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return void
|
* @return false|null
|
||||||
*/
|
*/
|
||||||
protected function _checkExpression(PhpParser\Node\Expr $stmt, array &$vars_in_scope, array &$vars_possibly_in_scope = [])
|
protected function _checkExpression(PhpParser\Node\Expr $stmt, array &$vars_in_scope, array &$vars_possibly_in_scope = [])
|
||||||
{
|
{
|
||||||
@ -921,7 +921,7 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return void
|
* @return false|null
|
||||||
*/
|
*/
|
||||||
protected function _checkVariable(PhpParser\Node\Expr\Variable $stmt, array &$vars_in_scope, array &$vars_possibly_in_scope, $method_id = null, $argument_offset = -1)
|
protected function _checkVariable(PhpParser\Node\Expr\Variable $stmt, array &$vars_in_scope, array &$vars_possibly_in_scope, $method_id = null, $argument_offset = -1)
|
||||||
{
|
{
|
||||||
@ -1159,7 +1159,7 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($stmt->cond as $condition) {
|
foreach ($stmt->cond as $condition) {
|
||||||
if ($this->_checkCondition($init, $for_vars, $vars_possibly_in_scope) === false) {
|
if ($this->_checkCondition($condition, $for_vars, $vars_possibly_in_scope) === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2019,7 +2019,7 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return void
|
* @return null|false
|
||||||
*/
|
*/
|
||||||
protected function _checkStaticPropertyFetch(PhpParser\Node\Expr\StaticPropertyFetch $stmt, array &$vars_in_scope, array &$vars_possibly_in_scope)
|
protected function _checkStaticPropertyFetch(PhpParser\Node\Expr\StaticPropertyFetch $stmt, array &$vars_in_scope, array &$vars_possibly_in_scope)
|
||||||
{
|
{
|
||||||
@ -2144,7 +2144,7 @@ class StatementsChecker
|
|||||||
return $this->_checkExpression($stmt->expr, $vars_in_scope, $vars_possibly_in_scope);
|
return $this->_checkExpression($stmt->expr, $vars_in_scope, $vars_possibly_in_scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function _checkSwitch(PhpParser\Node\Stmt\Switch_ $stmt, array &$vars_in_scope, array &$vars_possibly_in_scope)
|
protected function _checkSwitch(PhpParser\Node\Stmt\Switch_ $stmt, array &$vars_in_scope, array &$vars_possibly_in_scope, array &$for_vars_possibly_in_scope)
|
||||||
{
|
{
|
||||||
$type_candidate_var = null;
|
$type_candidate_var = null;
|
||||||
|
|
||||||
@ -2196,44 +2196,57 @@ class StatementsChecker
|
|||||||
|
|
||||||
$last_stmt = $case->stmts[count($case->stmts) - 1];
|
$last_stmt = $case->stmts[count($case->stmts) - 1];
|
||||||
|
|
||||||
if (!($last_stmt instanceof PhpParser\Node\Stmt\Return_)) {
|
// has a return/throw at end
|
||||||
$case_redefined_vars = [];
|
$has_ending_statments = ScopeChecker::doesLeaveBlock($case->stmts, false, false);
|
||||||
|
|
||||||
foreach ($old_case_vars as $case_var => $type) {
|
if (!$has_ending_statments) {
|
||||||
if ($case_vars_in_scope[$case_var] !== $type) {
|
$vars = array_diff_key($case_vars_possibly_in_scope, $vars_possibly_in_scope);
|
||||||
$case_redefined_vars[$case_var] = $case_vars_in_scope[$case_var];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($redefined_vars === null) {
|
$has_leaving_statements = ScopeChecker::doesLeaveBlock($case->stmts, true, false);
|
||||||
$redefined_vars = $case_redefined_vars;
|
|
||||||
|
// if we're leaving this block, add vars to outer for loop scope
|
||||||
|
if ($has_leaving_statements) {
|
||||||
|
$for_vars_possibly_in_scope = array_merge($vars, $for_vars_possibly_in_scope);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
foreach ($redefined_vars as $redefined_var => $type) {
|
$case_redefined_vars = [];
|
||||||
if (!isset($case_redefined_vars[$redefined_var])) {
|
|
||||||
unset($redefined_vars[$redefined_var]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($new_vars_in_scope === null) {
|
foreach ($old_case_vars as $case_var => $type) {
|
||||||
$new_vars_in_scope = array_diff_key($case_vars_in_scope, $vars_in_scope);
|
if ($case_vars_in_scope[$case_var] !== $type) {
|
||||||
$new_vars_possibly_in_scope = array_diff_key($case_vars_possibly_in_scope, $vars_possibly_in_scope);
|
$case_redefined_vars[$case_var] = $case_vars_in_scope[$case_var];
|
||||||
}
|
|
||||||
else {
|
|
||||||
foreach ($new_vars_in_scope as $new_var => $type) {
|
|
||||||
if (!isset($case_vars_in_scope[$new_var])) {
|
|
||||||
unset($new_vars_in_scope[$new_var]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$new_vars_possibly_in_scope = array_merge(
|
if ($redefined_vars === null) {
|
||||||
array_diff_key(
|
$redefined_vars = $case_redefined_vars;
|
||||||
$case_vars_possibly_in_scope,
|
}
|
||||||
$vars_possibly_in_scope
|
else {
|
||||||
),
|
foreach ($redefined_vars as $redefined_var => $type) {
|
||||||
$new_vars_possibly_in_scope
|
if (!isset($case_redefined_vars[$redefined_var])) {
|
||||||
);
|
unset($redefined_vars[$redefined_var]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($new_vars_in_scope === null) {
|
||||||
|
$new_vars_in_scope = array_diff_key($case_vars_in_scope, $vars_in_scope);
|
||||||
|
$new_vars_possibly_in_scope = array_diff_key($case_vars_possibly_in_scope, $vars_possibly_in_scope);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foreach ($new_vars_in_scope as $new_var => $type) {
|
||||||
|
if (!isset($case_vars_in_scope[$new_var])) {
|
||||||
|
unset($new_vars_in_scope[$new_var]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_vars_possibly_in_scope = array_merge(
|
||||||
|
array_diff_key(
|
||||||
|
$case_vars_possibly_in_scope,
|
||||||
|
$vars_possibly_in_scope
|
||||||
|
),
|
||||||
|
$new_vars_possibly_in_scope
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2243,7 +2256,8 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
// only update vars if there is a default
|
// only update vars if there is a default
|
||||||
if ($case->cond === null && !($last_stmt instanceof PhpParser\Node\Stmt\Return_)) {
|
// if that default has a throw/return/continue, that should be handled above
|
||||||
|
if ($case->cond === null) {
|
||||||
if ($new_vars_in_scope) {
|
if ($new_vars_in_scope) {
|
||||||
$vars_in_scope = array_merge($vars_in_scope, $new_vars_in_scope);
|
$vars_in_scope = array_merge($vars_in_scope, $new_vars_in_scope);
|
||||||
}
|
}
|
||||||
@ -2436,7 +2450,7 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return void
|
* @return false|null
|
||||||
*/
|
*/
|
||||||
public function _checkFunctionExists($method_id, $stmt)
|
public function _checkFunctionExists($method_id, $stmt)
|
||||||
{
|
{
|
||||||
@ -2789,7 +2803,7 @@ class StatementsChecker
|
|||||||
|
|
||||||
public static function isMock($absolute_class)
|
public static function isMock($absolute_class)
|
||||||
{
|
{
|
||||||
return in_array($absolute_class, self::$_mock_interfaces);
|
return in_array($absolute_class, Config::getInstance()->getMockClasses());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +16,7 @@ class TypeTest extends PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException CodeInspector\Exception\CodeException
|
* @expectedException CodeInspector\CodeException
|
||||||
*/
|
*/
|
||||||
public function testNullableMethodCall()
|
public function testNullableMethodCall()
|
||||||
{
|
{
|
||||||
@ -123,7 +123,7 @@ class TypeTest extends PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException CodeInspector\Exception\CodeException
|
* @expectedException CodeInspector\CodeException
|
||||||
*/
|
*/
|
||||||
public function testNullableMethodCallWithThis()
|
public function testNullableMethodCallWithThis()
|
||||||
{
|
{
|
||||||
@ -201,7 +201,7 @@ class TypeTest extends PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException CodeInspector\Exception\CodeException
|
* @expectedException CodeInspector\CodeException
|
||||||
*/
|
*/
|
||||||
public function testNullableMethodWithWrongIfGuard()
|
public function testNullableMethodWithWrongIfGuard()
|
||||||
{
|
{
|
||||||
@ -272,7 +272,7 @@ class TypeTest extends PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException CodeInspector\Exception\CodeException
|
* @expectedException CodeInspector\CodeException
|
||||||
*/
|
*/
|
||||||
public function testNullableMethodWithWrongBooleanIfGuard()
|
public function testNullableMethodWithWrongBooleanIfGuard()
|
||||||
{
|
{
|
||||||
@ -397,7 +397,7 @@ class TypeTest extends PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException CodeInspector\Exception\CodeException
|
* @expectedException CodeInspector\CodeException
|
||||||
*/
|
*/
|
||||||
public function testNullableMethodWithWrongIfGuardBefore()
|
public function testNullableMethodWithWrongIfGuardBefore()
|
||||||
{
|
{
|
||||||
@ -450,7 +450,7 @@ class TypeTest extends PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException CodeInspector\Exception\CodeException
|
* @expectedException CodeInspector\CodeException
|
||||||
*/
|
*/
|
||||||
public function testNullableMethodWithWrongBooleanIfGuardBefore()
|
public function testNullableMethodWithWrongBooleanIfGuardBefore()
|
||||||
{
|
{
|
||||||
@ -503,7 +503,7 @@ class TypeTest extends PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException CodeInspector\Exception\CodeException
|
* @expectedException CodeInspector\CodeException
|
||||||
*/
|
*/
|
||||||
public function testNullableMethodWithGuardedNestedIncompleteRedefinition()
|
public function testNullableMethodWithGuardedNestedIncompleteRedefinition()
|
||||||
{
|
{
|
||||||
@ -668,7 +668,7 @@ class TypeTest extends PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException CodeInspector\Exception\CodeException
|
* @expectedException CodeInspector\CodeException
|
||||||
*/
|
*/
|
||||||
public function testNullableMethodWithGuardedNestedRedefinitionWithUselessElseReturn()
|
public function testNullableMethodWithGuardedNestedRedefinitionWithUselessElseReturn()
|
||||||
{
|
{
|
||||||
@ -887,7 +887,7 @@ class TypeTest extends PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException CodeInspector\Exception\CodeException
|
* @expectedException CodeInspector\CodeException
|
||||||
*/
|
*/
|
||||||
public function testVariableReassignmentInIfWithOutsideCall()
|
public function testVariableReassignmentInIfWithOutsideCall()
|
||||||
{
|
{
|
||||||
@ -1004,4 +1004,30 @@ class TypeTest extends PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$this->assertSame('mixed', $return_stmt->returnType);
|
$this->assertSame('mixed', $return_stmt->returnType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSwitchVariableWithContinue()
|
||||||
|
{
|
||||||
|
$stmts = self::$_parser->parse('<?php
|
||||||
|
class B {
|
||||||
|
public function bar() {
|
||||||
|
foreach ([\'a\', \'b\', \'c\'] as $letter) {
|
||||||
|
switch ($letter) {
|
||||||
|
case \'a\':
|
||||||
|
$foo = 1;
|
||||||
|
break;
|
||||||
|
case \'b\':
|
||||||
|
$foo = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$moo = $foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}');
|
||||||
|
|
||||||
|
$file_checker = new \CodeInspector\FileChecker('somefile.php', $stmts);
|
||||||
|
$file_checker->check();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user