mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Merge branch '4.x' into upstream-master
This commit is contained in:
commit
11e60fa261
@ -14855,7 +14855,7 @@ return [
|
|||||||
'trader_wclprice' => ['array', 'high'=>'array', 'low'=>'array', 'close'=>'array'],
|
'trader_wclprice' => ['array', 'high'=>'array', 'low'=>'array', 'close'=>'array'],
|
||||||
'trader_willr' => ['array', 'high'=>'array', 'low'=>'array', 'close'=>'array', 'timePeriod='=>'int'],
|
'trader_willr' => ['array', 'high'=>'array', 'low'=>'array', 'close'=>'array', 'timePeriod='=>'int'],
|
||||||
'trader_wma' => ['array', 'real'=>'array', 'timePeriod='=>'int'],
|
'trader_wma' => ['array', 'real'=>'array', 'timePeriod='=>'int'],
|
||||||
'trait_exists' => ['?bool', 'trait'=>'string', 'autoload='=>'bool'],
|
'trait_exists' => ['bool', 'trait'=>'string', 'autoload='=>'bool'],
|
||||||
'Transliterator::create' => ['?Transliterator', 'id'=>'string', 'direction='=>'int'],
|
'Transliterator::create' => ['?Transliterator', 'id'=>'string', 'direction='=>'int'],
|
||||||
'Transliterator::createFromRules' => ['?Transliterator', 'rules'=>'string', 'direction='=>'int'],
|
'Transliterator::createFromRules' => ['?Transliterator', 'rules'=>'string', 'direction='=>'int'],
|
||||||
'Transliterator::createInverse' => ['Transliterator'],
|
'Transliterator::createInverse' => ['Transliterator'],
|
||||||
|
@ -15941,7 +15941,7 @@ return [
|
|||||||
'trader_wclprice' => ['array', 'high'=>'array', 'low'=>'array', 'close'=>'array'],
|
'trader_wclprice' => ['array', 'high'=>'array', 'low'=>'array', 'close'=>'array'],
|
||||||
'trader_willr' => ['array', 'high'=>'array', 'low'=>'array', 'close'=>'array', 'timePeriod='=>'int'],
|
'trader_willr' => ['array', 'high'=>'array', 'low'=>'array', 'close'=>'array', 'timePeriod='=>'int'],
|
||||||
'trader_wma' => ['array', 'real'=>'array', 'timePeriod='=>'int'],
|
'trader_wma' => ['array', 'real'=>'array', 'timePeriod='=>'int'],
|
||||||
'trait_exists' => ['?bool', 'trait'=>'string', 'autoload='=>'bool'],
|
'trait_exists' => ['bool', 'trait'=>'string', 'autoload='=>'bool'],
|
||||||
'transliterator_create' => ['?Transliterator', 'id'=>'string', 'direction='=>'int'],
|
'transliterator_create' => ['?Transliterator', 'id'=>'string', 'direction='=>'int'],
|
||||||
'transliterator_create_from_rules' => ['?Transliterator', 'rules'=>'string', 'direction='=>'int'],
|
'transliterator_create_from_rules' => ['?Transliterator', 'rules'=>'string', 'direction='=>'int'],
|
||||||
'transliterator_create_inverse' => ['Transliterator', 'transliterator'=>'Transliterator'],
|
'transliterator_create_inverse' => ['Transliterator', 'transliterator'=>'Transliterator'],
|
||||||
|
@ -8,7 +8,6 @@ This issue is emitted when a method overriding a native method is defined withou
|
|||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class A implements JsonSerializable {
|
class A implements JsonSerializable {
|
||||||
public function jsonSerialize() {
|
public function jsonSerialize() {
|
||||||
return ['type' => 'A'];
|
return ['type' => 'A'];
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
<code>$matches[0]</code>
|
<code>$matches[0]</code>
|
||||||
<code>$symbol_parts[1]</code>
|
<code>$symbol_parts[1]</code>
|
||||||
</PossiblyUndefinedIntArrayOffset>
|
</PossiblyUndefinedIntArrayOffset>
|
||||||
|
<PossiblyUnusedProperty occurrences="1">
|
||||||
|
<code>$analysis_php_version_id</code>
|
||||||
|
</PossiblyUnusedProperty>
|
||||||
</file>
|
</file>
|
||||||
<file src="src/Psalm/Config/FileFilter.php">
|
<file src="src/Psalm/Config/FileFilter.php">
|
||||||
<PossiblyUndefinedIntArrayOffset occurrences="1">
|
<PossiblyUndefinedIntArrayOffset occurrences="1">
|
||||||
@ -317,6 +320,11 @@
|
|||||||
<code>array_keys($template_type_map[$template_param_name])[0]</code>
|
<code>array_keys($template_type_map[$template_param_name])[0]</code>
|
||||||
</PossiblyUndefinedIntArrayOffset>
|
</PossiblyUndefinedIntArrayOffset>
|
||||||
</file>
|
</file>
|
||||||
|
<file src="src/Psalm/Issue/MethodSignatureMustProvideReturnType.php">
|
||||||
|
<UnusedClass occurrences="1">
|
||||||
|
<code>MethodSignatureMustProvideReturnType</code>
|
||||||
|
</UnusedClass>
|
||||||
|
</file>
|
||||||
<file src="src/Psalm/Node/Stmt/VirtualClass.php">
|
<file src="src/Psalm/Node/Stmt/VirtualClass.php">
|
||||||
<PropertyNotSetInConstructor occurrences="1">
|
<PropertyNotSetInConstructor occurrences="1">
|
||||||
<code>VirtualClass</code>
|
<code>VirtualClass</code>
|
||||||
|
@ -177,8 +177,7 @@ class FileFilter
|
|||||||
}
|
}
|
||||||
|
|
||||||
throw new ConfigException(
|
throw new ConfigException(
|
||||||
'Could not resolve config path to ' . $base_dir
|
'Could not resolve config path to ' . $prospective_directory_path
|
||||||
. DIRECTORY_SEPARATOR . $directory_path
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -886,14 +886,7 @@ class MethodComparator
|
|||||||
$implementer_signature_return_type,
|
$implementer_signature_return_type,
|
||||||
$guide_signature_return_type
|
$guide_signature_return_type
|
||||||
)
|
)
|
||||||
: (!$implementer_signature_return_type
|
: UnionTypeComparator::isContainedByInPhp($implementer_signature_return_type, $guide_signature_return_type);
|
||||||
&& $guide_signature_return_type->isMixed()
|
|
||||||
? false
|
|
||||||
: UnionTypeComparator::isContainedByInPhp(
|
|
||||||
$implementer_signature_return_type,
|
|
||||||
$guide_signature_return_type
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!$is_contained_by) {
|
if (!$is_contained_by) {
|
||||||
if ($codebase->analysis_php_version_id >= 8_00_00
|
if ($codebase->analysis_php_version_id >= 8_00_00
|
||||||
|
@ -38,6 +38,7 @@ use Psalm\Node\VirtualIdentifier;
|
|||||||
use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
|
use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
|
||||||
use Psalm\Plugin\EventHandler\Event\AfterEveryFunctionCallAnalysisEvent;
|
use Psalm\Plugin\EventHandler\Event\AfterEveryFunctionCallAnalysisEvent;
|
||||||
use Psalm\Storage\FunctionLikeParameter;
|
use Psalm\Storage\FunctionLikeParameter;
|
||||||
|
use Psalm\Storage\FunctionStorage;
|
||||||
use Psalm\Storage\Possibilities;
|
use Psalm\Storage\Possibilities;
|
||||||
use Psalm\Type;
|
use Psalm\Type;
|
||||||
use Psalm\Type\Atomic;
|
use Psalm\Type\Atomic;
|
||||||
@ -487,6 +488,8 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
|||||||
$is_maybe_root_function = !$function_name instanceof PhpParser\Node\Name\FullyQualified
|
$is_maybe_root_function = !$function_name instanceof PhpParser\Node\Name\FullyQualified
|
||||||
&& count($function_name->parts) === 1;
|
&& count($function_name->parts) === 1;
|
||||||
|
|
||||||
|
$args = $stmt->isFirstClassCallable() ? [] : $stmt->getArgs();
|
||||||
|
|
||||||
if (!$function_call_info->in_call_map) {
|
if (!$function_call_info->in_call_map) {
|
||||||
$predefined_functions = $codebase->config->getPredefinedFunctions();
|
$predefined_functions = $codebase->config->getPredefinedFunctions();
|
||||||
$is_predefined = isset($predefined_functions[strtolower($original_function_id)])
|
$is_predefined = isset($predefined_functions[strtolower($original_function_id)])
|
||||||
@ -498,11 +501,10 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
|||||||
$function_call_info->function_id,
|
$function_call_info->function_id,
|
||||||
$code_location,
|
$code_location,
|
||||||
$is_maybe_root_function
|
$is_maybe_root_function
|
||||||
) === false
|
) === false) {
|
||||||
) {
|
if ($args && ArgumentsAnalyzer::analyze(
|
||||||
if (ArgumentsAnalyzer::analyze(
|
|
||||||
$statements_analyzer,
|
$statements_analyzer,
|
||||||
$stmt->getArgs(),
|
$args,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
true,
|
true,
|
||||||
@ -662,7 +664,8 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($var_type_part instanceof TClosure || $var_type_part instanceof TCallable) {
|
if ($var_type_part instanceof TClosure || $var_type_part instanceof TCallable) {
|
||||||
if (!$var_type_part->is_pure && ($context->pure || $context->mutation_free)) {
|
if (!$var_type_part->is_pure) {
|
||||||
|
if ($context->pure || $context->mutation_free) {
|
||||||
IssueBuffer::maybeAdd(
|
IssueBuffer::maybeAdd(
|
||||||
new ImpureFunctionCall(
|
new ImpureFunctionCall(
|
||||||
'Cannot call an impure function from a mutation-free context',
|
'Cannot call an impure function from a mutation-free context',
|
||||||
@ -672,6 +675,14 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$function_call_info->function_storage) {
|
||||||
|
$function_call_info->function_storage = new FunctionStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
$function_call_info->function_storage->pure = false;
|
||||||
|
$function_call_info->function_storage->mutation_free = false;
|
||||||
|
}
|
||||||
|
|
||||||
$function_call_info->function_params = $var_type_part->params;
|
$function_call_info->function_params = $var_type_part->params;
|
||||||
|
|
||||||
if (($stmt_type = $statements_analyzer->node_data->getType($real_stmt))
|
if (($stmt_type = $statements_analyzer->node_data->getType($real_stmt))
|
||||||
|
@ -489,14 +489,15 @@ class AtomicStaticCallAnalyzer
|
|||||||
$class_storage->final
|
$class_storage->final
|
||||||
);
|
);
|
||||||
|
|
||||||
$context->vars_in_scope['$tmp_mixin_var'] = $new_lhs_type;
|
$mixin_context = clone $context;
|
||||||
|
$mixin_context->vars_in_scope['$__tmp_mixin_var__'] = $new_lhs_type;
|
||||||
|
|
||||||
return self::forwardCallToInstanceMethod(
|
return self::forwardCallToInstanceMethod(
|
||||||
$statements_analyzer,
|
$statements_analyzer,
|
||||||
$stmt,
|
$stmt,
|
||||||
$stmt_name,
|
$stmt_name,
|
||||||
$context,
|
$mixin_context,
|
||||||
'tmp_mixin_var',
|
'__tmp_mixin_var__',
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -700,18 +701,21 @@ class AtomicStaticCallAnalyzer
|
|||||||
// with nonexistent method, we try to forward to instance method call for resolve pseudo method.
|
// with nonexistent method, we try to forward to instance method call for resolve pseudo method.
|
||||||
|
|
||||||
// Use parent type as static type for the method call
|
// Use parent type as static type for the method call
|
||||||
$context->vars_in_scope['$tmp_parent_var'] = new Union([$lhs_type_part]);
|
$tmp_context = clone $context;
|
||||||
|
$tmp_context->vars_in_scope['$__tmp_parent_var__'] = new Union([$lhs_type_part]);
|
||||||
|
|
||||||
if (self::forwardCallToInstanceMethod(
|
if (self::forwardCallToInstanceMethod(
|
||||||
$statements_analyzer,
|
$statements_analyzer,
|
||||||
$stmt,
|
$stmt,
|
||||||
$stmt_name,
|
$stmt_name,
|
||||||
$context,
|
$tmp_context,
|
||||||
'tmp_parent_var'
|
'__tmp_parent_var__'
|
||||||
) === false) {
|
) === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unset($tmp_context);
|
||||||
|
|
||||||
// Resolve actual static return type according to caller (i.e. $this) static type
|
// Resolve actual static return type according to caller (i.e. $this) static type
|
||||||
if (isset($context->vars_in_scope['$this'])
|
if (isset($context->vars_in_scope['$this'])
|
||||||
&& $method_call_type = $statements_analyzer->node_data->getType($stmt)
|
&& $method_call_type = $statements_analyzer->node_data->getType($stmt)
|
||||||
|
@ -226,6 +226,7 @@ class CastAnalyzer
|
|||||||
if ($type instanceof Scalar) {
|
if ($type instanceof Scalar) {
|
||||||
$keyed_array = new TKeyedArray([new Union([$type])]);
|
$keyed_array = new TKeyedArray([new Union([$type])]);
|
||||||
$keyed_array->is_list = true;
|
$keyed_array->is_list = true;
|
||||||
|
$keyed_array->sealed = true;
|
||||||
$permissible_atomic_types[] = $keyed_array;
|
$permissible_atomic_types[] = $keyed_array;
|
||||||
} elseif ($type instanceof TNull) {
|
} elseif ($type instanceof TNull) {
|
||||||
$permissible_atomic_types[] = new TArray([Type::getNever(), Type::getNever()]);
|
$permissible_atomic_types[] = new TArray([Type::getNever(), Type::getNever()]);
|
||||||
|
@ -16,6 +16,8 @@ use Psalm\Type\Atomic\TCallableString;
|
|||||||
use Psalm\Type\Atomic\TNonEmptyString;
|
use Psalm\Type\Atomic\TNonEmptyString;
|
||||||
use Psalm\Type\Union;
|
use Psalm\Type\Union;
|
||||||
|
|
||||||
|
use function dirname;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
@ -87,10 +89,16 @@ class MagicConstAnalyzer
|
|||||||
} else {
|
} else {
|
||||||
$statements_analyzer->node_data->setType($stmt, new Union([new TCallableString]));
|
$statements_analyzer->node_data->setType($stmt, new Union([new TCallableString]));
|
||||||
}
|
}
|
||||||
} elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\File
|
} elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Dir) {
|
||||||
|| $stmt instanceof PhpParser\Node\Scalar\MagicConst\Dir
|
$statements_analyzer->node_data->setType(
|
||||||
) {
|
$stmt,
|
||||||
$statements_analyzer->node_data->setType($stmt, new Union([new TNonEmptyString()]));
|
Type::getString(dirname($statements_analyzer->getSource()->getFilePath()))
|
||||||
|
);
|
||||||
|
} elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\File) {
|
||||||
|
$statements_analyzer->node_data->setType(
|
||||||
|
$stmt,
|
||||||
|
Type::getString($statements_analyzer->getSource()->getFilePath())
|
||||||
|
);
|
||||||
} elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Trait_) {
|
} elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Trait_) {
|
||||||
if ($statements_analyzer->getSource() instanceof TraitAnalyzer) {
|
if ($statements_analyzer->getSource() instanceof TraitAnalyzer) {
|
||||||
$statements_analyzer->node_data->setType($stmt, new Union([new TNonEmptyString()]));
|
$statements_analyzer->node_data->setType($stmt, new Union([new TNonEmptyString()]));
|
||||||
|
@ -433,6 +433,18 @@ class SimpleTypeInferer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($stmt instanceof PhpParser\Node\Expr\New_) {
|
||||||
|
$resolved_class_name = $stmt->class->getAttribute('resolvedName');
|
||||||
|
|
||||||
|
if (!is_string($resolved_class_name)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Union([
|
||||||
|
new Type\Atomic\TNamedObject($resolved_class_name)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,7 +694,7 @@ final class IssueBuffer
|
|||||||
: $error_count . ' errors'
|
: $error_count . ' errors'
|
||||||
) . ' found' . "\n";
|
) . ' found' . "\n";
|
||||||
} else {
|
} else {
|
||||||
self::printSuccessMessage();
|
self::printSuccessMessage($project_analyzer);
|
||||||
}
|
}
|
||||||
|
|
||||||
$show_info = $project_analyzer->stdout_report_options->show_info;
|
$show_info = $project_analyzer->stdout_report_options->show_info;
|
||||||
@ -785,8 +785,12 @@ final class IssueBuffer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function printSuccessMessage(): void
|
public static function printSuccessMessage(ProjectAnalyzer $project_analyzer): void
|
||||||
{
|
{
|
||||||
|
if (!$project_analyzer->stdout_report_options) {
|
||||||
|
throw new UnexpectedValueException('Cannot print success message without stdout report options');
|
||||||
|
}
|
||||||
|
|
||||||
// this message will be printed
|
// this message will be printed
|
||||||
$message = "No errors found!";
|
$message = "No errors found!";
|
||||||
|
|
||||||
@ -811,9 +815,15 @@ final class IssueBuffer
|
|||||||
// text style, 1 = bold
|
// text style, 1 = bold
|
||||||
$style = "1";
|
$style = "1";
|
||||||
|
|
||||||
|
if ($project_analyzer->stdout_report_options->use_color) {
|
||||||
echo "\e[{$background};{$style}m{$paddingTop}\e[0m" . "\n";
|
echo "\e[{$background};{$style}m{$paddingTop}\e[0m" . "\n";
|
||||||
echo "\e[{$background};{$foreground};{$style}m{$messageWithPadding}\e[0m" . "\n";
|
echo "\e[{$background};{$foreground};{$style}m{$messageWithPadding}\e[0m" . "\n";
|
||||||
echo "\e[{$background};{$style}m{$paddingBottom}\e[0m" . "\n";
|
echo "\e[{$background};{$style}m{$paddingBottom}\e[0m" . "\n";
|
||||||
|
} else {
|
||||||
|
echo "\n";
|
||||||
|
echo "$messageWithPadding\n";
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,6 +56,13 @@ namespace {
|
|||||||
*/
|
*/
|
||||||
public function getBackingValue(): int|string;
|
public function getBackingValue(): int|string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ReflectionIntersectionType extends ReflectionType {
|
||||||
|
/**
|
||||||
|
* @return non-empty-list<ReflectionType>
|
||||||
|
*/
|
||||||
|
public function getTypes() {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace FTP {
|
namespace FTP {
|
||||||
|
@ -798,6 +798,11 @@ class ClosureTest extends TestCase
|
|||||||
'ignored_issues' => [],
|
'ignored_issues' => [],
|
||||||
'php_version' => '8.1'
|
'php_version' => '8.1'
|
||||||
],
|
],
|
||||||
|
'unknownFirstClassCallable' => [
|
||||||
|
'code' => '<?php
|
||||||
|
/** @psalm-suppress UndefinedFunction */
|
||||||
|
unknown(...);',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,14 @@
|
|||||||
|
|
||||||
namespace Psalm\Tests;
|
namespace Psalm\Tests;
|
||||||
|
|
||||||
|
use Psalm\Context;
|
||||||
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
|
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
|
||||||
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
|
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
|
||||||
|
|
||||||
|
use function getcwd;
|
||||||
|
|
||||||
|
use const DIRECTORY_SEPARATOR;
|
||||||
|
|
||||||
class ConstantTest extends TestCase
|
class ConstantTest extends TestCase
|
||||||
{
|
{
|
||||||
use InvalidCodeAnalysisTestTrait;
|
use InvalidCodeAnalysisTestTrait;
|
||||||
@ -42,6 +47,42 @@ class ConstantTest extends TestCase
|
|||||||
// $this->analyzeFile($file_path, new Context());
|
// $this->analyzeFile($file_path, new Context());
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
public function testUseObjectConstant(): void
|
||||||
|
{
|
||||||
|
$file1 = getcwd() . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'file1.php';
|
||||||
|
$file2 = getcwd() . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'file2.php';
|
||||||
|
|
||||||
|
$this->addFile(
|
||||||
|
$file1,
|
||||||
|
'<?php
|
||||||
|
namespace Foo;
|
||||||
|
|
||||||
|
final class Bar {}
|
||||||
|
const bar = new Bar();
|
||||||
|
'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->addFile(
|
||||||
|
$file2,
|
||||||
|
'<?php
|
||||||
|
namespace Baz;
|
||||||
|
|
||||||
|
use Foo\Bar;
|
||||||
|
use const Foo\bar;
|
||||||
|
|
||||||
|
require("tests/file1.php");
|
||||||
|
|
||||||
|
function bar(): Bar
|
||||||
|
{
|
||||||
|
return bar;
|
||||||
|
}
|
||||||
|
'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->analyzeFile($file1, new Context());
|
||||||
|
$this->analyzeFile($file2, new Context());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return iterable<string,array{code:string,assertions?:array<string,string>,ignored_issues?:list<string>, php_version?: string}>
|
* @return iterable<string,array{code:string,assertions?:array<string,string>,ignored_issues?:list<string>, php_version?: string}>
|
||||||
*/
|
*/
|
||||||
|
@ -218,10 +218,6 @@ class DocumentationTest extends TestCase
|
|||||||
$this->markTestSkipped();
|
$this->markTestSkipped();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strpos($error_message, 'MethodSignatureMustProvideReturnType') !== false) {
|
|
||||||
$php_version = '8.1';
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->project_analyzer->setPhpVersion($php_version, 'tests');
|
$this->project_analyzer->setPhpVersion($php_version, 'tests');
|
||||||
|
|
||||||
if ($check_references) {
|
if ($check_references) {
|
||||||
@ -290,6 +286,10 @@ class DocumentationTest extends TestCase
|
|||||||
case 'TraitMethodSignatureMismatch':
|
case 'TraitMethodSignatureMismatch':
|
||||||
continue 2;
|
continue 2;
|
||||||
|
|
||||||
|
/** @todo reinstate this test when the issue is restored */
|
||||||
|
case 'MethodSignatureMustProvideReturnType':
|
||||||
|
continue 2;
|
||||||
|
|
||||||
case 'InvalidFalsableReturnType':
|
case 'InvalidFalsableReturnType':
|
||||||
$ignored_issues = ['FalsableReturnStatement'];
|
$ignored_issues = ['FalsableReturnStatement'];
|
||||||
break;
|
break;
|
||||||
|
@ -211,6 +211,19 @@ class PureAnnotationAdditionTest extends FileManipulationTestCase
|
|||||||
'issues_to_fix' => ['MissingPureAnnotation'],
|
'issues_to_fix' => ['MissingPureAnnotation'],
|
||||||
'safe_types' => true,
|
'safe_types' => true,
|
||||||
],
|
],
|
||||||
|
'dontAddPureIfCallableNotPure' => [
|
||||||
|
'input' => '<?php
|
||||||
|
function pure(callable $callable): string{
|
||||||
|
return $callable();
|
||||||
|
}',
|
||||||
|
'output' => '<?php
|
||||||
|
function pure(callable $callable): string{
|
||||||
|
return $callable();
|
||||||
|
}',
|
||||||
|
'php_version' => '7.4',
|
||||||
|
'issues_to_fix' => ['MissingPureAnnotation'],
|
||||||
|
'safe_types' => true,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,8 +113,10 @@ class IssueBufferTest extends TestCase
|
|||||||
|
|
||||||
public function testPrintSuccessMessageWorks(): void
|
public function testPrintSuccessMessageWorks(): void
|
||||||
{
|
{
|
||||||
|
$project_analyzer = $this->createMock(ProjectAnalyzer::class);
|
||||||
|
$project_analyzer->stdout_report_options = new ReportOptions;
|
||||||
ob_start();
|
ob_start();
|
||||||
IssueBuffer::printSuccessMessage();
|
IssueBuffer::printSuccessMessage($project_analyzer);
|
||||||
$output = ob_get_clean();
|
$output = ob_get_clean();
|
||||||
|
|
||||||
$this->assertStringContainsString('No errors found!', $output);
|
$this->assertStringContainsString('No errors found!', $output);
|
||||||
|
@ -934,7 +934,7 @@ class MagicMethodAnnotationTest extends TestCase
|
|||||||
/** @var D<B> $d */
|
/** @var D<B> $d */
|
||||||
$d = new D();
|
$d = new D();
|
||||||
$e = $d->get();',
|
$e = $d->get();',
|
||||||
[
|
'assertions' => [
|
||||||
'$b' => 'B',
|
'$b' => 'B',
|
||||||
'$c' => 'B',
|
'$c' => 'B',
|
||||||
'$e' => 'B',
|
'$e' => 'B',
|
||||||
@ -1120,6 +1120,29 @@ class MagicMethodAnnotationTest extends TestCase
|
|||||||
class C {}',
|
class C {}',
|
||||||
'error_message' => 'InvalidDocblock',
|
'error_message' => 'InvalidDocblock',
|
||||||
],
|
],
|
||||||
|
'magicParentCallShouldNotPolluteContext' => [
|
||||||
|
'code' => '<?php
|
||||||
|
/**
|
||||||
|
* @method baz(): Foo
|
||||||
|
*/
|
||||||
|
class Foo
|
||||||
|
{
|
||||||
|
public function __call()
|
||||||
|
{
|
||||||
|
return new self();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Bar extends Foo
|
||||||
|
{
|
||||||
|
public function baz(): Foo
|
||||||
|
{
|
||||||
|
parent::baz();
|
||||||
|
return $__tmp_parent_var__;
|
||||||
|
}
|
||||||
|
}',
|
||||||
|
'error_message' => 'UndefinedVariable',
|
||||||
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -690,6 +690,30 @@ class MixinAnnotationTest extends TestCase
|
|||||||
}',
|
}',
|
||||||
'error_message' => 'LessSpecificReturnStatement'
|
'error_message' => 'LessSpecificReturnStatement'
|
||||||
],
|
],
|
||||||
|
'mixinStaticCallShouldNotPolluteContext' => [
|
||||||
|
'code' => '<?php
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
*/
|
||||||
|
class Foo
|
||||||
|
{
|
||||||
|
public function foobar(): void {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @mixin Foo<T>
|
||||||
|
*/
|
||||||
|
class Bar
|
||||||
|
{
|
||||||
|
public function baz(): self
|
||||||
|
{
|
||||||
|
self::foobar();
|
||||||
|
return $__tmp_mixin_var__;
|
||||||
|
}
|
||||||
|
}',
|
||||||
|
'error_message' => 'UndefinedVariable'
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user