Skip to content

Commit

Permalink
Update BoilerplateCommentSniff and add fixes.
Browse files Browse the repository at this point in the history
  • Loading branch information
micaherne committed Apr 22, 2024
1 parent 1ec84f4 commit cd34717
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 26 deletions.
202 changes: 178 additions & 24 deletions moodle/Sniffs/Files/BoilerplateCommentSniff.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<?php

// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -64,53 +63,132 @@ public function process(File $file, $stackptr) {

$tokens = $file->getTokens();

// Allow declare() call in same line as opening tag.
if (!strpos($tokens[$stackptr]['content'], "\n") === 0) {
if (array_key_exists($stackptr + 1, $tokens) && $tokens[$stackptr + 1]['code'] === T_DECLARE) {

Check warning on line 68 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L68

Added line #L68 was not covered by tests

// Don't use findEndOfStatement() as a declare statement can legally be followed directly by an expression.
$stackptr = $tokens[$stackptr + 1]['parenthesis_closer'];

Check warning on line 71 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L71

Added line #L71 was not covered by tests

// Invalid syntax if nothing after declare.
if ($stackptr === count($tokens) - 1) {
return;

Check warning on line 75 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L74-L75

Added lines #L74 - L75 were not covered by tests
}

$stackptr++;

Check warning on line 78 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L78

Added line #L78 was not covered by tests

// Allow multi-line whitespace as it will be picked up by SemicolonSpacingSniff.
while ($tokens[$stackptr]['code'] === T_WHITESPACE && array_key_exists($stackptr + 1, $tokens)) {
$stackptr++;

Check warning on line 82 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L81-L82

Added lines #L81 - L82 were not covered by tests
}

if ($tokens[$stackptr]['code'] !== T_SEMICOLON) {
$file->addError(
'First line declare may only be followed by whitespace and semicolon',
$stackptr,
'InvalidDeclare'

Check warning on line 89 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L85-L89

Added lines #L85 - L89 were not covered by tests
);
return;

Check warning on line 91 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L91

Added line #L91 was not covered by tests
}

if ($stackptr < count($tokens) - 1) {
$stackptr++;

Check warning on line 95 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L94-L95

Added lines #L94 - L95 were not covered by tests

if ($tokens[$stackptr]['code'] !== T_WHITESPACE || $tokens[$stackptr]['content'] !== "\n") {
$file->addError(
'Declare statement may only be followed by a new line',
$stackptr,
'InvalidTrailingCode'

Check warning on line 101 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L97-L101

Added lines #L97 - L101 were not covered by tests
);

return;

Check warning on line 104 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L104

Added line #L104 was not covered by tests
}
}

}

}

// Allow T_PHPCS_XXX comment annotations in the first line (skip them).
if ($commentptr = $file->findNext(Tokens::$phpcsCommentTokens, $stackptr + 1, $stackptr + 3)) {
$stackptr = $commentptr;
}

// Find count the number of newlines after the opening <?PHP. We only
// count enough to see if the number is right.
// Note that the opening PHP tag includes one newline.
$numnewlines = 0;
for ($i = $stackptr + 1; $i <= $stackptr + 5; ++$i) {
if (isset($tokens[$i]) && $tokens[$i]['code'] == T_WHITESPACE && $tokens[$i]['content'] == "\n") {
$numnewlines++;
} else {
break;
$expectedafter = $stackptr;

if (count($tokens) - 1 === $expectedafter) {
$fix = $file->addFixableError(
'Moodle boilerplate not found before end of file',
$stackptr,
'EndOfFile'
);

if ($fix) {
$this->insertBoilerplate($file, $stackptr);
}
return;
}

if ($numnewlines > 0) {
$file->addError(
'The opening <?php tag must be followed by exactly one newline.',
$stackptr + 1,
'WrongWhitespace'
$foundtoken = $tokens[$expectedafter + 1];

if ($foundtoken['code'] === T_COMMENT) {
$firstcommentlocation = $expectedafter + 1;
} else {
$fix = $file->addFixableError(
'Moodle boilerplate not found at first line',
$expectedafter + 1,
'NotAtFirstLine'
);
return;

if ($fix) {
$this->moveOrInsertBoilerplate($file, $expectedafter);
return;
}

// If boilerplate found after an acceptable amount of whitespace,
// allow it to be checked.
$nextnonwhitespace = $file->findNext(T_WHITESPACE, $stackptr + 1, $stackptr + 5, true);
if ($nextnonwhitespace === false || $tokens[$nextnonwhitespace]['code'] !== T_COMMENT) {
return;

Check warning on line 152 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L152

Added line #L152 was not covered by tests
}

$firstcommentlocation = $nextnonwhitespace;
}
$offset = $stackptr + $numnewlines + 1;

// Now check the text of the comment.
foreach (self::$comment as $lineindex => $line) {
$tokenptr = $offset + $lineindex;
$tokenptr = $firstcommentlocation + $lineindex;

if (!array_key_exists($tokenptr, $tokens)) {
$file->addError('Reached the end of the file before finding ' .
'all of the opening comment.', $tokenptr - 1, 'FileTooShort');
$fix = $file->addFixableError(
'Reached the end of the file before finding ' .
'all of the opening comment.',
$tokenptr - 1,
'FileTooShort'
);
if ($fix) {
$this->completeBoilerplate($file, $tokenptr - 1, $lineindex);
}
return;
}

if ($tokens[$tokenptr]['code'] != T_COMMENT) {
$fix = $file->addFixableError('Comment does not contain full Moodle boilerplate',
$tokenptr,
'CommentEndedTooSoon');
if ($fix) {
$this->completeBoilerplate($file, $tokenptr, $lineindex);

Check warning on line 180 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L176-L180

Added lines #L176 - L180 were not covered by tests
}
break;

Check warning on line 182 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L182

Added line #L182 was not covered by tests
}

$regex = str_replace(
['Moodle', 'http\\:'],
['.*', 'https?\\:'],
'/^' . preg_quote($line, '/') . '/'
);

if (
$tokens[$tokenptr]['code'] != T_COMMENT ||
!preg_match($regex, $tokens[$tokenptr]['content'])
) {
if (!preg_match($regex, $tokens[$tokenptr]['content'])) {
$file->addError(
'Line %s of the opening comment must start "%s".',
$tokenptr,
Expand All @@ -120,4 +198,80 @@ public function process(File $file, $stackptr) {
}
}
}

private function fullComment(): array {
$result = self::$comment;
$result[0] = '// This file is part of Moodle - https://moodle.org/';
return $result;
}

private function insertBoilerplate(File $file, int $stackptr): void {
$prefix = substr($file->getTokens()[$stackptr]['content'], -1) === "\n" ? '' : "\n";
$file->fixer->addContent($stackptr, $prefix . implode("\n", $this->fullComment()) . "\n");
}

private function moveOrInsertBoilerplate(File $file, int $stackptr): void {
$tokens = $file->getTokens();

$firstcomment = $file->findNext(T_COMMENT, $stackptr);

if ($firstcomment === false) {
$this->insertBoilerplate($file, $stackptr);
return;

Check warning on line 220 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L219-L220

Added lines #L219 - L220 were not covered by tests
}

$file->fixer->beginChangeset();

// If we have only whitespace between expected location and first comment, just remove it.
$nextnonwhitespace = $file->findPrevious(T_WHITESPACE, $firstcomment - 1, $stackptr, true);

// The token the boilerplate is expected after may well be a whitespace node.
if ($nextnonwhitespace === false) {
$nextnonwhitespace = $stackptr;

Check warning on line 230 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L230

Added line #L230 was not covered by tests
}

foreach (range($nextnonwhitespace + 1, $firstcomment - 1) as $whitespaceptr) {
$file->fixer->replaceToken($whitespaceptr, '');
}

// If there's nothing else between the first comment and the expected location we're done.
if ($nextnonwhitespace === $stackptr) {
$file->fixer->endChangeset();
return;
}

// Otherwise shift existing comment to correct place.
$existingboilerplate = [];
foreach (self::$comment as $lineindex => $line) {
$tokenptr = $firstcomment + $lineindex;

Check warning on line 246 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L244-L246

Added lines #L244 - L246 were not covered by tests

$regex = str_replace(
['Moodle', 'http\\:'],
['.*', 'https?\\:'],
'/^' . preg_quote($line, '/') . '/'

Check warning on line 251 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L248-L251

Added lines #L248 - L251 were not covered by tests
);

if ($tokens[$tokenptr]['code'] != T_COMMENT || !preg_match($regex, $tokens[$tokenptr]['content'])) {
$file->fixer->rollbackChangeset();
$this->insertBoilerplate($file, $stackptr);
return;

Check warning on line 257 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L254-L257

Added lines #L254 - L257 were not covered by tests
}

$existingboilerplate[] = $tokens[$tokenptr]['content'];

Check warning on line 260 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L260

Added line #L260 was not covered by tests
}

$file->fixer->addContent($stackptr, implode("", $existingboilerplate) . "\n");

Check warning on line 263 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L263

Added line #L263 was not covered by tests

foreach (array_keys(self::$comment) as $i) {
$tokenptr = $firstcomment + $i;
$file->fixer->replaceToken($tokenptr, '');

Check warning on line 267 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L265-L267

Added lines #L265 - L267 were not covered by tests
}

$file->fixer->endChangeset();

Check warning on line 270 in moodle/Sniffs/Files/BoilerplateCommentSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/Files/BoilerplateCommentSniff.php#L270

Added line #L270 was not covered by tests

}

private function completeBoilerplate(File $file, $stackptr, int $lineindex): void {
$file->fixer->addContentBefore($stackptr, implode("\n", array_slice($this->fullComment(), $lineindex)) . "\n");
}
}
4 changes: 2 additions & 2 deletions moodle/Tests/FilesBoilerPlateCommentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public function testMoodleFilesBoilerplateCommentBlank() {
$this->setFixture(__DIR__ . '/fixtures/files/boilerplatecomment/blank.php');

$this->setErrors([
2 => 'followed by exactly one newline',
2 => 'not found at first line',
]);
$this->setWarnings([]);

Expand All @@ -95,7 +95,7 @@ public function testMoodleFilesBoilerplateCommentShortEmpty() {
$this->setFixture(__DIR__ . '/fixtures/files/boilerplatecomment/short_empty.php');

$this->setErrors([
1 => 'FileTooShort',
1 => 'EndOfFile',
]);
$this->setWarnings([]);

Expand Down

0 comments on commit cd34717

Please sign in to comment.