Skip to content

Commit

Permalink
Merge pull request #7692 from AndrolGenhald/bugfix/7685-attribute-ana…
Browse files Browse the repository at this point in the history
…lysis

Analyze attribute statements instead of constructing virtual statements.
  • Loading branch information
orklah committed Feb 20, 2022
2 parents d8bec4c + 0476ca7 commit ad91df5
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 95 deletions.
90 changes: 18 additions & 72 deletions src/Psalm/Internal/Analyzer/AttributeAnalyzer.php
Expand Up @@ -2,19 +2,17 @@

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\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Internal\Codebase\ConstantTypeResolver;
use Psalm\Internal\Provider\NodeDataProvider;
use Psalm\Internal\Scanner\UnresolvedConstantComponent;
use Psalm\Internal\Stubs\Generator\StubsGenerator;
use Psalm\Issue\InvalidAttribute;
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\ClassLikeStorage;
use Psalm\Type\Union;
Expand All @@ -30,6 +28,7 @@ class AttributeAnalyzer
public static function analyze(
SourceAnalyzer $source,
AttributeStorage $attribute,
AttributeGroup $attribute_group,
array $suppressed_issues,
int $target,
?ClassLikeStorage $classlike_storage = null
Expand Down Expand Up @@ -107,77 +106,12 @@ public static function analyze(

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(
$source,
new NodeDataProvider()
);

$statements_analyzer->analyze(
[new VirtualExpression($new_stmt)],
new Context()
);
$statements_analyzer->analyze(self::attributeGroupToStmts($attribute_group), new Context());
}

/**
Expand Down Expand Up @@ -253,4 +187,16 @@ private static function checkAttributeTargets(
);
}
}

/**
* @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;
}
}
6 changes: 4 additions & 2 deletions src/Psalm/Internal/Analyzer/ClassAnalyzer.php
Expand Up @@ -397,10 +397,11 @@ public function analyze(
}
}

foreach ($storage->attributes as $attribute) {
foreach ($storage->attributes as $i => $attribute) {
AttributeAnalyzer::analyze(
$this,
$attribute,
$class->attrGroups[$i],
$storage->suppressed_issues + $this->getSuppressedIssues(),
1,
$storage
Expand Down Expand Up @@ -1522,10 +1523,11 @@ private function checkForMissingPropertyType(

$property_storage = $class_storage->properties[$property_name];

foreach ($property_storage->attributes as $attribute) {
foreach ($property_storage->attributes as $i => $attribute) {
AttributeAnalyzer::analyze(
$source,
$attribute,
$stmt->attrGroups[$i],
$this->source->getSuppressedIssues(),
8
);
Expand Down
13 changes: 10 additions & 3 deletions src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php
Expand Up @@ -5,6 +5,7 @@
use PhpParser;
use PhpParser\Node\Expr\ArrowFunction;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use Psalm\CodeLocation;
Expand Down Expand Up @@ -63,6 +64,7 @@
use function array_keys;
use function array_merge;
use function array_search;
use function array_values;
use function count;
use function end;
use function in_array;
Expand Down Expand Up @@ -351,6 +353,7 @@ public function analyze(
$storage,
$cased_method_id,
$params,
array_values($this->function->params),
$context,
(bool) $template_types
);
Expand Down Expand Up @@ -816,10 +819,11 @@ public function analyze(
);
}

foreach ($storage->attributes as $attribute) {
foreach ($storage->attributes as $i => $attribute) {
AttributeAnalyzer::analyze(
$this,
$attribute,
$this->function->attrGroups[$i],
$storage->suppressed_issues + $this->getSuppressedIssues(),
$storage instanceof MethodStorage ? 4 : 2
);
Expand Down Expand Up @@ -968,13 +972,15 @@ private function checkParamReferences(
}

/**
* @param array<int, FunctionLikeParameter> $params
* @param list<FunctionLikeParameter> $params
* @param list<Param> $param_stmts
*/
private function processParams(
StatementsAnalyzer $statements_analyzer,
FunctionLikeStorage $storage,
?string $cased_method_id,
array $params,
array $param_stmts,
Context $context,
bool $has_template_types
): bool {
Expand Down Expand Up @@ -1262,10 +1268,11 @@ private function processParams(
$context->hasVariable('$' . $function_param->name);
}

foreach ($function_param->attributes as $attribute) {
foreach ($function_param->attributes as $i => $attribute) {
AttributeAnalyzer::analyze(
$this,
$attribute,
$param_stmts[$offset]->attrGroups[$i],
$storage->suppressed_issues,
$function_param->promoted_property ? 8 : 32
);
Expand Down
3 changes: 2 additions & 1 deletion src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php
Expand Up @@ -96,10 +96,11 @@ public function analyze(): void

$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(
$this,
$attribute,
$this->class->attrGroups[$i],
$class_storage->suppressed_issues + $this->getSuppressedIssues(),
1,
$class_storage
Expand Down
2 changes: 1 addition & 1 deletion src/Psalm/Internal/Codebase/Methods.php
Expand Up @@ -361,7 +361,7 @@ public function methodExists(
/**
* @param list<PhpParser\Node\Arg> $args
*
* @return array<int, FunctionLikeParameter>
* @return list<FunctionLikeParameter>
*/
public function getMethodParams(
MethodIdentifier $method_id,
Expand Down
7 changes: 4 additions & 3 deletions src/Psalm/Internal/Provider/MethodParamsProvider.php
Expand Up @@ -13,6 +13,7 @@
use Psalm\StatementsSource;
use Psalm\Storage\FunctionLikeParameter;

use function array_values;
use function is_subclass_of;
use function strtolower;

Expand Down Expand Up @@ -101,7 +102,7 @@ public function has(string $fq_classlike_name): bool
/**
* @param ?list<Arg> $call_args
*
* @return ?array<int, FunctionLikeParameter>
* @return ?list<FunctionLikeParameter>
*/
public function getMethodParams(
string $fq_classlike_name,
Expand All @@ -122,7 +123,7 @@ public function getMethodParams(
);

if ($result !== null) {
return $result;
return array_values($result);
}
}

Expand All @@ -138,7 +139,7 @@ public function getMethodParams(
$result = $class_handler($event);

if ($result !== null) {
return $result;
return array_values($result);
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/Psalm/Storage/AttributeArg.php
Expand Up @@ -10,6 +10,7 @@ class AttributeArg
{
/**
* @var ?string
* @psalm-suppress PossiblyUnusedProperty It's part of the public API for now
*/
public $name;

Expand All @@ -20,11 +21,12 @@ class AttributeArg

/**
* @var CodeLocation
* @psalm-suppress PossiblyUnusedProperty It's part of the public API for now
*/
public $location;

/**
* @param Union|UnresolvedConstantComponent $type
* @param Union|UnresolvedConstantComponent $type
*/
public function __construct(
?string $name,
Expand Down

0 comments on commit ad91df5

Please sign in to comment.