Umherirrender has uploaded a new change for review. https://gerrit.wikimedia.org/r/61178
Change subject: Add a SpacesAroundSomeTokensSniff ...................................................................... Add a SpacesAroundSomeTokensSniff This sniff was used to find and fixed many spacing issues in mediawiki core, see https://gerrit.wikimedia.org/r/#/q/project:mediawiki/core+branch:master+topic:fix-spacing,n,z for an overview. This sniff has still false positive, so be carefully, when using. Feel free to expand or rename it. Change-Id: I2cbacf9b9987d23aec6b0f10a6d3f9e760126087 --- A MediaWiki/Sniffs/NamingConventions/SpacesAroundSomeTokensSniff.php 1 file changed, 427 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/tools/codesniffer refs/changes/78/61178/1 diff --git a/MediaWiki/Sniffs/NamingConventions/SpacesAroundSomeTokensSniff.php b/MediaWiki/Sniffs/NamingConventions/SpacesAroundSomeTokensSniff.php new file mode 100644 index 0000000..23abd6c --- /dev/null +++ b/MediaWiki/Sniffs/NamingConventions/SpacesAroundSomeTokensSniff.php @@ -0,0 +1,427 @@ +<?php +/** + * Check, if there are correct number of spaces around some tokens + */ +class MediaWiki_Sniffs_NamingConventions_SpacesAroundSomeTokensSniff implements PHP_CodeSniffer_Sniff { + + private $spaceOptions = array( + # string operations + T_STRING_CONCAT => array( // . + 'before' => 1, + 'after' => 1, + ), + T_CONCAT_EQUAL => array( // .= + 'before' => 1, + 'after' => 1, + ), + + # logical operations + T_GREATER_THAN => array( // > + 'before' => 1, + 'after' => 1, + ), + T_IS_GREATER_OR_EQUAL => array( // >= + 'before' => 1, + 'after' => 1, + ), + T_IS_EQUAL => array( // == + 'before' => 1, + 'after' => 1, + ), + T_IS_IDENTICAL => array( // === + 'before' => 1, + 'after' => 1, + ), + T_IS_NOT_EQUAL => array( // != or <> + 'before' => 1, + 'after' => 1, + ), + T_IS_NOT_IDENTICAL => array( // !== + 'before' => 1, + 'after' => 1, + ), + T_IS_SMALLER_OR_EQUAL => array( // <= + 'before' => 1, + 'after' => 1, + ), + T_LESS_THAN => array( // < + 'before' => 1, + 'after' => 1, + ), + + # Shifting + T_SL => array( // << + 'before' => 1, + 'after' => 1, + ), + T_SL_EQUAL => array( // <<= + 'before' => 1, + 'after' => 1, + ), + T_SR => array( // >> + 'before' => 1, + 'after' => 1, + ), + T_SR_EQUAL => array( // >>= + 'before' => 1, + 'after' => 1, + ), + + # operations + T_PLUS => array( // + + 'before' => 1, + 'after' => 1, + ), + T_MINUS => array( // - + 'before' => 1, 'beforeOK' => T_OPEN_SQUARE_BRACKET, # okay as array index + # no test after, because -1 standalone is okay + ), + T_MULTIPLY => array( // * + 'before' => 1, + 'after' => 1, + ), + T_DIVIDE => array( // / + 'before' => 1, + 'after' => 1, + ), + T_MODULUS => array( // % + 'before' => 1, + 'after' => 1, + ), + T_POWER => array( // ^ + 'before' => 1, + 'after' => 1, + ), + + # operation and assignment + T_AND_EQUAL => array( // &= + 'before' => 1, + 'after' => 1, + ), + T_OR_EQUAL => array( // |= + 'before' => 1, + 'after' => 1, + ), + T_XOR_EQUAL => array( // ^= + 'before' => 1, + 'after' => 1, + ), + T_PLUS_EQUAL => array( // += + 'before' => 1, + 'after' => 1, + ), + T_MINUS_EQUAL => array( // -= + 'before' => 1, + 'after' => 1, + ), + T_MUL_EQUAL => array( // *= + 'before' => 1, + 'after' => 1, + ), + T_DIV_EQUAL => array( // /= + 'before' => 1, + 'after' => 1, + ), + T_MOD_EQUAL => array( // %= + 'before' => 1, + 'after' => 1, + ), + + # conditions + T_LOGICAL_AND => array( // and + 'before' => 1, + 'after' => 1, + ), + T_LOGICAL_OR => array( // or + 'before' => 1, + 'after' => 1, + ), + T_LOGICAL_XOR => array( // xor + 'before' => 1, + 'after' => 1, + ), + T_BITWISE_AND => array( // &, also used as pass-by-references + 'before' => 1, 'beforeOK' => T_EQUAL, # OK: =& + 'after' => 1, 'afterOK' => array( T_VARIABLE, T_SELF, T_STRING ), # OK: &$var or &self:: or &functionName + ), + T_BITWISE_OR => array( // | + 'before' => 1, + 'after' => 1, + ), + T_BOOLEAN_AND => array( // && + 'before' => 1, + 'after' => 1, + ), + T_BOOLEAN_OR => array( // || + 'before' => 1, + 'after' => 1, + ), + T_BOOLEAN_NOT => array( // ! + 'before' => 1, 'beforeOK' => T_BOOLEAN_NOT, # some !! exists + # TODO no testing after, because some ! have it + ), + T_TRUE => array( // true + 'before' => 1, + 'after' => 1, 'afterOK' => array( T_COMMA, T_SEMICOLON, T_COLON ), # OK: true, true; true: + ), + T_FALSE => array( // false + 'before' => 1, + 'after' => 1, 'afterOK' => array( T_COMMA, T_SEMICOLON, T_COLON ), # OK: false, false; false: + ), + + # statements + T_IF => array( // if + 'before' => 1, 'beforeOK' => T_OPEN_TAG, + 'after' => 1, + ), + T_ELSEIF => array( // elseif + 'before' => 1, + 'after' => 1, + ), + T_ELSE => array( // else + 'before' => 1, + 'after' => 1, + ), + T_FOR => array( // for + 'before' => 1, 'beforeOK' => T_OPEN_TAG, + 'after' => 1, + ), + T_FOREACH => array( // foreach + 'before' => 1, 'beforeOK' => T_OPEN_TAG, + 'after' => 1, + ), + T_WHILE => array( // while + 'before' => 1, 'beforeOK' => T_OPEN_TAG, + 'after' => 1, + ), + T_DO => array( // do + 'before' => 1, + 'after' => 1, + ), + T_SWITCH => array( // switch + 'before' => 1, + 'after' => 1, + ), + T_TRY => array( // try + 'before' => 1, + 'after' => 1, + ), + T_CATCH => array( // try + 'before' => 1, + 'after' => 1, + ), + + # various + T_DOUBLE_ARROW => array( // => (array or foreach) + 'before' => 1, 'beforeOKin' => T_ARRAY, # TODO too many false positive in arrays at the moment + 'after' => 1, 'afterOKin' => T_ARRAY, # TODO too many false positive in arrays at the moment + ), + T_EQUAL => array( // = + 'before' => 1, + 'after' => 1, 'afterOK' => T_BITWISE_AND, # OK: =& (pass-by-references) + ), + T_AS => array( // as + 'before' => 1, + 'after' => 1, + ), + T_NEW => array( // new + 'before' => 1, + 'after' => 1, + ), + T_ARRAY => array( // array + 'before' => 1, 'beforeOK' => T_OBJECT_CAST, # OK: (object)array() + 'after' => 0, + ), + T_LIST => array( // list + 'before' => 1, 'beforeOK' => T_ASPERAND, # OK: @list + 'after' => 0, + ), + T_ISSET => array( // isset + 'before' => 1, 'beforeOK' => array( T_BOOLEAN_NOT, T_OPEN_SQUARE_BRACKET ), # OK: !isset [isset + 'after' => 0, + ), + T_UNSET => array( // unset + 'before' => 1, + 'after' => 0, + ), + T_EXIT => array( // exit or die + 'before' => 1, + 'after' => 0, + ), + T_EMPTY => array( // empty + 'before' => 1, 'beforeOK' => array( T_BOOLEAN_NOT, T_OPEN_SQUARE_BRACKET ), # OK: !empty [empty + 'after' => 0, + ), + T_GLOBAL => array( // global + 'before' => 0, 'beforeOK' => T_OPEN_TAG, + 'after' => 1, + ), + T_RETURN => array( // return + 'before' => 0, 'beforeOK' => array( T_COLON, T_OPEN_TAG, T_OPEN_CURLY_BRACKET ), #OK: case ...: return + 'after' => 1, 'afterOK' => T_SEMICOLON, # OK: return; + ), + T_COMMA => array( // , + 'before' => 0, 'beforeOKin' => T_LIST, # OK list( , , $var ) + 'after' => 1, 'afterOKin' => T_ARRAY, # TODO too many false positive in arrays at the moment + ), + T_SEMICOLON => array( // ; + 'before' => 0, 'beforeOKin' => T_FOR, # OK: for ( ; ...; ... ) + 'after' => 0, 'afterOK' => array( T_CLOSE_TAG, T_CLOSE_CURLY_BRACKET ), # OK: ; ? > + 'afterOKin' => T_FOR, #OK: for ( ...; ...; ... ) + ), + T_INLINE_THEN => array( // ? + 'before' => 1, + 'after' => 1, 'afterOK' => T_COLON, # OK: ?: + ), + T_COLON => array( // : + # TODO before: 0 when used in T_SWITCH with T_CASE - 1 when used in T_INLINE_THEN + 'after' => 1, + ), + T_DOUBLE_COLON => array( // :: + 'before' => 0, + 'after' => 0, + ), + T_OBJECT_OPERATOR => array( // -> + 'before' => 0, + 'after' => 0, + ), + T_CONSTANT_ENCAPSED_STRING => array( // "" or '' + 'before' => 1, 'beforeOK' => array( T_OPEN_SQUARE_BRACKET, T_CLOSE_SQUARE_BRACKET, T_CONSTANT_ENCAPSED_STRING ), + 'beforeOKin' => T_ARRAY, # TODO too many false positive at the moment with T_COMMA + 'after' => 1, 'afterOK' => array( T_OPEN_SQUARE_BRACKET, T_CLOSE_SQUARE_BRACKET, T_SEMICOLON, T_COMMA, T_COLON, T_CONSTANT_ENCAPSED_STRING ), + 'afterOKin' => T_ARRAY, # TODO too many false positive at the moment with T_COMMA + ), + T_ENCAPSED_AND_WHITESPACE => array( // "" or '' + 'before' => 1, 'beforeOK' => array( T_OPEN_SQUARE_BRACKET, T_CLOSE_SQUARE_BRACKET, T_BACKTICK, T_CONSTANT_ENCAPSED_STRING ), + 'beforeOKin' => T_ARRAY, # TODO too many false positive at the moment with T_COMMA + 'after' => 1, 'afterOK' => array( T_OPEN_SQUARE_BRACKET, T_CLOSE_SQUARE_BRACKET, T_SEMICOLON, T_COMMA, T_COLON, T_BACKTICK, T_CONSTANT_ENCAPSED_STRING ), + 'afterOKin' => T_ARRAY, # TODO too many false positive at the moment with T_COMMA + ), + T_OPEN_PARENTHESIS => array( // ( + # TODO before: 0 when T_STRING or other functions - 1 when if foreach or conditions + 'after' => 1, 'afterOK' => T_CLOSE_PARENTHESIS, # OK: () + ), + T_CLOSE_PARENTHESIS => array( // ( + 'before' => 1, 'beforeOK' => array( T_OPEN_PARENTHESIS, T_CLOSE_PARENTHESIS ), # OK: () ))) + 'beforeOKin' => T_ARRAY, # TODO too many false positive in arrays at the moment + 'after' => 1, 'afterOK' => array( T_SEMICOLON, T_COMMA, T_OBJECT_OPERATOR, T_CLOSE_SQUARE_BRACKET, T_CLOSE_PARENTHESIS ), # OK: ); ), )) )] )-> + ), + T_OPEN_CURLY_BRACKET => array( // { + 'before' => 1, 'beforeOK' => array( T_OPEN_TAG, T_OBJECT_OPERATOR ), + 'after' => 1, 'afterOK' => array( T_CLOSE_CURLY_BRACKET, T_CLOSE_TAG ), + ), + T_CLOSE_CURLY_BRACKET => array( // } + 'before' => 1, 'beforeOK' => array( T_OPEN_CURLY_BRACKET, T_OPEN_TAG ), + 'after' => 1, 'afterOK' => array( T_COMMA, T_SEMICOLON, T_CLOSE_TAG ), + ), + ); + + public function register() { + return array_keys( + $this->spaceOptions + ); + } + + public function process( PHP_CodeSniffer_File $phpcsFile, $stackPtr ) { + $tokens = $phpcsFile->getTokens(); + $maxLength = count( $tokens ); + $code = $tokens[$stackPtr]['code']; + $options = $this->spaceOptions[$code]; + if ( isset( $options['before'] ) ) { + $expectedSpacesBefore = $options['before']; + $beforeOK = array( T_COMMENT, T_DOC_COMMENT ); //allow comments every time + if ( isset( $options['beforeOK'] ) ) { + if ( !is_array( $options['beforeOK'] ) ) { + $options['beforeOK'] = array( $options['beforeOK'] ); + } + $beforeOK = array_merge( $beforeOK, $options['beforeOK'] ); + } + $beforeOKin = array(); + if ( isset( $options['beforeOKin'] ) ) { + $beforeOKin = $options['beforeOKin']; + if ( !is_array( $beforeOKin ) ) { + $beforeOKin = array( $beforeOKin ); + } + } + + $spaceBefore = 0; + $tokenSpaceBefore = $stackPtr; + while ( $tokenSpaceBefore > 0 && $tokens[$tokenSpaceBefore - 1]['code'] === T_WHITESPACE ) { + $tokenSpaceBefore--; + $spaceBefore += strlen( $tokens[$tokenSpaceBefore]['content'] ); + } + + //ignore multiline statements + if ( $spaceBefore !== $expectedSpacesBefore && + $tokens[$tokenSpaceBefore]['content'] !== "\n" && + !in_array( $tokens[$tokenSpaceBefore - 1]['code'], $beforeOK, true ) && + !$this->isInsideParenthesisOwner( $tokens, $stackPtr, $beforeOKin ) + ) { + $error = 'Found %s space(s) before token \'%s\', expected %s space(s)'; + $type = 'SpaceBefore' . $this->getTokenInCamelCase( $tokens[$stackPtr]['type'] ); + $data = array( $spaceBefore, $tokens[$stackPtr]['content'], $expectedSpacesBefore ); + $phpcsFile->addError( $error, $stackPtr, $type, $data ); + } + } + + if ( isset( $options['after'] ) ) { + $expectedSpacesAfter = $options['after']; + $afterOK = array( T_COMMENT, T_DOC_COMMENT ); //allow comments every time + if ( isset( $options['afterOK'] ) ) { + if ( !is_array( $options['afterOK'] ) ) { + $options['afterOK'] = array( $options['afterOK'] ); + } + $afterOK = array_merge( $afterOK, $options['afterOK'] ); + } + $afterOKin = array(); + if ( isset( $options['afterOKin'] ) ) { + $afterOKin = $options['afterOKin']; + if ( !is_array( $afterOKin ) ) { + $afterOKin = array( $afterOKin ); + } + } + + $spaceAfter = 0; + $tokenSpaceAfter = $stackPtr; + while ( $tokenSpaceAfter + 1 < $maxLength && $tokens[$tokenSpaceAfter + 1]['code'] === T_WHITESPACE ) { + $tokenSpaceAfter++; + $spaceAfter += strlen( $tokens[$tokenSpaceAfter]['content'] ); + } + + //ignore multiline concatenation + if ( $spaceAfter !== $expectedSpacesAfter && + $tokens[$stackPtr + 1]['content'] !== "\n" && + !in_array( $tokens[$tokenSpaceAfter + 1]['code'], $afterOK, true ) && + !$this->isInsideParenthesisOwner( $tokens, $stackPtr, $afterOKin ) + ) { + $error = 'Found %s space(s) after token \'%s\', expected %s space(s)'; + $type = 'SpaceAfter' . $this->getTokenInCamelCase( $tokens[$stackPtr]['type'] ); + $data = array( $spaceAfter, $tokens[$stackPtr]['content'], $expectedSpacesAfter ); + $phpcsFile->addError( $error, $stackPtr, $type, $data ); + } + } + } + + private function isInsideParenthesisOwner( $tokens, $stackPtr, $codes ) { + if ( !isset( $tokens[$stackPtr]['nested_parenthesis'] ) || !count( $codes ) ) { + return false; + } + $nestedParenthesis = $tokens[$stackPtr]['nested_parenthesis']; + foreach ( $nestedParenthesis as $open => $close ) { + if ( isset( $tokens[$open]['parenthesis_owner'] ) ) { + $parenthesisOwner = $tokens[$open]['parenthesis_owner']; + } else { + $parenthesisOwner = $open - 1; //maybe wrong due to T_WHITESPACE + } + if ( in_array( $tokens[$parenthesisOwner]['code'], $codes, true ) ) { + return true; + } + } + } + + private function getTokenInCamelCase( $token ) { + $tokenArray = explode( '_', strtolower( $token ) ); + array_shift( $tokenArray ); //remove T_ + return implode( '', array_map( 'ucfirst', $tokenArray ) ); + } +} -- To view, visit https://gerrit.wikimedia.org/r/61178 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I2cbacf9b9987d23aec6b0f10a6d3f9e760126087 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/tools/codesniffer Gerrit-Branch: master Gerrit-Owner: Umherirrender <umherirrender_de...@web.de> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits