-
-
Notifications
You must be signed in to change notification settings - Fork 194
Expand file tree
/
Copy pathNullableTypeForNullDefaultValueSniff.php
More file actions
119 lines (93 loc) · 3.28 KB
/
Copy pathNullableTypeForNullDefaultValueSniff.php
File metadata and controls
119 lines (93 loc) · 3.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<?php declare(strict_types = 1);
namespace SlevomatCodingStandard\Sniffs\TypeHints;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use SlevomatCodingStandard\Helpers\FixerHelper;
use SlevomatCodingStandard\Helpers\SuppressHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
use SlevomatCodingStandard\Helpers\TypeHintHelper;
use function in_array;
use function preg_match;
use function sprintf;
use function strtolower;
use function substr_count;
use const T_BITWISE_AND;
use const T_ELLIPSIS;
use const T_EQUAL;
use const T_NULL;
use const T_NULLABLE;
use const T_VARIABLE;
class NullableTypeForNullDefaultValueSniff implements Sniff
{
public const CODE_NULLABILITY_TYPE_MISSING = 'NullabilityTypeMissing';
private const NAME = 'SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue';
/**
* @return array<int, (int|string)>
*/
public function register(): array
{
return TokenHelper::FUNCTION_TOKEN_CODES;
}
public function process(File $phpcsFile, int $functionPointer): void
{
if (SuppressHelper::isSniffSuppressed($phpcsFile, $functionPointer, self::NAME)) {
return;
}
$tokens = $phpcsFile->getTokens();
$startPointer = $tokens[$functionPointer]['parenthesis_opener'] + 1;
$endPointer = $tokens[$functionPointer]['parenthesis_closer'];
for ($i = $startPointer; $i < $endPointer; $i++) {
if ($tokens[$i]['code'] !== T_VARIABLE) {
continue;
}
$parameterName = $tokens[$i]['content'];
$afterVariablePointer = TokenHelper::findNextEffective($phpcsFile, $i + 1);
if ($tokens[$afterVariablePointer]['code'] !== T_EQUAL) {
continue;
}
$afterEqualsPointer = TokenHelper::findNextEffective($phpcsFile, $afterVariablePointer + 1);
if ($tokens[$afterEqualsPointer]['code'] !== T_NULL) {
continue;
}
$ignoreTokensToFindTypeHint = [...TokenHelper::INEFFECTIVE_TOKEN_CODES, T_BITWISE_AND, T_ELLIPSIS];
$typeHintEndPointer = TokenHelper::findPreviousExcluding($phpcsFile, $ignoreTokensToFindTypeHint, $i - 1, $startPointer);
if (
$typeHintEndPointer === null
|| !in_array($tokens[$typeHintEndPointer]['code'], TokenHelper::ONLY_TYPE_HINT_TOKEN_CODES, true)
) {
continue;
}
$typeHintStartPointer = TypeHintHelper::getStartPointer($phpcsFile, $typeHintEndPointer);
$typeHint = TokenHelper::getContent($phpcsFile, $typeHintStartPointer, $typeHintEndPointer);
if (strtolower($typeHint) === 'mixed') {
continue;
}
$nullableSymbolPointer = TokenHelper::findPreviousEffective(
$phpcsFile,
$typeHintStartPointer - 1,
$tokens[$functionPointer]['parenthesis_opener'],
);
if ($nullableSymbolPointer !== null && $tokens[$nullableSymbolPointer]['code'] === T_NULLABLE) {
continue;
}
if (preg_match('~(?:^|(?:\|\s*))null(?:(?:\s*\|)|$)~i', $typeHint) === 1) {
continue;
}
$fix = $phpcsFile->addFixableError(
sprintf('Parameter %s has null default value, but is not marked as nullable.', $parameterName),
$i,
self::CODE_NULLABILITY_TYPE_MISSING,
);
if (!$fix) {
continue;
}
$phpcsFile->fixer->beginChangeset();
if (substr_count($typeHint, '|') > 0) {
FixerHelper::add($phpcsFile, $typeHintEndPointer, '|null');
} else {
FixerHelper::addBefore($phpcsFile, $typeHintStartPointer, '?');
}
$phpcsFile->fixer->endChangeset();
}
}
}