mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 04:45:20 +01:00
Merge pull request #7692 from AndrolGenhald/bugfix/7685-attribute-analysis
Analyze attribute statements instead of constructing virtual statements.
This commit is contained in:
commit
ad91df5ee1
@ -2,19 +2,17 @@
|
|||||||
|
|
||||||
namespace Psalm\Internal\Analyzer;
|
namespace Psalm\Internal\Analyzer;
|
||||||
|
|
||||||
|
use PhpParser\Node\AttributeGroup;
|
||||||
|
use PhpParser\Node\Expr\New_;
|
||||||
|
use PhpParser\Node\Stmt;
|
||||||
|
use PhpParser\Node\Stmt\Expression;
|
||||||
use Psalm\Context;
|
use Psalm\Context;
|
||||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||||
use Psalm\Internal\Codebase\ConstantTypeResolver;
|
use Psalm\Internal\Codebase\ConstantTypeResolver;
|
||||||
use Psalm\Internal\Provider\NodeDataProvider;
|
use Psalm\Internal\Provider\NodeDataProvider;
|
||||||
use Psalm\Internal\Scanner\UnresolvedConstantComponent;
|
use Psalm\Internal\Scanner\UnresolvedConstantComponent;
|
||||||
use Psalm\Internal\Stubs\Generator\StubsGenerator;
|
|
||||||
use Psalm\Issue\InvalidAttribute;
|
use Psalm\Issue\InvalidAttribute;
|
||||||
use Psalm\IssueBuffer;
|
use Psalm\IssueBuffer;
|
||||||
use Psalm\Node\Expr\VirtualNew;
|
|
||||||
use Psalm\Node\Name\VirtualFullyQualified;
|
|
||||||
use Psalm\Node\Stmt\VirtualExpression;
|
|
||||||
use Psalm\Node\VirtualArg;
|
|
||||||
use Psalm\Node\VirtualIdentifier;
|
|
||||||
use Psalm\Storage\AttributeStorage;
|
use Psalm\Storage\AttributeStorage;
|
||||||
use Psalm\Storage\ClassLikeStorage;
|
use Psalm\Storage\ClassLikeStorage;
|
||||||
use Psalm\Type\Union;
|
use Psalm\Type\Union;
|
||||||
@ -30,6 +28,7 @@ class AttributeAnalyzer
|
|||||||
public static function analyze(
|
public static function analyze(
|
||||||
SourceAnalyzer $source,
|
SourceAnalyzer $source,
|
||||||
AttributeStorage $attribute,
|
AttributeStorage $attribute,
|
||||||
|
AttributeGroup $attribute_group,
|
||||||
array $suppressed_issues,
|
array $suppressed_issues,
|
||||||
int $target,
|
int $target,
|
||||||
?ClassLikeStorage $classlike_storage = null
|
?ClassLikeStorage $classlike_storage = null
|
||||||
@ -107,77 +106,12 @@ class AttributeAnalyzer
|
|||||||
|
|
||||||
self::checkAttributeTargets($source, $attribute, $target);
|
self::checkAttributeTargets($source, $attribute, $target);
|
||||||
|
|
||||||
$node_args = [];
|
|
||||||
|
|
||||||
foreach ($attribute->args as $storage_arg) {
|
|
||||||
$type = $storage_arg->type;
|
|
||||||
|
|
||||||
if ($type instanceof UnresolvedConstantComponent) {
|
|
||||||
$type = new Union([
|
|
||||||
ConstantTypeResolver::resolve(
|
|
||||||
$codebase->classlikes,
|
|
||||||
$type,
|
|
||||||
$source instanceof StatementsAnalyzer ? $source : null
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($type->isMixed()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$type_expr = StubsGenerator::getExpressionFromType(
|
|
||||||
$type
|
|
||||||
);
|
|
||||||
|
|
||||||
$arg_attributes = [
|
|
||||||
'startFilePos' => $storage_arg->location->raw_file_start,
|
|
||||||
'endFilePos' => $storage_arg->location->raw_file_end,
|
|
||||||
'startLine' => $storage_arg->location->raw_line_number
|
|
||||||
];
|
|
||||||
|
|
||||||
$type_expr->setAttributes($arg_attributes);
|
|
||||||
|
|
||||||
$node_args[] = new VirtualArg(
|
|
||||||
$type_expr,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
$arg_attributes,
|
|
||||||
$storage_arg->name
|
|
||||||
? new VirtualIdentifier(
|
|
||||||
$storage_arg->name,
|
|
||||||
$arg_attributes
|
|
||||||
)
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$new_stmt = new VirtualNew(
|
|
||||||
new VirtualFullyQualified(
|
|
||||||
$attribute->fq_class_name,
|
|
||||||
[
|
|
||||||
'startFilePos' => $attribute->name_location->raw_file_start,
|
|
||||||
'endFilePos' => $attribute->name_location->raw_file_end,
|
|
||||||
'startLine' => $attribute->name_location->raw_line_number
|
|
||||||
]
|
|
||||||
),
|
|
||||||
$node_args,
|
|
||||||
[
|
|
||||||
'startFilePos' => $attribute->location->raw_file_start,
|
|
||||||
'endFilePos' => $attribute->location->raw_file_end,
|
|
||||||
'startLine' => $attribute->location->raw_line_number
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
$statements_analyzer = new StatementsAnalyzer(
|
$statements_analyzer = new StatementsAnalyzer(
|
||||||
$source,
|
$source,
|
||||||
new NodeDataProvider()
|
new NodeDataProvider()
|
||||||
);
|
);
|
||||||
|
|
||||||
$statements_analyzer->analyze(
|
$statements_analyzer->analyze(self::attributeGroupToStmts($attribute_group), new Context());
|
||||||
[new VirtualExpression($new_stmt)],
|
|
||||||
new Context()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -253,4 +187,16 @@ class AttributeAnalyzer
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<Stmt>
|
||||||
|
*/
|
||||||
|
private static function attributeGroupToStmts(AttributeGroup $attribute_group): array
|
||||||
|
{
|
||||||
|
$stmts = [];
|
||||||
|
foreach ($attribute_group->attrs as $attr) {
|
||||||
|
$stmts[] = new Expression(new New_($attr->name, $attr->args, $attr->getAttributes()));
|
||||||
|
}
|
||||||
|
return $stmts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -397,10 +397,11 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($storage->attributes as $attribute) {
|
foreach ($storage->attributes as $i => $attribute) {
|
||||||
AttributeAnalyzer::analyze(
|
AttributeAnalyzer::analyze(
|
||||||
$this,
|
$this,
|
||||||
$attribute,
|
$attribute,
|
||||||
|
$class->attrGroups[$i],
|
||||||
$storage->suppressed_issues + $this->getSuppressedIssues(),
|
$storage->suppressed_issues + $this->getSuppressedIssues(),
|
||||||
1,
|
1,
|
||||||
$storage
|
$storage
|
||||||
@ -1522,10 +1523,11 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
|||||||
|
|
||||||
$property_storage = $class_storage->properties[$property_name];
|
$property_storage = $class_storage->properties[$property_name];
|
||||||
|
|
||||||
foreach ($property_storage->attributes as $attribute) {
|
foreach ($property_storage->attributes as $i => $attribute) {
|
||||||
AttributeAnalyzer::analyze(
|
AttributeAnalyzer::analyze(
|
||||||
$source,
|
$source,
|
||||||
$attribute,
|
$attribute,
|
||||||
|
$stmt->attrGroups[$i],
|
||||||
$this->source->getSuppressedIssues(),
|
$this->source->getSuppressedIssues(),
|
||||||
8
|
8
|
||||||
);
|
);
|
||||||
|
@ -5,6 +5,7 @@ namespace Psalm\Internal\Analyzer;
|
|||||||
use PhpParser;
|
use PhpParser;
|
||||||
use PhpParser\Node\Expr\ArrowFunction;
|
use PhpParser\Node\Expr\ArrowFunction;
|
||||||
use PhpParser\Node\Expr\Closure;
|
use PhpParser\Node\Expr\Closure;
|
||||||
|
use PhpParser\Node\Param;
|
||||||
use PhpParser\Node\Stmt\ClassMethod;
|
use PhpParser\Node\Stmt\ClassMethod;
|
||||||
use PhpParser\Node\Stmt\Function_;
|
use PhpParser\Node\Stmt\Function_;
|
||||||
use Psalm\CodeLocation;
|
use Psalm\CodeLocation;
|
||||||
@ -63,6 +64,7 @@ use function array_key_exists;
|
|||||||
use function array_keys;
|
use function array_keys;
|
||||||
use function array_merge;
|
use function array_merge;
|
||||||
use function array_search;
|
use function array_search;
|
||||||
|
use function array_values;
|
||||||
use function count;
|
use function count;
|
||||||
use function end;
|
use function end;
|
||||||
use function in_array;
|
use function in_array;
|
||||||
@ -351,6 +353,7 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer
|
|||||||
$storage,
|
$storage,
|
||||||
$cased_method_id,
|
$cased_method_id,
|
||||||
$params,
|
$params,
|
||||||
|
array_values($this->function->params),
|
||||||
$context,
|
$context,
|
||||||
(bool) $template_types
|
(bool) $template_types
|
||||||
);
|
);
|
||||||
@ -816,10 +819,11 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($storage->attributes as $attribute) {
|
foreach ($storage->attributes as $i => $attribute) {
|
||||||
AttributeAnalyzer::analyze(
|
AttributeAnalyzer::analyze(
|
||||||
$this,
|
$this,
|
||||||
$attribute,
|
$attribute,
|
||||||
|
$this->function->attrGroups[$i],
|
||||||
$storage->suppressed_issues + $this->getSuppressedIssues(),
|
$storage->suppressed_issues + $this->getSuppressedIssues(),
|
||||||
$storage instanceof MethodStorage ? 4 : 2
|
$storage instanceof MethodStorage ? 4 : 2
|
||||||
);
|
);
|
||||||
@ -968,13 +972,15 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<int, FunctionLikeParameter> $params
|
* @param list<FunctionLikeParameter> $params
|
||||||
|
* @param list<Param> $param_stmts
|
||||||
*/
|
*/
|
||||||
private function processParams(
|
private function processParams(
|
||||||
StatementsAnalyzer $statements_analyzer,
|
StatementsAnalyzer $statements_analyzer,
|
||||||
FunctionLikeStorage $storage,
|
FunctionLikeStorage $storage,
|
||||||
?string $cased_method_id,
|
?string $cased_method_id,
|
||||||
array $params,
|
array $params,
|
||||||
|
array $param_stmts,
|
||||||
Context $context,
|
Context $context,
|
||||||
bool $has_template_types
|
bool $has_template_types
|
||||||
): bool {
|
): bool {
|
||||||
@ -1262,10 +1268,11 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer
|
|||||||
$context->hasVariable('$' . $function_param->name);
|
$context->hasVariable('$' . $function_param->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($function_param->attributes as $attribute) {
|
foreach ($function_param->attributes as $i => $attribute) {
|
||||||
AttributeAnalyzer::analyze(
|
AttributeAnalyzer::analyze(
|
||||||
$this,
|
$this,
|
||||||
$attribute,
|
$attribute,
|
||||||
|
$param_stmts[$offset]->attrGroups[$i],
|
||||||
$storage->suppressed_issues,
|
$storage->suppressed_issues,
|
||||||
$function_param->promoted_property ? 8 : 32
|
$function_param->promoted_property ? 8 : 32
|
||||||
);
|
);
|
||||||
|
@ -96,10 +96,11 @@ class InterfaceAnalyzer extends ClassLikeAnalyzer
|
|||||||
|
|
||||||
$class_storage = $codebase->classlike_storage_provider->get($fq_interface_name);
|
$class_storage = $codebase->classlike_storage_provider->get($fq_interface_name);
|
||||||
|
|
||||||
foreach ($class_storage->attributes as $attribute) {
|
foreach ($class_storage->attributes as $i => $attribute) {
|
||||||
AttributeAnalyzer::analyze(
|
AttributeAnalyzer::analyze(
|
||||||
$this,
|
$this,
|
||||||
$attribute,
|
$attribute,
|
||||||
|
$this->class->attrGroups[$i],
|
||||||
$class_storage->suppressed_issues + $this->getSuppressedIssues(),
|
$class_storage->suppressed_issues + $this->getSuppressedIssues(),
|
||||||
1,
|
1,
|
||||||
$class_storage
|
$class_storage
|
||||||
|
@ -361,7 +361,7 @@ class Methods
|
|||||||
/**
|
/**
|
||||||
* @param list<PhpParser\Node\Arg> $args
|
* @param list<PhpParser\Node\Arg> $args
|
||||||
*
|
*
|
||||||
* @return array<int, FunctionLikeParameter>
|
* @return list<FunctionLikeParameter>
|
||||||
*/
|
*/
|
||||||
public function getMethodParams(
|
public function getMethodParams(
|
||||||
MethodIdentifier $method_id,
|
MethodIdentifier $method_id,
|
||||||
|
@ -13,6 +13,7 @@ use Psalm\Plugin\Hook\MethodParamsProviderInterface as LegacyMethodParamsProvide
|
|||||||
use Psalm\StatementsSource;
|
use Psalm\StatementsSource;
|
||||||
use Psalm\Storage\FunctionLikeParameter;
|
use Psalm\Storage\FunctionLikeParameter;
|
||||||
|
|
||||||
|
use function array_values;
|
||||||
use function is_subclass_of;
|
use function is_subclass_of;
|
||||||
use function strtolower;
|
use function strtolower;
|
||||||
|
|
||||||
@ -101,7 +102,7 @@ class MethodParamsProvider
|
|||||||
/**
|
/**
|
||||||
* @param ?list<Arg> $call_args
|
* @param ?list<Arg> $call_args
|
||||||
*
|
*
|
||||||
* @return ?array<int, FunctionLikeParameter>
|
* @return ?list<FunctionLikeParameter>
|
||||||
*/
|
*/
|
||||||
public function getMethodParams(
|
public function getMethodParams(
|
||||||
string $fq_classlike_name,
|
string $fq_classlike_name,
|
||||||
@ -122,7 +123,7 @@ class MethodParamsProvider
|
|||||||
);
|
);
|
||||||
|
|
||||||
if ($result !== null) {
|
if ($result !== null) {
|
||||||
return $result;
|
return array_values($result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,7 +139,7 @@ class MethodParamsProvider
|
|||||||
$result = $class_handler($event);
|
$result = $class_handler($event);
|
||||||
|
|
||||||
if ($result !== null) {
|
if ($result !== null) {
|
||||||
return $result;
|
return array_values($result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ class AttributeArg
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var ?string
|
* @var ?string
|
||||||
|
* @psalm-suppress PossiblyUnusedProperty It's part of the public API for now
|
||||||
*/
|
*/
|
||||||
public $name;
|
public $name;
|
||||||
|
|
||||||
@ -20,11 +21,12 @@ class AttributeArg
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @var CodeLocation
|
* @var CodeLocation
|
||||||
|
* @psalm-suppress PossiblyUnusedProperty It's part of the public API for now
|
||||||
*/
|
*/
|
||||||
public $location;
|
public $location;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Union|UnresolvedConstantComponent $type
|
* @param Union|UnresolvedConstantComponent $type
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
?string $name,
|
?string $name,
|
||||||
|
@ -5,6 +5,8 @@ namespace Psalm\Tests;
|
|||||||
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
|
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
|
||||||
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
|
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
|
||||||
|
|
||||||
|
use const DIRECTORY_SEPARATOR;
|
||||||
|
|
||||||
class AttributeTest extends TestCase
|
class AttributeTest extends TestCase
|
||||||
{
|
{
|
||||||
use InvalidCodeAnalysisTestTrait;
|
use InvalidCodeAnalysisTestTrait;
|
||||||
@ -240,7 +242,28 @@ class AttributeTest extends TestCase
|
|||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
'8.1'
|
'8.1'
|
||||||
]
|
],
|
||||||
|
'createObjectAsAttributeArg' => [
|
||||||
|
'<?php
|
||||||
|
#[Attribute]
|
||||||
|
class B
|
||||||
|
{
|
||||||
|
public function __construct(?array $listOfB = null) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Attribute(Attribute::TARGET_CLASS)]
|
||||||
|
class A
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param B[] $listOfB
|
||||||
|
*/
|
||||||
|
public function __construct(?array $listOfB = null) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[A([new B])]
|
||||||
|
class C {}
|
||||||
|
',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,7 +279,7 @@ class AttributeTest extends TestCase
|
|||||||
|
|
||||||
#[A]
|
#[A]
|
||||||
class B {}',
|
class B {}',
|
||||||
'error_message' => 'InvalidAttribute',
|
'error_message' => 'InvalidAttribute - src' . DIRECTORY_SEPARATOR . 'somefile.php:4:23',
|
||||||
[],
|
[],
|
||||||
false,
|
false,
|
||||||
'8.0'
|
'8.0'
|
||||||
@ -267,7 +290,7 @@ class AttributeTest extends TestCase
|
|||||||
|
|
||||||
#[Pure]
|
#[Pure]
|
||||||
class Video {}',
|
class Video {}',
|
||||||
'error_message' => 'UndefinedAttributeClass',
|
'error_message' => 'UndefinedAttributeClass - src' . DIRECTORY_SEPARATOR . 'somefile.php:4:23',
|
||||||
[],
|
[],
|
||||||
false,
|
false,
|
||||||
'8.0'
|
'8.0'
|
||||||
@ -278,7 +301,7 @@ class AttributeTest extends TestCase
|
|||||||
|
|
||||||
#[Pure]
|
#[Pure]
|
||||||
function foo() : void {}',
|
function foo() : void {}',
|
||||||
'error_message' => 'UndefinedAttributeClass',
|
'error_message' => 'UndefinedAttributeClass - src' . DIRECTORY_SEPARATOR . 'somefile.php:4:23',
|
||||||
[],
|
[],
|
||||||
false,
|
false,
|
||||||
'8.0'
|
'8.0'
|
||||||
@ -288,7 +311,7 @@ class AttributeTest extends TestCase
|
|||||||
use Foo\Bar\Pure;
|
use Foo\Bar\Pure;
|
||||||
|
|
||||||
function foo(#[Pure] string $str) : void {}',
|
function foo(#[Pure] string $str) : void {}',
|
||||||
'error_message' => 'UndefinedAttributeClass',
|
'error_message' => 'UndefinedAttributeClass - src' . DIRECTORY_SEPARATOR . 'somefile.php:4:36',
|
||||||
[],
|
[],
|
||||||
false,
|
false,
|
||||||
'8.0'
|
'8.0'
|
||||||
@ -304,7 +327,7 @@ class AttributeTest extends TestCase
|
|||||||
|
|
||||||
#[Table()]
|
#[Table()]
|
||||||
class Video {}',
|
class Video {}',
|
||||||
'error_message' => 'TooFewArguments',
|
'error_message' => 'TooFewArguments - src' . DIRECTORY_SEPARATOR . 'somefile.php:9:23',
|
||||||
[],
|
[],
|
||||||
false,
|
false,
|
||||||
'8.0'
|
'8.0'
|
||||||
@ -321,7 +344,7 @@ class AttributeTest extends TestCase
|
|||||||
|
|
||||||
#[Foo("foo")]
|
#[Foo("foo")]
|
||||||
class Bar{}',
|
class Bar{}',
|
||||||
'error_message' => 'InvalidScalarArgument',
|
'error_message' => 'InvalidScalarArgument - src' . DIRECTORY_SEPARATOR . 'somefile.php:10:27',
|
||||||
[],
|
[],
|
||||||
false,
|
false,
|
||||||
'8.0'
|
'8.0'
|
||||||
@ -337,7 +360,7 @@ class AttributeTest extends TestCase
|
|||||||
|
|
||||||
#[Table("videos")]
|
#[Table("videos")]
|
||||||
function foo() : void {}',
|
function foo() : void {}',
|
||||||
'error_message' => 'InvalidAttribute',
|
'error_message' => 'InvalidAttribute - src' . DIRECTORY_SEPARATOR . 'somefile.php:9:23',
|
||||||
[],
|
[],
|
||||||
false,
|
false,
|
||||||
'8.0'
|
'8.0'
|
||||||
@ -346,7 +369,7 @@ class AttributeTest extends TestCase
|
|||||||
'<?php
|
'<?php
|
||||||
#[Attribute]
|
#[Attribute]
|
||||||
interface Foo {}',
|
interface Foo {}',
|
||||||
'error_message' => 'InvalidAttribute',
|
'error_message' => 'InvalidAttribute - src' . DIRECTORY_SEPARATOR . 'somefile.php:2:23',
|
||||||
[],
|
[],
|
||||||
false,
|
false,
|
||||||
'8.0'
|
'8.0'
|
||||||
@ -355,7 +378,7 @@ class AttributeTest extends TestCase
|
|||||||
'<?php
|
'<?php
|
||||||
#[Attribute]
|
#[Attribute]
|
||||||
interface Foo {}',
|
interface Foo {}',
|
||||||
'error_message' => 'InvalidAttribute',
|
'error_message' => 'InvalidAttribute - src' . DIRECTORY_SEPARATOR . 'somefile.php:2:23',
|
||||||
[],
|
[],
|
||||||
false,
|
false,
|
||||||
'8.0'
|
'8.0'
|
||||||
@ -364,7 +387,7 @@ class AttributeTest extends TestCase
|
|||||||
'<?php
|
'<?php
|
||||||
#[Attribute]
|
#[Attribute]
|
||||||
abstract class Baz {}',
|
abstract class Baz {}',
|
||||||
'error_message' => 'InvalidAttribute',
|
'error_message' => 'InvalidAttribute - src' . DIRECTORY_SEPARATOR . 'somefile.php:2:23',
|
||||||
[],
|
[],
|
||||||
false,
|
false,
|
||||||
'8.0'
|
'8.0'
|
||||||
@ -375,7 +398,7 @@ class AttributeTest extends TestCase
|
|||||||
class Baz {
|
class Baz {
|
||||||
private function __construct() {}
|
private function __construct() {}
|
||||||
}',
|
}',
|
||||||
'error_message' => 'InvalidAttribute',
|
'error_message' => 'InvalidAttribute - src' . DIRECTORY_SEPARATOR . 'somefile.php:2:23',
|
||||||
[],
|
[],
|
||||||
false,
|
false,
|
||||||
'8.0'
|
'8.0'
|
||||||
|
Loading…
Reference in New Issue
Block a user