Skip to content

Commit 5834c64

Browse files
committed
WIP / review - TODO: rest of sniff + tests reviewen
1 parent a8d53eb commit 5834c64

File tree

7 files changed

+72
-55
lines changed

7 files changed

+72
-55
lines changed

Universal/Docs/DeclareStatements/BlockModeStandard.xml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ declare(strict_types=1);
1616
declare(encoding='utf-8');
1717
1818
declare(ticks=10):
19-
// Code.
19+
// Code.
2020
enddeclare;
2121
]]>
2222
</code>
@@ -36,20 +36,24 @@ enddeclare;
3636
</code_comparison>
3737
<standard>
3838
<![CDATA[
39-
strict_types declaration directive mustn't be written using curly braces, or with alternative syntax.
39+
A declare statement for the `strict_types` directive is not allowed to be written using curly braces or with alternative syntax.
4040
]]>
4141
</standard>
4242
<code_comparison>
43-
<code title="Valid: strict_types written without curly braces.">
43+
<code title="Valid: strict_types statement not using block mode.">
4444
<![CDATA[
4545
declare(strict_types=1);
4646
]]>
4747
</code>
48-
<code title="Invalid: strict_types written using alternative syntax/curly braces.">
48+
<code title="Invalid: strict_types statement using alternative syntax/curly braces.">
4949
<![CDATA[
50-
declare(strict_types=1, ticks=1) <em>{</em>
51-
// Code.
50+
declare(strict_types=1) <em>{</em>
51+
// Code.
5252
<em>}</em>
53+
54+
declare(strict_types=1) <em>:</em>
55+
// Code.
56+
<em>enddeclare</em>;
5357
]]>
5458
</code>
5559
</code_comparison>

Universal/Sniffs/DeclareStatements/BlockModeSniff.php

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
/**
1919
* Checks the style of the declare statement.
2020
*
21-
* `declare` directives can be written in two different styles:
21+
* Declare statements can be written in two different styles:
2222
* 1. Applied to the rest of the file, usually written at the top of a file like `declare(strict_types=1);`.
2323
* 2. Applied to a limited scope using curly braces or using alternative control structure syntax
24-
* (the exception to this rule is the `strict_types` directive). This is known as a block mode.
24+
* (the exception to this rule is the `strict_types` directive). This is known as block mode.
2525
*
26-
* You can also have multiple directives written inside the `declare` directive.
27-
* This sniff will check the preferred mode of the `declare` directive.
26+
* There can be multiple directives inside a `declare` statement.
27+
* This sniff checks the preferred mode for the `declare` statements.
2828
*
2929
* You can modify the sniff by changing the whether the block mode of the encoding and the ticks directives
3030
* is allowed, disallowed or required. By default, the ticks directive, if written in
@@ -45,7 +45,7 @@ class BlockModeSniff implements Sniff
4545
*
4646
* @var string
4747
*/
48-
const DECLARE_SCOPE_METRIC = 'Declare directive scope';
48+
const DECLARE_SCOPE_METRIC = 'Declare statement scope';
4949

5050
/**
5151
* Name of the metric.
@@ -57,10 +57,10 @@ class BlockModeSniff implements Sniff
5757
const DECLARE_TYPE_METRIC = 'Declare directive type';
5858

5959
/**
60-
* The option for the encoding directive.
60+
* Whether block mode is allowed for `encoding` directives.
6161
*
62-
* Can be one of: 'disallow', 'allow' (no preference), 'require'.
63-
* By default it's disallowed.
62+
* Can be one of: 'disallow', 'allow' (no preference), or 'require'.
63+
* Defaults to: 'disallow'.
6464
*
6565
* @since 1.0.0
6666
*
@@ -69,10 +69,10 @@ class BlockModeSniff implements Sniff
6969
public $encodingBlockMode = 'disallow';
7070

7171
/**
72-
* The option for the ticks directive.
72+
* Whether block mode is allowed for `ticks` directives.
7373
*
74-
* Can be one of: 'disallow', 'allow' (no preference), 'require'.
75-
* By default it's allowed.
74+
* Can be one of: 'disallow', 'allow' (no preference), or 'require'.
75+
* Defaults to: 'allow'.
7676
*
7777
* @since 1.0.0
7878
*
@@ -83,7 +83,7 @@ class BlockModeSniff implements Sniff
8383
/**
8484
* The default option for the strict_types directive.
8585
*
86-
* Only directive that cannot be written in block mode is strict_types.
86+
* Block mode is not allowed for the `strict_types` directive.
8787
* Using it in block mode will throw a PHP fatal error.
8888
*
8989
* @since 1.0.0
@@ -114,9 +114,7 @@ class BlockModeSniff implements Sniff
114114
*/
115115
public function register()
116116
{
117-
return [
118-
T_DECLARE
119-
];
117+
return [T_DECLARE];
120118
}
121119

122120
/**
@@ -146,10 +144,12 @@ public function process(File $phpcsFile, $stackPtr)
146144
// Get the next string and check if it's an allowed directive.
147145
// Find all the directive strings inside the declare statement.
148146
for ($i = $openParenPtr; $i <= $closeParenPtr; $i++) {
149-
if ($tokens[$i]['code'] === \T_STRING
150-
&& isset($this->allowedDirectives[\strtolower($tokens[$i]['content'])])
151-
) {
152-
$directiveStrings[$tokens[$i]['content']] = true;
147+
if ($tokens[$i]['code'] === \T_STRING) {
148+
$contentsLC = \strtolower($tokens[$i]['content']);
149+
if (isset($this->allowedDirectives[$contentsLC])) {
150+
$phpcsFile->recordMetric($i, self::DECLARE_TYPE_METRIC, $contentsLC);
151+
$directiveStrings[$contentsLC] = true;
152+
}
153153
}
154154
}
155155

@@ -163,40 +163,59 @@ public function process(File $phpcsFile, $stackPtr)
163163
$usesBlockMode = isset($tokens[$stackPtr]['scope_opener']);
164164

165165
if ($usesBlockMode) {
166-
$phpcsFile->recordMetric($stackPtr, self::DECLARE_SCOPE_METRIC, 'Block');
166+
$phpcsFile->recordMetric($stackPtr, self::DECLARE_SCOPE_METRIC, 'Block mode');
167167
} else {
168-
$phpcsFile->recordMetric($stackPtr, self::DECLARE_SCOPE_METRIC, 'Global');
168+
$phpcsFile->recordMetric($stackPtr, self::DECLARE_SCOPE_METRIC, 'File mode');
169169
}
170170

171171
// If strict types is defined using block mode, throw error.
172172
if ($usesBlockMode && isset($directiveStrings['strict_types'])) {
173-
$phpcsFile->addError(
174-
'strict_types declaration must not use block mode.',
175-
$stackPtr,
176-
'Forbidden'
177-
);
173+
$error = 'strict_types declaration must not use block mode.';
174+
$code = 'Forbidden';
175+
176+
if (isset($tokens[$stackPtr]['scope_closer'])) {
177+
// If there is no scope closer, we cannot auto-fix.
178+
$phpcsFile->addError($error, $stackPtr, $code);
179+
return;
180+
}
181+
182+
$fix = $phpcsFile->addFixableError($error, $stackPtr, $code);
183+
184+
if ($fix === true) {
185+
$phpcsFile->fixer->beginChangeset();
186+
$phpcsFile->fixer->addContent($closeParenPtr, ';');
187+
$phpcsFile->fixer->replaceToken($tokens[$stackPtr]['scope_opener'], '');
188+
189+
// Remove potential whitespace between parenthesis closer and the brace.
190+
for ($i = ($tokens[$stackPtr]['scope_opener'] - 1); $i > 0; $i--) {
191+
if ($tokens[$i]['code'] !== \T_WHITESPACE) {
192+
break;
193+
}
194+
195+
$phpcsFile->fixer->replaceToken($i, '');
196+
}
197+
198+
$phpcsFile->fixer->replaceToken($tokens[$stackPtr]['scope_closer'], '');
199+
$phpcsFile->fixer->endChangeset();
200+
}
178201
return;
179202
}
180203

181-
// Check if there is a code between the declare statement and opening brace/alternative syntax.
182-
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($closeParenPtr + 1), null, true);
183-
$directiveCloserTokens = [\T_SEMICOLON, \T_CLOSE_TAG, \T_OPEN_CURLY_BRACKET, \T_COLON];
184-
185-
if (!in_array($tokens[$nextNonEmpty]['code'], $directiveCloserTokens, true)) {
204+
// Check if there is code between the declare statement and opening brace/alternative syntax.
205+
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($closeParenPtr + 1), null, true);
206+
if ($tokens[$nextNonEmpty]['code'] !== \T_SEMICOLON
207+
&& $tokens[$nextNonEmpty]['code'] !== \T_CLOSE_TAG
208+
&& $tokens[$nextNonEmpty]['code'] !== \T_OPEN_CURLY_BRACKET
209+
&& $tokens[$nextNonEmpty]['code'] !== \T_COLON
210+
) {
186211
$phpcsFile->addError(
187-
'Unexpected code found after opening the declare statement without closing it.',
212+
'Unexpected code found after the declare statement.',
188213
$stackPtr,
189214
'UnexpectedCodeFound'
190215
);
191216
return;
192217
}
193218

194-
foreach (\array_keys($directiveStrings) as $directiveString) {
195-
if (isset($this->allowedDirectives[$directiveString])) {
196-
$phpcsFile->recordMetric($stackPtr, self::DECLARE_TYPE_METRIC, $directiveString);
197-
}
198-
}
199-
200219
// Multiple directives - if one requires block mode usage, other has to as well.
201220
if (count($directiveStrings) > 1
202221
&& (($this->encodingBlockMode === 'disallow' && $this->ticksBlockMode !== 'disallow')
@@ -205,7 +224,7 @@ public function process(File $phpcsFile, $stackPtr)
205224
$phpcsFile->addError(
206225
'Multiple directives found, but one of them is disallowing the use of block mode.',
207226
$stackPtr,
208-
'Forbidden'
227+
'Forbidden' // <= Duplicate error code for different message (line 175)
209228
);
210229
return;
211230
}

Universal/Tests/DeclareStatements/BlockModeUnitTest.1.inc

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,3 @@ echo 'hi!';
7676
}
7777

7878
declare ticks=1; // Intentional live coding error. Ignore.
79-
80-
/* Rest to the default directives for the next run */
81-
// phpcs:set Universal.DeclareStatements.BlockMode encodingBlockMode disallow
82-
// phpcs:set Universal.DeclareStatements.BlockMode ticksBlockMode allow

Universal/Tests/DeclareStatements/BlockModeUnitTest.2.inc

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,4 @@ declare(enCodINg='1');
77
declare(STRICT_TYPES=1);
88
declare(random_directive=false);
99

10-
/* Rest to the default directives for the next run */
11-
// phpcs:set Universal.DeclareStatements.BlockMode encodingBlockMode disallow
12-
// phpcs:set Universal.DeclareStatements.BlockMode ticksBlockMode allow
10+
// What's invalid about these ? (safe for line 4 and 8) Directives are case-INsensitive.

Universal/Tests/DeclareStatements/BlockModeUnitTest.3.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,6 @@ declare(strict_types=1, encoding='ISO-8859-1'): // Error.
4545
enddeclare;
4646
enddeclare;
4747

48-
/* Rest to the default directives for the next run */
48+
/* Reset to the default directives for the next run */
4949
// phpcs:set Universal.DeclareStatements.BlockMode encodingBlockMode disallow
5050
// phpcs:set Universal.DeclareStatements.BlockMode ticksBlockMode allow

Universal/Tests/DeclareStatements/BlockModeUnitTest.4.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,6 @@ declare(ticks=1): // Ok.
203203
enddeclare;
204204
enddeclare;
205205

206-
/* Rest to the default directives for the next run */
206+
/* Reset to the default directives for the next run */
207207
// phpcs:set Universal.DeclareStatements.BlockMode encodingBlockMode disallow
208208
// phpcs:set Universal.DeclareStatements.BlockMode ticksBlockMode allow

Universal/Tests/DeclareStatements/BlockModeUnitTest.5.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,6 @@ declare(ticks=1) { // Ok.
296296
// Code.
297297
}
298298

299-
/* Rest to the default directives for the next run */
299+
/* Reset to the default directives for the next run */
300300
// phpcs:set Universal.DeclareStatements.BlockMode encodingBlockMode disallow
301301
// phpcs:set Universal.DeclareStatements.BlockMode ticksBlockMode allow

0 commit comments

Comments
 (0)