From 41f7dc4a522f97a454c420c13877ec4604059c3e Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Thu, 9 Feb 2017 17:49:13 -0500 Subject: [PATCH] Fix #93 - fix MethodSignatureMismatch checks on builtin class methods --- src/Psalm/Checker/FunctionLikeChecker.php | 32 ++++++++++- tests/MethodSignatureTest.php | 68 +++++++++++++++++++++++ 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/src/Psalm/Checker/FunctionLikeChecker.php b/src/Psalm/Checker/FunctionLikeChecker.php index 453287df8..28875e968 100644 --- a/src/Psalm/Checker/FunctionLikeChecker.php +++ b/src/Psalm/Checker/FunctionLikeChecker.php @@ -164,6 +164,10 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo continue; } + list($implemented_fq_class_name) = explode('::', $implemented_method_id); + + $class_storage = ClassLikeChecker::$storage[strtolower($implemented_fq_class_name)]; + $implemented_storage = MethodChecker::getStorage($implemented_method_id); if ($implemented_storage->visibility < $storage->visibility) { @@ -200,8 +204,8 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo break; } - if ((string)$storage->params[$i]->signature_type !== - (string)$implemented_param->signature_type + if ($class_storage->user_defined && + (string)$storage->params[$i]->signature_type !== (string)$implemented_param->signature_type ) { $cased_method_id = MethodChecker::getCasedMethodId((string)$this->getMethodId()); $parent_method_id = MethodChecker::getCasedMethodId($implemented_method_id); @@ -221,6 +225,29 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo $have_emitted = true; break; } + + if (!$class_storage->user_defined && + !$implemented_param->type->isMixed() && + (string)$storage->params[$i]->type !== (string)$implemented_param->type + ) { + $cased_method_id = MethodChecker::getCasedMethodId((string)$this->getMethodId()); + $parent_method_id = MethodChecker::getCasedMethodId($implemented_method_id); + + if (IssueBuffer::accepts( + new MethodSignatureMismatch( + 'Argument ' . ($i + 1) . ' of ' . $cased_method_id .' has wrong type \'' . + $storage->params[$i]->type . '\', expecting \'' . + $implemented_param->type . '\' as defined by ' . + $parent_method_id, + $storage->params[$i]->code_location ?: new CodeLocation($this, $this->function, true) + ) + )) { + return false; + } + + $have_emitted = true; + break; + } } if ($storage->cased_name !== '__construct' && @@ -604,7 +631,6 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo return $storage; } - $docblock_info = null; try { diff --git a/tests/MethodSignatureTest.php b/tests/MethodSignatureTest.php index 555d0a068..62f42ee3b 100644 --- a/tests/MethodSignatureTest.php +++ b/tests/MethodSignatureTest.php @@ -114,4 +114,72 @@ class MethodSignatureTest extends PHPUnit_Framework_TestCase $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } + + /** + * @return void + */ + public function testExtendDocblockParamType() + { + $stmts = self::$parser->parse(' $arguments + * @param array $options default null + * @param array $input_headers default null + * @param array $output_headers default null + * @return mixed + */ + public function __soapCall( + $function_name, + $arguments, + $options = [], + $input_headers = [], + &$output_headers = [] + ) { + + } + } + '); + + $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); + $context = new Context(); + $file_checker->visitAndAnalyzeMethods($context); + } + + /** + * @expectedException \Psalm\Exception\CodeException + * @expectedExceptionMessage MethodSignatureMismatch + * @return void + */ + public function testExtendDocblockParamTypeWithWrongParam() + { + $stmts = self::$parser->parse(' $options default null + * @param array $input_headers default null + * @param array $output_headers default null + * @return mixed + */ + public function __soapCall( + $function_name, + string $arguments, + $options = [], + $input_headers = [], + &$output_headers = [] + ) { + + } + } + '); + + $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); + $context = new Context(); + $file_checker->visitAndAnalyzeMethods($context); + } }