Skip to content

Commit

Permalink
Add Assert, AssertIfTrue and AssertIfFalse attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
carlos-granados committed Sep 12, 2024
1 parent 44f151d commit 3aaf8e1
Show file tree
Hide file tree
Showing 15 changed files with 497 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ These are the available attributes and their corresponding PHPDoc annotations:

| Attribute | PHPDoc Annotations |
|-------------------------------------------------------------------------------------------------------------------|--------------------------------------|
| [Assert](https://github.com/php-static-analysis/attributes/blob/main/doc/Assert.md) | `@assert` |
| [DefineType](https://github.com/php-static-analysis/attributes/blob/main/doc/DefineType.md) | `@type` |
| [Deprecated](https://github.com/php-static-analysis/attributes/blob/main/doc/Deprecated.md) | `@deprecated` |
| [Immmutable](https://github.com/php-static-analysis/attributes/blob/main/doc/Immmutable.md) | `@immmutable` |
Expand Down
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
"prefer-stable": true,
"require": {
"php": ">=8.0",
"php-static-analysis/attributes": "^0.3.0 || dev-main",
"php-static-analysis/node-visitor": "^0.3.0 || dev-main",
"phpstan/phpstan": "^1.8"
"php-static-analysis/attributes": "^0.3.1 || dev-main",
"php-static-analysis/node-visitor": "^0.3.1 || dev-main",
"phpstan/phpstan": "^1.8",
"webmozart/assert": "^1.11"
},
"require-dev": {
"php-static-analysis/psalm-plugin": "dev-main",
Expand Down
3 changes: 2 additions & 1 deletion src/Parser/AttributeParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PhpStaticAnalysis\Attributes\Param;
use PhpStaticAnalysis\Attributes\Returns;
use PhpStaticAnalysis\NodeVisitor\AttributeNodeVisitor;
use Webmozart\Assert\Assert;

class AttributeParser implements Parser
{
Expand Down Expand Up @@ -39,7 +40,7 @@ private function traverseAst(array $ast): array
$traverser->addVisitor($nodeVisitor);

$ast = $traverser->traverse($ast);
/** @var Stmt[] $ast */
Assert::allIsInstanceOf($ast, Stmt::class);
return $ast;
}
}
30 changes: 30 additions & 0 deletions tests/AssertAttributeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension;

class AssertAttributeTest extends BaseAttributeTestCase
{
public function testMethodAssertAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/Assert/MethodAssertAttribute.php');
$this->assertCount(0, $errors);
}

public function testFunctionAssertAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/Assert/FunctionAssertAttribute.php');
$this->assertCount(0, $errors);
}

public function testInvalidMethodAssertAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/Assert/InvalidMethodAssertAttribute.php');

$expectedErrors = [
'Parameter #1 ...$params of attribute class PhpStaticAnalysis\Attributes\Assert constructor expects string, int given.' => 9,
'Attribute class PhpStaticAnalysis\Attributes\Assert does not have the property target.' => 14,
];

$this->checkExpectedErrors($errors, $expectedErrors);
}
}
31 changes: 31 additions & 0 deletions tests/AssertIfFalseAttributeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php


use test\PhpStaticAnalysis\PHPStanExtension\BaseAttributeTestCase;

class AssertIfFalseAttributeTest extends BaseAttributeTestCase
{
public function testMethodAssertIfFalseAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/AssertIfFalse/MethodAssertIfFalseAttribute.php');
$this->assertCount(0, $errors);
}

public function testFunctionAssertIfFalseAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/AssertIfFalse/FunctionAssertIfFalseAttribute.php');
$this->assertCount(0, $errors);
}

public function testInvalidMethodAssertIfFalseAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/AssertIfFalse/InvalidMethodAssertIfFalseAttribute.php');

$expectedErrors = [
'Parameter #1 ...$params of attribute class PhpStaticAnalysis\Attributes\AssertIfFalse constructor expects string, int given.' => 9,
'Attribute class PhpStaticAnalysis\Attributes\AssertIfFalse does not have the property target.' => 15,
];

$this->checkExpectedErrors($errors, $expectedErrors);
}
}
31 changes: 31 additions & 0 deletions tests/AssertIfTrueAttributeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php


use test\PhpStaticAnalysis\PHPStanExtension\BaseAttributeTestCase;

class AssertIfTrueAttributeTest extends BaseAttributeTestCase
{
public function testMethodAssertIfTrueAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/AssertIfTrue/MethodAssertIfTrueAttribute.php');
$this->assertCount(0, $errors);
}

public function testFunctionAssertIfTrueAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/AssertIfTrue/FunctionAssertIfTrueAttribute.php');
$this->assertCount(0, $errors);
}

public function testInvalidMethodAssertIfTrueAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/AssertIfTrue/InvalidMethodAssertIfTrueAttribute.php');

$expectedErrors = [
'Parameter #1 ...$params of attribute class PhpStaticAnalysis\Attributes\AssertIfTrue constructor expects string, int given.' => 9,
'Attribute class PhpStaticAnalysis\Attributes\AssertIfTrue does not have the property target.' => 15,
];

$this->checkExpectedErrors($errors, $expectedErrors);
}
}
14 changes: 14 additions & 0 deletions tests/data/Assert/FunctionAssertAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\Assert;

use Exception;
use PhpStaticAnalysis\Attributes\Assert;

#[Assert(name: 'string')]
function checkString(mixed $name): void
{
if (!is_string($name)) {
throw new Exception();
}
}
16 changes: 16 additions & 0 deletions tests/data/Assert/InvalidMethodAssertAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\Assert;

use PhpStaticAnalysis\Attributes\Assert;

class InvalidMethodAssertAttribute
{
#[Assert(0)]
public function checkString(mixed $name): void
{
}

#[Assert(property: 'string')]
public string $property;
}
114 changes: 114 additions & 0 deletions tests/data/Assert/MethodAssertAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\Assert;

use Exception;
use PhpStaticAnalysis\Attributes\Assert;

class MethodAssertAttribute
{
#[Assert(name: 'string')] // checks name is string
public function checkString(mixed $name): void
{
if (!is_string($name)) {
throw new Exception();
}
}

#[Assert(exception: Exception::class)]
public function checkException(mixed $exception): void
{
if (!$exception instanceof Exception) {
throw new Exception();
}
}

#[Assert('string $name')]
public function checkOtherString(mixed $name): void
{
if (!is_string($name)) {
throw new Exception();
}
}

/**
* @deprecated
*/
#[Assert(name: 'string')]
public function checkAnotherString(mixed $name): void
{
if (!is_string($name)) {
throw new Exception();
}
}

/**
* @assert int $name
*/
#[Assert(name: 'string')]
public function checkEvenMoreString(mixed $name): void
{
if (!is_string($name)) {
throw new Exception();
}
}

#[Assert(
name1: 'string',
name2: 'string'
)]
public function checkStrings(mixed $name1, mixed $name2): void
{
if (!is_string($name1) || !is_string($name2)) {
throw new Exception();
}
}

#[Assert(name1: 'string')]
#[Assert(name2: 'string')]
public function checkOtherStrings(mixed $name1, mixed $name2): void
{
if (!is_string($name1) || !is_string($name2)) {
throw new Exception();
}
}

/**
* @assert string $name
*/
public function checkMoreAndMoreString(mixed $name): void
{
if (!is_string($name)) {
throw new Exception();
}
}

public function checkStringInParam(
#[Assert('string')]
mixed $name
): void {
if (!is_string($name)) {
throw new Exception();
}
}

public function checkStringInParamWithName(
#[Assert(name: 'string')]
mixed $name
): void {
if (!is_string($name)) {
throw new Exception();
}
}

public function checkStringInTwoParams(
#[Assert('string')]
mixed $name1,
#[Assert('string')]
mixed $name2
): void {
if (!is_string($name1) || !is_string($name2)) {
throw new Exception();
}
}
}
12 changes: 12 additions & 0 deletions tests/data/AssertIfFalse/FunctionAssertIfFalseAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\AssertIfFalse;

use Exception;
use PhpStaticAnalysis\Attributes\AssertIfFalse;

#[AssertIfFalse(name: 'string')]
function checkString(mixed $name): bool
{
return !is_string($name);
}
17 changes: 17 additions & 0 deletions tests/data/AssertIfFalse/InvalidMethodAssertIfFalseAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\AssertIfFalse;

use PhpStaticAnalysis\Attributes\AssertIfFalse;

class InvalidMethodAssertIfFalseAttribute
{
#[AssertIfFalse(0)]
public function checkString(mixed $name): bool
{
return false;
}

#[AssertIfFalse(property: 'string')]
public string $property;
}
Loading

0 comments on commit 3aaf8e1

Please sign in to comment.