1
0
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:
Matthew Brown 2016-06-10 14:47:44 -04:00
parent 4edd11cd44
commit 46005ddd29
8 changed files with 193 additions and 114 deletions

View File

@ -0,0 +1,7 @@
<?php
namespace CodeInspector;
class CodeException extends \Exception {
}

View File

@ -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;
}
} }

View File

@ -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;
}
} }

View File

@ -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());
}
} }
} }

View File

@ -2,6 +2,6 @@
namespace CodeInspector\Issue; namespace CodeInspector\Issue;
class PossiblyUndefinedVariableError extends CodeError class PossiblyUndefinedVariableNotice extends CodeIssue
{ {
} }

View File

@ -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;
} }
} }

View File

@ -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());
} }
/** /**

View File

@ -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();
}
} }