mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Allow InvalidCatch and InvalidThrow to be suppressed per-class
Ref #411
This commit is contained in:
parent
1ed61f42b9
commit
db3ddaa42c
@ -150,7 +150,7 @@
|
||||
<xs:element name="InvalidArrayAssignment" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="InvalidArrayOffset" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="InvalidCast" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="InvalidCatch" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="InvalidCatch" type="ClassIssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="InvalidClass" type="ClassIssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="InvalidClone" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="InvalidParamDefault" type="IssueHandlerType" minOccurs="0" />
|
||||
@ -173,7 +173,7 @@
|
||||
<xs:element name="InvalidStaticInvocation" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="InvalidStaticVariable" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="InvalidStringClass" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="InvalidThrow" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="InvalidThrow" type="ClassIssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="InvalidToString" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="LessSpecificImplementedReturnType" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="LessSpecificReturnStatement" type="IssueHandlerType" minOccurs="0" />
|
||||
|
@ -176,7 +176,8 @@ class TryChecker
|
||||
if (IssueBuffer::accepts(
|
||||
new InvalidCatch(
|
||||
'Class/interface ' . $fq_catch_class . ' cannot be caught',
|
||||
new CodeLocation($statements_checker->getSource(), $stmt)
|
||||
new CodeLocation($statements_checker->getSource(), $stmt),
|
||||
$fq_catch_class
|
||||
),
|
||||
$statements_checker->getSuppressedIssues()
|
||||
)) {
|
||||
|
@ -33,20 +33,26 @@ class ThrowChecker
|
||||
$file_checker = $statements_checker->getFileChecker();
|
||||
$project_checker = $file_checker->project_checker;
|
||||
|
||||
if (!TypeChecker::isContainedBy($project_checker->codebase, $throw_type, $exception_type)) {
|
||||
if (IssueBuffer::accepts(
|
||||
new InvalidThrow(
|
||||
'Cannot throw ' . $throw_type . ' as it does not extend Exception or implement Throwable',
|
||||
new CodeLocation($file_checker, $stmt)
|
||||
),
|
||||
$statements_checker->getSuppressedIssues()
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
} elseif ($context->collect_exceptions) {
|
||||
foreach ($throw_type->getTypes() as $throw_atomic_type) {
|
||||
if ($throw_atomic_type instanceof TNamedObject) {
|
||||
$context->possibly_thrown_exceptions[$throw_atomic_type->value] = true;
|
||||
foreach ($throw_type->getTypes() as $throw_type_part) {
|
||||
$throw_type_candidate = new Union([$throw_type_part]);
|
||||
|
||||
if (!TypeChecker::isContainedBy($project_checker->codebase, $throw_type_candidate, $exception_type)) {
|
||||
if (IssueBuffer::accepts(
|
||||
new InvalidThrow(
|
||||
'Cannot throw ' . $throw_type_part
|
||||
. ' as it does not extend Exception or implement Throwable',
|
||||
new CodeLocation($file_checker, $stmt),
|
||||
(string) $throw_type_part
|
||||
),
|
||||
$statements_checker->getSuppressedIssues()
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
} elseif ($context->collect_exceptions) {
|
||||
foreach ($throw_type->getTypes() as $throw_atomic_type) {
|
||||
if ($throw_atomic_type instanceof TNamedObject) {
|
||||
$context->possibly_thrown_exceptions[$throw_atomic_type->value] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
namespace Psalm\Issue;
|
||||
|
||||
class InvalidCatch extends CodeIssue
|
||||
class InvalidCatch extends ClassIssue
|
||||
{
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
namespace Psalm\Issue;
|
||||
|
||||
class InvalidThrow extends CodeIssue
|
||||
class InvalidThrow extends ClassIssue
|
||||
{
|
||||
}
|
||||
|
@ -764,6 +764,162 @@ class ConfigTest extends TestCase
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Psalm\Exception\CodeException
|
||||
* @expectedExceptionMessage InvalidCatch
|
||||
* @return void
|
||||
*/
|
||||
public function testValidThrowInvalidCatch()
|
||||
{
|
||||
$this->project_checker = $this->getProjectCheckerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<issueHandlers>
|
||||
<InvalidThrow>
|
||||
<errorLevel type="suppress">
|
||||
<referencedClass name="I" />
|
||||
</errorLevel>
|
||||
</InvalidThrow>
|
||||
</issueHandlers>
|
||||
</psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
interface I {}
|
||||
|
||||
class E extends Exception implements I {}
|
||||
|
||||
function foo() : void {
|
||||
throw new E();
|
||||
}
|
||||
|
||||
function handleThrow(I $e) : void {
|
||||
echo "about to throw";
|
||||
throw $e;
|
||||
}
|
||||
|
||||
try {
|
||||
foo();
|
||||
} catch (I $e) {
|
||||
handleThrow($e);
|
||||
}'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Psalm\Exception\CodeException
|
||||
* @expectedExceptionMessage InvalidThrow
|
||||
* @return void
|
||||
*/
|
||||
public function testInvalidThrowValidCatch()
|
||||
{
|
||||
$this->project_checker = $this->getProjectCheckerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<issueHandlers>
|
||||
<InvalidCatch>
|
||||
<errorLevel type="suppress">
|
||||
<referencedClass name="I" />
|
||||
</errorLevel>
|
||||
</InvalidCatch>
|
||||
</issueHandlers>
|
||||
</psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
interface I {}
|
||||
|
||||
class E extends Exception implements I {}
|
||||
|
||||
function foo() : void {
|
||||
throw new E();
|
||||
}
|
||||
|
||||
function handleThrow(I $e) : void {
|
||||
echo "about to throw";
|
||||
throw $e;
|
||||
}
|
||||
|
||||
try {
|
||||
foo();
|
||||
} catch (I $e) {
|
||||
handleThrow($e);
|
||||
}'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testValidThrowValidCatch()
|
||||
{
|
||||
$this->project_checker = $this->getProjectCheckerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
dirname(__DIR__),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<issueHandlers>
|
||||
<InvalidCatch>
|
||||
<errorLevel type="suppress">
|
||||
<referencedClass name="I" />
|
||||
</errorLevel>
|
||||
</InvalidCatch>
|
||||
<InvalidThrow>
|
||||
<errorLevel type="suppress">
|
||||
<referencedClass name="I" />
|
||||
</errorLevel>
|
||||
</InvalidThrow>
|
||||
</issueHandlers>
|
||||
</psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
interface I {}
|
||||
|
||||
class E extends Exception implements I {}
|
||||
|
||||
function foo() : void {
|
||||
throw new E();
|
||||
}
|
||||
|
||||
function handleThrow(I $e) : void {
|
||||
echo "about to throw";
|
||||
throw $e;
|
||||
}
|
||||
|
||||
try {
|
||||
foo();
|
||||
} catch (I $e) {
|
||||
handleThrow($e);
|
||||
}'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user