Skip to content

Commit

Permalink
Add DefineType and ImportType attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
carlos-granados committed Mar 5, 2024
1 parent 3f1c657 commit cd6091a
Show file tree
Hide file tree
Showing 15 changed files with 237 additions and 9 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,10 @@ These are the available attributes and their corresponding PHPDoc annotations:

| Attribute | PHPDoc Annotations |
|-------------------------------------------------------------------------------------------------------------------|--------------------------------------|
| [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` |
| [ImportType](https://github.com/php-static-analysis/attributes/blob/main/doc/ImportType.md) | `@import-type` |
| [Impure](https://github.com/php-static-analysis/attributes/blob/main/doc/Impure.md) | `@impure` |
| [Internal](https://github.com/php-static-analysis/attributes/blob/main/doc/Internal.md) | `@internal` |
| [IsReadOnly](https://github.com/php-static-analysis/attributes/blob/main/doc/IsReadOnly.md) | `@readonly` |
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
"prefer-stable": true,
"require": {
"php": ">=8.0",
"php-static-analysis/attributes": "^0.1.17 || dev-main",
"php-static-analysis/node-visitor": "^0.1.17 || dev-main",
"php-static-analysis/attributes": "^0.2.2 || dev-main",
"php-static-analysis/node-visitor": "^0.2.2 || dev-main",
"phpstan/phpstan": "^1.8"
},
"require-dev": {
Expand Down
40 changes: 40 additions & 0 deletions tests/DefineTypeAttributeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension;

class DefineTypeAttributeTest extends BaseAttributeTestCase
{
public function testClassDefineTypeAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/DefineType/ClassDefineTypeAttribute.php');
$this->assertCount(0, $errors);
}

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

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

public function testInvalidClassDefineTypeAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/DefineType/InvalidClassDefineTypeAttribute.php');

$expectedErrors = [
'PHPDoc tag @phpstan-type has invalid value (): Unexpected token "\n * ", expected type at offset 20' => 7,
'PHPDoc tag @phpstan-type name has invalid value: Unexpected token "(", expected TOKEN_PHPDOC_EOL at offset 72' => 7,
'PHPDoc tag @phpstan-type string has invalid value: Unexpected token "\n * ", expected type at offset 44' => 7,
'Parameter #1 ...$types of attribute class PhpStaticAnalysis\Attributes\DefineType constructor expects string, int given.' => 7,
'Type alias has an invalid name: string.' => 7,
'Attribute class PhpStaticAnalysis\Attributes\DefineType does not have the method target.' => 12,
];

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

namespace test\PhpStaticAnalysis\PHPStanExtension;

class ImportTypeAttributeTest extends BaseAttributeTestCase
{
public function testClassImportTypeAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/ImportType/ClassImportTypeAttribute.php');
$this->assertCount(0, $errors);
}

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

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

public function testInvalidClassImportTypeAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/ImportType/InvalidClassImportTypeAttribute.php');

$expectedErrors = [
'PHPDoc tag @phpstan-import-type has invalid value (Unexpected token "(", expected \'*/\' at offset 98 on line 4): Unexpected token "(", expected \'*/\' at offset 98' => 8,
'Parameter #1 ...$from of attribute class PhpStaticAnalysis\Attributes\ImportType constructor expects string, int given.' => 8,
'Attribute class PhpStaticAnalysis\Attributes\ImportType does not have the method target.' => 13,
];

$this->checkExpectedErrors($errors, $expectedErrors);
}
}
12 changes: 6 additions & 6 deletions tests/TypeAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ public function testInvalidPropertyTypeAttribute(): void
$errors = $this->analyse(__DIR__ . '/data/Type/InvalidPropertyTypeAttribute.php');

$expectedErrors = [
'Attribute class PhpStaticAnalysis\Attributes\Type does not have the class target.' => 7,
'PHPDoc tag @var has invalid value (): Unexpected token "\n ", expected type at offset 11' => 10,
'Parameter #1 $type of attribute class PhpStaticAnalysis\Attributes\Type constructor expects string, int given.' => 10,
'Attribute class PhpStaticAnalysis\Attributes\Type is not repeatable but is already present above the property.' => 14,
'Attribute class PhpStaticAnalysis\Attributes\Type constructor invoked with 2 parameters, 1 required.' => 17,
'PHPDoc tag @var has invalid value ($a + $b): Unexpected token "$a", expected type at offset 12' => 20,
'PHPDoc tag @var has invalid value (): Unexpected token "\n ", expected type at offset 11' => 9,
'Parameter #1 $type of attribute class PhpStaticAnalysis\Attributes\Type constructor expects string, int given.' => 9,
'Attribute class PhpStaticAnalysis\Attributes\Type is not repeatable but is already present above the property.' => 13,
'Attribute class PhpStaticAnalysis\Attributes\Type constructor invoked with 2 parameters, 1 required.' => 16,
'PHPDoc tag @var has invalid value ($a + $b): Unexpected token "$a", expected type at offset 12' => 19,
'Attribute class PhpStaticAnalysis\Attributes\Type does not have the parameter target.' => 23,
];

$this->checkExpectedErrors($errors, $expectedErrors);
Expand Down
21 changes: 21 additions & 0 deletions tests/data/DefineType/ClassDefineTypeAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\DefineType;

use PhpStaticAnalysis\Attributes\DefineType;
use PhpStaticAnalysis\Attributes\Param;

#[DefineType(UserAddress: 'array{street: string, city: string, zip: string}')] // this is an alias of the listed type
#[DefineType('UserName array{firstName: string, lastName: string}')]
#[DefineType(
StringArray: 'string[]',
IntArray: 'int[]',
)]
class ClassDefineTypeAttribute
{
#[Param(address: 'UserAddress')]
public function getZip($address): string
{
return $address['zip'];
}
}
10 changes: 10 additions & 0 deletions tests/data/DefineType/InterfaceDefineTypeAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\DefineType;

use PhpStaticAnalysis\Attributes\DefineType;

#[DefineType(StringArray: 'string[]')]
interface InterfaceDefineTypeAttribute
{
}
17 changes: 17 additions & 0 deletions tests/data/DefineType/InvalidClassDefineTypeAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\DefineType;

use PhpStaticAnalysis\Attributes\DefineType;

#[DefineType(0)]
#[DefineType('string')]
#[DefineType(name: 'count($a)')]
class InvalidClassDefineTypeAttribute
{
#[DefineType('StringArray string[]')]
public function getName(): string
{
return "John";
}
}
10 changes: 10 additions & 0 deletions tests/data/DefineType/TraitDefineTypeAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\DefineType;

use PhpStaticAnalysis\Attributes\DefineType;

#[DefineType(StringArray: 'string[]')]
trait TraitDefineTypeAttribute
{
}
30 changes: 30 additions & 0 deletions tests/data/ImportType/ClassImportTypeAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\ImportType;

use PhpStaticAnalysis\Attributes\DefineType;
use PhpStaticAnalysis\Attributes\ImportType;
use PhpStaticAnalysis\Attributes\Param;

#[ImportType(UserAddress: TypeClass::class)] // we import this alias from another class
#[ImportType('UserName from TypeClass')]
#[ImportType(
StringArray: 'TypeClass',
IntArray: TypeClass::class,
)]
class ClassImportTypeAttribute
{
#[Param(address: 'UserAddress')]
public function getZip($address): string
{
return $address['zip'];
}
}

#[DefineType(UserAddress: 'array{street: string, city: string, zip: string}')]
#[DefineType('UserName array{firstName: string, lastName: string}')]
#[DefineType(StringArray: 'string[]')]
#[DefineType(IntArray: 'int[]')]
class TypeClass
{
}
16 changes: 16 additions & 0 deletions tests/data/ImportType/InterfaceImportTypeAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\ImportType;

use PhpStaticAnalysis\Attributes\DefineType;
use PhpStaticAnalysis\Attributes\ImportType;

#[ImportType(StringArray: StringClass::class)]
interface InterfaceImportTypeAttribute
{
}

#[DefineType(StringArray: 'string[]')]
class StringClass
{
}
23 changes: 23 additions & 0 deletions tests/data/ImportType/InvalidClassImportTypeAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\ImportType;

use PhpStaticAnalysis\Attributes\DefineType;
use PhpStaticAnalysis\Attributes\ImportType;

#[ImportType(0)]
#[ImportType('string')]
#[ImportType(name: 'count($a)')]
class InvalidClassImportTypeAttribute
{
#[ImportType(StringArray: StringClass::class)]
public function getName(): string
{
return "John";
}
}

#[DefineType(StringArray: 'string[]')]
class StringClass
{
}
16 changes: 16 additions & 0 deletions tests/data/ImportType/TraitImportTypeAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\ImportType;

use PhpStaticAnalysis\Attributes\DefineType;
use PhpStaticAnalysis\Attributes\ImportType;

#[ImportType(StringArray: StringClass::class)]
trait TraitImportTypeAttribute
{
}

#[DefineType(StringArray: 'string[]')]
class StringClass
{
}
7 changes: 6 additions & 1 deletion tests/data/Type/InvalidPropertyTypeAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use PhpStaticAnalysis\Attributes\Type;

#[Type('string')]
class InvalidPropertyTypeAttribute
{
#[Type(0)]
Expand All @@ -19,4 +18,10 @@ class InvalidPropertyTypeAttribute

#[Type('$a + $b')]
public string $andAnotherinvalidProperty;

public function getName(
#[Type('string')]
string $user
): void {
}
}
1 change: 1 addition & 0 deletions tests/data/Type/PropertyTypeAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Exception;
use PhpStaticAnalysis\Attributes\Type;

#[Type('StringArray string[]')]
class PropertyTypeAttribute
{
#[Type('string')]
Expand Down

0 comments on commit cd6091a

Please sign in to comment.