Skip to content

Commit

Permalink
Merge branch '5.x' into fix_literal_union_key
Browse files Browse the repository at this point in the history
  • Loading branch information
danog authored Dec 3, 2023
2 parents de53638 + 62f32f4 commit 37cf82e
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 14 deletions.
3 changes: 2 additions & 1 deletion src/Psalm/Config/FileFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SimpleXMLElement;
use Symfony\Component\Filesystem\Path;

use function array_filter;
use function array_map;
Expand Down Expand Up @@ -127,7 +128,7 @@ public static function loadFromArray(
$resolve_symlinks = (bool) ($directory['resolveSymlinks'] ?? false);
$declare_strict_types = (bool) ($directory['useStrictTypes'] ?? false);

if ($directory_path[0] === '/' && DIRECTORY_SEPARATOR === '/') {
if (Path::isAbsolute($directory_path)) {
/** @var non-empty-string */
$prospective_directory_path = $directory_path;
} else {
Expand Down
8 changes: 7 additions & 1 deletion src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,13 @@ public function analyze(
|| !in_array("UnusedPsalmSuppress", $storage->suppressed_issues)
) {
foreach ($storage->suppressed_issues as $offset => $issue_name) {
IssueBuffer::addUnusedSuppression($this->getFilePath(), $offset, $issue_name);
IssueBuffer::addUnusedSuppression(
$storage->location !== null
? $storage->location->file_path
: $this->getFilePath(),
$offset,
$issue_name,
);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public static function analyze(
|| $root_expr instanceof PhpParser\Node\Expr\MethodCall
|| $root_expr instanceof PhpParser\Node\Expr\StaticCall
|| $root_expr instanceof PhpParser\Node\Expr\Cast
|| $root_expr instanceof PhpParser\Node\Expr\Match_
|| $root_expr instanceof PhpParser\Node\Expr\NullsafePropertyFetch
|| $root_expr instanceof PhpParser\Node\Expr\NullsafeMethodCall
|| $root_expr instanceof PhpParser\Node\Expr\Ternary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -566,12 +566,12 @@ private static function handleNamedCall(
true,
$context->insideUse(),
)) {
$callstatic_appearing_id = $codebase->methods->getAppearingMethodId($callstatic_id);
assert($callstatic_appearing_id !== null);
$callstatic_declaring_id = $codebase->methods->getDeclaringMethodId($callstatic_id);
assert($callstatic_declaring_id !== null);
$callstatic_pure = false;
$callstatic_mutation_free = false;
if ($codebase->methods->hasStorage($callstatic_appearing_id)) {
$callstatic_storage = $codebase->methods->getStorage($callstatic_appearing_id);
if ($codebase->methods->hasStorage($callstatic_declaring_id)) {
$callstatic_storage = $codebase->methods->getStorage($callstatic_declaring_id);
$callstatic_pure = $callstatic_storage->pure;
$callstatic_mutation_free = $callstatic_storage->mutation_free;
}
Expand Down
5 changes: 3 additions & 2 deletions src/Psalm/Internal/CliUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ public static function getArguments(): array
}

if ($input_path[0] === '-' && strlen($input_path) === 2) {
if ($input_path[1] === 'c' || $input_path[1] === 'f') {
if ($input_path[1] === 'c' || $input_path[1] === 'f' || $input_path[1] === 'r') {
++$i;
}
continue;
Expand Down Expand Up @@ -271,7 +271,7 @@ public static function getPathsToCheck($f_paths): ?array
$input_path = $input_paths[$i];

if ($input_path[0] === '-' && strlen($input_path) === 2) {
if ($input_path[1] === 'c' || $input_path[1] === 'f') {
if ($input_path[1] === 'c' || $input_path[1] === 'f' || $input_path[1] === 'r') {
++$i;
}
continue;
Expand All @@ -287,6 +287,7 @@ public static function getPathsToCheck($f_paths): ?array
$ignored_arguments = array(
'config',
'printer',
'root',
);

if (in_array(substr($input_path, 2), $ignored_arguments, true)) {
Expand Down
13 changes: 8 additions & 5 deletions src/Psalm/IssueBuffer.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ final class IssueBuffer
*/
public static function accepts(CodeIssue $e, array $suppressed_issues = [], bool $is_fixable = false): bool
{
$config = Config::getInstance();
$project_analyzer = ProjectAnalyzer::getInstance();
$codebase = $project_analyzer->getCodebase();
$event = new BeforeAddIssueEvent($e, $is_fixable, $codebase);
if ($config->eventDispatcher->dispatchBeforeAddIssue($event) === false) {
return false;
}

if (self::isSuppressed($e, $suppressed_issues)) {
return false;
}
Expand Down Expand Up @@ -258,11 +266,6 @@ public static function add(CodeIssue $e, bool $is_fixable = false): bool
$project_analyzer = ProjectAnalyzer::getInstance();
$codebase = $project_analyzer->getCodebase();

$event = new BeforeAddIssueEvent($e, $is_fixable, $codebase);
if ($config->eventDispatcher->dispatchBeforeAddIssue($event) === false) {
return false;
}

$fqcn_parts = explode('\\', get_class($e));
$issue_type = array_pop($fqcn_parts);

Expand Down
2 changes: 1 addition & 1 deletion stubs/extensions/dom.phpstub
Original file line number Diff line number Diff line change
Expand Up @@ -975,7 +975,7 @@ class DOMXPath
public function evaluate(string $expression, ?DOMNode $contextNode = null, bool $registerNodeNS = true): mixed {}

/**
* @return DOMNodeList<DOMNode>|false
* @return DOMNodeList<DOMNode|DOMNameSpaceNode>|false
*/
public function query(string $expression, ?DOMNode $contextNode = null, bool $registerNodeNS = true): mixed {}

Expand Down
51 changes: 51 additions & 0 deletions tests/CodebaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PhpParser\Node\Stmt\Class_;
use Psalm\Codebase;
use Psalm\Context;
use Psalm\Exception\CodeException;
use Psalm\Exception\UnpopulatedClasslikeException;
use Psalm\Issue\InvalidReturnStatement;
use Psalm\Issue\InvalidReturnType;
Expand All @@ -21,6 +22,9 @@
use function array_map;
use function array_values;
use function get_class;
use function getcwd;

use const DIRECTORY_SEPARATOR;

class CodebaseTest extends TestCase
{
Expand Down Expand Up @@ -246,4 +250,51 @@ public static function beforeAddIssue(BeforeAddIssueEvent $event): ?bool
$this->analyzeFile('somefile.php', new Context);
self::assertSame(0, IssueBuffer::getErrorCount());
}
/**
* @test
*/
public function addingCodeIssueIsMarkedAsRedundant(): void
{
$this->expectException(CodeException::class);
$this->expectExceptionMessage('UnusedPsalmSuppress');

$this->addFile(
(string) getcwd() . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'somefile.php',
'<?php
namespace Psalm\CurrentTest;
/** @psalm-suppress InvalidReturnType */
function invalidReturnType(int $value): string
{
/** @psalm-suppress InvalidReturnStatement */
return $value;
}
echo invalidReturnType(123);
',
);
$eventHandler = new class implements BeforeAddIssueInterface
{
public static function beforeAddIssue(BeforeAddIssueEvent $event): ?bool
{
$issue = $event->getIssue();
if ($issue->code_location->file_path !== (string) getcwd() . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'somefile.php') {
return null;
}
if ($issue instanceof InvalidReturnStatement && $event->isFixable() === false) {
return false;
} elseif ($issue instanceof InvalidReturnType && $event->isFixable() === true) {
return false;
}
return null;
}
};

(new PluginRegistrationSocket($this->codebase->config, $this->codebase))
->registerHooksFromClass(get_class($eventHandler));

$this->analyzeFile(
(string) getcwd() . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'somefile.php',
new Context,
);
}
}
15 changes: 15 additions & 0 deletions tests/MatchTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,21 @@ function process(Obj1|Obj2 $obj): int|string
'ignored_issues' => [],
'php_version' => '8.0',
],
'nullCoalesce' => [
'code' => <<<'PHP'
<?php
function foo(): bool { return false; }
$match = match (foo()) {
false => null,
true => 1,
} ?? 2;
PHP,
'assertions' => [
'$match' => 'int',
],
'ignored_issues' => [],
'php_version' => '8.0',
],
];
}

Expand Down
75 changes: 75 additions & 0 deletions tests/PureAnnotationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,81 @@ function gimmeFoo(): MyEnum
return MyEnum::FOO();
}',
],
'pureThroughCallStaticInTrait' => [
'code' => '<?php
/**
* @method static static foo()
*/
trait TestTrait {
/** @psalm-pure */
public static function __callStatic(string $name, array $params): static
{
throw new BadMethodCallException("not implemented");
}
}
class Test {
use TestTrait;
}
/** @psalm-pure */
function gimmeFoo(): Test
{
return Test::foo();
}',
],
'pureThroughCallStaticInNestedTrait' => [
'code' => '<?php
/**
* @method static static foo()
*/
trait InnerTestTrait {
/** @psalm-pure */
public static function __callStatic(string $name, array $params): static
{
throw new BadMethodCallException("not implemented");
}
}
trait TestTrait {
use InnerTestTrait;
}
class Test {
use TestTrait;
}
/** @psalm-pure */
function gimmeFoo(): Test
{
return Test::foo();
}',
],
'pureThroughAliasedCallStaticInTrait' => [
'code' => '<?php
/**
* @method static static foo()
*/
trait TestTrait {
/** @psalm-pure */
public static function toBeCallStatic(string $name, array $params): static
{
throw new BadMethodCallException("not implemented");
}
}
class Test {
use TestTrait {
TestTrait::toBeCallStatic as __callStatic;
}
}
/** @psalm-pure */
function gimmeFoo(): Test
{
return Test::foo();
}',
],
'dontCrashWhileCheckingPurityOnCallStaticInATrait' => [
'code' => '<?php
/**
Expand Down

0 comments on commit 37cf82e

Please sign in to comment.