Skip to content

Commit

Permalink
Namespace anonymous classes
Browse files Browse the repository at this point in the history
Fixes #10755
  • Loading branch information
weirdan committed Mar 3, 2024
1 parent 20e8604 commit e4a1437
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 7 deletions.
25 changes: 20 additions & 5 deletions src/Psalm/Internal/Analyzer/ClassAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public function __construct(PhpParser\Node\Stmt $class, SourceAnalyzer $source,
throw new UnexpectedValueException('Anonymous enums are not allowed');
}

$fq_class_name = self::getAnonymousClassName($class, $source->getFilePath());
$fq_class_name = self::getAnonymousClassName($class, $source->getAliases(), $source->getFilePath());
}

parent::__construct($class, $source, $fq_class_name);
Expand All @@ -137,10 +137,25 @@ public function __construct(PhpParser\Node\Stmt $class, SourceAnalyzer $source,
}

/** @return non-empty-string */
public static function getAnonymousClassName(PhpParser\Node\Stmt\Class_ $class, string $file_path): string
{
return preg_replace('/[^A-Za-z0-9]/', '_', $file_path)
. '_' . $class->getLine() . '_' . (int)$class->getAttribute('startFilePos');
public static function getAnonymousClassName(
PhpParser\Node\Stmt\Class_ $class,
Aliases $aliases,
string $file_path
): string {
$class_name = preg_replace('/[^A-Za-z0-9]/', '_', $file_path)
. '_' . $class->getLine()
. '_' . (int)$class->getAttribute('startFilePos');

$fq_class_name = Type::getFQCLNFromString(
$class_name,
$aliases,
);

if ($fq_class_name === '') {
throw new LogicException('Invalid class name, should never happen');
}

return $fq_class_name;
}

public function analyze(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,11 @@ public static function analyze(
}
} elseif ($stmt->class instanceof PhpParser\Node\Stmt\Class_) {
$statements_analyzer->analyze([$stmt->class], $context);
$fq_class_name = ClassAnalyzer::getAnonymousClassName($stmt->class, $statements_analyzer->getFilePath());
$fq_class_name = ClassAnalyzer::getAnonymousClassName(
$stmt->class,
$statements_analyzer->getAliases(),
$statements_analyzer->getFilePath(),
);
} else {
self::analyzeConstructorExpression(
$statements_analyzer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ public function start(PhpParser\Node\Stmt\ClassLike $node): ?bool
throw new LogicException('Anonymous classes are always classes');
}

$fq_classlike_name = ClassAnalyzer::getAnonymousClassName($node, $this->file_path);
$fq_classlike_name = ClassAnalyzer::getAnonymousClassName($node, $this->aliases, $this->file_path);
} else {
$name_location = new CodeLocation($this->file_scanner, $node->name);

Expand Down
24 changes: 24 additions & 0 deletions tests/InternalAnnotationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,30 @@ public function baz(): void
}
',
],
'callToInternalMethodFromAnonymousClass' => [
'code' => <<<'PHP'
<?php
namespace X;
/**
* @internal
* @psalm-internal X
*/
class A
{
public function a(): void {}
}
new class (new A)
{
public function __construct(
private A $a
) {
$a->a();
}
};
PHP,
]
];
}

Expand Down

0 comments on commit e4a1437

Please sign in to comment.