Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic/LowerCaseType: add support for examining DNF types #478

Merged
merged 1 commit into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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