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

feat: Clickable rule names in TextReporter pointing to official documentation #8003

Closed
25 changes: 24 additions & 1 deletion src/AbstractFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use PhpCsFixer\Console\Application;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\Fixer\FixerReportInterface;
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
use PhpCsFixer\FixerConfiguration\DeprecatedFixerOption;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
Expand All @@ -33,7 +34,7 @@
*
* @internal
*/
abstract class AbstractFixer implements FixerInterface
abstract class AbstractFixer implements FixerInterface, FixerReportInterface
{
/**
* @var null|array<string, mixed>
Expand Down Expand Up @@ -65,6 +66,15 @@ public function __construct()
}
}

public function getExtraInfoFixers(): array
{
return [
'helpUri' => $this->getHelpUri(),
'definition' => $this->getDefinition(),
'risky' => $this->isRisky(),
Comment on lines +73 to +74
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh? no proxy methods please
we already have isRisky() and getDefinition(), no need for method that is duplicating this knowledge

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't want to have external SARIF output format to your project I understand. Otherwise, consider that, as helpUri the extra info that missed from ReportSummary class, to generate full (validated) SARIF results which identify complety all rules/fixers applied !

I know that is not in context of this PR, but as we try to add an additional info (helpUri) here I took advange of the situation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we should focus on minimal changes required for achieving clickable URLs in the output - nothing more, nothing less. That would make this PR much easier to process and finish. Then, it could be extender or/and refactored to achieve SARIF reporting - for now I don't even have the context what it is, what it needs and I don't want to dig into that at this point. Having said that, I agree with @keradus' comment, and I would prefer first option from this comment. Let's don't model stuff for things that will or won't land later.

That's why I don't like FixerReportInterface because we don't need to focus here on reporting (clickable URL can be also added to describe CLI command), I would prefer something more atomic, focused on docs URI. ProvidesDocumentationUriInterface or something like that, that would allow adding only this particular feature to fixers.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: If you wan't to know what is SARIF, consider to take time to read this page https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning.
It's a good intro !

];
}

final public function fix(\SplFileInfo $file, Tokens $tokens): void
{
if ($this instanceof ConfigurableFixerInterface && null === $this->configuration) {
Expand Down Expand Up @@ -170,6 +180,19 @@ public function setWhitespacesConfig(WhitespacesFixerConfig $config): void
$this->whitespacesConfig = $config;
}

protected function getHelpUri(): string
{
$nameParts = explode('\\', static::class);
$group = $nameParts[\count($nameParts) - 2];
$name = substr(end($nameParts), 0, -\strlen('Fixer'));

return sprintf(
'https://cs.symfony.com/doc/rules/%s/%s.html',
Utils::camelCaseToUnderscore($group),
Utils::camelCaseToUnderscore($name)
);
}

abstract protected function applyFix(\SplFileInfo $file, Tokens $tokens): void;

protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
Expand Down
22 changes: 17 additions & 5 deletions src/Console/Report/FixReport/ReportSummary.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

namespace PhpCsFixer\Console\Report\FixReport;

use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;

/**
* @author Dariusz Rumiński <[email protected]>
*
Expand All @@ -22,7 +24,11 @@
final class ReportSummary
{
/**
* @var array<string, array{appliedFixers: list<string>, diff: string}>
* @var array<string, array{
* appliedFixers: list<string>,
* diff: string,
* extraInfoFixers: array<string, array{"helpUri"?: string, "definition"?: FixerDefinitionInterface, "risky"?: bool}>
* }>
*/
private array $changed;

Expand All @@ -39,9 +45,11 @@ final class ReportSummary
private bool $isDecoratedOutput;

/**
* @param array<string, array{appliedFixers: list<string>, diff: string}> $changed
* @param int $time duration in milliseconds
* @param int $memory memory usage in bytes
* @param array<string, array{
* appliedFixers: list<string>,
* diff: string,
* extraInfoFixers: array<string, array{"helpUri"?: string, "definition"?: FixerDefinitionInterface, "risky"?: bool}>
* }> $changed
*/
public function __construct(
array $changed,
Expand Down Expand Up @@ -72,7 +80,11 @@ public function isDryRun(): bool
}

/**
* @return array<string, array{appliedFixers: list<string>, diff: string}>
* @return array<string, array{
* appliedFixers: list<string>,
* diff: string,
* extraInfoFixers: array<string, array{"helpUri"?: string, "definition"?: FixerDefinitionInterface, "risky"?: bool}>
* }>
*/
public function getChanged(): array
{
Expand Down
21 changes: 18 additions & 3 deletions src/Console/Report/FixReport/TextReporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
namespace PhpCsFixer\Console\Report\FixReport;

use PhpCsFixer\Differ\DiffConsoleFormatter;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use Symfony\Component\Console\Formatter\OutputFormatter;

/**
* @author Boris Gorbylev <[email protected]>
Expand All @@ -41,6 +43,7 @@
$output .= $this->getAppliedFixers(
$reportSummary->isDecoratedOutput(),
$fixResult['appliedFixers'],
$fixResult['extraInfoFixers']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really wonder if we need to change the fixResult object, or simply have one standalone method that is converting fixer-name to fixer-url

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will let you think again then ...

);
}

Expand All @@ -58,13 +61,25 @@
}

/**
* @param list<string> $appliedFixers
* @param list<string> $appliedFixers
* @param array<string, array{"helpUri"?: string, "definition"?: FixerDefinitionInterface, "risky"?: bool}> $extraInfoFixers
*/
private function getAppliedFixers(bool $isDecoratedOutput, array $appliedFixers): string
private function getAppliedFixers(bool $isDecoratedOutput, array $appliedFixers, array $extraInfoFixers = []): string
{
$fixers = [];

foreach ($appliedFixers as $appliedFixer) {
$url = $extraInfoFixers[$appliedFixer]['helpUri'] ?? '';

Check warning on line 72 in src/Console/Report/FixReport/TextReporter.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "Coalesce": --- Original +++ New @@ @@ { $fixers = []; foreach ($appliedFixers as $appliedFixer) { - $url = $extraInfoFixers[$appliedFixer]['helpUri'] ?? ''; + $url = '' ?? $extraInfoFixers[$appliedFixer]['helpUri']; if ($isDecoratedOutput && '' !== $url) { $fixers[] = sprintf('<href=%s;fg=yellow>%s</>', OutputFormatter::escape($url), $appliedFixer); } else {
if ($isDecoratedOutput && '' !== $url) {

Check warning on line 73 in src/Console/Report/FixReport/TextReporter.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "LogicalAndSingleSubExprNegation": --- Original +++ New @@ @@ $fixers = []; foreach ($appliedFixers as $appliedFixer) { $url = $extraInfoFixers[$appliedFixer]['helpUri'] ?? ''; - if ($isDecoratedOutput && '' !== $url) { + if (!$isDecoratedOutput && '' !== $url) { $fixers[] = sprintf('<href=%s;fg=yellow>%s</>', OutputFormatter::escape($url), $appliedFixer); } else { $fixers[] = $appliedFixer;
$fixers[] = sprintf('<href=%s;fg=yellow>%s</>', OutputFormatter::escape($url), $appliedFixer);
} else {
$fixers[] = $appliedFixer;
}
}

return sprintf(
$isDecoratedOutput ? ' (<comment>%s</comment>)' : ' (%s)',
implode(', ', $appliedFixers)
implode(', ', $fixers)
);
}

Expand Down
31 changes: 31 additions & 0 deletions src/Fixer/FixerReportInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

/*
* This file is part of PHP CS Fixer.
*
* (c) Fabien Potencier <[email protected]>
* Dariusz Rumiński <[email protected]>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace PhpCsFixer\Fixer;

use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;

/**
* @author Laurent Laville
*/
interface FixerReportInterface
{
/**
* - "helpUri" identify the absolute URI [RFC3986](http://www.rfc-editor.org/info/rfc3986)
* * of the primary documentation for the fixer.
*
* @return array{"helpUri"?: string, "definition"?: FixerDefinitionInterface, "risky"?: bool}
*/
public function getExtraInfoFixers(): array;
}
20 changes: 18 additions & 2 deletions src/Runner/Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
use PhpCsFixer\Error\ErrorsManager;
use PhpCsFixer\FileReader;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\Fixer\FixerReportInterface;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerFileProcessedEvent;
use PhpCsFixer\Linter\LinterInterface;
use PhpCsFixer\Linter\LintingException;
Expand Down Expand Up @@ -92,7 +94,11 @@
}

/**
* @return array<string, array{appliedFixers: list<string>, diff: string}>
* @return array<string, array{
* appliedFixers: list<string>,
* diff: string,
* extraInfoFixers: array<string, array{"helpUri"?: string, "definition"?: FixerDefinitionInterface, "risky"?: bool}>
* }>
*/
public function fix(): array
{
Expand Down Expand Up @@ -130,7 +136,11 @@
}

/**
* @return null|array{appliedFixers: list<string>, diff: string}
* @return null|array{
* appliedFixers: list<string>,
* diff: string,
* extraInfoFixers: array<string, array{"helpUri"?: string, "definition"?: FixerDefinitionInterface, "risky"?: bool}>
* }
*/
private function fixFile(\SplFileInfo $file, LintingResultInterface $lintingResult): ?array
{
Expand Down Expand Up @@ -158,8 +168,9 @@
$newHash = $oldHash;

$appliedFixers = [];
$extraInfoFixers = [];

try {

Check warning on line 173 in src/Runner/Runner.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "CatchBlockRemoval": --- Original +++ New @@ @@ } } } - } catch (\ParseError $e) { - $this->dispatchEvent(FixerFileProcessedEvent::NAME, new FixerFileProcessedEvent(FixerFileProcessedEvent::STATUS_LINT)); - $this->errorsManager->report(new Error(Error::TYPE_LINT, $name, $e)); - return null; } catch (\Throwable $e) { $this->processException($name, $e); return null;

Check warning on line 173 in src/Runner/Runner.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "CatchBlockRemoval": --- Original +++ New @@ @@ $this->dispatchEvent(FixerFileProcessedEvent::NAME, new FixerFileProcessedEvent(FixerFileProcessedEvent::STATUS_LINT)); $this->errorsManager->report(new Error(Error::TYPE_LINT, $name, $e)); return null; - } catch (\Throwable $e) { - $this->processException($name, $e); - return null; } $fixInfo = null; if ([] !== $appliedFixers) {
foreach ($this->fixers as $fixer) {
// for custom fixers we don't know is it safe to run `->fix()` without checking `->supports()` and `->isCandidate()`,
// thus we need to check it and conditionally skip fixing
Expand All @@ -176,6 +187,10 @@
$tokens->clearEmptyTokens();
$tokens->clearChanged();
$appliedFixers[] = $fixer->getName();

if ($fixer instanceof FixerReportInterface) {

Check warning on line 191 in src/Runner/Runner.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "InstanceOf_": --- Original +++ New @@ @@ $tokens->clearEmptyTokens(); $tokens->clearChanged(); $appliedFixers[] = $fixer->getName(); - if ($fixer instanceof FixerReportInterface) { + if (true) { $extraInfoFixers[$fixer->getName()] = $fixer->getExtraInfoFixers(); } }

Check warning on line 191 in src/Runner/Runner.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "InstanceOf_": --- Original +++ New @@ @@ $tokens->clearEmptyTokens(); $tokens->clearChanged(); $appliedFixers[] = $fixer->getName(); - if ($fixer instanceof FixerReportInterface) { + if (false) { $extraInfoFixers[$fixer->getName()] = $fixer->getExtraInfoFixers(); } }
$extraInfoFixers[$fixer->getName()] = $fixer->getExtraInfoFixers();
}
}
}
} catch (\ParseError $e) {
Expand Down Expand Up @@ -208,6 +223,7 @@
$fixInfo = [
'appliedFixers' => $appliedFixers,
'diff' => $this->differ->diff($old, $new, $file),
'extraInfoFixers' => $extraInfoFixers,
];

try {
Expand Down
10 changes: 10 additions & 0 deletions tests/AbstractProxyFixerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ public function getDefinition(): FixerDefinitionInterface
throw new \BadMethodCallException('Not implemented.');
}

public function getHelpUri(): string
{
throw new \BadMethodCallException('Not implemented.');
}

public function getName(): string
{
return uniqid('abstract_proxy_test_');
Expand Down Expand Up @@ -211,6 +216,11 @@ public function fix(\SplFileInfo $file, Tokens $tokens): void
throw new \BadMethodCallException('Not implemented.');
}

public function getHelpUri(): string
{
throw new \BadMethodCallException('Not implemented.');
}

public function getName(): string
{
return uniqid('abstract_proxy_aware_test_');
Expand Down
10 changes: 10 additions & 0 deletions tests/Console/Command/DescribeCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,11 @@ public function getDefinition(): FixerDefinition
return new FixerDefinition('Fixes stuff.', []);
}

public function getHelpUri(): string
{
throw new \LogicException('Not implemented.');
}

public function getName(): string
{
return 'Foo/bar_baz';
Expand Down Expand Up @@ -578,6 +583,11 @@ public function getDefinition(): FixerDefinition
);
}

public function getHelpUri(): string
{
throw new \LogicException('Not implemented.');
}

public function getName(): string
{
return 'Foo/bar';
Expand Down
6 changes: 6 additions & 0 deletions tests/Console/Report/FixReport/AbstractReporterTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class Foo
{
}
}',
'extraInfoFixers' => [],
],
],
10,
Expand Down Expand Up @@ -122,6 +123,7 @@ class Foo
{
}
}',
'extraInfoFixers' => [],
],
],
10,
Expand All @@ -140,6 +142,7 @@ class Foo
'someFile.php' => [
'appliedFixers' => ['some_fixer_name_here_1', 'some_fixer_name_here_2'],
'diff' => '',
'extraInfoFixers' => [],
],
],
10,
Expand Down Expand Up @@ -168,6 +171,7 @@ class Foo
{
}
}',
'extraInfoFixers' => [],
],
],
10,
Expand All @@ -186,10 +190,12 @@ class Foo
'someFile.php' => [
'appliedFixers' => ['some_fixer_name_here_1', 'some_fixer_name_here_2'],
'diff' => 'this text is a diff ;)',
'extraInfoFixers' => [],
],
'anotherFile.php' => [
'appliedFixers' => ['another_fixer_name_here'],
'diff' => 'another diff here ;)',
'extraInfoFixers' => [],
],
],
10,
Expand Down
1 change: 1 addition & 0 deletions tests/Console/Report/FixReport/ReportSummaryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public function testReportSummary(): void
'someFile.php' => [
'appliedFixers' => ['some_fixer_name_here'],
'diff' => 'this text is a diff ;)',
'extraInfoFixers' => [],
],
];
$filesCount = 10;
Expand Down
20 changes: 20 additions & 0 deletions tests/FixerFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,11 @@ public function getDefinition(): FixerDefinitionInterface
throw new \LogicException('Not implemented.');
}

public function getHelpUri(): string
{
throw new \LogicException('Not implemented.');
}

public function getName(): string
{
return 'foo';
Expand Down Expand Up @@ -458,6 +463,11 @@ public function getDefinition(): FixerDefinitionInterface
throw new \LogicException('Not implemented.');
}

public function getHelpUri(): string
{
throw new \LogicException('Not implemented.');
}

public function getName(): string
{
return 'foo';
Expand Down Expand Up @@ -531,6 +541,11 @@ public function getDefinition(): FixerDefinitionInterface
throw new \LogicException('Not implemented.');
}

public function getHelpUri(): string
{
throw new \LogicException('Not implemented.');
}

public function getName(): string
{
return 'foo';
Expand Down Expand Up @@ -586,6 +601,11 @@ public function getDefinition(): FixerDefinitionInterface
throw new \LogicException('Not implemented.');
}

public function getHelpUri(): string
{
throw new \LogicException('Not implemented.');
}

public function getName(): string
{
return $this->name;
Expand Down