mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Transition to more flexible error handling
This commit is contained in:
parent
281751d85c
commit
d8854fd4ef
@ -5,6 +5,7 @@ namespace CodeInspector;
|
||||
use CodeInspector\Exception\InvalidClassException;
|
||||
use CodeInspector\Exception\UndefinedClassException;
|
||||
use CodeInspector\Exception\UndefinedTraitException;
|
||||
use CodeInspector\ExceptionHandler;
|
||||
use PhpParser;
|
||||
use PhpParser\Error;
|
||||
use PhpParser\ParserFactory;
|
||||
@ -95,7 +96,11 @@ class ClassChecker implements StatementsSource
|
||||
foreach ($stmt->traits as $trait) {
|
||||
$trait_name = self::getAbsoluteClassFromName($trait, $this->_namespace, $this->_aliased_classes);
|
||||
if (!trait_exists($trait_name)) {
|
||||
throw new UndefinedTraitException('Trait ' . $trait_name . ' does not exist', $this->_file_name, $trait->getLine());
|
||||
if (ExceptionHandler::accepts(
|
||||
new UndefinedTraitException('Trait ' . $trait_name . ' does not exist', $this->_file_name, $trait->getLine())
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$this->_registerInheritedMethods($trait_name, $method_map);
|
||||
}
|
||||
@ -198,14 +203,22 @@ class ClassChecker implements StatementsSource
|
||||
}
|
||||
|
||||
if (!class_exists($absolute_class, true) && !interface_exists($absolute_class, true)) {
|
||||
throw new UndefinedClassException('Class ' . $absolute_class . ' does not exist', $file_name, $stmt->getLine());
|
||||
if (ExceptionHandler::accepts(
|
||||
new UndefinedClassException('Class ' . $absolute_class . ' does not exist', $file_name, $stmt->getLine())
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (class_exists($absolute_class, true) && strpos($absolute_class, '\\') === false) {
|
||||
$reflection_class = new ReflectionClass($absolute_class);
|
||||
|
||||
if ($reflection_class->getName() !== $absolute_class) {
|
||||
throw new InvalidClassException('Class ' . $absolute_class . ' has wrong casing', $file_name, $stmt->getLine());
|
||||
if (ExceptionHandler::accepts(
|
||||
new InvalidClassException('Class ' . $absolute_class . ' has wrong casing', $file_name, $stmt->getLine())
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,18 +74,26 @@ class ClassMethodChecker extends FunctionChecker
|
||||
$doc_comment = $this->_function->getDocComment();
|
||||
|
||||
$this->_registerNewDocComment($return_types, $doc_comment);
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
throw new ReturnTypeException(
|
||||
|
||||
if (ExceptionHandler::accepts(
|
||||
new ReturnTypeException(
|
||||
'The given return type for ' . $method_id . ' is incorrect, expecting ' . implode('|', $return_types),
|
||||
$this->_file_name,
|
||||
$this->_function->getLine()
|
||||
);
|
||||
)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if ($update_doc_comment) {
|
||||
|
||||
if ($update_doc_comment) {
|
||||
$return_types = EffectsAnalyser::getReturnTypes($this->_function->stmts, true);
|
||||
|
||||
if ($return_types && $return_types !== ['mixed']) {
|
||||
@ -460,7 +468,11 @@ class ClassMethodChecker extends FunctionChecker
|
||||
return;
|
||||
}
|
||||
|
||||
throw new UndefinedMethodException('Method ' . $method_id . ' does not exist', $file_name, $stmt->getLine());
|
||||
if (ExceptionHandler::accepts(
|
||||
new UndefinedMethodException('Method ' . $method_id . ' does not exist', $file_name, $stmt->getLine())
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected static function _populateData($method_id)
|
||||
@ -486,7 +498,11 @@ class ClassMethodChecker extends FunctionChecker
|
||||
$method_name = explode('::', $method_id)[1];
|
||||
|
||||
if (!isset(self::$_method_visibility[$method_id])) {
|
||||
throw new InaccessibleMethodException('Cannot access method ' . $method_id, $file_name, $line_number);
|
||||
if (ExceptionHandler::accepts(
|
||||
new InaccessibleMethodException('Cannot access method ' . $method_id, $file_name, $line_number)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (self::$_method_visibility[$method_id]) {
|
||||
@ -495,7 +511,11 @@ class ClassMethodChecker extends FunctionChecker
|
||||
|
||||
case self::VISIBILITY_PRIVATE:
|
||||
if (!$calling_context || $method_class !== $calling_context) {
|
||||
throw new InaccessibleMethodException('Cannot access private method ' . $method_id . ' from context ' . $calling_context, $file_name, $line_number);
|
||||
if (ExceptionHandler::accepts(
|
||||
new InaccessibleMethodException('Cannot access private method ' . $method_id . ' from context ' . $calling_context, $file_name, $line_number)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@ -505,7 +525,11 @@ class ClassMethodChecker extends FunctionChecker
|
||||
}
|
||||
|
||||
if (!$calling_context) {
|
||||
throw new InaccessibleMethodException('Cannot access protected method ' . $method_id, $file_name, $line_number);
|
||||
if (ExceptionHandler::accepts(
|
||||
new InaccessibleMethodException('Cannot access protected method ' . $method_id, $file_name, $line_number)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_subclass_of($method_class, $calling_context) && method_exists($calling_context, $method_name)) {
|
||||
@ -513,7 +537,11 @@ class ClassMethodChecker extends FunctionChecker
|
||||
}
|
||||
|
||||
if (!is_subclass_of($calling_context, $method_class)) {
|
||||
throw new InaccessibleMethodException('Cannot access protected method ' . $method_id . ' from context ' . $calling_context, $file_name, $line_number);
|
||||
if (ExceptionHandler::accepts(
|
||||
new InaccessibleMethodException('Cannot access protected method ' . $method_id . ' from context ' . $calling_context, $file_name, $line_number)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace CodeInspector\Exception;
|
||||
|
||||
class PossiblyUndefinedVariableException extends CodeException
|
||||
{
|
||||
}
|
12
src/CodeInspector/ExceptionHandler.php
Normal file
12
src/CodeInspector/ExceptionHandler.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace CodeInspector;
|
||||
|
||||
class ExceptionHandler
|
||||
{
|
||||
public static function accepts(Exception\CodeException $e)
|
||||
{
|
||||
throw $e;
|
||||
//return true;
|
||||
}
|
||||
}
|
@ -41,7 +41,7 @@ class FunctionChecker implements StatementsSource
|
||||
if (ClassChecker::getThisClass() && $this instanceof ClassMethodChecker) {
|
||||
$hash = $this->getMethodId() . json_encode([$vars_in_scope, $vars_possibly_in_scope]);
|
||||
|
||||
// if we know that the function has no effects on vars, we don't bother checking
|
||||
// if we know that the function has no effects on vars, we don't bother rechecking
|
||||
if (isset(self::$_no_effects_hashes[$hash])) {
|
||||
list($vars_in_scope, $vars_possibly_in_scope) = self::$_no_effects_hashes[$hash];
|
||||
|
||||
@ -66,10 +66,9 @@ class FunctionChecker implements StatementsSource
|
||||
$param->default->name->parts = ['null'];
|
||||
|
||||
if ($param->type && is_object($param->type)) {
|
||||
$vars_in_scope[$param->name] =
|
||||
$param->type->parts === ['self'] ?
|
||||
$this->_absolute_class :
|
||||
ClassChecker::getAbsoluteClassFromName($param->type, $this->_namespace, $this->_aliased_classes);
|
||||
$vars_in_scope[$param->name] = $param->type->parts === ['self'] ?
|
||||
$this->_absolute_class :
|
||||
ClassChecker::getAbsoluteClassFromName($param->type, $this->_namespace, $this->_aliased_classes);
|
||||
|
||||
if ($is_nullable) {
|
||||
$vars_in_scope[$param->name] .= '|null';
|
||||
@ -78,6 +77,9 @@ class FunctionChecker implements StatementsSource
|
||||
elseif (is_string($param->type)) {
|
||||
$vars_in_scope[$param->name] = $param->type;
|
||||
}
|
||||
else {
|
||||
$vars_in_scope[$param->name] = 'mixed';
|
||||
}
|
||||
|
||||
$vars_possibly_in_scope[$param->name] = true;
|
||||
$statements_checker->registerVariable($param->name, $param->getLine());
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,7 @@ namespace CodeInspector;
|
||||
|
||||
use CodeInspector\Exception\InvalidArgumentException;
|
||||
use CodeInspector\Exception\TypeResolutionException;
|
||||
use CodeInspector\ExceptionHandler;
|
||||
use PhpParser;
|
||||
|
||||
class TypeChecker
|
||||
@ -33,7 +34,15 @@ class TypeChecker
|
||||
}
|
||||
|
||||
if ($return_type === 'void') {
|
||||
throw new TypeResolutionException('Argument ' . ($arg_offset + 1) . ' of ' . $method_id . ' cannot be void, but possibly void value was supplied', $file_name, $line_number);
|
||||
if (ExceptionHandler::accepts(
|
||||
new TypeResolutionException(
|
||||
'Argument ' . ($arg_offset + 1) . ' of ' . $method_id . ' cannot be void, but possibly void value was supplied',
|
||||
$file_name,
|
||||
$line_number
|
||||
)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$method_params = ClassMethodChecker::getMethodParams($method_id);
|
||||
@ -53,12 +62,14 @@ class TypeChecker
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($check_nulls) {
|
||||
throw new InvalidArgumentException(
|
||||
if (ExceptionHandler::accepts(
|
||||
new InvalidArgumentException(
|
||||
'Argument ' . ($arg_offset + 1) . ' of ' . $method_id . ' cannot be null, but possibly null value was supplied',
|
||||
$file_name,
|
||||
$line_number
|
||||
);
|
||||
)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -81,7 +92,15 @@ class TypeChecker
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('Argument ' . ($arg_offset + 1) . ' of ' . $method_id . ' has incorrect type of ' . $return_type . ', expecting ' . $expected_type, $file_name, $line_number);
|
||||
if (ExceptionHandler::accepts(
|
||||
new InvalidArgumentException(
|
||||
'Argument ' . ($arg_offset + 1) . ' of ' . $method_id . ' has incorrect type of ' . $return_type . ', expecting ' . $expected_type,
|
||||
$file_name,
|
||||
$line_number
|
||||
)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -480,7 +499,11 @@ class TypeChecker
|
||||
|
||||
if (empty($existing_var_types)) {
|
||||
if ($strict) {
|
||||
throw new TypeResolutionException('Cannot resolve types for ' . $key, $file_name, $line_number);
|
||||
if (ExceptionHandler::accepts(
|
||||
new TypeResolutionException('Cannot resolve types for ' . $key, $file_name, $line_number)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user