Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check $this in static closures #9164

Merged
merged 2 commits into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Psalm/Internal/Analyzer/ClosureAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public static function analyzeExpression(

$codebase = $statements_analyzer->getCodebase();

if (!$statements_analyzer->isStatic()) {
if (!$statements_analyzer->isStatic() && !$closure_analyzer->isStatic()) {
if ($context->collect_mutations &&
$context->self &&
$codebase->classExtends(
Expand Down
4 changes: 3 additions & 1 deletion src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,9 @@ public function analyze(
$this->suppressed_issues += $appearing_class_storage->suppressed_issues;
}

if ($storage instanceof MethodStorage && $storage->is_static) {
if (($storage instanceof MethodStorage || $storage instanceof FunctionStorage)
&& $storage->is_static
) {
$this->is_static = true;
}

Expand Down
2 changes: 1 addition & 1 deletion src/Psalm/Internal/LanguageServer/Client/TextDocument.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function xcontent(TextDocumentIdentifier $textDocument): Promise
/**
* @return Generator<int, Promise<object>, object, TextDocumentItem>
*/
static function () use ($textDocument) {
function () use ($textDocument) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tm1000 it looks like a number of closures used by the language server were invalid. This means the LS behaviour may change when this PR is merged, so I'd like to request your review here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weirdan thanks for the heads up. No idea why these were static I don't think it'll affect anything too bad. Once you merge this I'll update my WIP pr and see how it runs for a bit

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like all of those problematic closures were used in the code path handling textDocument/xcontent request. textDocument/xcontent is not a part of a standard protocol, it's a Sourcegraph extension, so chances are VSCode never used it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xcontent is also removed in my pr. It existed when Felix was adding features that Microsoft hadn't yet.

Those features are now in Microsoft's lsp spec.

/** @var Promise<object> */
$promise = $this->handler->request(
'textDocument/xcontent',
Expand Down
4 changes: 2 additions & 2 deletions src/Psalm/Internal/LanguageServer/ClientHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function request(string $method, $params): Promise
/**
* @return Generator<int, Promise, mixed, Promise<mixed>>
*/
static function () use ($id, $method, $params): Generator {
function () use ($id, $method, $params): Generator {
yield $this->protocolWriter->write(
new Message(
new Request($id, $method, (object) $params),
Expand All @@ -58,7 +58,7 @@ static function () use ($id, $method, $params): Generator {
$deferred = new Deferred();

$listener =
static function (Message $msg) use ($id, $deferred, &$listener): void {
function (Message $msg) use ($id, $deferred, &$listener): void {
error_log('request handler');
/**
* @psalm-suppress UndefinedPropertyFetch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,8 @@ private function createStorageForFunctionLike(

$storage = $this->storage = $this->file_storage->functions[$function_id] = new FunctionStorage();

$storage->is_static = $stmt->static;

if ($stmt instanceof PhpParser\Node\Expr\Closure) {
foreach ($stmt->uses as $closure_use) {
if ($closure_use->byRef && is_string($closure_use->var->name)) {
Expand Down
6 changes: 6 additions & 0 deletions src/Psalm/Storage/FunctionStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@ final class FunctionStorage extends FunctionLikeStorage
{
/** @var array<string, bool> */
public $byref_uses = [];

/**
* @var bool
* @todo lift this property to FunctionLikeStorage in Psalm 6
*/
public $is_static = false;
}
28 changes: 28 additions & 0 deletions tests/ClosureTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1368,6 +1368,34 @@ public static function __callStatic(string $name, array $args): mixed {
'ignored_issues' => [],
'php_version' => '8.1',
],
'thisInStaticClosure' => [
'code' => '<?php
class C {
public string $a = "zzz";
public function f(): void {
$f = static function (): void {
echo $this->a;
};
$f();
}
}
',
'error_message' => 'InvalidScope',
],
'thisInStaticArrowFunction' => [
'code' => '<?php
class C {
public int $a = 1;
public function f(): int {
$f = static fn(): int => $this->a;
return $f();;
}
}
',
'error_message' => 'InvalidScope',
'ignored_issues' => [],
'php_version' => '7.4',
],
];
}
}