@@ -30,8 +30,12 @@ final class NoEmptyStatementFixer extends AbstractFixer
3030 public function getDefinition ()
3131 {
3232 return new FixerDefinition (
33- 'Remove useless semicolon statements. ' ,
34- [new CodeSample ("<?php \$a = 1;; \n" )]
33+ 'Remove useless (semicolon) statements. ' ,
34+ [
35+ new CodeSample ("<?php \$a = 1;; \n" ),
36+ new CodeSample ("<?php echo 1;2; \n" ),
37+ new CodeSample ("<?php while(foo()){ \n continue 1; \n} \n" ),
38+ ]
3539 );
3640 }
3741
@@ -59,7 +63,17 @@ public function isCandidate(Tokens $tokens)
5963 protected function applyFix (\SplFileInfo $ file , Tokens $ tokens )
6064 {
6165 for ($ index = 0 , $ count = $ tokens ->count (); $ index < $ count ; ++$ index ) {
62- // skip T_FOR parenthesis to ignore duplicated `;` like `for ($i = 1; ; ++$i) {...}`
66+ if ($ tokens [$ index ]->isGivenKind ([T_BREAK , T_CONTINUE ])) {
67+ $ index = $ tokens ->getNextMeaningfulToken ($ index );
68+
69+ if ($ tokens [$ index ]->equals ([T_LNUMBER , '1 ' ])) {
70+ $ tokens ->clearTokenAndMergeSurroundingWhitespace ($ index );
71+ }
72+
73+ continue ;
74+ }
75+
76+ // skip T_FOR parenthesis to ignore double `;` like `for ($i = 1; ; ++$i) {...}`
6377 if ($ tokens [$ index ]->isGivenKind (T_FOR )) {
6478 $ index = $ tokens ->findBlockEnd (Tokens::BLOCK_TYPE_PARENTHESIS_BRACE , $ tokens ->getNextMeaningfulToken ($ index )) + 1 ;
6579
@@ -82,6 +96,19 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
8296 // A semicolon might be removed if it follows a '}' but only if the brace is part of certain structures.
8397 if ($ tokens [$ previousMeaningfulIndex ]->equals ('} ' )) {
8498 $ this ->fixSemicolonAfterCurlyBraceClose ($ tokens , $ index , $ previousMeaningfulIndex );
99+
100+ continue ;
101+ }
102+
103+ // A semicolon might be removed together with its noop statement, for example "<?php 1;"
104+ $ prePreviousMeaningfulIndex = $ tokens ->getPrevMeaningfulToken ($ previousMeaningfulIndex );
105+
106+ if (
107+ $ tokens [$ prePreviousMeaningfulIndex ]->equalsAny (['; ' , '{ ' , '} ' , [T_OPEN_TAG ]])
108+ && $ tokens [$ previousMeaningfulIndex ]->isGivenKind ([T_CONSTANT_ENCAPSED_STRING , T_DNUMBER , T_LNUMBER , T_STRING , T_VARIABLE ])
109+ ) {
110+ $ tokens ->clearTokenAndMergeSurroundingWhitespace ($ index );
111+ $ tokens ->clearTokenAndMergeSurroundingWhitespace ($ previousMeaningfulIndex );
85112 }
86113 }
87114 }
@@ -112,27 +139,27 @@ private function fixSemicolonAfterCurlyBraceClose(Tokens $tokens, $index, $curly
112139 }
113140
114141 $ curlyOpeningIndex = $ tokens ->findBlockStart (Tokens::BLOCK_TYPE_CURLY_BRACE , $ curlyCloseIndex );
115- $ beforeCurlyOpening = $ tokens ->getPrevMeaningfulToken ($ curlyOpeningIndex );
142+ $ beforeCurlyOpeningIndex = $ tokens ->getPrevMeaningfulToken ($ curlyOpeningIndex );
116143
117- if ($ tokens [$ beforeCurlyOpening ]->isGivenKind ($ beforeCurlyOpeningKinds ) || $ tokens [$ beforeCurlyOpening ]->equalsAny (['; ' , '{ ' , '} ' ])) {
144+ if ($ tokens [$ beforeCurlyOpeningIndex ]->isGivenKind ($ beforeCurlyOpeningKinds ) || $ tokens [$ beforeCurlyOpeningIndex ]->equalsAny (['; ' , '{ ' , '} ' ])) {
118145 $ tokens ->clearTokenAndMergeSurroundingWhitespace ($ index );
119146
120147 return ;
121148 }
122149
123150 // check for namespaces and class, interface and trait definitions
124- if ($ tokens [$ beforeCurlyOpening ]->isGivenKind (T_STRING )) {
125- $ classyTest = $ tokens ->getPrevMeaningfulToken ($ beforeCurlyOpening );
151+ if ($ tokens [$ beforeCurlyOpeningIndex ]->isGivenKind (T_STRING )) {
152+ $ classyTestIndex = $ tokens ->getPrevMeaningfulToken ($ beforeCurlyOpeningIndex );
126153
127- while ($ tokens [$ classyTest ]->equals (', ' ) || $ tokens [$ classyTest ]->isGivenKind ([T_STRING , T_NS_SEPARATOR , T_EXTENDS , T_IMPLEMENTS ])) {
128- $ classyTest = $ tokens ->getPrevMeaningfulToken ($ classyTest );
154+ while ($ tokens [$ classyTestIndex ]->equals (', ' ) || $ tokens [$ classyTestIndex ]->isGivenKind ([T_STRING , T_NS_SEPARATOR , T_EXTENDS , T_IMPLEMENTS ])) {
155+ $ classyTestIndex = $ tokens ->getPrevMeaningfulToken ($ classyTestIndex );
129156 }
130157
131158 $ tokensAnalyzer = new TokensAnalyzer ($ tokens );
132159
133160 if (
134- $ tokens [$ classyTest ]->isGivenKind (T_NAMESPACE ) ||
135- ($ tokens [$ classyTest ]->isClassy () && !$ tokensAnalyzer ->isAnonymousClass ($ classyTest ))
161+ $ tokens [$ classyTestIndex ]->isGivenKind (T_NAMESPACE )
162+ || ($ tokens [$ classyTestIndex ]->isClassy () && !$ tokensAnalyzer ->isAnonymousClass ($ classyTestIndex ))
136163 ) {
137164 $ tokens ->clearTokenAndMergeSurroundingWhitespace ($ index );
138165 }
@@ -141,24 +168,24 @@ private function fixSemicolonAfterCurlyBraceClose(Tokens $tokens, $index, $curly
141168 }
142169
143170 // early return check, below only control structures with conditions are fixed
144- if (!$ tokens [$ beforeCurlyOpening ]->equals (') ' )) {
171+ if (!$ tokens [$ beforeCurlyOpeningIndex ]->equals (') ' )) {
145172 return ;
146173 }
147174
148- $ openingBrace = $ tokens ->findBlockStart (Tokens::BLOCK_TYPE_PARENTHESIS_BRACE , $ beforeCurlyOpening );
149- $ beforeOpeningBrace = $ tokens ->getPrevMeaningfulToken ($ openingBrace );
175+ $ openingBraceIndex = $ tokens ->findBlockStart (Tokens::BLOCK_TYPE_PARENTHESIS_BRACE , $ beforeCurlyOpeningIndex );
176+ $ beforeOpeningBraceIndex = $ tokens ->getPrevMeaningfulToken ($ openingBraceIndex );
150177
151- if ($ tokens [$ beforeOpeningBrace ]->isGivenKind ([T_IF , T_ELSEIF , T_FOR , T_FOREACH , T_WHILE , T_SWITCH , T_CATCH , T_DECLARE ])) {
178+ if ($ tokens [$ beforeOpeningBraceIndex ]->isGivenKind ([T_IF , T_ELSEIF , T_FOR , T_FOREACH , T_WHILE , T_SWITCH , T_CATCH , T_DECLARE ])) {
152179 $ tokens ->clearTokenAndMergeSurroundingWhitespace ($ index );
153180
154181 return ;
155182 }
156183
157184 // check for function definition
158- if ($ tokens [$ beforeOpeningBrace ]->isGivenKind (T_STRING )) {
159- $ beforeString = $ tokens ->getPrevMeaningfulToken ($ beforeOpeningBrace );
185+ if ($ tokens [$ beforeOpeningBraceIndex ]->isGivenKind (T_STRING )) {
186+ $ beforeStringIndex = $ tokens ->getPrevMeaningfulToken ($ beforeOpeningBraceIndex );
160187
161- if ($ tokens [$ beforeString ]->isGivenKind (T_FUNCTION )) {
188+ if ($ tokens [$ beforeStringIndex ]->isGivenKind (T_FUNCTION )) {
162189 $ tokens ->clearTokenAndMergeSurroundingWhitespace ($ index ); // implicit return
163190 }
164191 }
0 commit comments