mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Prevent pass by ref when type type is given
This commit is contained in:
parent
d2968081f8
commit
079ac44263
@ -104,6 +104,9 @@ class ReturnTypeAnalyzer
|
||||
/** @var PhpParser\Node\Stmt[] */
|
||||
$function_stmts = $function->getStmts();
|
||||
|
||||
$ignore_nullable_issues = false;
|
||||
$ignore_falsable_issues = false;
|
||||
|
||||
$inferred_return_type_parts = ReturnTypeCollector::getReturnTypes(
|
||||
$function_stmts,
|
||||
$inferred_yield_types,
|
||||
|
@ -24,9 +24,9 @@ class ReturnTypeCollector
|
||||
public static function getReturnTypes(
|
||||
array $stmts,
|
||||
array &$yield_types,
|
||||
&$ignore_nullable_issues = false,
|
||||
&$ignore_falsable_issues = false,
|
||||
$collapse_types = false
|
||||
bool &$ignore_nullable_issues = false,
|
||||
bool &$ignore_falsable_issues = false,
|
||||
bool $collapse_types = false
|
||||
) {
|
||||
$return_types = [];
|
||||
|
||||
|
@ -630,6 +630,9 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer implements Statements
|
||||
|
||||
$closure_yield_types = [];
|
||||
|
||||
$ignore_nullable_issues = false;
|
||||
$ignore_falsable_issues = false;
|
||||
|
||||
$closure_return_types = ReturnTypeCollector::getReturnTypes(
|
||||
$this->function->stmts,
|
||||
$closure_yield_types,
|
||||
|
@ -1294,7 +1294,8 @@ class CallAnalyzer
|
||||
$arg->value,
|
||||
$by_ref_type,
|
||||
$context,
|
||||
$method_id && (strpos($method_id, '::') !== false || !CallMap::inCallMap($method_id))
|
||||
$method_id && (strpos($method_id, '::') !== false || !CallMap::inCallMap($method_id)),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -632,7 +632,8 @@ class ExpressionAnalyzer
|
||||
PhpParser\Node\Expr $stmt,
|
||||
Type\Union $by_ref_type,
|
||||
Context $context,
|
||||
$constrain_type = true
|
||||
bool $constrain_type = true,
|
||||
bool $prevent_null = false
|
||||
) {
|
||||
$var_id = self::getVarId(
|
||||
$stmt,
|
||||
@ -652,6 +653,24 @@ class ExpressionAnalyzer
|
||||
$location = new CodeLocation($statements_analyzer, $stmt);
|
||||
$statements_analyzer->registerVariable($var_id, $location, null);
|
||||
|
||||
if ($constrain_type
|
||||
&& $prevent_null
|
||||
&& !$by_ref_type->isMixed()
|
||||
&& !$by_ref_type->isNullable()
|
||||
&& !strpos($var_id, '->')
|
||||
&& !strpos($var_id, '::')
|
||||
) {
|
||||
if (IssueBuffer::accepts(
|
||||
new \Psalm\Issue\NullArgument(
|
||||
'Not expecting null argument passed by reference',
|
||||
new CodeLocation($statements_analyzer->getSource(), $stmt)
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues()
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
|
||||
if ($context->collect_references) {
|
||||
$context->unreferenced_vars[$var_id] = [$location->getHash() => $location];
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ class FunctionCallTest extends TestCase
|
||||
],
|
||||
'byRefNewString' => [
|
||||
'<?php
|
||||
function fooFoo(string &$v): void {}
|
||||
function fooFoo(?string &$v): void {}
|
||||
fooFoo($a);',
|
||||
],
|
||||
'byRefVariableFunctionExistingArray' => [
|
||||
@ -1452,7 +1452,15 @@ class FunctionCallTest extends TestCase
|
||||
],
|
||||
[],
|
||||
'7.3'
|
||||
]
|
||||
],
|
||||
'nullableByRef' => [
|
||||
'<?php
|
||||
function foo(?string &$s) : void {}
|
||||
|
||||
function bar() : void {
|
||||
foo($bar);
|
||||
}'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -1936,6 +1944,15 @@ class FunctionCallTest extends TestCase
|
||||
accepts(arr());',
|
||||
'error_message' => 'InvalidArgument',
|
||||
],
|
||||
'nonNullableByRef' => [
|
||||
'<?php
|
||||
function foo(string &$s) : void {}
|
||||
|
||||
function bar() : void {
|
||||
foo($bar);
|
||||
}',
|
||||
'error_message' => 'NullArgument',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user