Update to PHPStan 2.x

This commit is contained in:
Dylan K. Taylor 2025-01-07 22:34:43 +00:00
parent d25ec58a6f
commit 9633b7d8a7
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
18 changed files with 876 additions and 344 deletions

View File

@ -52,9 +52,9 @@
"symfony/filesystem": "~6.4.0" "symfony/filesystem": "~6.4.0"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "1.11.11", "phpstan/phpstan": "2.1.1",
"phpstan/phpstan-phpunit": "^1.1.0", "phpstan/phpstan-phpunit": "^2.0.0",
"phpstan/phpstan-strict-rules": "^1.2.0", "phpstan/phpstan-strict-rules": "^2.0.0",
"phpunit/phpunit": "^10.5.24" "phpunit/phpunit": "^10.5.24"
}, },
"autoload": { "autoload": {

58
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "732102eca72dc1d29e7b67dfbce07653", "content-hash": "994ccffe45f066768542019f6f9d237b",
"packages": [ "packages": [
{ {
"name": "adhocore/json-comment", "name": "adhocore/json-comment",
@ -1386,20 +1386,20 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.11.11", "version": "2.1.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3" "reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/707c2aed5d8d0075666e673a5e71440c1d01a5a3", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7",
"reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3", "reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2|^8.0" "php": "^7.4|^8.0"
}, },
"conflict": { "conflict": {
"phpstan/phpstan-shim": "*" "phpstan/phpstan-shim": "*"
@ -1440,34 +1440,33 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-08-19T14:37:29+00:00" "time": "2025-01-05T16:43:48+00:00"
}, },
{ {
"name": "phpstan/phpstan-phpunit", "name": "phpstan/phpstan-phpunit",
"version": "1.4.0", "version": "2.0.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git", "url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "f3ea021866f4263f07ca3636bf22c64be9610c11" "reference": "e32ac656788a5bf3dedda89e6a2cad5643bf1a18"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/f3ea021866f4263f07ca3636bf22c64be9610c11", "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/e32ac656788a5bf3dedda89e6a2cad5643bf1a18",
"reference": "f3ea021866f4263f07ca3636bf22c64be9610c11", "reference": "e32ac656788a5bf3dedda89e6a2cad5643bf1a18",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2 || ^8.0", "php": "^7.4 || ^8.0",
"phpstan/phpstan": "^1.11" "phpstan/phpstan": "^2.0.4"
}, },
"conflict": { "conflict": {
"phpunit/phpunit": "<7.0" "phpunit/phpunit": "<7.0"
}, },
"require-dev": { "require-dev": {
"nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2", "php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-strict-rules": "^1.5.1", "phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^9.5" "phpunit/phpunit": "^9.6"
}, },
"type": "phpstan-extension", "type": "phpstan-extension",
"extra": { "extra": {
@ -1490,34 +1489,33 @@
"description": "PHPUnit extensions and rules for PHPStan", "description": "PHPUnit extensions and rules for PHPStan",
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues", "issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.4.0" "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.3"
}, },
"time": "2024-04-20T06:39:00+00:00" "time": "2024-12-19T09:14:43+00:00"
}, },
{ {
"name": "phpstan/phpstan-strict-rules", "name": "phpstan/phpstan-strict-rules",
"version": "1.6.0", "version": "2.0.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git", "url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "363f921dd8441777d4fc137deb99beb486c77df1" "reference": "ed6fea0ad4ad9c7e25f3ad2e7c4d420cf1e67fe3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/363f921dd8441777d4fc137deb99beb486c77df1", "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/ed6fea0ad4ad9c7e25f3ad2e7c4d420cf1e67fe3",
"reference": "363f921dd8441777d4fc137deb99beb486c77df1", "reference": "ed6fea0ad4ad9c7e25f3ad2e7c4d420cf1e67fe3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2 || ^8.0", "php": "^7.4 || ^8.0",
"phpstan/phpstan": "^1.11" "phpstan/phpstan": "^2.0.4"
}, },
"require-dev": { "require-dev": {
"nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2", "php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-deprecation-rules": "^1.1", "phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^9.5" "phpunit/phpunit": "^9.6"
}, },
"type": "phpstan-extension", "type": "phpstan-extension",
"extra": { "extra": {
@ -1539,9 +1537,9 @@
"description": "Extra strict and opinionated rules for PHPStan", "description": "Extra strict and opinionated rules for PHPStan",
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues", "issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.6.0" "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.1"
}, },
"time": "2024-04-20T06:37:51+00:00" "time": "2024-12-12T20:21:10+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",

View File

@ -1,6 +1,7 @@
includes: includes:
- tests/phpstan/analyse-for-current-php-version.neon.php - tests/phpstan/analyse-for-current-php-version.neon.php
- tests/phpstan/configs/actual-problems.neon - tests/phpstan/configs/actual-problems.neon
- tests/phpstan/configs/dependency-problems.neon
- tests/phpstan/configs/impossible-generics.neon - tests/phpstan/configs/impossible-generics.neon
- tests/phpstan/configs/php-bugs.neon - tests/phpstan/configs/php-bugs.neon
- tests/phpstan/configs/phpstan-bugs.neon - tests/phpstan/configs/phpstan-bugs.neon
@ -31,6 +32,7 @@ parameters:
paths: paths:
- build - build
- src - src
- tests/phpstan/DummyPluginOwned.php
- tests/phpstan/rules - tests/phpstan/rules
- tests/phpunit - tests/phpunit
- tests/plugins/TesterPlugin - tests/plugins/TesterPlugin

View File

@ -51,9 +51,8 @@ final class CommandStringHelper{
foreach($matches[0] as $k => $_){ foreach($matches[0] as $k => $_){
for($i = 1; $i <= 2; ++$i){ for($i = 1; $i <= 2; ++$i){
if($matches[$i][$k] !== ""){ if($matches[$i][$k] !== ""){
/** @var string $match */ //phpstan can't understand preg_match and friends by itself :(
$match = $matches[$i][$k]; $match = $matches[$i][$k];
$args[(int) $k] = preg_replace('/\\\\([\\\\"])/u', '$1', $match) ?? throw new AssumptionFailedError(preg_last_error_msg()); $args[] = preg_replace('/\\\\([\\\\"])/u', '$1', $match) ?? throw new AssumptionFailedError(preg_last_error_msg());
break; break;
} }
} }

View File

@ -167,6 +167,7 @@ final class Utils{
/** /**
* @phpstan-return \Closure(object) : object * @phpstan-return \Closure(object) : object
* @deprecated
*/ */
public static function cloneCallback() : \Closure{ public static function cloneCallback() : \Closure{
return static function(object $o){ return static function(object $o){
@ -179,15 +180,13 @@ final class Utils{
* @phpstan-template TValue of object * @phpstan-template TValue of object
* *
* @param object[] $array * @param object[] $array
* @phpstan-param array<TKey, TValue> $array * @phpstan-param array<TKey, TValue>|list<TValue> $array
* *
* @return object[] * @return object[]
* @phpstan-return array<TKey, TValue> * @phpstan-return ($array is list<TValue> ? list<TValue> : array<TKey, TValue>)
*/ */
public static function cloneObjectArray(array $array) : array{ public static function cloneObjectArray(array $array) : array{
/** @phpstan-var \Closure(TValue) : TValue $callback */ return array_map(fn(object $o) => clone $o, $array);
$callback = self::cloneCallback();
return array_map($callback, $array);
} }
/** /**

View File

@ -215,6 +215,9 @@ abstract class RegionWorldProvider extends BaseWorldProvider{
return null; return null;
} }
/**
* @phpstan-return \RegexIterator<mixed, string, \FilesystemIterator>
*/
private function createRegionIterator() : \RegexIterator{ private function createRegionIterator() : \RegexIterator{
return new \RegexIterator( return new \RegexIterator(
new \FilesystemIterator( new \FilesystemIterator(

View File

@ -117,7 +117,7 @@ final class FlatGeneratorOptions{
} }
} }
} }
$options[(string) $option] = $params; $options[$option] = $params;
} }
return new self($structure, $biomeId, $options); return new self($structure, $biomeId, $options);
} }

View File

@ -0,0 +1,28 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\plugin;
class DummyPluginOwned{
use PluginOwnedTrait;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,73 @@
parameters:
ignoreErrors:
-
message: '#^Method pocketmine\\network\\mcpe\\convert\\BlockStateDictionary\:\:loadPaletteFromString\(\) should return list\<pocketmine\\data\\bedrock\\block\\BlockStateData\> but returns array\<pocketmine\\data\\bedrock\\block\\BlockStateData\>\.$#'
identifier: return.type
count: 1
path: ../../../src/network/mcpe/convert/BlockStateDictionary.php
-
message: '#^Parameter \#3 \$entityNBT of class pocketmine\\world\\format\\io\\ChunkData constructor expects list\<pocketmine\\nbt\\tag\\CompoundTag\>, array\<pocketmine\\nbt\\tag\\CompoundTag\> given\.$#'
identifier: argument.type
count: 1
path: ../../../src/world/format/io/leveldb/LevelDB.php
-
message: '#^Parameter \#4 \$tileNBT of class pocketmine\\world\\format\\io\\ChunkData constructor expects list\<pocketmine\\nbt\\tag\\CompoundTag\>, array\<pocketmine\\nbt\\tag\\CompoundTag\> given\.$#'
identifier: argument.type
count: 1
path: ../../../src/world/format/io/leveldb/LevelDB.php
-
message: '#^Parameter \#1 \$array of static method pocketmine\\world\\format\\io\\ChunkUtils\:\:convertBiomeColors\(\) expects list\<int\>, array\<int\> given\.$#'
identifier: argument.type
count: 1
path: ../../../src/world/format/io/region/Anvil.php
-
message: '#^Parameter \#1 \$array of static method pocketmine\\world\\format\\io\\ChunkUtils\:\:convertBiomeColors\(\) expects list\<int\>, array\<int\> given\.$#'
identifier: argument.type
count: 1
path: ../../../src/world/format/io/region/McRegion.php
-
message: '#^Parameter \#1 \$array of static method pocketmine\\world\\format\\io\\ChunkUtils\:\:convertBiomeColors\(\) expects list\<int\>, array\<int\> given\.$#'
identifier: argument.type
count: 1
path: ../../../src/world/format/io/region/PMAnvil.php
-
message: '#^Parameter \#1 \$oldNewStateList of function pocketmine\\tools\\blockstate_upgrade_schema_utils\\buildUpgradeTableFromData expects list\<pocketmine\\nbt\\TreeRoot\>, array\<pocketmine\\nbt\\TreeRoot\> given\.$#'
identifier: argument.type
count: 1
path: ../../../tools/blockstate-upgrade-schema-utils.php
-
message: '#^Parameter \#1 \$input of class pocketmine\\crafting\\json\\ShapelessRecipeData constructor expects list\<pocketmine\\crafting\\json\\RecipeIngredientData\>, array\<pocketmine\\crafting\\json\\RecipeIngredientData\> given\.$#'
identifier: argument.type
count: 1
path: ../../../tools/generate-bedrock-data-from-packets.php
-
message: '#^Parameter \#2 \$output of class pocketmine\\crafting\\json\\ShapelessRecipeData constructor expects list\<pocketmine\\crafting\\json\\ItemStackData\>, array\<pocketmine\\crafting\\json\\ItemStackData\> given\.$#'
identifier: argument.type
count: 1
path: ../../../tools/generate-bedrock-data-from-packets.php
-
message: '#^Parameter \#3 \$output of class pocketmine\\crafting\\json\\ShapedRecipeData constructor expects list\<pocketmine\\crafting\\json\\ItemStackData\>, array\<pocketmine\\crafting\\json\\ItemStackData\> given\.$#'
identifier: argument.type
count: 1
path: ../../../tools/generate-bedrock-data-from-packets.php
-
message: '#^Parameter \#5 \$unlockingIngredients of class pocketmine\\crafting\\json\\ShapelessRecipeData constructor expects list\<pocketmine\\crafting\\json\\RecipeIngredientData\>, array\<pocketmine\\crafting\\json\\RecipeIngredientData\> given\.$#'
identifier: argument.type
count: 1
path: ../../../tools/generate-bedrock-data-from-packets.php
-
message: '#^Parameter \#6 \$unlockingIngredients of class pocketmine\\crafting\\json\\ShapedRecipeData constructor expects list\<pocketmine\\crafting\\json\\RecipeIngredientData\>, array\<pocketmine\\crafting\\json\\RecipeIngredientData\> given\.$#'
identifier: argument.type
count: 1
path: ../../../tools/generate-bedrock-data-from-packets.php

View File

@ -1,12 +1,14 @@
parameters: parameters:
ignoreErrors: ignoreErrors:
- -
message: "#^Method pocketmine\\\\event\\\\RegisteredListener\\:\\:__construct\\(\\) has parameter \\$handler with no signature specified for Closure\\.$#" message: '#^Method pocketmine\\event\\RegisteredListener\:\:__construct\(\) has parameter \$handler with no signature specified for Closure\.$#'
identifier: missingType.callable
count: 1 count: 1
path: ../../../src/event/RegisteredListener.php path: ../../../src/event/RegisteredListener.php
- -
message: "#^Method pocketmine\\\\event\\\\RegisteredListener\\:\\:getHandler\\(\\) return type has no signature specified for Closure\\.$#" message: '#^Method pocketmine\\event\\RegisteredListener\:\:getHandler\(\) return type has no signature specified for Closure\.$#'
identifier: missingType.callable
count: 1 count: 1
path: ../../../src/event/RegisteredListener.php path: ../../../src/event/RegisteredListener.php

View File

@ -1,112 +1,260 @@
parameters: parameters:
ignoreErrors: ignoreErrors:
- -
message: "#^Method pocketmine\\\\block\\\\DoubleTallGrass\\:\\:traitGetDropsForIncompatibleTool\\(\\) return type has no value type specified in iterable type array\\.$#" message: '#^Access to an undefined property object\:\:\$crashId\.$#'
identifier: property.notFound
count: 1
path: ../../../src/Server.php
-
message: '#^Access to an undefined property object\:\:\$crashUrl\.$#'
identifier: property.notFound
count: 1
path: ../../../src/Server.php
-
message: '#^Access to an undefined property object\:\:\$error\.$#'
identifier: property.notFound
count: 1
path: ../../../src/Server.php
-
message: '#^Method pocketmine\\block\\Block\:\:readStateFromWorld\(\) is marked as impure but does not have any side effects\.$#'
identifier: impureMethod.pure
count: 1
path: ../../../src/block/Block.php
-
message: '#^Method pocketmine\\block\\DoubleTallGrass\:\:traitGetDropsForIncompatibleTool\(\) return type has no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1 count: 1
path: ../../../src/block/DoubleTallGrass.php path: ../../../src/block/DoubleTallGrass.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:ACACIA_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:ACACIA_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:BIRCH_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:BIRCH_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:CHERRY_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:CHERRY_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:CRIMSON_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:CRIMSON_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:DARK_OAK_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:DARK_OAK_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:JUNGLE_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:JUNGLE_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:MANGROVE_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:MANGROVE_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:OAK_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:OAK_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:PALE_OAK_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:PALE_OAK_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:SPRUCE_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:SPRUCE_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:WARPED_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:WARPED_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Call to function assert\\(\\) with false and 'unknown hit type' will always evaluate to false\\.$#" message: '#^Strict comparison using \=\=\= between \*NEVER\* and 5 will always evaluate to false\.$#'
identifier: identical.alwaysFalse
count: 1
path: ../../../src/command/defaults/TeleportCommand.php
-
message: '#^Method pocketmine\\crafting\\ShapedRecipe\:\:getIngredientMap\(\) should return list\<list\<pocketmine\\crafting\\RecipeIngredient\|null\>\> but returns array\<int\<0, max\>, non\-empty\-array\<int\<0, max\>, pocketmine\\crafting\\RecipeIngredient\|null\>\>\.$#'
identifier: return.type
count: 1
path: ../../../src/crafting/ShapedRecipe.php
-
message: '#^Property pocketmine\\crash\\CrashDumpData\:\:\$parameters \(list\<string\>\) does not accept array\.$#'
identifier: assign.propertyType
count: 1
path: ../../../src/crash/CrashDump.php
-
message: '#^Call to function assert\(\) with false and ''unknown hit type'' will always evaluate to false\.$#'
identifier: function.impossibleType
count: 1 count: 1
path: ../../../src/entity/projectile/Projectile.php path: ../../../src/entity/projectile/Projectile.php
- -
message: "#^Property pocketmine\\\\network\\\\mcpe\\\\raklib\\\\PthreadsChannelWriter\\:\\:\\$buffer is never read, only written\\.$#" message: '#^Property pocketmine\\item\\WritableBookBase\:\:\$pages \(list\<pocketmine\\item\\WritableBookPage\>\) does not accept non\-empty\-array\<int, pocketmine\\item\\WritableBookPage\>\.$#'
identifier: assign.propertyType
count: 1
path: ../../../src/item/WritableBookBase.php
-
message: '#^Method pocketmine\\network\\mcpe\\compression\\ZlibCompressor\:\:getNetworkId\(\) never returns 1 so it can be removed from the return type\.$#'
identifier: return.unusedType
count: 1
path: ../../../src/network/mcpe/compression/ZlibCompressor.php
-
message: '#^Method pocketmine\\network\\mcpe\\compression\\ZlibCompressor\:\:getNetworkId\(\) never returns 255 so it can be removed from the return type\.$#'
identifier: return.unusedType
count: 1
path: ../../../src/network/mcpe/compression/ZlibCompressor.php
-
message: '#^Parameter \#1 \$states of class pocketmine\\network\\mcpe\\convert\\BlockStateDictionary constructor expects list\<pocketmine\\network\\mcpe\\convert\\BlockStateDictionaryEntry\>, array\<int\<0, max\>, pocketmine\\network\\mcpe\\convert\\BlockStateDictionaryEntry\> given\.$#'
identifier: argument.type
count: 1
path: ../../../src/network/mcpe/convert/BlockStateDictionary.php
-
message: '#^Cannot access offset ''default'' on mixed\.$#'
identifier: offsetAccess.nonOffsetAccessible
count: 1
path: ../../../src/network/mcpe/convert/LegacySkinAdapter.php
-
message: '#^Property pocketmine\\network\\mcpe\\raklib\\PthreadsChannelWriter\:\:\$buffer is never read, only written\.$#'
identifier: property.onlyWritten
count: 1 count: 1
path: ../../../src/network/mcpe/raklib/PthreadsChannelWriter.php path: ../../../src/network/mcpe/raklib/PthreadsChannelWriter.php
- -
message: "#^Property pocketmine\\\\network\\\\mcpe\\\\raklib\\\\SnoozeAwarePthreadsChannelWriter\\:\\:\\$buffer is never read, only written\\.$#" message: '#^Property pocketmine\\network\\mcpe\\raklib\\SnoozeAwarePthreadsChannelWriter\:\:\$buffer is never read, only written\.$#'
identifier: property.onlyWritten
count: 1 count: 1
path: ../../../src/network/mcpe/raklib/SnoozeAwarePthreadsChannelWriter.php path: ../../../src/network/mcpe/raklib/SnoozeAwarePthreadsChannelWriter.php
- -
message: "#^Dead catch \\- RuntimeException is never thrown in the try block\\.$#" message: '#^Dead catch \- RuntimeException is never thrown in the try block\.$#'
identifier: catch.neverThrown
count: 1 count: 1
path: ../../../src/plugin/PluginManager.php path: ../../../src/plugin/PluginManager.php
- -
message: "#^Method pocketmine\\\\timings\\\\TimingsHandler\\:\\:lazyGetSet\\(\\) should return pocketmine\\\\utils\\\\ObjectSet\\<T of object\\> but returns pocketmine\\\\utils\\\\ObjectSet\\<object\\>\\.$#" message: '#^Binary operation "\." between mixed and ''/''\|''\\\\'' results in an error\.$#'
identifier: binaryOp.invalid
count: 1
path: ../../../src/thread/ThreadSafeClassLoader.php
-
message: '#^Binary operation "\." between mixed and non\-falsy\-string results in an error\.$#'
identifier: binaryOp.invalid
count: 1
path: ../../../src/thread/ThreadSafeClassLoader.php
-
message: '#^Method pocketmine\\timings\\TimingsHandler\:\:lazyGetSet\(\) should return pocketmine\\utils\\ObjectSet\<T of object\> but returns pocketmine\\utils\\ObjectSet\<object\>\.$#'
identifier: return.type
count: 1 count: 1
path: ../../../src/timings/TimingsHandler.php path: ../../../src/timings/TimingsHandler.php
- -
message: "#^Casting to int something that's already int\\.$#" message: '#^Parameter &\$where @param\-out type of method pocketmine\\timings\\TimingsHandler\:\:lazyGetSet\(\) expects pocketmine\\utils\\ObjectSet\<T of object\>, pocketmine\\utils\\ObjectSet\<object\> given\.$#'
identifier: paramOut.type
count: 1
path: ../../../src/timings/TimingsHandler.php
-
message: '#^Binary operation "\*" between mixed and 3600 results in an error\.$#'
identifier: binaryOp.invalid
count: 1
path: ../../../src/utils/Timezone.php
-
message: '#^Binary operation "\*" between mixed and 60 results in an error\.$#'
identifier: binaryOp.invalid
count: 1
path: ../../../src/utils/Timezone.php
-
message: '#^Binary operation "\+" between \(float\|int\) and mixed results in an error\.$#'
identifier: binaryOp.invalid
count: 1
path: ../../../src/utils/Timezone.php
-
message: '#^Property pocketmine\\world\\format\\io\\region\\RegionLoader\:\:\$locationTable \(list\<pocketmine\\world\\format\\io\\region\\RegionLocationTableEntry\|null\>\) does not accept non\-empty\-array\<int, pocketmine\\world\\format\\io\\region\\RegionLocationTableEntry\|null\>\.$#'
identifier: assign.propertyType
count: 2
path: ../../../src/world/format/io/region/RegionLoader.php
-
message: '#^Property pocketmine\\world\\format\\io\\region\\RegionLoader\:\:\$locationTable \(list\<pocketmine\\world\\format\\io\\region\\RegionLocationTableEntry\|null\>\) does not accept non\-empty\-array\<int\<0, max\>, pocketmine\\world\\format\\io\\region\\RegionLocationTableEntry\|null\>\.$#'
identifier: assign.propertyType
count: 3
path: ../../../src/world/format/io/region/RegionLoader.php
-
message: '#^Method pocketmine\\world\\format\\io\\region\\RegionWorldProvider\:\:createRegionIterator\(\) should return RegexIterator\<mixed, string, FilesystemIterator\> but returns RegexIterator\<mixed, mixed, Traversable\<TKey, TValue\>\>\.$#'
identifier: return.type
count: 1
path: ../../../src/world/format/io/region/RegionWorldProvider.php
-
message: '#^Casting to int something that''s already int\.$#'
identifier: cast.useless
count: 1 count: 1
path: ../../../src/world/generator/normal/Normal.php path: ../../../src/world/generator/normal/Normal.php
- -
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertFalse\\(\\) with false will always evaluate to true\\.$#" message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertFalse\(\) with false will always evaluate to true\.$#'
identifier: staticMethod.alreadyNarrowedType
count: 1 count: 1
path: ../../phpunit/promise/PromiseTest.php path: ../../phpunit/promise/PromiseTest.php
- -
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertTrue\\(\\) with false and 'All promise should…' will always evaluate to false\\.$#" message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertTrue\(\) with false and ''All promise should…'' will always evaluate to false\.$#'
identifier: staticMethod.impossibleType
count: 1 count: 1
path: ../../phpunit/promise/PromiseTest.php path: ../../phpunit/promise/PromiseTest.php
- -
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertTrue\\(\\) with false will always evaluate to false\\.$#" message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertTrue\(\) with false will always evaluate to false\.$#'
identifier: staticMethod.impossibleType
count: 2 count: 2
path: ../../phpunit/promise/PromiseTest.php path: ../../phpunit/promise/PromiseTest.php
- -
message: "#^Strict comparison using \\=\\=\\= between 0 and 0 will always evaluate to true\\.$#" message: '#^Strict comparison using \=\=\= between 0 and 0 will always evaluate to true\.$#'
identifier: identical.alwaysTrue
count: 1 count: 1
path: ../rules/UnsafeForeachArrayOfStringRule.php path: ../rules/UnsafeForeachArrayOfStringRule.php

View File

@ -1,22 +1,32 @@
parameters: parameters:
ignoreErrors: ignoreErrors:
- -
message: "#^Cannot call method collectGarbage\\(\\) on pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\.$#" message: '#^Cannot call method collectGarbage\(\) on pocketmine\\world\\format\\SubChunk\|null\.$#'
identifier: method.nonObject
count: 1 count: 1
path: ../../../src/world/format/Chunk.php path: ../../../src/world/format/Chunk.php
- -
message: "#^Method pocketmine\\\\world\\\\format\\\\Chunk\\:\\:getSubChunks\\(\\) should return array\\<int, pocketmine\\\\world\\\\format\\\\SubChunk\\> but returns array\\<int, pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\>\\.$#" message: '#^Method pocketmine\\world\\format\\Chunk\:\:getSubChunks\(\) should return array\<int, pocketmine\\world\\format\\SubChunk\> but returns array\<int, pocketmine\\world\\format\\SubChunk\|null\>\.$#'
identifier: return.type
count: 1 count: 1
path: ../../../src/world/format/Chunk.php path: ../../../src/world/format/Chunk.php
- -
message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\)\\: mixed\\)\\|null, Closure\\(pocketmine\\\\world\\\\format\\\\SubChunk\\)\\: pocketmine\\\\world\\\\format\\\\SubChunk given\\.$#" message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(pocketmine\\world\\format\\SubChunk\|null\)\: mixed\)\|null, Closure\(pocketmine\\world\\format\\SubChunk\)\: pocketmine\\world\\format\\SubChunk given\.$#'
identifier: argument.type
count: 1 count: 1
path: ../../../src/world/format/Chunk.php path: ../../../src/world/format/Chunk.php
- -
message: "#^Method pocketmine\\\\world\\\\format\\\\HeightArray\\:\\:getValues\\(\\) should return non\\-empty\\-array\\<int, int\\> but returns array\\<int, int\\|null\\>\\.$#" message: '#^Method pocketmine\\world\\format\\HeightArray\:\:getValues\(\) should return non\-empty\-list\<int\> but returns array\<int, int\|null\>\.$#'
identifier: return.type
count: 1 count: 1
path: ../../../src/world/format/HeightArray.php path: ../../../src/world/format/HeightArray.php
-
message: '#^Offset int might not exist on SplFixedArray\<float\>\|null\.$#'
identifier: offsetAccess.notFound
count: 4
path: ../../../src/world/generator/noise/Noise.php

View File

@ -28,7 +28,6 @@ use PhpParser\Node\Expr\StaticCall;
use PHPStan\Analyser\Scope; use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule; use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\TypeWithClassName;
use pocketmine\utils\LegacyEnumShimTrait; use pocketmine\utils\LegacyEnumShimTrait;
use function sprintf; use function sprintf;
@ -51,18 +50,15 @@ final class DeprecatedLegacyEnumAccessRule implements Rule{
$scope->resolveTypeByName($node->class) : $scope->resolveTypeByName($node->class) :
$scope->getType($node->class); $scope->getType($node->class);
if(!$classType instanceof TypeWithClassName){ $errors = [];
return []; $reflections = $classType->getObjectClassReflections();
} foreach($reflections as $reflection){
if(!$reflection->hasTraitUse(LegacyEnumShimTrait::class) || !$reflection->implementsInterface(\UnitEnum::class)){
continue;
}
$reflection = $classType->getClassReflection(); if(!$reflection->hasNativeMethod($caseName)){
if($reflection === null || !$reflection->hasTraitUse(LegacyEnumShimTrait::class) || !$reflection->implementsInterface(\UnitEnum::class)){ $errors[] = RuleErrorBuilder::message(sprintf(
return [];
}
if(!$reflection->hasNativeMethod($caseName)){
return [
RuleErrorBuilder::message(sprintf(
'Use of legacy enum case accessor %s::%s().', 'Use of legacy enum case accessor %s::%s().',
$reflection->getName(), $reflection->getName(),
$caseName $caseName
@ -70,10 +66,11 @@ final class DeprecatedLegacyEnumAccessRule implements Rule{
'Access the enum constant directly instead (remove the brackets), e.g. %s::%s', 'Access the enum constant directly instead (remove the brackets), e.g. %s::%s',
$reflection->getName(), $reflection->getName(),
$caseName $caseName
))->build() ))->identifier('pocketmine.enum.deprecatedAccessor')
]; ->build();
}
} }
return []; return $errors;
} }
} }

View File

@ -30,7 +30,6 @@ use PhpParser\Node\Expr\BinaryOp\NotIdentical;
use PHPStan\Analyser\Scope; use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule; use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type; use PHPStan\Type\Type;
use PHPStan\Type\UnionType; use PHPStan\Type\UnionType;
use PHPStan\Type\VerbosityLevel; use PHPStan\Type\VerbosityLevel;
@ -61,7 +60,7 @@ class DisallowEnumComparisonRule implements Rule{
$node instanceof Identical ? '===' : '!==', $node instanceof Identical ? '===' : '!==',
$leftType->describe(VerbosityLevel::value()), $leftType->describe(VerbosityLevel::value()),
$rightType->describe(VerbosityLevel::value()) $rightType->describe(VerbosityLevel::value())
))->build()]; ))->identifier('pocketmine.enum.badComparison')->build()];
} }
return []; return [];
} }
@ -69,7 +68,7 @@ class DisallowEnumComparisonRule implements Rule{
private function checkForEnumTypes(Type $comparedType) : bool{ private function checkForEnumTypes(Type $comparedType) : bool{
//TODO: what we really want to do here is iterate over the contained types, but there's no universal way to //TODO: what we really want to do here is iterate over the contained types, but there's no universal way to
//do that. This might break with other circumstances. //do that. This might break with other circumstances.
if($comparedType instanceof ObjectType){ if($comparedType->isObject()->yes()){
$types = [$comparedType]; $types = [$comparedType];
}elseif($comparedType instanceof UnionType){ }elseif($comparedType instanceof UnionType){
$types = $comparedType->getTypes(); $types = $comparedType->getTypes();
@ -77,12 +76,14 @@ class DisallowEnumComparisonRule implements Rule{
return false; return false;
} }
foreach($types as $containedType){ foreach($types as $containedType){
if(!($containedType instanceof ObjectType)){ if(!($containedType->isObject()->yes())){
continue; continue;
} }
$class = $containedType->getClassReflection(); $classes = $containedType->getObjectClassReflections();
if($class !== null && $class->hasTraitUse(EnumTrait::class)){ foreach($classes as $class){
return true; if($class->hasTraitUse(EnumTrait::class)){
return true;
}
} }
} }
return false; return false;

View File

@ -44,6 +44,7 @@ final class DisallowForeachByReferenceRule implements Rule{
return [ return [
RuleErrorBuilder::message("Foreach by-reference is not allowed, because it has surprising behaviour.") RuleErrorBuilder::message("Foreach by-reference is not allowed, because it has surprising behaviour.")
->tip("If the value variable is used outside of the foreach construct (e.g. in a second foreach), the iterable's contents will be unexpectedly altered.") ->tip("If the value variable is used outside of the foreach construct (e.g. in a second foreach), the iterable's contents will be unexpectedly altered.")
->identifier('pocketmine.foreach.byRef')
->build() ->build()
]; ];
} }

View File

@ -101,7 +101,7 @@ final class UnsafeForeachArrayOfStringRule implements Rule{
RuleErrorBuilder::message(sprintf( RuleErrorBuilder::message(sprintf(
"Unsafe foreach on array with key type %s (they might be casted to int).", "Unsafe foreach on array with key type %s (they might be casted to int).",
$iterableType->getIterableKeyType()->describe(VerbosityLevel::value()) $iterableType->getIterableKeyType()->describe(VerbosityLevel::value())
))->tip($tip)->build() ))->tip($tip)->identifier('pocketmine.foreach.stringKeys')->build()
]; ];
} }
return []; return [];

View File

@ -23,6 +23,6 @@ declare(strict_types=1);
namespace pocketmine\utils\fixtures; namespace pocketmine\utils\fixtures;
trait TestTrait{ trait TestTrait{ // @phpstan-ignore trait.unused
} }