@@ -62,14 +62,19 @@ public function process(File $phpcsFile, $stackPtr) {
6262 $ this ->loadStatements ($ phpcsFile );
6363 $ this ->findSentinel ($ phpcsFile );
6464
65- if ($ tokens [$ stackPtr ]['code ' ] === T_CLASS ) {
65+ if ($ tokens [$ stackPtr ]['code ' ] === T_CLASS || $ tokens [ $ stackPtr ][ ' code ' ] === T_INTERFACE || $ tokens [ $ stackPtr ][ ' code ' ] === T_TRAIT ) {
6666 $ this ->checkUseForClass ($ phpcsFile , $ stackPtr );
6767 } elseif ($ tokens [$ stackPtr ]['code ' ] === T_NEW ) {
6868 $ this ->checkUseForNew ($ phpcsFile , $ stackPtr );
6969 } elseif ($ tokens [$ stackPtr ]['code ' ] === T_DOUBLE_COLON ) {
7070 $ this ->checkUseForStatic ($ phpcsFile , $ stackPtr );
71+ } elseif ($ tokens [$ stackPtr ]['code ' ] === T_INSTANCEOF ) {
72+ $ this ->checkUseForInstanceOf ($ phpcsFile , $ stackPtr );
73+ } elseif ($ tokens [$ stackPtr ]['code ' ] === T_CATCH || $ tokens [$ stackPtr ]['code ' ] === T_CALLABLE ) {
74+ $ this ->checkUseForCatchOrCallable ($ phpcsFile , $ stackPtr );
7175 } else {
7276 $ this ->checkUseForSignature ($ phpcsFile , $ stackPtr );
77+ $ this ->checkUseForReturnTypeHint ($ phpcsFile , $ stackPtr );
7378 }
7479 $ this ->insertUseWhenSentinel ($ phpcsFile , $ stackPtr );
7580 }
@@ -575,6 +580,218 @@ protected function checkUseForStatic(File $phpcsFile, $stackPtr) {
575580 $ phpcsFile ->fixer ->endChangeset ();
576581 }
577582
583+ /**
584+ * @param \PHP_CodeSniffer\Files\File $phpcsFile
585+ * @param int $stackPtr
586+ *
587+ * @return void
588+ */
589+ protected function checkUseForInstanceOf (File $ phpcsFile , $ stackPtr ) {
590+ $ tokens = $ phpcsFile ->getTokens ();
591+
592+ $ classNameIndex = $ phpcsFile ->findNext (Tokens::$ emptyTokens , $ stackPtr + 1 , null , true );
593+
594+ $ lastIndex = null ;
595+ $ i = $ classNameIndex ;
596+ $ extractedUseStatement = '' ;
597+ $ lastSeparatorIndex = null ;
598+ while (true ) {
599+ if (!$ this ->isGivenKind ([T_NS_SEPARATOR , T_STRING ], $ tokens [$ i ])) {
600+ break ;
601+ }
602+ $ lastIndex = $ i ;
603+ $ extractedUseStatement .= $ tokens [$ i ]['content ' ];
604+
605+ if ($ this ->isGivenKind ([T_NS_SEPARATOR ], $ tokens [$ i ])) {
606+ $ lastSeparatorIndex = $ i ;
607+ }
608+ ++$ i ;
609+ }
610+
611+ if ($ lastIndex === null || $ lastSeparatorIndex === null ) {
612+ return ;
613+ }
614+
615+ $ extractedUseStatement = ltrim ($ extractedUseStatement , '\\' );
616+
617+ $ className = '' ;
618+ for ($ i = $ lastSeparatorIndex + 1 ; $ i <= $ lastIndex ; ++$ i ) {
619+ $ className .= $ tokens [$ i ]['content ' ];
620+ }
621+
622+ $ error = 'Use statement ' . $ extractedUseStatement . ' for class ' . $ className . ' should be in use block. ' ;
623+ $ fix = $ phpcsFile ->addFixableError ($ error , $ stackPtr , 'InstanceOf ' );
624+ if (!$ fix ) {
625+ return ;
626+ }
627+
628+ $ phpcsFile ->fixer ->beginChangeset ();
629+
630+ $ addedUseStatement = $ this ->addUseStatement ($ className , $ extractedUseStatement );
631+ $ firstSeparatorIndex = $ classNameIndex ;
632+
633+ for ($ k = $ lastSeparatorIndex ; $ k > $ firstSeparatorIndex ; --$ k ) {
634+ $ phpcsFile ->fixer ->replaceToken ($ k , '' );
635+ }
636+ $ phpcsFile ->fixer ->replaceToken ($ firstSeparatorIndex , '' );
637+
638+ if ($ addedUseStatement ['alias ' ] !== null ) {
639+ $ phpcsFile ->fixer ->replaceToken ($ lastIndex , $ addedUseStatement ['alias ' ]);
640+ for ($ k = $ lastSeparatorIndex + 1 ; $ k < $ lastIndex ; ++$ k ) {
641+ $ phpcsFile ->fixer ->replaceToken ($ k , '' );
642+ }
643+ }
644+
645+ $ phpcsFile ->fixer ->endChangeset ();
646+ }
647+
648+ /**
649+ * @param \PHP_CodeSniffer\Files\File $phpcsFile
650+ * @param int $stackPtr
651+ *
652+ * @return void
653+ */
654+ public function checkUseForCatchOrCallable (File $ phpcsFile , $ stackPtr ) {
655+ $ tokens = $ phpcsFile ->getTokens ();
656+
657+ $ openParenthesisIndex = $ phpcsFile ->findNext (T_OPEN_PARENTHESIS , $ stackPtr + 1 );
658+ $ closeParenthesisIndex = $ tokens [$ openParenthesisIndex ]['parenthesis_closer ' ];
659+ $ classNameIndex = $ phpcsFile ->findNext (Tokens::$ emptyTokens , $ openParenthesisIndex + 1 , null , true );
660+
661+ $ lastIndex = null ;
662+ $ i = $ classNameIndex ;
663+ $ extractedUseStatement = '' ;
664+ $ lastSeparatorIndex = null ;
665+ while ($ i < $ closeParenthesisIndex ) {
666+ if (!$ this ->isGivenKind ([T_NS_SEPARATOR , T_STRING ], $ tokens [$ i ])) {
667+ break ;
668+ }
669+ $ lastIndex = $ i ;
670+ $ extractedUseStatement .= $ tokens [$ i ]['content ' ];
671+
672+ if ($ this ->isGivenKind ([T_NS_SEPARATOR ], $ tokens [$ i ])) {
673+ $ lastSeparatorIndex = $ i ;
674+ }
675+ ++$ i ;
676+ }
677+
678+ if ($ lastIndex === null || $ lastSeparatorIndex === null ) {
679+ return ;
680+ }
681+
682+ $ extractedUseStatement = ltrim ($ extractedUseStatement , '\\' );
683+
684+ $ className = '' ;
685+ for ($ k = $ lastSeparatorIndex + 1 ; $ k <= $ lastIndex ; ++$ k ) {
686+ $ className .= $ tokens [$ k ]['content ' ];
687+ }
688+
689+ $ error = 'Use statement ' . $ extractedUseStatement . ' for class ' . $ className . ' should be in use block. ' ;
690+ $ fix = $ phpcsFile ->addFixableError ($ error , $ stackPtr , 'Catch ' );
691+ if (!$ fix ) {
692+ return ;
693+ }
694+
695+ $ startIndex = $ openParenthesisIndex ;
696+
697+ $ phpcsFile ->fixer ->beginChangeset ();
698+
699+ $ firstSeparatorIndex = $ phpcsFile ->findNext (Tokens::$ emptyTokens , $ startIndex + 1 , null , true );
700+
701+ $ addedUseStatement = $ this ->addUseStatement ($ className , $ extractedUseStatement );
702+
703+ for ($ k = $ lastSeparatorIndex ; $ k > $ firstSeparatorIndex ; --$ k ) {
704+ $ phpcsFile ->fixer ->replaceToken ($ k , '' );
705+ }
706+ $ phpcsFile ->fixer ->replaceToken ($ firstSeparatorIndex , '' );
707+
708+ if ($ addedUseStatement ['alias ' ] !== null ) {
709+ $ phpcsFile ->fixer ->replaceToken ($ firstSeparatorIndex + 1 , $ addedUseStatement ['alias ' ]);
710+ for ($ i = $ firstSeparatorIndex + 2 ; $ i <= $ lastIndex ; ++$ i ) {
711+ $ phpcsFile ->fixer ->replaceToken ($ i , '' );
712+ }
713+ }
714+
715+ $ phpcsFile ->fixer ->endChangeset ();
716+ }
717+
718+ /**
719+ * @param \PHP_CodeSniffer\Files\File $phpcsFile
720+ * @param int $stackPtr
721+ *
722+ * @return void
723+ */
724+ protected function checkUseForReturnTypeHint (File $ phpcsFile , $ stackPtr ) {
725+ $ tokens = $ phpcsFile ->getTokens ();
726+
727+ $ openParenthesisIndex = $ phpcsFile ->findNext (T_OPEN_PARENTHESIS , $ stackPtr + 1 );
728+ $ closeParenthesisIndex = $ tokens [$ openParenthesisIndex ]['parenthesis_closer ' ];
729+
730+ $ colonIndex = $ phpcsFile ->findNext (Tokens::$ emptyTokens , $ closeParenthesisIndex + 1 , null , true );
731+ if (!$ colonIndex ) {
732+ return ;
733+ }
734+
735+ $ startIndex = $ phpcsFile ->findNext (Tokens::$ emptyTokens , $ colonIndex + 1 , $ colonIndex + 3 , true );
736+ if (!$ startIndex ) {
737+ return ;
738+ }
739+
740+ if ($ tokens [$ startIndex ]['type ' ] === 'T_NULLABLE ' ) {
741+ $ startIndex = $ phpcsFile ->findNext (Tokens::$ emptyTokens , $ startIndex + 1 , $ startIndex + 3 , true );
742+ }
743+
744+ $ lastIndex = null ;
745+ $ j = $ startIndex ;
746+ $ extractedUseStatement = '' ;
747+ $ lastSeparatorIndex = null ;
748+ while (true ) {
749+ if (!$ this ->isGivenKind ([T_NS_SEPARATOR , T_STRING , T_RETURN_TYPE ], $ tokens [$ j ])) {
750+ break ;
751+ }
752+
753+ $ lastIndex = $ j ;
754+ $ extractedUseStatement .= $ tokens [$ j ]['content ' ];
755+ if ($ this ->isGivenKind ([T_NS_SEPARATOR ], $ tokens [$ j ])) {
756+ $ lastSeparatorIndex = $ j ;
757+ }
758+ ++$ j ;
759+ }
760+
761+ if ($ lastIndex === null || $ lastSeparatorIndex === null ) {
762+ return ;
763+ }
764+
765+ $ extractedUseStatement = ltrim ($ extractedUseStatement , '\\' );
766+ $ className = '' ;
767+ for ($ k = $ lastSeparatorIndex + 1 ; $ k <= $ lastIndex ; ++$ k ) {
768+ $ className .= $ tokens [$ k ]['content ' ];
769+ }
770+
771+ $ error = 'Use statement ' . $ extractedUseStatement . ' for class ' . $ className . ' should be in use block. ' ;
772+ $ fix = $ phpcsFile ->addFixableError ($ error , $ colonIndex , 'ReturnSignature ' );
773+ if (!$ fix ) {
774+ return ;
775+ }
776+
777+ $ phpcsFile ->fixer ->beginChangeset ();
778+
779+ $ firstSeparatorIndex = $ phpcsFile ->findNext (Tokens::$ emptyTokens , $ startIndex , null , true );
780+
781+ $ addedUseStatement = $ this ->addUseStatement ($ className , $ extractedUseStatement );
782+
783+ for ($ k = $ lastSeparatorIndex ; $ k > $ firstSeparatorIndex ; --$ k ) {
784+ $ phpcsFile ->fixer ->replaceToken ($ k , '' );
785+ }
786+ $ phpcsFile ->fixer ->replaceToken ($ firstSeparatorIndex , '' );
787+
788+ if ($ addedUseStatement ['alias ' ] !== null ) {
789+ $ phpcsFile ->fixer ->replaceToken ($ lastIndex , $ addedUseStatement ['alias ' ]);
790+ }
791+
792+ $ phpcsFile ->fixer ->endChangeset ();
793+ }
794+
578795 /**
579796 * @param \PHP_CodeSniffer\Files\File $phpcsFile
580797 * @param int $stackPtr
0 commit comments