From 3978c756e0a6c02fc5c16c18ad96bed9aa522cce Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 26 Dec 2019 14:13:51 +0100 Subject: [PATCH] :package: Drop some type hint support to improve the way it works --- .../NamingConventions/ValidTypeHintSniff.php | 134 +++++++++++------- .../ValidTypeHintUnitTest.inc | 16 +-- .../ValidTypeHintUnitTest.inc.fixed | 16 +-- .../ValidTypeHintUnitTest.php | 6 - 4 files changed, 86 insertions(+), 86 deletions(-) diff --git a/SymfonyCustom/Sniffs/NamingConventions/ValidTypeHintSniff.php b/SymfonyCustom/Sniffs/NamingConventions/ValidTypeHintSniff.php index 4c35ec5..01b191f 100644 --- a/SymfonyCustom/Sniffs/NamingConventions/ValidTypeHintSniff.php +++ b/SymfonyCustom/Sniffs/NamingConventions/ValidTypeHintSniff.php @@ -20,6 +20,38 @@ class ValidTypeHintSniff implements Sniff private const CLOSER = '\>|\]|\}|\)'; private const SEPARATOR = '\&|\|'; + /* + * is any non-array, non-generic, non-alternated type, eg `int` or `\Foo` + * is array of , eg `int[]` or `\Foo[]` + * is generic collection type, like `array`, `Collection` and more complex like `Collection>` + * is , or type, like `int`, `bool[]` or `Collection` + * is one or more types alternated via `|`, like `int|bool[]|Collection` + */ + private const REGEX_TYPES = ' + (? + (? + (? + (?&simple)(\[\])* + ) + | + (? + [@$?]?[\\\\\w]+ + ) + | + (? + (?(?&simple)) + < + (?:(?(?&types)),\s*)?(?(?&types)|(?&generic)) + > + ) + ) + (?: + \| + (?:(?&simple)|(?&array)|(?&generic)) + )* + ) + '; + /** * @return array */ @@ -37,81 +69,79 @@ public function process(File $phpcsFile, $stackPtr): void $tokens = $phpcsFile->getTokens(); if (in_array($tokens[$stackPtr]['content'], SniffHelper::TAGS_WITH_TYPE)) { - preg_match( - '`^((?:' - .'(?:'.self::OPENER.'|'.self::MIDDLE.'|'.self::SEPARATOR.')\s+' - .'(?='.self::TEXT.'|'.self::OPENER.'|'.self::MIDDLE.'|'.self::CLOSER.'|'.self::SEPARATOR.')' - .'|'.self::OPENER.'|'.self::MIDDLE.'|'.self::SEPARATOR - .'|(?:'.self::TEXT.'|'.self::CLOSER.')\s+' - .'(?='.self::OPENER.'|'.self::MIDDLE.'|'.self::CLOSER.'|'.self::SEPARATOR.')' - .'|'.self::TEXT.'|'.self::CLOSER.'' - .')+)(.*)?`i', - $tokens[($stackPtr + 2)]['content'], - $match + $matchingResult = preg_match( + '{^'.self::REGEX_TYPES.'(?:[ \t].*)?$}sx', + $tokens[$stackPtr + 2]['content'], + $matches ); - if (isset($match[1]) === false) { - return; - } + $content = 1 === $matchingResult ? $matches['types'] : ''; + $endOfContent = preg_replace('/'.preg_quote($content, '/').'/', '', $tokens[$stackPtr + 2]['content'], 1); - $type = $match[1]; - $suggestedType = $this->getValidTypeName($type); - if ($type !== $suggestedType) { + $suggestedType = $this->getValidTypes($content); + + if ($content !== $suggestedType) { $fix = $phpcsFile->addFixableError( 'For type-hinting in PHPDocs, use %s instead of %s', $stackPtr + 2, 'Invalid', - [$suggestedType, $type] + [$suggestedType, $content] ); if ($fix) { - $replacement = $suggestedType; - if (isset($match[2])) { - $replacement .= $match[2]; - } - - $phpcsFile->fixer->replaceToken($stackPtr + 2, $replacement); + $phpcsFile->fixer->replaceToken($stackPtr + 2, $suggestedType.$endOfContent); } } } } /** - * @param string $typeName + * @param string $content + * + * @return array + */ + private function getTypes(string $content): array + { + $types = []; + while ('' !== $content && false !== $content) { + preg_match('{^'.self::REGEX_TYPES.'$}x', $content, $matches); + + $types[] = $matches['type']; + $content = substr($content, strlen($matches['type']) + 1); + } + + return $types; + } + + /** + * @param string $content * * @return string */ - private function getValidTypeName(string $typeName): string + private function getValidTypes(string $content): string { - $typeNameWithoutSpace = str_replace(' ', '', $typeName); - $parts = preg_split( - '/('.self::OPENER.'|'.self::MIDDLE.'|'.self::CLOSER.'|'.self::SEPARATOR.')/', - $typeNameWithoutSpace, - -1, - PREG_SPLIT_DELIM_CAPTURE - ); - $partsNumber = count($parts) - 1; - - $validType = ''; - for ($i = 0; $i < $partsNumber; $i += 2) { - $validType .= $this->suggestType($parts[$i]); - - if ('=>' === $parts[$i + 1]) { - $validType .= ' '; - } + $types = $this->getTypes($content); + + foreach ($types as $index => $type) { + $type = str_replace(' ', '', $type); - $validType .= $parts[$i + 1]; + preg_match('{^'.self::REGEX_TYPES.'$}x', $type, $matches); + if (isset($matches['generic'])) { + $validType = $this->getValidType($matches['genericName']).'<'; - if (preg_match('/'.self::MIDDLE.'/', $parts[$i + 1])) { - $validType .= ' '; + if ('' !== $matches['genericKey']) { + $validType .= $this->getValidTypes($matches['genericKey']).', '; + } + + $validType .= $this->getValidTypes($matches['genericValue']).'>'; + } else { + $validType = $this->getValidType($type); } - } - if ('' !== $parts[$partsNumber]) { - $validType .= $this->suggestType($parts[$partsNumber]); + $types[$index] = $validType; } - return trim($validType); + return implode('|', $types); } /** @@ -119,8 +149,12 @@ private function getValidTypeName(string $typeName): string * * @return string */ - private function suggestType(string $typeName): string + private function getValidType(string $typeName): string { + if ('[]' === substr($typeName, -2)) { + return $this->getValidType(substr($typeName, 0, -2)).'[]'; + } + $lowerType = strtolower($typeName); switch ($lowerType) { case 'bool': diff --git a/SymfonyCustom/Tests/NamingConventions/ValidTypeHintUnitTest.inc b/SymfonyCustom/Tests/NamingConventions/ValidTypeHintUnitTest.inc index 2e4fdd3..4dd4b3c 100644 --- a/SymfonyCustom/Tests/NamingConventions/ValidTypeHintUnitTest.inc +++ b/SymfonyCustom/Tests/NamingConventions/ValidTypeHintUnitTest.inc @@ -44,18 +44,4 @@ echo ( float ) $a; /** @method integer|string */ /** @method array|integer[]|array truc */ -/** @method array| integer[] |string|(integer|boolean)[] truc */ -/** @method array{scheme:string,host:string} truc */ -/** @method array(int=>string) truc */ - -/** - * Avoid weird bug - * - * @method array{ - * @method array{scheme:integer,host: - * @method array< - * @method array|integer[]|array> truc */ diff --git a/SymfonyCustom/Tests/NamingConventions/ValidTypeHintUnitTest.inc.fixed b/SymfonyCustom/Tests/NamingConventions/ValidTypeHintUnitTest.inc.fixed index da67070..b1fbe31 100644 --- a/SymfonyCustom/Tests/NamingConventions/ValidTypeHintUnitTest.inc.fixed +++ b/SymfonyCustom/Tests/NamingConventions/ValidTypeHintUnitTest.inc.fixed @@ -44,18 +44,4 @@ echo ( float ) $a; /** @method int|string */ /** @method array|int[]|array truc */ -/** @method array|int[]|string|(int|bool)[] truc */ -/** @method array{scheme: string, host: string} truc */ -/** @method array(int => string) truc */ - -/** - * Avoid weird bug - * - * @method array{ - * @method array{scheme: int, host: - * @method array< - * @method array|int[]|array> truc */ diff --git a/SymfonyCustom/Tests/NamingConventions/ValidTypeHintUnitTest.php b/SymfonyCustom/Tests/NamingConventions/ValidTypeHintUnitTest.php index cc51866..3fa9aa7 100644 --- a/SymfonyCustom/Tests/NamingConventions/ValidTypeHintUnitTest.php +++ b/SymfonyCustom/Tests/NamingConventions/ValidTypeHintUnitTest.php @@ -37,12 +37,6 @@ protected function getErrorList(): array 45 => 1, 46 => 1, 47 => 1, - 48 => 1, - 49 => 1, - 55 => 1, - 57 => 1, - 60 => 1, - 61 => 1, ]; }