diff --git a/.github/workflows/codestyle.yml b/.github/workflows/codestyle.yml index 6385049..b12941f 100644 --- a/.github/workflows/codestyle.yml +++ b/.github/workflows/codestyle.yml @@ -5,7 +5,7 @@ on: pull_request: jobs: - psalm: + phpcs: name: Code Style runs-on: ${{ matrix.os }} strategy: diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 939d4a2..0c22d6f 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -4,7 +4,7 @@ on: push: pull_request: schedule: - - cron: '0 0 * * *' + - cron: '0 0 * * 0' jobs: security: @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ '8.3' ] + php: [ '8.4' ] os: [ ubuntu-latest ] steps: - name: Set Git To Use LF diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 3fccdb7..27fc95e 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -5,13 +5,13 @@ on: pull_request: jobs: - psalm: - name: Psalm + linter: + name: Linter runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - php: [ '8.3' ] + php: [ '8.4' ] os: [ ubuntu-latest ] steps: - name: Set Git To Use LF diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0ef0d03..485da25 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,7 +4,7 @@ on: push: pull_request: schedule: - - cron: '0 0 * * *' + - cron: '0 0 * * 0' jobs: tests: @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ '8.1', '8.2', '8.3' ] + php: [ '8.1', '8.2', '8.3', '8.4' ] os: [ ubuntu-latest, macos-latest, windows-latest ] stability: [ prefer-lowest, prefer-stable ] steps: diff --git a/composer.json b/composer.json index 4df3f53..737882e 100644 --- a/composer.json +++ b/composer.json @@ -19,11 +19,9 @@ "require-dev": { "friendsofphp/php-cs-fixer": "^3.53", "jetbrains/phpstorm-attributes": "^1.0", - "phpstan/extension-installer": "^1.3", - "phpstan/phpstan": "^1.11", - "phpstan/phpstan-strict-rules": "^1.6", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^10.5|^11.0", - "rector/rector": "^1.0", "type-lang/parser": "^1.0" }, "autoload-dev": { @@ -33,8 +31,8 @@ }, "extra": { "branch-alias": { - "dev-master": "1.0.x-dev", - "dev-main": "1.0.x-dev" + "dev-master": "1.x-dev", + "dev-main": "1.x-dev" } }, "config": { @@ -44,9 +42,6 @@ "optimize-autoloader": true, "preferred-install": { "*": "dist" - }, - "allow-plugins": { - "phpstan/extension-installer": true } }, "scripts": { @@ -60,11 +55,7 @@ "phpcs": "@phpcs:check", "phpcs:check": "php-cs-fixer fix --config=.php-cs-fixer.php --allow-risky=yes --dry-run --verbose --diff", - "phpcs:fix": "php-cs-fixer fix --config=.php-cs-fixer.php --allow-risky=yes --verbose --diff", - - "rector": "@rector:check", - "rector:check": "rector --dry-run --config=rector.php", - "rector:fix": "rector --config=rector.php" + "phpcs:fix": "php-cs-fixer fix --config=.php-cs-fixer.php --allow-risky=yes --verbose --diff" }, "minimum-stability": "dev", "prefer-stable": true diff --git a/phpstan.neon b/phpstan.neon index 73151ee..3be61f5 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,8 @@ includes: - phar://phpstan.phar/conf/bleedingEdge.neon + - vendor/phpstan/phpstan-strict-rules/rules.neon parameters: - level: 9 + level: max strictRules: allRules: true fileExtensions: diff --git a/rector.php b/rector.php deleted file mode 100644 index a68f327..0000000 --- a/rector.php +++ /dev/null @@ -1,15 +0,0 @@ -paths([__DIR__ . '/src']); - $config->cacheDirectory(__DIR__ . '/vendor/.cache.rector'); - - $config->sets([ - LevelSetList::UP_TO_PHP_81, - ]); -}; diff --git a/src/Exception/ParsingException.php b/src/Exception/ParsingException.php index 810a820..5d2a6b4 100644 --- a/src/Exception/ParsingException.php +++ b/src/Exception/ParsingException.php @@ -45,8 +45,8 @@ public function withSource(string $source, int $offset): self return new static( source: $source, offset: $offset, - message: $this->message, - code: $this->code, + message: $this->getMessage(), + code: $this->getCode(), previous: $this, ); } diff --git a/src/Parser.php b/src/Parser.php index ef4fb67..74ed185 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -42,11 +42,7 @@ public function parse(#[Language('PHP')] string $docblock): DocBlock $mapper = new SourceMap(); try { - /** - * @var Segment $segment - * - * @psalm-suppress InvalidIterator - */ + /** @var Segment $segment */ foreach ($result = $this->analyze($docblock) as $segment) { $mapper->add($segment->offset, $segment->text); } @@ -72,7 +68,6 @@ public function parse(#[Language('PHP')] string $docblock): DocBlock */ private function analyze(string $docblock): \Generator { - /** @psalm-suppress InvalidIterator */ yield from $blocks = $this->groupByCommentSections($docblock); $description = null; diff --git a/src/Parser/Comment/RegexCommentParser.php b/src/Parser/Comment/RegexCommentParser.php index 0cf870b..c6eed7d 100644 --- a/src/Parser/Comment/RegexCommentParser.php +++ b/src/Parser/Comment/RegexCommentParser.php @@ -79,18 +79,18 @@ private function lex(string $docblock): iterable * Returns significant parts of the DocBlock comment with their offsets of * the returned section, relative to the beginning. * - * ```php + * ``` * $result = $reader->read(<<<'DOC' * /** * * Example line 1 * * - * * @tag1 type Description of tag1 - * *∕ + * * @​tag1 type Description of tag1 + * *​/ * DOC); * * // The $result contains: * // object(Segment) { offset: 7, text: 'Example line 1' } - * // object(Segment) { offset: 28, text: '@tag1 type Description of tag1' } + * // object(Segment) { offset: 28, text: '@​tag1 type Description of tag1' } * ``` * * @return iterable diff --git a/src/Parser/Description/DescriptionParserInterface.php b/src/Parser/Description/DescriptionParserInterface.php index 2fc19e7..e448af5 100644 --- a/src/Parser/Description/DescriptionParserInterface.php +++ b/src/Parser/Description/DescriptionParserInterface.php @@ -13,7 +13,7 @@ interface DescriptionParserInterface * * ```php * $description = $parser->parse(<<<'DOC' - * This is a description with {@link Example}. + * This is a description with {@​link Example}. * DOC); * * // $description MAY contains: diff --git a/src/Parser/Tag/TagParserInterface.php b/src/Parser/Tag/TagParserInterface.php index b8d6616..9942973 100644 --- a/src/Parser/Tag/TagParserInterface.php +++ b/src/Parser/Tag/TagParserInterface.php @@ -12,8 +12,8 @@ interface TagParserInterface /** * Returns concrete tag instance by the tag signature. * - * ```php - * $tag = $parser->parse('@param string $tag'); + * ``` + * $tag = $parser->parse('@​param string $tag'); * * // $tag may contains: * // object(ParamTag) { diff --git a/src/Tag/Content.php b/src/Tag/Content.php index 623a7be..366fac4 100644 --- a/src/Tag/Content.php +++ b/src/Tag/Content.php @@ -24,8 +24,6 @@ class Content implements \Stringable /** * @var int<0, max> - * - * @psalm-readonly-allow-private-mutation */ public int $offset = 0; diff --git a/src/Tag/Content/OptionalIdentifierApplicator.php b/src/Tag/Content/OptionalIdentifierApplicator.php index 0742dba..4e877e4 100644 --- a/src/Tag/Content/OptionalIdentifierApplicator.php +++ b/src/Tag/Content/OptionalIdentifierApplicator.php @@ -16,13 +16,14 @@ final class OptionalIdentifierApplicator extends Applicator */ public function __invoke(Content $lexer): ?string { + // @phpstan-ignore-next-line : PHPStan false positive if ($lexer->value === '') { return null; } \preg_match('/([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\b/u', $lexer->value, $matches); - if (\count($matches) !== 2 || $matches[1] === '') { + if (\count($matches) !== 2) { return null; } diff --git a/src/Tag/Content/OptionalVariableNameApplicator.php b/src/Tag/Content/OptionalVariableNameApplicator.php index 55e9e69..0710677 100644 --- a/src/Tag/Content/OptionalVariableNameApplicator.php +++ b/src/Tag/Content/OptionalVariableNameApplicator.php @@ -22,7 +22,7 @@ public function __invoke(Content $lexer): ?string \preg_match('/\$([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\b/u', $lexer->value, $matches); - if (\count($matches) !== 2 || $matches[1] === '') { + if (\count($matches) !== 2) { return null; } diff --git a/src/Tag/Content/TypeParserApplicator.php b/src/Tag/Content/TypeParserApplicator.php index 0e10c0e..d3b0c3e 100644 --- a/src/Tag/Content/TypeParserApplicator.php +++ b/src/Tag/Content/TypeParserApplicator.php @@ -32,7 +32,6 @@ public function __invoke(Content $lexer): TypeStatement try { $type = $this->parser->parse($lexer->value); } catch (ParserExceptionInterface $e) { - /** @psalm-suppress InvalidArgument */ throw $lexer->getTagException( message: \sprintf('Tag @%s contains an incorrect type', $this->tag), previous: $e, diff --git a/src/Tag/Description/DescriptionInterface.php b/src/Tag/Description/DescriptionInterface.php index ef0a3ec..20d65a1 100644 --- a/src/Tag/Description/DescriptionInterface.php +++ b/src/Tag/Description/DescriptionInterface.php @@ -16,8 +16,6 @@ interface DescriptionInterface extends \Stringable * Magic method {@link https://www.php.net/manual/en/language.oop5.magic.php#object.tostring} * allows a class to decide how it will react when it is treated like * a string. - * - * @psalm-immutable Each call to the method must return the same value. */ public function __toString(): string; } diff --git a/src/Tag/Description/DescriptionProviderInterface.php b/src/Tag/Description/DescriptionProviderInterface.php index 3319b4a..c91b92f 100644 --- a/src/Tag/Description/DescriptionProviderInterface.php +++ b/src/Tag/Description/DescriptionProviderInterface.php @@ -9,8 +9,6 @@ interface DescriptionProviderInterface extends OptionalDescriptionProviderInterf /** * Returns description object which can be represented as a string and * contains additional information. - * - * @psalm-immutable Each call to the method must return the same value. */ public function getDescription(): DescriptionInterface; } diff --git a/src/Tag/Description/OptionalDescriptionProviderInterface.php b/src/Tag/Description/OptionalDescriptionProviderInterface.php index c93a60f..0de030c 100644 --- a/src/Tag/Description/OptionalDescriptionProviderInterface.php +++ b/src/Tag/Description/OptionalDescriptionProviderInterface.php @@ -9,8 +9,6 @@ interface OptionalDescriptionProviderInterface /** * Returns description object which can be represented as a string and * contains additional information. - * - * @psalm-immutable Each call to the method must return the same value. */ public function getDescription(): ?DescriptionInterface; } diff --git a/src/Tag/OptionalTypeProviderInterface.php b/src/Tag/OptionalTypeProviderInterface.php index 16cf45d..7ceeb65 100644 --- a/src/Tag/OptionalTypeProviderInterface.php +++ b/src/Tag/OptionalTypeProviderInterface.php @@ -17,8 +17,6 @@ interface OptionalTypeProviderInterface /** * Returns an AST object of the type or {@see null} in case the * type is not specified. - * - * @psalm-immutable Each call to the method must return the same value. */ public function getType(): ?TypeStatement; } diff --git a/src/Tag/OptionalVariableNameProviderInterface.php b/src/Tag/OptionalVariableNameProviderInterface.php index 8c6052c..82189c8 100644 --- a/src/Tag/OptionalVariableNameProviderInterface.php +++ b/src/Tag/OptionalVariableNameProviderInterface.php @@ -11,8 +11,6 @@ interface OptionalVariableNameProviderInterface * which this tag is attached or {@see null} in case of the tag does * not contain a name. * - * @psalm-immutable Each call to the method must return the same value. - * * @return non-empty-string|null */ public function getVariableName(): ?string; diff --git a/src/Tag/TagInterface.php b/src/Tag/TagInterface.php index d51f230..dc2e5c4 100644 --- a/src/Tag/TagInterface.php +++ b/src/Tag/TagInterface.php @@ -15,8 +15,6 @@ interface TagInterface extends OptionalDescriptionProviderInterface, \Stringable * that is, can contain all the characters that can match the PHP FQN, as * well as the '-' character. * - * @psalm-immutable Each call to the method must return the same value. - * * @return non-empty-string */ public function getName(): string; @@ -26,8 +24,6 @@ public function getName(): string; * allows a class to decide how it will react when it is treated like * a string. * - * @psalm-immutable Each call to the method must return the same value. - * * @return string returns string representation of the object that * implements this interface (and/or {@see __toString()} magic * method) diff --git a/src/Tag/TagsProviderInterface.php b/src/Tag/TagsProviderInterface.php index 8fa3bfb..c721b54 100644 --- a/src/Tag/TagsProviderInterface.php +++ b/src/Tag/TagsProviderInterface.php @@ -13,8 +13,6 @@ interface TagsProviderInterface /** * Returns the tags for this object. * - * @psalm-immutable Each call to the method must return the same value. - * * @return iterable */ public function getTags(): iterable; diff --git a/src/Tag/TypeProviderInterface.php b/src/Tag/TypeProviderInterface.php index 2dfbdcb..d1b13b8 100644 --- a/src/Tag/TypeProviderInterface.php +++ b/src/Tag/TypeProviderInterface.php @@ -10,8 +10,6 @@ interface TypeProviderInterface extends OptionalTypeProviderInterface { /** * Returns an AST object of the type. - * - * @psalm-immutable Each call to the method must return the same value. */ public function getType(): TypeStatement; } diff --git a/src/Tag/VariableNameProviderInterface.php b/src/Tag/VariableNameProviderInterface.php index b39e76c..7e2d766 100644 --- a/src/Tag/VariableNameProviderInterface.php +++ b/src/Tag/VariableNameProviderInterface.php @@ -10,8 +10,6 @@ interface VariableNameProviderInterface extends OptionalVariableNameProviderInte * Returns the name of the variable (parameter, field, etc.) to * which this tag is attached. * - * @psalm-immutable Each call to the method must return the same value. - * * @return non-empty-string */ public function getVariableName(): string;