1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Don’t investigate property mutations if they’re not visible

This commit is contained in:
Matthew Brown 2018-01-13 01:52:46 -05:00
parent 95553ffc0e
commit f3bfb089ad
3 changed files with 87 additions and 19 deletions

View File

@ -1576,15 +1576,17 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
* @param StatementsSource $source * @param StatementsSource $source
* @param CodeLocation $code_location * @param CodeLocation $code_location
* @param array $suppressed_issues * @param array $suppressed_issues
* @param bool $emit_issues
* *
* @return false|null * @return bool|null
*/ */
public static function checkPropertyVisibility( public static function checkPropertyVisibility(
$property_id, $property_id,
$calling_context, $calling_context,
StatementsSource $source, StatementsSource $source,
CodeLocation $code_location, CodeLocation $code_location,
array $suppressed_issues array $suppressed_issues,
$emit_issues = true
) { ) {
$project_checker = $source->getFileChecker()->project_checker; $project_checker = $source->getFileChecker()->project_checker;
@ -1601,11 +1603,11 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
// if the calling class is the same, we know the property exists, so it must be visible // if the calling class is the same, we know the property exists, so it must be visible
if ($appearing_property_class === $calling_context) { if ($appearing_property_class === $calling_context) {
return null; return $emit_issues ? null : true;
} }
if ($source->getSource() instanceof TraitChecker && $declaring_property_class === $source->getFQCLN()) { if ($source->getSource() instanceof TraitChecker && $declaring_property_class === $source->getFQCLN()) {
return null; return $emit_issues ? null : true;
} }
$class_storage = $project_checker->classlike_storage_provider->get($declaring_property_class); $class_storage = $project_checker->classlike_storage_provider->get($declaring_property_class);
@ -1618,11 +1620,12 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
switch ($storage->visibility) { switch ($storage->visibility) {
case self::VISIBILITY_PUBLIC: case self::VISIBILITY_PUBLIC:
return null; return $emit_issues ? null : true;
case self::VISIBILITY_PRIVATE: case self::VISIBILITY_PRIVATE:
if (!$calling_context || $appearing_property_class !== $calling_context) { if (!$calling_context || $appearing_property_class !== $calling_context) {
if (IssueBuffer::accepts( if ($emit_issues
&& IssueBuffer::accepts(
new InaccessibleProperty( new InaccessibleProperty(
'Cannot access private property ' . $property_id . ' from context ' . $calling_context, 'Cannot access private property ' . $property_id . ' from context ' . $calling_context,
$code_location $code_location
@ -1631,9 +1634,11 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
)) { )) {
return false; return false;
} }
return null;
} }
return null; return $emit_issues ? null : true;
case self::VISIBILITY_PROTECTED: case self::VISIBILITY_PROTECTED:
if ($appearing_property_class === $calling_context) { if ($appearing_property_class === $calling_context) {
@ -1641,7 +1646,8 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
} }
if (!$calling_context) { if (!$calling_context) {
if (IssueBuffer::accepts( if ($emit_issues
&& IssueBuffer::accepts(
new InaccessibleProperty( new InaccessibleProperty(
'Cannot access protected property ' . $property_id, 'Cannot access protected property ' . $property_id,
$code_location $code_location
@ -1655,11 +1661,12 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
} }
if (ClassChecker::classExtends($project_checker, $appearing_property_class, $calling_context)) { if (ClassChecker::classExtends($project_checker, $appearing_property_class, $calling_context)) {
return null; return $emit_issues ? null : true;
} }
if (!ClassChecker::classExtends($project_checker, $calling_context, $appearing_property_class)) { if (!ClassChecker::classExtends($project_checker, $calling_context, $appearing_property_class)) {
if (IssueBuffer::accepts( if ($emit_issues
&& IssueBuffer::accepts(
new InaccessibleProperty( new InaccessibleProperty(
'Cannot access protected property ' . $property_id . ' from context ' . $calling_context, 'Cannot access protected property ' . $property_id . ' from context ' . $calling_context,
$code_location $code_location
@ -1668,10 +1675,12 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
)) { )) {
return false; return false;
} }
return null;
} }
} }
return null; return $emit_issues ? null : true;
} }
/** /**

View File

@ -792,14 +792,27 @@ class AssignmentChecker
$property_exists = true; $property_exists = true;
if (ClassLikeChecker::checkPropertyVisibility( if (!$context->collect_mutations) {
$property_id, if (ClassLikeChecker::checkPropertyVisibility(
$context->self, $property_id,
$statements_checker->getSource(), $context->self,
new CodeLocation($statements_checker->getSource(), $stmt), $statements_checker->getSource(),
$statements_checker->getSuppressedIssues() new CodeLocation($statements_checker->getSource(), $stmt),
) === false) { $statements_checker->getSuppressedIssues()
return false; ) === false) {
return false;
}
} else {
if (ClassLikeChecker::checkPropertyVisibility(
$property_id,
$context->self,
$statements_checker->getSource(),
new CodeLocation($statements_checker->getSource(), $stmt),
$statements_checker->getSuppressedIssues(),
false
) !== true) {
continue;
}
} }
$declaring_property_class = ClassLikeChecker::getDeclaringClassForProperty( $declaring_property_class = ClassLikeChecker::getDeclaringClassForProperty(

View File

@ -643,6 +643,52 @@ class PropertyTypeTest extends TestCase
'MissingReturnType', 'MissingReturnType',
], ],
], ],
'privatePropertyAccessible' => [
'<?php
class A {
/** @var string */
private $foo;
public function __construct(string $foo) {
$this->foo = $foo;
}
private function bar() : void {}
}
class B extends A {
/** @var string */
private $foo;
public function __construct(string $foo) {
$this->foo = $foo;
parent::__construct($foo);
}
}',
],
'privatePropertyAccessibleDifferentType' => [
'<?php
class A {
/** @var int */
private $foo;
public function __construct(string $foo) {
$this->foo = 5;
}
private function bar() : void {}
}
class B extends A {
/** @var string */
private $foo;
public function __construct(string $foo) {
$this->foo = $foo;
parent::__construct($foo);
}
}',
],
]; ];
} }