Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid incompatibility between Generic and PSR2 standards for function call indentation #38

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/Standards/Generic/Tests/WhiteSpace/ScopeIndentUnitTest.3.inc
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,18 @@ if ($foo) {
$this->foo()
->bar()
->baz();

// https://github.com/squizlabs/PHP_CodeSniffer/issues/2078#issuecomment-401641650
// See also PSR2.Methods.FunctionCallSignature
$repository->foo()
->bar(
function () {
return true;
}
);
$repository->foo()
->bar(
function () {
return true;
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,18 @@ if ($foo) {
$this->foo()
->bar()
->baz();

// https://github.com/squizlabs/PHP_CodeSniffer/issues/2078#issuecomment-401641650
// See also PSR2.Methods.FunctionCallSignature
$repository->foo()
->bar(
function () {
return true;
}
);
$repository->foo()
->bar(
function () {
return true;
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ public function getErrorList($testFile='')
6 => 1,
7 => 1,
10 => 1,
40 => 1,
41 => 1,
42 => 1,
];
}

Expand Down
130 changes: 99 additions & 31 deletions src/Standards/PEAR/Sniffs/Functions/FunctionCallSignatureSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ public function processMultiLineCall(File $phpcsFile, $stackPtr, $openBracket, $
// We need to work out how far indented the function
// call itself is, so we can work out how far to
// indent the arguments.
$first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $stackPtr, true);
$first = $phpcsFile->findFirstOnLine([T_WHITESPACE, T_INLINE_HTML], $stackPtr, true);
if ($first !== false
&& $tokens[$first]['code'] === T_CONSTANT_ENCAPSED_STRING
&& $tokens[($first - 1)]['code'] === T_CONSTANT_ENCAPSED_STRING
Expand Down Expand Up @@ -376,30 +376,16 @@ public function processMultiLineCall(File $phpcsFile, $stackPtr, $openBracket, $
// at a tab stop. Without this, the function will be indented a further
// $indent spaces to the right.
$functionIndent = (int) (floor($foundFunctionIndent / $this->indent) * $this->indent);
$adjustment = 0;
$adjustment = ($functionIndent - $foundFunctionIndent);

if ($foundFunctionIndent !== $functionIndent) {
$error = 'Opening statement of multi-line function call not indented correctly; expected %s spaces but found %s';
$data = [
$this->complainOpenStatementWrongIndent(
$phpcsFile,
$first,
$tokens,
$functionIndent,
$foundFunctionIndent,
];

$fix = $phpcsFile->addFixableError($error, $first, 'OpeningIndent', $data);
if ($fix === true) {
// Set adjustment for use later to determine whether argument indentation is correct when fixing.
$adjustment = ($functionIndent - $foundFunctionIndent);

$padding = str_repeat(' ', $functionIndent);
if ($foundFunctionIndent === 0) {
$phpcsFile->fixer->addContentBefore($first, $padding);
} else if ($tokens[$first]['code'] === T_INLINE_HTML) {
$newContent = $padding.ltrim($tokens[$first]['content']);
$phpcsFile->fixer->replaceToken($first, $newContent);
} else {
$phpcsFile->fixer->replaceToken(($first - 1), $padding);
}
}
$foundFunctionIndent
);
}//end if

$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($openBracket + 1), null, true);
Expand Down Expand Up @@ -465,7 +451,7 @@ public function processMultiLineCall(File $phpcsFile, $stackPtr, $openBracket, $
$i--;
}

for ($i; $i < $closeBracket; $i++) {
for (; $i < $closeBracket; $i++) {
if ($i > $argStart && $i < $argEnd) {
$inArg = true;
} else {
Expand Down Expand Up @@ -530,25 +516,52 @@ public function processMultiLineCall(File $phpcsFile, $stackPtr, $openBracket, $
if ($tokens[$i]['code'] === T_COMMENT
&& $tokens[($i - 1)]['code'] === T_COMMENT
) {
$trimmedLength = strlen(ltrim($tokens[$i]['content']));
$content = $tokens[$i]['content'];
$trimmedLength = strlen(ltrim($content));
if ($trimmedLength === 0) {
// This is a blank comment line, so indenting it is
// pointless.
continue;
}

$foundIndent = (strlen($tokens[$i]['content']) - $trimmedLength);
$content = str_replace("\t", str_repeat(' ', $this->indent), $content);
$foundIndent = (strlen($content) - $trimmedLength ) ;
} else {
$foundIndent = 0;
}
} else {
$foundIndent = $tokens[$i]['length'];
}
$content = str_replace("\t", str_repeat(' ', $this->indent), $tokens[$i]['content']);
$foundIndent = strlen($content);
}//end if

if ($foundIndent < $expectedIndent
|| ($inArg === false
&& $expectedIndent !== $foundIndent)
) {
$indentCorrect = true;

if ($foundIndent < $expectedIndent) {
$indentCorrect = false;
} else if ($inArg === false && $expectedIndent !== $foundIndent) {
$indentCorrect = false;

// It is permitted to indent chains further than one tab stop to
// align vertically with the previous method call.
if ($i === ($closeBracket - 1)) {
if ($foundIndent === $foundFunctionIndent) {
// This is the closing paren; it lines up vertically with the opening paren.
$indentCorrect = true;
}
} else {
if ($foundIndent === ($tokens[$openBracket]['column'] - 1)) {
// This is a parameter; it lines up vertically with the opening paren.
$indentCorrect = true;
}

if ($foundIndent === ($foundFunctionIndent + ($this->indent))) {
// This is a parameter; it is indented one more step than the function call around it.
$indentCorrect = true;
}
}
}//end if

if ($indentCorrect === false) {
$error = 'Multi-line function call not indented correctly; expected %s spaces but found %s';
$data = [
$expectedIndent,
Expand Down Expand Up @@ -631,4 +644,59 @@ public function processMultiLineCall(File $phpcsFile, $stackPtr, $openBracket, $
}//end processMultiLineCall()


/**
* Add a complaint (and auto-fix) if the function indent is 'wrong'.
*
* @param File $phpcsFile The file being scanned.
* @param int $first Pointer to the first empty token on this line.
* @param array $tokens The stack of tokens that make up the file.
* @param int $functionIndent The expected indent for this function definition.
* @param int $foundFunctionIndent The actual indent for this function definition.
*
* @return void
*/
protected function complainOpenStatementWrongIndent(
$phpcsFile,
$first,
$tokens,
$functionIndent,
$foundFunctionIndent
) {
if ($foundFunctionIndent === $functionIndent) {
return;
}

$firstToken = $tokens[$first];
if ($firstToken['code'] === T_OPEN_TAG || $firstToken['code'] === T_OPEN_TAG_WITH_ECHO) {
$prevToken = $tokens[($first - 1)];
if ($prevToken !== false && $firstToken['line'] === $prevToken['line']) {
return;
}
}

$error = 'Opening statement of multi-line function call not indented correctly; expected %s spaces but found %s';
$data = [
$functionIndent,
$foundFunctionIndent,
];

$fix = $phpcsFile->addFixableError($error, $first, 'OpeningIndent', $data);
if ($fix !== true) {
return;
}

$padding = str_repeat(' ', $functionIndent);

if ($foundFunctionIndent === 0) {
$phpcsFile->fixer->addContentBefore($first, $padding);
} else if ($tokens[$first]['code'] === T_INLINE_HTML) {
$newContent = $padding.ltrim($tokens[$first]['content']);
$phpcsFile->fixer->replaceToken($first, $newContent);
} else if ($tokens[($first - 1)]['code'] === T_WHITESPACE) {
$phpcsFile->fixer->replaceToken(($first - 1), $padding);
}

}//end complainOpenStatementWrongIndent()


}//end class
Original file line number Diff line number Diff line change
Expand Up @@ -574,3 +574,75 @@ content
'my_file.php'
); ?>
<?php endif; ?>

<?php
// https://github.com/squizlabs/PHP_CodeSniffer/issues/2078#issuecomment-401641650
$repository->foo()
->bar(
function () {
return true;
}
);
$repository->foo()
->bar(
function () {
return true;
}
);

// Initial call spaces only, params mixed.
$result = doThing($one, $two ,
$three,
$four,
$five,
$six,
$seven,
$eight);

// Initial call tab only, param spaces.
$result = doThing($one,
$two);

// Initial call tab only, param tab.
$result = doThing($one,
$two);

// Initial call tab and space, param tab and space.
$result = doThing($one,
$two);

// Initial call space only, param tab and space.
$result = doThing($one,
$two);

// Initial call space only, param tab only.
$result = doThing($one,
$two);

// Real example of mixed tab/space indention from WordPress codebase.
$v_binary_data = pack("VvvvvvVVVvv", 0x04034b50,
$p_header['version_extracted'], $p_header['flag'],
$p_header['compression'], $v_mtime, $v_mdate,
$p_header['crc'], $p_header['compressed_size'],
$p_header['size'],
strlen($p_header['stored_filename']),
$p_header['extra_len']);

// Real example of mixed tab/space indention from WordPress codebase.
PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION,
"Filename '".$v_header['stored_filename']."' is "
."compressed by an unsupported compression "
."method (".$v_header['compression'].") ");

// Real example of mixed tab/space indention from WordPress codebase.
if ($v_size > 2) {
PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER,
"Invalid number / type of arguments");
return 0;
}

// Real example of mixed tab/space indention from WordPress codebase.
// Not aligned on a 4-space indent
return chr(0x07 & (ord($utf8[0]) >> 2))
. chr((0xC0 & (ord($utf8[0]) << 6))
| (0x3F & ord($utf8[1])));
Loading