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

Add measure feature #38

Merged
merged 2 commits into from
May 20, 2024
Merged
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
30 changes: 25 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The param type is defined, but property and return types are missing.

* 1 out of 3 = 33 % coverage

How do we get to the 100 %?
Our code has only one third quality it could have. Let's get to 100 %!

```diff
final class ConferenceFactory
Expand All @@ -52,7 +52,7 @@ How do we get to the 100 %?
}
```

This technique is very simple and useful to start with even on legacy project. You also know, how high coverage your project has right now.
This technique is very simple to start even on legacy project. Also, you're now aware exactly how high coverage your project has.

<br>

Expand All @@ -70,15 +70,35 @@ The package is available on PHP 7.2+ version in tagged releases.

With [PHPStan extension installer](https://github.com/phpstan/extension-installer), everything is ready to run.

Enable each item on their own with simple configuration:
Enable each item on their own:

```neon
```yaml
# phpstan.neon
parameters:
type_coverage:
return: 50
param: 35.5
property: 70
# also, how many files has declare strict types
```

## Measure Strict Declares coverage

Once you've reached 100 % type coverage, make use [your code is strict and uses types](https://tomasvotruba.com/blog/how-adding-type-declarations-makes-your-code-dangerous):

```php
<?php

declare(strict_types=1);
```

Again, raise level percent by percent in your own pace:

```yaml
parameters:
type_coverage:
declare: 40
```

<br>

Happy coding!
5 changes: 5 additions & 0 deletions config/extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ parametersSchema:
return: anyOf(schema(float(), nullable()), schema(int(), nullable()))
param: anyOf(schema(float(), nullable()), schema(int(), nullable()))
property: anyOf(schema(float(), nullable()), schema(int(), nullable()))

# measure
measure: bool()
])

# default parameters
Expand All @@ -28,6 +31,8 @@ parameters:
param: null
property: null

measure: false

services:
- TomasVotruba\TypeCoverage\Formatter\TypeCoverageFormatter
- TomasVotruba\TypeCoverage\CollectorDataNormalizer
Expand Down
5 changes: 4 additions & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ parameters:
param_type: 100
property_type: 100

# only show final data, no error report
# measure: true

checkGenericClassInNonGenericObjectType: false
ignoreErrors:
- identifier: missingType.generics

excludePaths:
- "*/Fixture/*"
Expand Down
3 changes: 2 additions & 1 deletion src/Collectors/DeclareCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace TomasVotruba\TypeCoverage\Collectors;

use PhpParser\Node;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Stmt\Declare_;
use PHPStan\Analyser\Scope;
use PHPStan\Collectors\Collector;
Expand Down Expand Up @@ -35,7 +36,7 @@ public function processNode(Node $node, Scope $scope): bool
}

if (
! $declare->value instanceof Node\Scalar\LNumber
! $declare->value instanceof LNumber
|| $declare->value->value !== 1
) {
return false;
Expand Down
5 changes: 5 additions & 0 deletions src/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@ public function getRequiredDeclareLevel(): float|int
{
return $this->parameters['declare'];
}

public function showOnlyMeasure(): bool
{
return $this->parameters['measure'];
}
}
30 changes: 20 additions & 10 deletions src/Rules/DeclareCoverageRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,9 @@ public function processNode(Node $node, Scope $scope): array
{
$requiredDeclareLevel = $this->configuration->getRequiredDeclareLevel();

// not enabled
if ($requiredDeclareLevel === 0) {
return [];
}

$declareCollector = $node->get(DeclareCollector::class);
$totalPossibleDeclares = count($declareCollector);

// nothing to handle
if ($totalPossibleDeclares === 0) {
return [];
}

$coveredDeclares = 0;
$notCoveredDeclareFilePaths = [];

Expand All @@ -72,6 +62,26 @@ public function processNode(Node $node, Scope $scope): array

$declareCoverage = ($coveredDeclares / $totalPossibleDeclares) * 100;

if ($this->configuration->showOnlyMeasure()) {
return [
sprintf(
'Strict declares coverage is %.1f %% out of %d possible',
$declareCoverage,
$totalPossibleDeclares
),
];
}

// not enabled
if ($requiredDeclareLevel === 0) {
return [];
}

// nothing to handle
if ($totalPossibleDeclares === 0) {
return [];
}

// we meet the limit, all good
if ($declareCoverage >= $requiredDeclareLevel) {
return [];
Expand Down
18 changes: 14 additions & 4 deletions src/Rules/ParamTypeCoverageRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,24 @@ public function getNodeType(): string
*/
public function processNode(Node $node, Scope $scope): array
{
if ($this->configuration->getRequiredParamTypeLevel() === 0) {
return [];
}

$paramTypeDeclarationCollector = $node->get(ParamTypeDeclarationCollector::class);

$typeCountAndMissingTypes = $this->collectorDataNormalizer->normalize($paramTypeDeclarationCollector);

if ($this->configuration->showOnlyMeasure()) {
return [
sprintf(
'Param type coverage is %.1f %% out of %d possible',
$typeCountAndMissingTypes->getCoveragePercentage(),
$typeCountAndMissingTypes->getTotalCount()
),
];
}

if ($this->configuration->getRequiredParamTypeLevel() === 0) {
return [];
}

return $this->typeCoverageFormatter->formatErrors(
self::ERROR_MESSAGE,
$this->configuration->getRequiredParamTypeLevel(),
Expand Down
16 changes: 13 additions & 3 deletions src/Rules/PropertyTypeCoverageRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,23 @@ public function getNodeType(): string
*/
public function processNode(Node $node, Scope $scope): array
{
$propertyTypeDeclarationCollector = $node->get(PropertyTypeDeclarationCollector::class);
$typeCountAndMissingTypes = $this->collectorDataNormalizer->normalize($propertyTypeDeclarationCollector);

if ($this->configuration->showOnlyMeasure()) {
return [
sprintf(
'Property type coverage is %.1f %% out of %d possible',
$typeCountAndMissingTypes->getCoveragePercentage(),
$typeCountAndMissingTypes->getTotalCount()
),
];
}

if ($this->configuration->getRequiredPropertyTypeLevel() === 0) {
return [];
}

$propertyTypeDeclarationCollector = $node->get(PropertyTypeDeclarationCollector::class);
$typeCountAndMissingTypes = $this->collectorDataNormalizer->normalize($propertyTypeDeclarationCollector);

return $this->typeCoverageFormatter->formatErrors(
self::ERROR_MESSAGE,
$this->configuration->getRequiredPropertyTypeLevel(),
Expand Down
14 changes: 14 additions & 0 deletions src/Rules/ReturnTypeCoverageRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ public function processNode(Node $node, Scope $scope): array
$returnSeaLevelDataByFilePath = $node->get(ReturnTypeDeclarationCollector::class);
$typeCountAndMissingTypes = $this->collectorDataNormalizer->normalize($returnSeaLevelDataByFilePath);

if ($this->configuration->showOnlyMeasure()) {
return [
sprintf(
'Return type coverage is %.1f %% out of %d possible',
$typeCountAndMissingTypes->getCoveragePercentage(),
$typeCountAndMissingTypes->getTotalCount()
),
];
}

if ($this->configuration->getRequiredReturnTypeLevel() === 0) {
return [];
}

return $this->typeCoverageFormatter->formatErrors(
self::ERROR_MESSAGE,
$this->configuration->getRequiredReturnTypeLevel(),
Expand Down
4 changes: 4 additions & 0 deletions src/ValueObject/TypeCountAndMissingTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ public function getMissingTypeLinesByFilePath(): array

public function getCoveragePercentage(): float
{
if ($this->totalCount === 0) {
return 100.0;
}

$relative = 100 * ($this->getTypedCount() / $this->totalCount);

// round down with one decimal, to make error message clear that required value is not reached yet
Expand Down
Loading