1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Treat methods of internal or psalm internal classes as internal (#3698)

When both the method and the class are annotated as psalm-internal,
but to different namespaces, we consider the method internal to
whichever namespace is longer, i.e. the smaller code module.

Issue reported at https://github.com/vimeo/psalm/issues/3457
This commit is contained in:
Barney Laurance 2020-06-28 18:15:54 +01:00 committed by GitHub
parent 0f727e7607
commit 3f8aa64ee9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 121 additions and 9 deletions

View File

@ -68,6 +68,7 @@ use function substr;
use function trim; use function trim;
use function preg_split; use function preg_split;
use php_user_filter; use php_user_filter;
use function strlen;
/** /**
* @internal * @internal
@ -2133,6 +2134,12 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
$doc_comment = $stmt->getDocComment(); $doc_comment = $stmt->getDocComment();
if ($class_storage && ! $class_storage->is_trait) {
$storage->internal = $class_storage->internal;
$storage->psalm_internal = $class_storage->psalm_internal;
}
if (!$doc_comment) { if (!$doc_comment) {
if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod
&& $stmt->name->name === '__construct' && $stmt->name->name === '__construct'
@ -2190,7 +2197,12 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
$storage->internal = true; $storage->internal = true;
} }
if ($docblock_info->psalm_internal) { if (null === $class_storage ||
null === $class_storage->psalm_internal ||
(null !== $docblock_info->psalm_internal &&
strlen($docblock_info->psalm_internal) > strlen($class_storage->psalm_internal)
)
) {
$storage->psalm_internal = $docblock_info->psalm_internal; $storage->psalm_internal = $docblock_info->psalm_internal;
} }

View File

@ -52,6 +52,30 @@ class InternalAnnotationTest extends TestCase
} }
}', }',
], ],
'internalClassWithInstanceCall' => [
'<?php
namespace A {
/**
* @internal
*/
class Foo {
public function barBar(): void {
}
}
function getFoo(): Foo {
return new Foo();
}
}
namespace A\B {
class Bat {
public function batBat(): void {
\A\getFoo()->barBar();
}
}
}',
],
'internalClassExtendingNamespaceWithStaticCall' => [ 'internalClassExtendingNamespaceWithStaticCall' => [
'<?php '<?php
namespace A { namespace A {
@ -244,6 +268,31 @@ class InternalAnnotationTest extends TestCase
}', }',
'error_message' => 'InternalClass', 'error_message' => 'InternalClass',
], ],
'internalClassWithInstanceCall' => [
'<?php
namespace A {
/**
* @internal
*/
class Foo {
public function barBar(): void {
}
}
function getFoo(): Foo {
return new Foo();
}
}
namespace B {
class Bat {
public function batBat(): void {
\A\getFoo()->barBar();
}
}
}',
'error_message' => 'InternalMethod',
],
'internalClassWithNew' => [ 'internalClassWithNew' => [
'<?php '<?php
namespace A { namespace A {

View File

@ -64,7 +64,7 @@ class PsalmInternalAnnotationTest extends TestCase
*/ */
public static function barBar(): void { public static function barBar(): void {
} }
public static function foo(): void { public static function foo(): void {
self::barBar(); self::barBar();
} }
@ -92,6 +92,31 @@ class PsalmInternalAnnotationTest extends TestCase
} }
}', }',
], ],
'internalClassWithInstanceCall' => [
'<?php
namespace A\B {
/**
* @internal
* @psalm-internal A\B
*/
class Foo {
public function barBar(): void {
}
}
function getFoo(): Foo {
return new Foo();
}
}
namespace A\B\C {
class Bat {
public function batBat(\A\B\Foo $instance): void {
\A\B\getFoo()->barBar();
}
}
}',
],
'internalClassExtendingNamespaceWithStaticCall' => [ 'internalClassExtendingNamespaceWithStaticCall' => [
'<?php '<?php
namespace A { namespace A {
@ -294,6 +319,32 @@ class PsalmInternalAnnotationTest extends TestCase
}', }',
'error_message' => 'InternalClass', 'error_message' => 'InternalClass',
], ],
'internalClassWithInstanceCall' => [
'<?php
namespace A\B {
/**
* @internal
* @psalm-internal A\B
*/
class Foo {
public function barBar(): void {
}
}
function getFoo(): Foo {
return new Foo();
}
}
namespace A\C {
class Bat {
public function batBat(): void {
\A\B\getFoo()->barBar();
}
}
}',
'error_message' => 'The method A\B\Foo::barBar has been marked as internal to A\B',
],
'internalClassWithNew' => [ 'internalClassWithNew' => [
'<?php '<?php
namespace A\B { namespace A\B {
@ -387,10 +438,10 @@ class PsalmInternalAnnotationTest extends TestCase
'internalPropertyMissingNamespace' => [ 'internalPropertyMissingNamespace' => [
'<?php '<?php
class Foo { class Foo {
/** /**
* @var int * @var int
* @internal * @internal
* @psalm-internal * @psalm-internal
*/ */
var $bar; var $bar;
} }
@ -401,8 +452,8 @@ class PsalmInternalAnnotationTest extends TestCase
'<?php '<?php
class Foo { class Foo {
/** /**
* @internal * @internal
* @psalm-internal * @psalm-internal
*/ */
function Bar(): void {} function Bar(): void {}
} }
@ -429,7 +480,7 @@ class PsalmInternalAnnotationTest extends TestCase
* @var int * @var int
* @psalm-internal A\B * @psalm-internal A\B
*/ */
public $foo; public $foo;
} }
} }
', ',
@ -438,13 +489,13 @@ class PsalmInternalAnnotationTest extends TestCase
'internalFunctionMissingInternalAnnotation' => [ 'internalFunctionMissingInternalAnnotation' => [
'<?php '<?php
namespace A\B { namespace A\B {
class Foo { class Foo {
/** /**
* @psalm-internal A\B * @psalm-internal A\B
*/ */
public function foo() public function foo()
{ {
} }
} }
} }
', ',