diff --git a/config.xsd b/config.xsd
index 061d84db6..9928e7153 100644
--- a/config.xsd
+++ b/config.xsd
@@ -160,6 +160,7 @@
+
diff --git a/src/Psalm/Checker/Statements/Expression/FetchChecker.php b/src/Psalm/Checker/Statements/Expression/FetchChecker.php
index 4659f33a2..c33b61f7a 100644
--- a/src/Psalm/Checker/Statements/Expression/FetchChecker.php
+++ b/src/Psalm/Checker/Statements/Expression/FetchChecker.php
@@ -27,6 +27,7 @@ use Psalm\Issue\NullPropertyFetch;
use Psalm\Issue\NullReference;
use Psalm\Issue\ParentNotFound;
use Psalm\Issue\PossiblyInvalidArrayAccess;
+use Psalm\Issue\PossiblyInvalidPropertyFetch;
use Psalm\Issue\PossiblyNullArrayAccess;
use Psalm\Issue\PossiblyNullPropertyFetch;
use Psalm\Issue\PossiblyNullReference;
@@ -167,25 +168,22 @@ class FetchChecker
return null;
}
+ $invalid_fetch_types = [];
+ $has_valid_fetch_type = false;
+
foreach ($stmt_var_type->types as $lhs_type_part) {
if ($lhs_type_part instanceof TNull) {
continue;
}
if (!$lhs_type_part instanceof TNamedObject && !$lhs_type_part instanceof TObject) {
- if (IssueBuffer::accepts(
- new InvalidPropertyFetch(
- 'Cannot fetch property on non-object ' . $stmt_var_id . ' of type ' . $lhs_type_part,
- new CodeLocation($statements_checker->getSource(), $stmt)
- ),
- $statements_checker->getSuppressedIssues()
- )) {
- // fall through
- }
+ $invalid_fetch_types[] = (string)$lhs_type_part;
continue;
}
+ $has_valid_fetch_type = true;
+
// stdClass and SimpleXMLElement are special cases where we cannot infer the return types
// but we don't want to throw an error
// Hack has a similar issue: https://github.com/facebook/hhvm/issues/5164
@@ -365,6 +363,32 @@ class FetchChecker
}
}
+ if ($invalid_fetch_types) {
+ $lhs_type_part = $invalid_fetch_types[0];
+
+ if ($has_valid_fetch_type) {
+ if (IssueBuffer::accepts(
+ new PossiblyInvalidPropertyFetch(
+ 'Cannot fetch property on possible non-object ' . $stmt_var_id . ' of type ' . $lhs_type_part,
+ new CodeLocation($statements_checker->getSource(), $stmt)
+ ),
+ $statements_checker->getSuppressedIssues()
+ )) {
+ // fall through
+ }
+ } else {
+ if (IssueBuffer::accepts(
+ new InvalidPropertyFetch(
+ 'Cannot fetch property on non-object ' . $stmt_var_id . ' of type ' . $lhs_type_part,
+ new CodeLocation($statements_checker->getSource(), $stmt)
+ ),
+ $statements_checker->getSuppressedIssues()
+ )) {
+ // fall through
+ }
+ }
+ }
+
if ($var_id) {
$context->vars_in_scope[$var_id] = isset($stmt->inferredType) ? $stmt->inferredType : Type::getMixed();
}
diff --git a/src/Psalm/Issue/PossiblyInvalidPropertyFetch.php b/src/Psalm/Issue/PossiblyInvalidPropertyFetch.php
new file mode 100644
index 000000000..92d3f7bea
--- /dev/null
+++ b/src/Psalm/Issue/PossiblyInvalidPropertyFetch.php
@@ -0,0 +1,6 @@
+foo;',
'error_message' => 'InvalidPropertyFetch',
],
+ 'possiblyBadFetch' => [
+ ' 3 ? "hello" : new stdClass;
+ echo $a->foo;',
+ 'error_message' => 'PossiblyInvalidPropertyFetch',
+ ],
'mixedPropertyFetch' => [
'