diff --git a/config.xsd b/config.xsd
index 4c1f462d0..f764fe3a6 100644
--- a/config.xsd
+++ b/config.xsd
@@ -150,7 +150,7 @@
-
+
@@ -173,7 +173,7 @@
-
+
diff --git a/src/Psalm/Checker/Statements/Block/TryChecker.php b/src/Psalm/Checker/Statements/Block/TryChecker.php
index 7b612e587..6d062f95c 100644
--- a/src/Psalm/Checker/Statements/Block/TryChecker.php
+++ b/src/Psalm/Checker/Statements/Block/TryChecker.php
@@ -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()
)) {
diff --git a/src/Psalm/Checker/Statements/ThrowChecker.php b/src/Psalm/Checker/Statements/ThrowChecker.php
index 4c09e7150..67f7aaea0 100644
--- a/src/Psalm/Checker/Statements/ThrowChecker.php
+++ b/src/Psalm/Checker/Statements/ThrowChecker.php
@@ -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;
+ }
}
}
}
diff --git a/src/Psalm/Issue/InvalidCatch.php b/src/Psalm/Issue/InvalidCatch.php
index 45611d740..ba28f75ff 100644
--- a/src/Psalm/Issue/InvalidCatch.php
+++ b/src/Psalm/Issue/InvalidCatch.php
@@ -1,6 +1,6 @@
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__),
+ '
+
+
+
+
+
+
+
+
+ '
+ )
+ );
+
+ $file_path = getcwd() . '/src/somefile.php';
+
+ $this->addFile(
+ $file_path,
+ '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__),
+ '
+
+
+
+
+
+
+
+
+ '
+ )
+ );
+
+ $file_path = getcwd() . '/src/somefile.php';
+
+ $this->addFile(
+ $file_path,
+ 'analyzeFile($file_path, new Context());
+ }
+
+ /**
+ * @return void
+ */
+ public function testValidThrowValidCatch()
+ {
+ $this->project_checker = $this->getProjectCheckerWithConfig(
+ TestConfig::loadFromXML(
+ dirname(__DIR__),
+ '
+
+
+
+
+
+
+
+
+
+
+
+
+
+ '
+ )
+ );
+
+ $file_path = getcwd() . '/src/somefile.php';
+
+ $this->addFile(
+ $file_path,
+ 'analyzeFile($file_path, new Context());
+ }
+
/**
* @return void
*/