Skip to content

Commit 8a71fc6

Browse files
committed
feat: implement DisallowVoidType sniff and related tests
- Add `DisallowVoidTypeSniff` to disallow explicit `: void` return types. - Create documentation for the disallowed void type standard. - Add unit tests to verify detection of explicit void return types. - Update existing test files to remove explicit void return types. - Update README with the new sniff documentation.
1 parent b59ffbd commit 8a71fc6

8 files changed

Lines changed: 367 additions & 6 deletions

File tree

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,56 @@ $bar = (integer) $count;
579579
</tr>
580580
</table>
581581

582+
### yCodeTech.Types.DisallowVoidType
583+
584+
Explicit `void` return type declarations are disallowed on functions, methods, and closures. The absence of a return type already implies void.
585+
586+
<table>
587+
<tr>
588+
<th>Rules</th>
589+
<th>Fixable?</th>
590+
</tr>
591+
<tr>
592+
<td>Explicit <code>: void</code> return type declarations must be removed.</td>
593+
<td>✔️</td>
594+
</tr>
595+
</table>
596+
597+
#### Violation Codes:
598+
599+
`yCodeTech.Types.DisallowVoidType.Found`
600+
601+
#### Examples:
602+
603+
<table>
604+
<tr>
605+
<th>✔️ Valid: No return type (implicitly void)</th>
606+
<th>❌ Invalid: Explicit void return type</th>
607+
</tr>
608+
<tr>
609+
<td>
610+
611+
```php
612+
function doSomething()
613+
{
614+
echo "Hello";
615+
}
616+
```
617+
618+
</td>
619+
<td>
620+
621+
```php
622+
function doSomething(): void
623+
{
624+
echo "Hello";
625+
}
626+
```
627+
628+
</td>
629+
</tr>
630+
</table>
631+
582632
## Testing
583633

584634
To test the standard against the provided comprehensive test file, please see [the specific instructions](./test_utils/README.md).

test_utils/TestFile.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,11 +270,14 @@ public function testMissingGeneratorReturn()
270270
***********************/
271271

272272
/**
273-
* Function that returns void with an explicit `void` typing
274-
* (should NOT be flagged for missing `@return` tag).
273+
* Function that returns void with an explicit `void` typing
274+
* (should be flagged for explicit void type, but NOT for missing `@return` tag).
275+
*
276+
* The following should be fixed:
277+
* - The explicit `: void` return type should be removed.
275278
*
276279
* The following should NOT be fixed:
277-
* - A `@return` tag should not be added for an explicit `void` return.
280+
* - A `@return` tag should not be added.
278281
*
279282
* @param string $message Message to display
280283
*/
@@ -360,6 +363,7 @@ function testVoidFunctionWithAnonymousFunction()
360363
*
361364
* The following should be fixed:
362365
* - The `@return` tag should be removed.
366+
* - The explicit `: void` return type should be removed.
363367
*
364368
* @return void
365369
*/

test_utils/TestFile_WithErrors_DoNotFix.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,11 +270,14 @@ public function testMissingGeneratorReturn()
270270
***********************/
271271

272272
/**
273-
* Function that returns void with an explicit `void` typing
274-
* (should NOT be flagged for missing `@return` tag).
273+
* Function that returns void with an explicit `void` typing
274+
* (should be flagged for explicit void type, but NOT for missing `@return` tag).
275+
*
276+
* The following should be fixed:
277+
* - The explicit `: void` return type should be removed.
275278
*
276279
* The following should NOT be fixed:
277-
* - A `@return` tag should not be added for an explicit `void` return.
280+
* - A `@return` tag should not be added.
278281
*
279282
* @param string $message Message to display
280283
*/
@@ -360,6 +363,7 @@ function testVoidFunctionWithAnonymousFunction()
360363
*
361364
* The following should be fixed:
362365
* - The `@return` tag should be removed.
366+
* - The explicit `: void` return type should be removed.
363367
*
364368
* @return void
365369
*/
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<documentation title="Disallow Void Type">
2+
<standard>
3+
<![CDATA[
4+
Explicit void return type declarations are disallowed. The absence of a return type already implies void.
5+
6+
---
7+
8+
]]>
9+
</standard>
10+
<code_comparison>
11+
<code title="✔️ Valid: No return type (implicitly void)">
12+
<![CDATA[
13+
function doSomething()
14+
{
15+
echo "Hello";
16+
}
17+
]]>
18+
</code>
19+
<code title="❌ Invalid: Explicit void return type">
20+
<![CDATA[
21+
function doSomething()<em>: void</em>
22+
{
23+
echo "Hello";
24+
}
25+
]]>
26+
</code>
27+
</code_comparison>
28+
</documentation>
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<?php
2+
3+
/**
4+
* DisallowVoidType sniff for yCodeTech PHPCS Standard.
5+
*
6+
* Disallows explicit void return type declarations on functions and methods.
7+
*
8+
* @category PHP
9+
* @package PHP_CodeSniffer
10+
* @author yCodeTech
11+
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
12+
*/
13+
14+
namespace yCodeTech\Sniffs\Types;
15+
16+
use PHP_CodeSniffer\Sniffs\Sniff;
17+
use PHP_CodeSniffer\Files\File;
18+
19+
/**
20+
* DisallowVoidType sniff.
21+
*
22+
* Disallows explicit `: void` return type declarations on functions, methods,
23+
* and closures. The absence of a return type already implies void.
24+
*/
25+
class DisallowVoidTypeSniff implements Sniff
26+
{
27+
/**
28+
* Returns an array of tokens this test wants to listen for.
29+
*
30+
* @return array<int>
31+
*/
32+
public function register()
33+
{
34+
return [T_FUNCTION, T_CLOSURE];
35+
}
36+
37+
/**
38+
* Processes this test, when one of its tokens is encountered.
39+
*
40+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
41+
* @param int $stackPtr The position of the current token in the
42+
* stack passed in $tokens.
43+
*/
44+
public function process(File $phpcsFile, $stackPtr)
45+
{
46+
$voidPtr = $this->getExplicitVoidTypePtr($phpcsFile, $stackPtr);
47+
if ($voidPtr === false) {
48+
return;
49+
}
50+
51+
$error = 'Explicit void return type is not allowed and should be removed';
52+
$fix = $phpcsFile->addFixableError($error, $voidPtr, 'Found');
53+
if ($fix === true) {
54+
$this->removeExplicitVoidType($phpcsFile, $stackPtr);
55+
}
56+
}
57+
58+
/**
59+
* Get the position of the explicit `void` return type token, if present.
60+
*
61+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
62+
* @param int $stackPtr The position of the function token.
63+
*
64+
* @return int|false The position of the void token, or false if not found.
65+
*/
66+
private function getExplicitVoidTypePtr(File $phpcsFile, $stackPtr)
67+
{
68+
$tokens = $phpcsFile->getTokens();
69+
70+
$openParen = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr);
71+
if ($openParen === false) {
72+
return false;
73+
}
74+
75+
$closeParen = $tokens[$openParen]['parenthesis_closer'] ?? null;
76+
if ($closeParen === null) {
77+
return false;
78+
}
79+
80+
$scopeOpener = $tokens[$stackPtr]['scope_opener'] ?? null;
81+
$semicolonPtr = $phpcsFile->findNext(T_SEMICOLON, $closeParen + 1);
82+
$searchEnd = $scopeOpener ?? ($semicolonPtr !== false ? $semicolonPtr + 1 : null);
83+
84+
$colonPtr = $phpcsFile->findNext(T_COLON, $closeParen + 1, $searchEnd);
85+
if ($colonPtr === false) {
86+
return false;
87+
}
88+
89+
$returnTypePtr = $phpcsFile->findNext(T_WHITESPACE, $colonPtr + 1, null, true);
90+
if ($returnTypePtr === false) {
91+
return false;
92+
}
93+
94+
if ($tokens[$returnTypePtr]['code'] === T_STRING && $tokens[$returnTypePtr]['content'] === 'void') {
95+
return $returnTypePtr;
96+
}
97+
98+
return false;
99+
}
100+
101+
/**
102+
* Remove explicit `: void` return type from a function signature.
103+
*
104+
* Removes the colon, any whitespace between colon and void, and the `void`
105+
* token itself, leaving the rest of the signature intact.
106+
*
107+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
108+
* @param int $stackPtr The position of the function token.
109+
*/
110+
private function removeExplicitVoidType(File $phpcsFile, $stackPtr)
111+
{
112+
$tokens = $phpcsFile->getTokens();
113+
114+
$openParen = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr);
115+
if ($openParen === false) {
116+
return;
117+
}
118+
119+
$closeParen = $tokens[$openParen]['parenthesis_closer'] ?? null;
120+
if ($closeParen === null) {
121+
return;
122+
}
123+
124+
$scopeOpener = $tokens[$stackPtr]['scope_opener'] ?? null;
125+
$semicolonPtr = $phpcsFile->findNext(T_SEMICOLON, $closeParen + 1);
126+
$searchEnd = $scopeOpener ?? ($semicolonPtr !== false ? $semicolonPtr + 1 : null);
127+
128+
$colonPtr = $phpcsFile->findNext(T_COLON, $closeParen + 1, $searchEnd);
129+
if ($colonPtr === false) {
130+
return;
131+
}
132+
133+
$voidPtr = $phpcsFile->findNext(T_WHITESPACE, $colonPtr + 1, null, true);
134+
if ($voidPtr === false || $tokens[$voidPtr]['content'] !== 'void') {
135+
return;
136+
}
137+
138+
$phpcsFile->fixer->beginChangeset();
139+
for ($i = $colonPtr; $i <= $voidPtr; $i++) {
140+
$phpcsFile->fixer->replaceToken($i, '');
141+
}
142+
$phpcsFile->fixer->endChangeset();
143+
}
144+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
// @phpcs:disable yCodeTech.Commenting.FunctionComment
4+
// We're not concerned with docblocks here.
5+
6+
/**
7+
* Test file for DisallowVoidTypeSniff.
8+
*
9+
* Tests detection of explicit void return type declarations.
10+
*/
11+
12+
class TestClass
13+
{
14+
public function explicitVoidMethod(): void
15+
{
16+
echo "Hello";
17+
}
18+
19+
public function explicitVoidWithParam(string $message): void
20+
{
21+
echo $message;
22+
}
23+
24+
// Should not flag - no void type
25+
public function regularMethod(): string
26+
{
27+
return "Hello";
28+
}
29+
30+
// Should not flag - no return type
31+
public function noReturnType()
32+
{
33+
echo "Hello";
34+
}
35+
}
36+
37+
function explicitVoidFunction(): void
38+
{
39+
echo "Hello";
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
// @phpcs:disable yCodeTech.Commenting.FunctionComment
4+
// We're not concerned with docblocks here.
5+
6+
/**
7+
* Test file for DisallowVoidTypeSniff.
8+
*
9+
* Tests detection of explicit void return type declarations.
10+
*/
11+
12+
class TestClass
13+
{
14+
public function explicitVoidMethod()
15+
{
16+
echo "Hello";
17+
}
18+
19+
public function explicitVoidWithParam(string $message)
20+
{
21+
echo $message;
22+
}
23+
24+
// Should not flag - no void type
25+
public function regularMethod(): string
26+
{
27+
return "Hello";
28+
}
29+
30+
// Should not flag - no return type
31+
public function noReturnType()
32+
{
33+
echo "Hello";
34+
}
35+
}
36+
37+
function explicitVoidFunction()
38+
{
39+
echo "Hello";
40+
}

0 commit comments

Comments
 (0)