Skip to content

Commit

Permalink
Generic/LowerCaseType: add support for examining DNF types
Browse files Browse the repository at this point in the history
The `Generic.PHP.LowerCaseType` sniff needs to be updated to also handle non-lowercase types which are part of a DNF type declaration.

This commit updates the `processUnionType()` method to not only examine union types, but to examine all multi-token types and to do so in a slightly more performant manner and calls that method now for all multi-token type declarations.

Note: The method name now doesn't properly cover the functionality anymore, however, renaming the method would be a breaking change as the class is not `final` and the method not `private`.

Includes unit tests.

Related to 105

Closes 105
  • Loading branch information
jrfnl committed May 6, 2024
1 parent 4abcf57 commit 9bf4e15
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 58 deletions.
110 changes: 53 additions & 57 deletions src/Standards/Generic/Sniffs/PHP/LowerCaseTypeSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,40 +132,23 @@ public function process(File $phpcsFile, $stackPtr)
if ($startOfType !== $constName) {
$endOfType = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($constName - 1), null, true);

$type = '';
$isUnionType = false;
$isIntersectionType = false;
for ($j = $startOfType; $j <= $endOfType; $j++) {
if (isset($ignore[$tokens[$j]['code']]) === true) {
continue;
}

if ($tokens[$j]['code'] === T_TYPE_UNION) {
$isUnionType = true;
}

if ($tokens[$j]['code'] === T_TYPE_INTERSECTION) {
$isIntersectionType = true;
}

$type .= $tokens[$j]['content'];
}

$error = 'PHP constant type declarations must be lowercase; expected "%s" but found "%s"';
$errorCode = 'ConstantTypeFound';

if ($isIntersectionType === true) {
// Intersection types don't support simple types.
} else if ($isUnionType === true) {
if ($startOfType !== $endOfType) {
// Multi-token type.
$this->processUnionType(
$phpcsFile,
$startOfType,
$endOfType,
$error,
$errorCode
);
} else if (isset($this->phpTypes[strtolower($type)]) === true) {
$this->processType($phpcsFile, $startOfType, $type, $error, $errorCode);
} else {
$type = $tokens[$startOfType]['content'];
if (isset($this->phpTypes[strtolower($type)]) === true) {
$this->processType($phpcsFile, $startOfType, $type, $error, $errorCode);
}
}
}//end if

Expand Down Expand Up @@ -195,9 +178,8 @@ public function process(File $phpcsFile, $stackPtr)
$error = 'PHP property type declarations must be lowercase; expected "%s" but found "%s"';
$errorCode = 'PropertyTypeFound';

if (strpos($type, '&') !== false) {
// Intersection types don't support simple types.
} else if (strpos($type, '|') !== false) {
if ($props['type_token'] !== $props['type_end_token']) {
// Multi-token type.
$this->processUnionType(
$phpcsFile,
$props['type_token'],
Expand Down Expand Up @@ -227,9 +209,8 @@ public function process(File $phpcsFile, $stackPtr)
$error = 'PHP return type declarations must be lowercase; expected "%s" but found "%s"';
$errorCode = 'ReturnTypeFound';

if (strpos($returnType, '&') !== false) {
// Intersection types don't support simple types.
} else if (strpos($returnType, '|') !== false) {
if ($props['return_type_token'] !== $props['return_type_end_token']) {
// Multi-token type.
$this->processUnionType(
$phpcsFile,
$props['return_type_token'],
Expand Down Expand Up @@ -259,9 +240,8 @@ public function process(File $phpcsFile, $stackPtr)
$error = 'PHP parameter type declarations must be lowercase; expected "%s" but found "%s"';
$errorCode = 'ParamTypeFound';

if (strpos($typeHint, '&') !== false) {
// Intersection types don't support simple types.
} else if (strpos($typeHint, '|') !== false) {
if ($param['type_hint_token'] !== $param['type_hint_end_token']) {
// Multi-token type.
$this->processUnionType(
$phpcsFile,
$param['type_hint_token'],
Expand All @@ -279,7 +259,9 @@ public function process(File $phpcsFile, $stackPtr)


/**
* Processes a union type declaration.
* Processes a multi-token type declaration.
*
* {@internal The method name is superseded by the reality, but changing it would be a BC-break.}
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $typeDeclStart The position of the start of the type token.
Expand All @@ -291,37 +273,51 @@ public function process(File $phpcsFile, $stackPtr)
*/
protected function processUnionType(File $phpcsFile, $typeDeclStart, $typeDeclEnd, $error, $errorCode)
{
$tokens = $phpcsFile->getTokens();
$current = $typeDeclStart;

do {
$endOfType = $phpcsFile->findNext(T_TYPE_UNION, $current, $typeDeclEnd);
if ($endOfType === false) {
// This must be the last type in the union.
$endOfType = ($typeDeclEnd + 1);
}
$tokens = $phpcsFile->getTokens();
$typeTokenCount = 0;
$typeStart = null;
$type = '';

$hasNsSep = $phpcsFile->findNext(T_NS_SEPARATOR, $current, $endOfType);
if ($hasNsSep !== false) {
// Multi-token class based type. Ignore.
$current = ($endOfType + 1);
for ($i = $typeDeclStart; $i <= $typeDeclEnd; $i++) {
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
continue;
}

// Type consisting of a single token.
$startOfType = $phpcsFile->findNext(Tokens::$emptyTokens, $current, $endOfType, true);
if ($startOfType === false) {
// Parse error.
return;
if ($tokens[$i]['code'] === T_TYPE_UNION
|| $tokens[$i]['code'] === T_TYPE_INTERSECTION
|| $tokens[$i]['code'] === T_TYPE_OPEN_PARENTHESIS
|| $tokens[$i]['code'] === T_TYPE_CLOSE_PARENTHESIS
) {
if ($typeTokenCount === 1
&& $type !== ''
&& isset($this->phpTypes[strtolower($type)]) === true
) {
$this->processType($phpcsFile, $typeStart, $type, $error, $errorCode);
}

// Reset for the next type in the type string.
$typeTokenCount = 0;
$typeStart = null;
$type = '';

continue;
}

$type = $tokens[$startOfType]['content'];
if (isset($this->phpTypes[strtolower($type)]) === true) {
$this->processType($phpcsFile, $startOfType, $type, $error, $errorCode);
if (isset($typeStart) === false) {
$typeStart = $i;
}

$current = ($endOfType + 1);
} while ($current <= $typeDeclEnd);
++$typeTokenCount;
$type .= $tokens[$i]['content'];
}//end for

// Handle type at end of type string.
if ($typeTokenCount === 1
&& $type !== ''
&& isset($this->phpTypes[strtolower($type)]) === true
) {
$this->processType($phpcsFile, $typeStart, $type, $error, $errorCode);
}

}//end processUnionType()

Expand Down
14 changes: 14 additions & 0 deletions src/Standards/Generic/Tests/PHP/LowerCaseTypeUnitTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,20 @@ enum TypedEnumConstants {
public const sTRing | aRRaY | FaLSe FOURTH = 'fourth';
}

class DNFTypes {
const (Parent&Something)|Float CONST_NAME = 1.5;

public readonly TRUE|(\A&B) $prop;

function DNFParamTypes (
null|(\Package\ClassName&\Package\Other_Class)|INT $DNFinMiddle,
(\Package\ClassName&\Package\Other_Class)|ARRAY $parensAtStart,
False|(\Package\ClassName&\Package\Other_Class) $parentAtEnd,
) {}

function DNFReturnTypes ($var): object|(Self&\Package\Other_Class)|sTRINg|false {}
}

// Intentional error, should be ignored by the sniff.
interface PropertiesNotAllowed {
public $notAllowed;
Expand Down
14 changes: 14 additions & 0 deletions src/Standards/Generic/Tests/PHP/LowerCaseTypeUnitTest.inc.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,20 @@ enum TypedEnumConstants {
public const string | array | false FOURTH = 'fourth';
}

class DNFTypes {
const (parent&Something)|float CONST_NAME = 1.5;

public readonly true|(\A&B) $prop;

function DNFParamTypes (
null|(\Package\ClassName&\Package\Other_Class)|int $DNFinMiddle,
(\Package\ClassName&\Package\Other_Class)|array $parensAtStart,
false|(\Package\ClassName&\Package\Other_Class) $parentAtEnd,
) {}

function DNFReturnTypes ($var): object|(self&\Package\Other_Class)|string|false {}
}

// Intentional error, should be ignored by the sniff.
interface PropertiesNotAllowed {
public $notAllowed;
Expand Down
8 changes: 7 additions & 1 deletion src/Standards/Generic/Tests/PHP/LowerCaseTypeUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ public function getErrorList()
123 => 2,
124 => 3,
125 => 3,
129 => 2,
131 => 1,
134 => 1,
135 => 1,
136 => 1,
139 => 2,
];

}//end getErrorList()
Expand All @@ -103,7 +109,7 @@ public function getErrorList()
public function getWarningList()
{
// Warning from getMemberProperties() about parse error.
return [130 => 1];
return [144 => 1];

}//end getWarningList()

Expand Down

0 comments on commit 9bf4e15

Please sign in to comment.