allow vendord
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 32s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 1s
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 32s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 1s
This commit is contained in:
56
vendor/cuyz/valinor/qa/PHPStan/Extension/ApiAndInternalAnnotationCheck.php
vendored
Normal file
56
vendor/cuyz/valinor/qa/PHPStan/Extension/ApiAndInternalAnnotationCheck.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\QA\PHPStan\Extension;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Node\InClassNode;
|
||||
use PHPStan\Rules\Rule;
|
||||
use PHPStan\Rules\RuleErrorBuilder;
|
||||
|
||||
use function str_contains;
|
||||
use function str_starts_with;
|
||||
|
||||
/**
|
||||
* @implements Rule<InClassNode>
|
||||
*/
|
||||
final class ApiAndInternalAnnotationCheck implements Rule
|
||||
{
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return InClassNode::class;
|
||||
}
|
||||
|
||||
public function processNode(Node $node, Scope $scope): array
|
||||
{
|
||||
$reflection = $scope->getClassReflection();
|
||||
|
||||
if (! $reflection) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($reflection->isAnonymous()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (str_contains($reflection->getFileName() ?? '', '/tests/')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (str_starts_with($reflection->getName(), 'CuyZ\Valinor\QA')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (! preg_match('/@(api|internal)\s+/', $reflection->getResolvedPhpDoc()?->getPhpDocString() ?? '')) {
|
||||
return [
|
||||
RuleErrorBuilder::message(
|
||||
'Missing annotation `@api` or `@internal`.'
|
||||
)->identifier('valinor.apiOrInternalAnnotation')->build(),
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
61
vendor/cuyz/valinor/qa/PHPStan/Extension/ArgumentsMapperPHPStanExtension.php
vendored
Normal file
61
vendor/cuyz/valinor/qa/PHPStan/Extension/ArgumentsMapperPHPStanExtension.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\QA\PHPStan\Extension;
|
||||
|
||||
use CuyZ\Valinor\Mapper\ArgumentsMapper;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\MethodReflection;
|
||||
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
|
||||
use PHPStan\Type\Constant\ConstantStringType;
|
||||
use PHPStan\Type\DynamicMethodReturnTypeExtension;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
|
||||
use function count;
|
||||
|
||||
final class ArgumentsMapperPHPStanExtension implements DynamicMethodReturnTypeExtension
|
||||
{
|
||||
public function getClass(): string
|
||||
{
|
||||
return ArgumentsMapper::class;
|
||||
}
|
||||
|
||||
public function isMethodSupported(MethodReflection $methodReflection): bool
|
||||
{
|
||||
return $methodReflection->getName() === 'mapArguments';
|
||||
}
|
||||
|
||||
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
|
||||
{
|
||||
$arguments = $methodCall->getArgs();
|
||||
|
||||
if (count($arguments) === 0) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$type = $scope->getType($arguments[0]->value);
|
||||
|
||||
if (! $type->isCallable()->yes()) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$acceptors = $type->getCallableParametersAcceptors($scope);
|
||||
|
||||
if (count($acceptors) !== 1) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$parameters = $acceptors[0]->getParameters();
|
||||
|
||||
$builder = ConstantArrayTypeBuilder::createEmpty();
|
||||
|
||||
foreach ($parameters as $parameter) {
|
||||
$builder->setOffsetValueType(new ConstantStringType($parameter->getName()), $parameter->getType(), $parameter->isOptional());
|
||||
}
|
||||
|
||||
return $builder->getArray();
|
||||
}
|
||||
}
|
||||
91
vendor/cuyz/valinor/qa/PHPStan/Extension/SuppressPureErrors.php
vendored
Normal file
91
vendor/cuyz/valinor/qa/PHPStan/Extension/SuppressPureErrors.php
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\QA\PHPStan\Extension;
|
||||
|
||||
use CuyZ\Valinor\MapperBuilder;
|
||||
use CuyZ\Valinor\NormalizerBuilder;
|
||||
use CuyZ\Valinor\Utility\Polyfill;
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser\Error;
|
||||
use PHPStan\Analyser\IgnoreErrorExtension;
|
||||
use PHPStan\Analyser\Scope;
|
||||
|
||||
use function in_array;
|
||||
|
||||
/**
|
||||
* This extension should not be used by default, as it suppresses errors that
|
||||
* are typically indicative of purity issues in the codebase. It is advised to
|
||||
* first address the root causes of these errors before enabling this extension.
|
||||
*
|
||||
* When enabled, it will ignore all purity-related errors of method calls in the
|
||||
* context of `MapperBuilder` and `NormalizerBuilder`.
|
||||
*/
|
||||
final class SuppressPureErrors implements IgnoreErrorExtension
|
||||
{
|
||||
public function shouldIgnore(Error $error, Node $node, Scope $scope): bool
|
||||
{
|
||||
if ($error->getIdentifier() !== 'argument.type') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $node instanceof Node\Expr\MethodCall) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$type = $scope->getType($node->var);
|
||||
|
||||
if (! $type->isObject()->yes()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! Polyfill::array_find($type->getObjectClassNames(), fn (string $className) => $className === MapperBuilder::class || $className === NormalizerBuilder::class)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $node->name instanceof Node\Identifier) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$methodName = $node->name->toString();
|
||||
|
||||
if (! in_array($methodName, [
|
||||
'infer',
|
||||
'registerConstructor',
|
||||
'registerConverter',
|
||||
'registerTransformer',
|
||||
], true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($node->args === []) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$callableArgumentIndex = [
|
||||
'infer' => 1,
|
||||
'registerConstructor' => 0,
|
||||
'registerConverter' => 0,
|
||||
'registerTransformer' => 0,
|
||||
][$methodName];
|
||||
|
||||
$argument = $node->args[$callableArgumentIndex];
|
||||
|
||||
if (! $argument instanceof Node\Arg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$argumentType = $scope->getType($argument->value);
|
||||
|
||||
if ($argumentType->isCallable()->yes()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($argumentType->isArray()->yes() && $argumentType->getIterableValueType()->isCallable()->yes()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
80
vendor/cuyz/valinor/qa/PHPStan/Extension/TreeMapperPHPStanExtension.php
vendored
Normal file
80
vendor/cuyz/valinor/qa/PHPStan/Extension/TreeMapperPHPStanExtension.php
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\QA\PHPStan\Extension;
|
||||
|
||||
use CuyZ\Valinor\Mapper\TreeMapper;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\PhpDoc\TypeStringResolver;
|
||||
use PHPStan\PhpDocParser\Parser\ParserException;
|
||||
use PHPStan\Reflection\MethodReflection;
|
||||
use PHPStan\Type\DynamicMethodReturnTypeExtension;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
|
||||
use function implode;
|
||||
use function method_exists;
|
||||
|
||||
final class TreeMapperPHPStanExtension implements DynamicMethodReturnTypeExtension
|
||||
{
|
||||
public function __construct(private TypeStringResolver $resolver) {}
|
||||
|
||||
public function getClass(): string
|
||||
{
|
||||
return TreeMapper::class;
|
||||
}
|
||||
|
||||
public function isMethodSupported(MethodReflection $methodReflection): bool
|
||||
{
|
||||
return $methodReflection->getName() === 'map';
|
||||
}
|
||||
|
||||
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
|
||||
{
|
||||
$arguments = $methodCall->getArgs();
|
||||
|
||||
if (count($arguments) === 0) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$type = $scope->getType($arguments[0]->value);
|
||||
|
||||
if ($type instanceof UnionType) {
|
||||
return $type->traverse(fn (Type $type) => $this->type($type));
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->type($type);
|
||||
} catch (ParserException) {
|
||||
// Fallback to `mixed` type if the type cannot be resolved. This can
|
||||
// occur with a type that is not understood/supported by PHPStan. If
|
||||
// that happens, returning a mixed type is the safest option, as it
|
||||
// will not make the analysis fail.
|
||||
return new MixedType();
|
||||
}
|
||||
}
|
||||
|
||||
private function type(Type $type): Type
|
||||
{
|
||||
if ($type->isConstantValue()->yes()) {
|
||||
$value = implode('', $type->getConstantScalarValues());
|
||||
|
||||
return $this->resolver->resolve($value);
|
||||
}
|
||||
|
||||
// @phpstan-ignore function.alreadyNarrowedType (support for PHPStan v1)
|
||||
if (method_exists($type, 'isClassString') && $type->isClassString()->yes()) {
|
||||
return $type->getClassStringObjectType();
|
||||
}
|
||||
|
||||
// @phpstan-ignore method.nonObject (support for PHPStan v1)
|
||||
if (method_exists($type, 'isClassStringType') && $type->isClassStringType()->yes()) {
|
||||
return $type->getClassStringObjectType();
|
||||
}
|
||||
|
||||
return new MixedType();
|
||||
}
|
||||
}
|
||||
19
vendor/cuyz/valinor/qa/PHPStan/valinor-phpstan-configuration.php
vendored
Normal file
19
vendor/cuyz/valinor/qa/PHPStan/valinor-phpstan-configuration.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
use CuyZ\Valinor\QA\PHPStan\Extension\ArgumentsMapperPHPStanExtension;
|
||||
use CuyZ\Valinor\QA\PHPStan\Extension\TreeMapperPHPStanExtension;
|
||||
|
||||
require_once 'Extension/ArgumentsMapperPHPStanExtension.php';
|
||||
require_once 'Extension/TreeMapperPHPStanExtension.php';
|
||||
|
||||
return [
|
||||
'services' => [
|
||||
[
|
||||
'class' => TreeMapperPHPStanExtension::class,
|
||||
'tags' => ['phpstan.broker.dynamicMethodReturnTypeExtension']
|
||||
], [
|
||||
'class' => ArgumentsMapperPHPStanExtension::class,
|
||||
'tags' => ['phpstan.broker.dynamicMethodReturnTypeExtension']
|
||||
],
|
||||
],
|
||||
];
|
||||
14
vendor/cuyz/valinor/qa/PHPStan/valinor-phpstan-suppress-pure-errors.php
vendored
Normal file
14
vendor/cuyz/valinor/qa/PHPStan/valinor-phpstan-suppress-pure-errors.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
use CuyZ\Valinor\QA\PHPStan\Extension\SuppressPureErrors;
|
||||
|
||||
require_once 'Extension/SuppressPureErrors.php';
|
||||
|
||||
return [
|
||||
'services' => [
|
||||
[
|
||||
'class' => SuppressPureErrors::class,
|
||||
'tags' => ['phpstan.ignoreErrorExtension'],
|
||||
],
|
||||
],
|
||||
];
|
||||
75
vendor/cuyz/valinor/qa/Psalm/Plugin/ArgumentsMapperPsalmPlugin.php
vendored
Normal file
75
vendor/cuyz/valinor/qa/Psalm/Plugin/ArgumentsMapperPsalmPlugin.php
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\QA\Psalm\Plugin;
|
||||
|
||||
use CuyZ\Valinor\Mapper\ArgumentsMapper;
|
||||
use Psalm\Internal\Type\Comparator\CallableTypeComparator;
|
||||
use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent;
|
||||
use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface;
|
||||
use Psalm\Type\Atomic\TClosure;
|
||||
use Psalm\Type\Atomic\TKeyedArray;
|
||||
use Psalm\Type\Atomic\TMixed;
|
||||
use Psalm\Type\Union;
|
||||
|
||||
use function count;
|
||||
use function reset;
|
||||
|
||||
final class ArgumentsMapperPsalmPlugin implements MethodReturnTypeProviderInterface
|
||||
{
|
||||
public static function getClassLikeNames(): array
|
||||
{
|
||||
return [ArgumentsMapper::class];
|
||||
}
|
||||
|
||||
public static function getMethodReturnType(MethodReturnTypeProviderEvent $event): ?Union
|
||||
{
|
||||
if ($event->getMethodNameLowercase() !== 'maparguments') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$arguments = $event->getCallArgs();
|
||||
|
||||
if (count($arguments) === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$types = $event->getSource()->getNodeTypeProvider()->getType($arguments[0]->value);
|
||||
|
||||
if ($types === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$types = $types->getAtomicTypes();
|
||||
|
||||
if (count($types) !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = reset($types);
|
||||
|
||||
if ($type instanceof TKeyedArray) {
|
||||
// Internal class usage, see https://github.com/vimeo/psalm/issues/8726
|
||||
$type = CallableTypeComparator::getCallableFromAtomic($event->getSource()->getCodebase(), $type);
|
||||
}
|
||||
|
||||
if (! $type instanceof TClosure) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$typeParams = $type->params ?? [];
|
||||
|
||||
if ($typeParams === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$params = [];
|
||||
|
||||
foreach ($typeParams as $param) {
|
||||
$params[$param->name] = $param->type ?? new Union([new TMixed()]);
|
||||
}
|
||||
|
||||
return new Union([new TKeyedArray($params)]);
|
||||
}
|
||||
}
|
||||
66
vendor/cuyz/valinor/qa/Psalm/Plugin/TreeMapperPsalmPlugin.php
vendored
Normal file
66
vendor/cuyz/valinor/qa/Psalm/Plugin/TreeMapperPsalmPlugin.php
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\QA\Psalm\Plugin;
|
||||
|
||||
use CuyZ\Valinor\Mapper\TreeMapper;
|
||||
use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent;
|
||||
use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic;
|
||||
use Psalm\Type\Atomic\TClassString;
|
||||
use Psalm\Type\Atomic\TDependentGetClass;
|
||||
use Psalm\Type\Atomic\TLiteralString;
|
||||
use Psalm\Type\Union;
|
||||
|
||||
final class TreeMapperPsalmPlugin implements MethodReturnTypeProviderInterface
|
||||
{
|
||||
public static function getClassLikeNames(): array
|
||||
{
|
||||
return [TreeMapper::class];
|
||||
}
|
||||
|
||||
public static function getMethodReturnType(MethodReturnTypeProviderEvent $event): ?Union
|
||||
{
|
||||
if ($event->getMethodNameLowercase() !== 'map') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$arguments = $event->getCallArgs();
|
||||
|
||||
if (count($arguments) === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = $event->getSource()->getNodeTypeProvider()->getType($arguments[0]->value);
|
||||
|
||||
if (! $type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$types = [];
|
||||
|
||||
foreach ($type->getAtomicTypes() as $node) {
|
||||
$inferred = self::type($node);
|
||||
|
||||
if ($inferred === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$types[] = $inferred;
|
||||
}
|
||||
|
||||
return Type::combineUnionTypeArray($types, $event->getSource()->getCodebase());
|
||||
}
|
||||
|
||||
private static function type(Atomic $node): ?Union
|
||||
{
|
||||
return match (true) {
|
||||
$node instanceof TLiteralString => Type::parseString($node->value),
|
||||
$node instanceof TDependentGetClass => $node->as_type,
|
||||
$node instanceof TClassString && $node->as_type => new Union([$node->as_type]),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
21
vendor/cuyz/valinor/qa/Psalm/ValinorPsalmPlugin.php
vendored
Normal file
21
vendor/cuyz/valinor/qa/Psalm/ValinorPsalmPlugin.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace CuyZ\Valinor\QA\Psalm;
|
||||
|
||||
use CuyZ\Valinor\QA\Psalm\Plugin\ArgumentsMapperPsalmPlugin;
|
||||
use CuyZ\Valinor\QA\Psalm\Plugin\TreeMapperPsalmPlugin;
|
||||
use Psalm\Plugin\PluginEntryPointInterface;
|
||||
use Psalm\Plugin\RegistrationInterface;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class ValinorPsalmPlugin implements PluginEntryPointInterface
|
||||
{
|
||||
public function __invoke(RegistrationInterface $api, ?SimpleXMLElement $config = null): void
|
||||
{
|
||||
require_once __DIR__ . '/Plugin/TreeMapperPsalmPlugin.php';
|
||||
require_once __DIR__ . '/Plugin/ArgumentsMapperPsalmPlugin.php';
|
||||
|
||||
$api->registerHooksFromClass(TreeMapperPsalmPlugin::class);
|
||||
$api->registerHooksFromClass(ArgumentsMapperPsalmPlugin::class);
|
||||
}
|
||||
}
|
||||
11
vendor/cuyz/valinor/qa/Psalm/valinor-psalm-suppress-pure-errors.xml
vendored
Normal file
11
vendor/cuyz/valinor/qa/Psalm/valinor-psalm-suppress-pure-errors.xml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issueHandlers xmlns="https://getpsalm.org/schema/config">
|
||||
<InvalidArgument>
|
||||
<errorLevel type="suppress">
|
||||
<referencedFunction name="CuyZ\Valinor\NormalizerBuilder::registerTransformer"/>
|
||||
<referencedFunction name="\CuyZ\Valinor\MapperBuilder::infer"/>
|
||||
<referencedFunction name="\CuyZ\Valinor\MapperBuilder::registerConstructor"/>
|
||||
<referencedFunction name="\CuyZ\Valinor\MapperBuilder::registerConverter"/>
|
||||
</errorLevel>
|
||||
</InvalidArgument>
|
||||
</issueHandlers>
|
||||
Reference in New Issue
Block a user