diff --git a/coder_sniffer/Drupal/Sniffs/Commenting/FunctionCommentSniff.php b/coder_sniffer/Drupal/Sniffs/Commenting/FunctionCommentSniff.php index ffa6a88ff68608959d3b7d771ebdc16d161687de..9affdc9b081a2b00e1acd501bd489b1c18d42067 100644 --- a/coder_sniffer/Drupal/Sniffs/Commenting/FunctionCommentSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Commenting/FunctionCommentSniff.php @@ -49,36 +49,6 @@ class FunctionCommentSniff implements Sniff 'TRUEFALSE' => 'bool', ]; - /** - * An array of variable types for param/var we will check. - * - * @var array<string> - */ - public $allowedTypes = [ - 'array', - 'array-key', - 'bool', - 'callable', - 'double', - 'float', - 'int', - 'positive-int', - 'negative-int', - 'iterable', - 'mixed', - 'object', - 'resource', - 'callable', - 'true', - 'false', - 'null', - 'scalar', - 'stdClass', - '\stdClass', - 'string', - 'void', - ]; - /** * Returns an array of tokens this test wants to listen for. @@ -756,72 +726,6 @@ class FunctionCommentSniff implements Sniff } }//end if - $suggestedName = ''; - $typeName = ''; - if (count($typeNames) === 1) { - $typeName = $param['type']; - $suggestedName = static::suggestType($typeName); - } - - // This runs only if there is only one type name and the type name - // is not one of the disallowed type names. - if (count($typeNames) === 1 && $typeName === $suggestedName) { - // Check type hint for array and custom type. - $suggestedTypeHint = ''; - if (strpos($suggestedName, 'array') !== false && $suggestedName !== 'array-key') { - $suggestedTypeHint = 'array'; - } else if (strpos($suggestedName, 'callable') !== false) { - $suggestedTypeHint = 'callable'; - } else if (substr($suggestedName, -2) === '[]') { - $suggestedTypeHint = 'array'; - } else if ($suggestedName === 'object') { - $suggestedTypeHint = ''; - } else if (in_array($typeName, $this->allowedTypes) === false) { - $suggestedTypeHint = $suggestedName; - } - - if ($suggestedTypeHint !== '' && isset($realParams[$checkPos]) === true) { - $typeHint = $realParams[$checkPos]['type_hint']; - // Primitive type hints are allowed to be omitted. - if ($typeHint === '' && in_array($suggestedTypeHint, $this->allowedTypes) === false) { - $error = 'Type hint "%s" missing for %s'; - $data = [ - $suggestedTypeHint, - $param['var'], - ]; - $phpcsFile->addError($error, $stackPtr, 'TypeHintMissing', $data); - } else if ($typeHint !== $suggestedTypeHint && $typeHint !== '') { - // The type hint could be fully namespaced, so we check - // for the part after the last "\". - $nameParts = explode('\\', $suggestedTypeHint); - $lastPart = end($nameParts); - if ($lastPart !== $typeHint && $this->isAliasedType($typeHint, $suggestedTypeHint, $phpcsFile) === false) { - $error = 'Expected type hint "%s"; found "%s" for %s'; - $data = [ - $lastPart, - $typeHint, - $param['var'], - ]; - $phpcsFile->addError($error, $stackPtr, 'IncorrectTypeHint', $data); - } - }//end if - } else if ($suggestedTypeHint === '' - && isset($realParams[$checkPos]) === true - ) { - $typeHint = $realParams[$checkPos]['type_hint']; - if ($typeHint !== '' - && in_array($typeHint, $this->allowedTypes) === false - ) { - $error = 'Unknown type hint "%s" found for %s'; - $data = [ - $typeHint, - $param['var'], - ]; - $phpcsFile->addError($error, $stackPtr, 'InvalidTypeHint', $data); - } - }//end if - }//end if - // Check number of spaces after the type. $spaces = 1; if ($param['type_space'] !== $spaces) { @@ -1025,73 +929,6 @@ class FunctionCommentSniff implements Sniff }//end suggestType() - /** - * Checks if a used type hint is an alias defined by a "use" statement. - * - * @param string $typeHint The type hint used. - * @param string $suggestedTypeHint The fully qualified type to - * check against. - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being checked. - * - * @return boolean - */ - protected function isAliasedType($typeHint, $suggestedTypeHint, File $phpcsFile) - { - $tokens = $phpcsFile->getTokens(); - - // Iterate over all "use" statements in the file. - $usePtr = 0; - while ($usePtr !== false) { - $usePtr = $phpcsFile->findNext(T_USE, ($usePtr + 1)); - if ($usePtr === false) { - return false; - } - - // Only check use statements in the global scope. - if (empty($tokens[$usePtr]['conditions']) === false) { - continue; - } - - // Now comes the original class name, possibly with namespace - // backslashes. - $originalClass = $phpcsFile->findNext(Tokens::$emptyTokens, ($usePtr + 1), null, true); - if ($originalClass === false || ($tokens[$originalClass]['code'] !== T_STRING - && $tokens[$originalClass]['code'] !== T_NS_SEPARATOR) - ) { - continue; - } - - $originalClassName = ''; - while (in_array($tokens[$originalClass]['code'], [T_STRING, T_NS_SEPARATOR]) === true) { - $originalClassName .= $tokens[$originalClass]['content']; - $originalClass++; - } - - if (ltrim($originalClassName, '\\') !== ltrim($suggestedTypeHint, '\\')) { - continue; - } - - // Now comes the "as" keyword signaling an alias name for the class. - $asPtr = $phpcsFile->findNext(Tokens::$emptyTokens, ($originalClass + 1), null, true); - if ($asPtr === false || $tokens[$asPtr]['code'] !== T_AS) { - continue; - } - - // Now comes the name the class is aliased to. - $aliasPtr = $phpcsFile->findNext(Tokens::$emptyTokens, ($asPtr + 1), null, true); - if ($aliasPtr === false || $tokens[$aliasPtr]['code'] !== T_STRING - || $tokens[$aliasPtr]['content'] !== $typeHint - ) { - continue; - } - - // We found a use statement that aliases the used type hint! - return true; - }//end while - - }//end isAliasedType() - - /** * Determines if a comment line is part of an @code/@endcode example. * diff --git a/tests/Drupal/Commenting/FunctionCommentUnitTest.inc b/tests/Drupal/Commenting/FunctionCommentUnitTest.inc index 3174a394301fe8cfd111d69c001f34b69720f0d8..b2a397a51fedb4cb035c3e84d826675019bad011 100644 --- a/tests/Drupal/Commenting/FunctionCommentUnitTest.inc +++ b/tests/Drupal/Commenting/FunctionCommentUnitTest.inc @@ -172,7 +172,7 @@ function test14(stdClass $user) { } /** - * Array parameter type mismatch. + * Array parameter type mismatch is allowed, use PHPStan to validate types. * * @param array $foo * Comment here. @@ -822,3 +822,84 @@ function test_return_integer_min(): int { function test_return_integer_max(): int { return 50; } + +/** + * PHPStan: Advanced string types. + * + * @param class-string $param1 + * Parameter. + * @param class-string<Foo> $param2 + * Parameter. + * @param callable-string $param3 + * Parameter. + * @param numeric-string $param4 + * Parameter. + * @param non-empty-string $param5 + * Parameter. + * @param non-falsy-string $param6 + * Parameter. + * @param literal-string $param7 + * Parameter. + * @param numeric-string|null $param8 + * Parameter. + * + * @see https://phpstan.org/writing-php-code/phpdoc-types#other-advanced-string-types + */ +function test_string_types(string $param1, string $param2, string $param3, string $param4, string $param5, string $param6, string $param7, $param8) { +} + +/** + * @return class-string + * Class string. + */ +function test_return_class_string(): string { + return ''; +} + +/** + * @return class-string<Foo> + * Class string. + */ +function test_return_class_string_foo(): string { + return ''; +} + +/** + * @return callable-string + * Callable string. + */ +function test_return_callable_string(): string { + return ''; +} + +/** + * @return numeric-string + * Numeric string. + */ +function test_return_numeric_string(): string { + return ''; +} + +/** + * @return non-empty-string + * Non empty string. + */ +function test_return_non_empty_string(): string { + return 'foo'; +} + +/** + * @return non-falsy-string + * Non falsy string. + */ +function test_return_non_falsy_string(): string { + return ''; +} + +/** + * @return literal-string + * Literal string. + */ +function test_return_literal_string(): string { + return ''; +} diff --git a/tests/Drupal/Commenting/FunctionCommentUnitTest.inc.fixed b/tests/Drupal/Commenting/FunctionCommentUnitTest.inc.fixed index 34f2340d9b1bf80feadcd3726aca8b50496d8993..363885a7d0fe05dd3d73f67ecf93c4a8300aa446 100644 --- a/tests/Drupal/Commenting/FunctionCommentUnitTest.inc.fixed +++ b/tests/Drupal/Commenting/FunctionCommentUnitTest.inc.fixed @@ -183,7 +183,7 @@ function test14(stdClass $user) { } /** - * Array parameter type mismatch. + * Array parameter type mismatch is allowed, use PHPStan to validate types. * * @param array $foo * Comment here. @@ -848,3 +848,84 @@ function test_return_integer_min(): int { function test_return_integer_max(): int { return 50; } + +/** + * PHPStan: Advanced string types. + * + * @param class-string $param1 + * Parameter. + * @param class-string<Foo> $param2 + * Parameter. + * @param callable-string $param3 + * Parameter. + * @param numeric-string $param4 + * Parameter. + * @param non-empty-string $param5 + * Parameter. + * @param non-falsy-string $param6 + * Parameter. + * @param literal-string $param7 + * Parameter. + * @param numeric-string|null $param8 + * Parameter. + * + * @see https://phpstan.org/writing-php-code/phpdoc-types#other-advanced-string-types + */ +function test_string_types(string $param1, string $param2, string $param3, string $param4, string $param5, string $param6, string $param7, $param8) { +} + +/** + * @return class-string + * Class string. + */ +function test_return_class_string(): string { + return ''; +} + +/** + * @return class-string<Foo> + * Class string. + */ +function test_return_class_string_foo(): string { + return ''; +} + +/** + * @return callable-string + * Callable string. + */ +function test_return_callable_string(): string { + return ''; +} + +/** + * @return numeric-string + * Numeric string. + */ +function test_return_numeric_string(): string { + return ''; +} + +/** + * @return non-empty-string + * Non empty string. + */ +function test_return_non_empty_string(): string { + return 'foo'; +} + +/** + * @return non-falsy-string + * Non falsy string. + */ +function test_return_non_falsy_string(): string { + return ''; +} + +/** + * @return literal-string + * Literal string. + */ +function test_return_literal_string(): string { + return ''; +} diff --git a/tests/Drupal/Commenting/FunctionCommentUnitTest.php b/tests/Drupal/Commenting/FunctionCommentUnitTest.php index d1094e201d6741d6b6834ed64e412b29241c2c69..462b7d4367d950f5359caa94c3f19fed0f05b77e 100644 --- a/tests/Drupal/Commenting/FunctionCommentUnitTest.php +++ b/tests/Drupal/Commenting/FunctionCommentUnitTest.php @@ -38,7 +38,6 @@ class FunctionCommentUnitTest extends CoderSniffUnitTest 126 => 2, 147 => 1, 148 => 2, - 180 => 1, 187 => 1, 195 => 1, 205 => 1,