mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Allow opt-in to strict return type checking
This commit is contained in:
parent
3483c59d9b
commit
1389dc6adf
@ -76,6 +76,7 @@
|
||||
<xs:attribute name="allowInternalNamedArgumentCalls" type="xs:boolean" default="true" />
|
||||
<xs:attribute name="allowNamedArgumentCalls" type="xs:boolean" default="true" />
|
||||
<xs:attribute name="reportInfo" type="xs:boolean" default="true" />
|
||||
<xs:attribute name="restrictReturnTypes" type="xs:boolean" default="false" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="ProjectFilesType">
|
||||
|
@ -433,6 +433,11 @@ class Config
|
||||
*/
|
||||
public $resolve_from_config_file = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $restrict_return_types = false;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
@ -835,6 +840,7 @@ class Config
|
||||
'allowNamedArgumentCalls' => 'allow_named_arg_calls',
|
||||
'findUnusedPsalmSuppress' => 'find_unused_psalm_suppress',
|
||||
'reportInfo' => 'report_info',
|
||||
'restrictReturnTypes' => 'restrict_return_types',
|
||||
];
|
||||
|
||||
foreach ($booleanAttributes as $xmlName => $internalName) {
|
||||
|
@ -525,63 +525,68 @@ class ReturnTypeAnalyzer
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} elseif ($codebase->alter_code
|
||||
&& isset($project_analyzer->getIssuesToFix()['LessSpecificReturnType'])
|
||||
&& !in_array('LessSpecificReturnType', $suppressed_issues)
|
||||
&& !($function_like_storage instanceof MethodStorage && $function_like_storage->inheritdoc)
|
||||
) {
|
||||
if (!UnionTypeComparator::isContainedBy(
|
||||
} elseif (!$inferred_return_type->hasMixed()
|
||||
&& !UnionTypeComparator::isContainedBy(
|
||||
$codebase,
|
||||
$declared_return_type,
|
||||
$inferred_return_type,
|
||||
false,
|
||||
false
|
||||
)) {
|
||||
self::addOrUpdateReturnType(
|
||||
$function,
|
||||
$project_analyzer,
|
||||
$inferred_return_type,
|
||||
$source,
|
||||
$compatible_method_ids
|
||||
|| (($project_analyzer->only_replace_php_types_with_non_docblock_types
|
||||
|| $unsafe_return_type)
|
||||
&& $inferred_return_type->from_docblock),
|
||||
$function_like_storage
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
} elseif (!$inferred_return_type->hasMixed()
|
||||
&& ((!$inferred_return_type->isNullable() && $declared_return_type->isNullable())
|
||||
|| (!$inferred_return_type->isFalsable() && $declared_return_type->isFalsable()))
|
||||
)
|
||||
) {
|
||||
if ($function instanceof Function_
|
||||
|| $function instanceof Closure
|
||||
|| $function instanceof ArrowFunction
|
||||
|| $function->isPrivate()
|
||||
) {
|
||||
$check_for_less_specific_type = true;
|
||||
} elseif ($source instanceof StatementsAnalyzer) {
|
||||
if ($function_like_storage instanceof MethodStorage) {
|
||||
$check_for_less_specific_type = !$function_like_storage->overridden_somewhere;
|
||||
if ($codebase->alter_code) {
|
||||
if (isset($project_analyzer->getIssuesToFix()['LessSpecificReturnType'])
|
||||
&& !in_array('LessSpecificReturnType', $suppressed_issues)
|
||||
&& !($function_like_storage instanceof MethodStorage && $function_like_storage->inheritdoc)
|
||||
) {
|
||||
self::addOrUpdateReturnType(
|
||||
$function,
|
||||
$project_analyzer,
|
||||
$inferred_return_type,
|
||||
$source,
|
||||
$compatible_method_ids
|
||||
|| (($project_analyzer->only_replace_php_types_with_non_docblock_types
|
||||
|| $unsafe_return_type)
|
||||
&& $inferred_return_type->from_docblock),
|
||||
$function_like_storage
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if ($function instanceof Function_
|
||||
|| $function instanceof Closure
|
||||
|| $function instanceof ArrowFunction
|
||||
|| $function->isPrivate()
|
||||
) {
|
||||
$check_for_less_specific_type = true;
|
||||
} elseif ($source instanceof StatementsAnalyzer) {
|
||||
if ($function_like_storage instanceof MethodStorage) {
|
||||
$check_for_less_specific_type = !$function_like_storage->overridden_somewhere;
|
||||
} else {
|
||||
$check_for_less_specific_type = false;
|
||||
}
|
||||
} else {
|
||||
$check_for_less_specific_type = false;
|
||||
}
|
||||
} else {
|
||||
$check_for_less_specific_type = false;
|
||||
}
|
||||
|
||||
if ($check_for_less_specific_type) {
|
||||
if (IssueBuffer::accepts(
|
||||
new LessSpecificReturnType(
|
||||
'The inferred return type \'' . $inferred_return_type . '\' for ' . $cased_method_id .
|
||||
' is more specific than the declared return type \'' . $declared_return_type . '\'',
|
||||
$return_type_location
|
||||
),
|
||||
$suppressed_issues,
|
||||
!($function_like_storage instanceof MethodStorage && $function_like_storage->inheritdoc)
|
||||
)) {
|
||||
return false;
|
||||
if ($check_for_less_specific_type
|
||||
&& (\Psalm\Config::getInstance()->restrict_return_types
|
||||
|| (!$inferred_return_type->isNullable() && $declared_return_type->isNullable())
|
||||
|| (!$inferred_return_type->isFalsable() && $declared_return_type->isFalsable()))
|
||||
) {
|
||||
if (IssueBuffer::accepts(
|
||||
new LessSpecificReturnType(
|
||||
'The inferred return type \''
|
||||
. $inferred_return_type->getId()
|
||||
. '\' for ' . $cased_method_id
|
||||
. ' is more specific than the declared return type \''
|
||||
. $declared_return_type->getId() . '\'',
|
||||
$return_type_location
|
||||
),
|
||||
$suppressed_issues,
|
||||
!($function_like_storage instanceof MethodStorage && $function_like_storage->inheritdoc)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -597,7 +597,6 @@ class ConditionalReturnTypeTest extends TestCase
|
||||
* @template T of mixed|false|null
|
||||
* @param T $i
|
||||
* @return (T is false ? no-return : T is null ? no-return : T)
|
||||
* @psalm-suppress LessSpecificReturnType
|
||||
*/
|
||||
function orThrow($i) {
|
||||
if ($i === false || $i === null) {
|
||||
|
Loading…
Reference in New Issue
Block a user