1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

Find issues with impossible property values

This commit is contained in:
Brown 2019-12-11 17:08:02 -05:00
parent 612f74f481
commit 05783eb616
14 changed files with 74 additions and 86 deletions

View File

@ -747,7 +747,7 @@ class ReturnTypeAnalyzer
return false;
}
if ($classlike_storage && $context->self && $function->name) {
if ($classlike_storage && $context->self) {
$class_template_params = MethodCallAnalyzer::getClassTemplateParams(
$codebase,
$classlike_storage,

View File

@ -244,12 +244,6 @@ class ProjectAnalyzer
$progress
);
if ($stdout_report_options
&& !in_array($stdout_report_options->format, Report::SUPPORTED_OUTPUT_TYPES, true)
) {
throw new \UnexpectedValueException('Unrecognised output format ' . $stdout_report_options->format);
}
$this->stdout_report_options = $stdout_report_options;
$this->generated_report_options = $generated_report_options;

View File

@ -624,8 +624,6 @@ class IfAnalyzer
// to $outer_context don't mess with elseif/else blocks
$original_context = clone $outer_context;
$if_conditional_context->inside_conditional = true;
if ($internally_applied_if_cond_expr !== $cond
|| $externally_applied_if_cond_expr !== $cond
) {
@ -635,10 +633,14 @@ class IfAnalyzer
$referenced_var_ids = $outer_context->referenced_var_ids;
$if_conditional_context->referenced_var_ids = [];
$if_conditional_context->inside_conditional = true;
if (ExpressionAnalyzer::analyze($statements_analyzer, $cond, $if_conditional_context) === false) {
throw new \Psalm\Exception\ScopeAnalysisException();
}
$if_conditional_context->inside_conditional = false;
/** @var array<string, bool> */
$more_cond_referenced_var_ids = $if_conditional_context->referenced_var_ids;
$if_conditional_context->referenced_var_ids = array_merge(
@ -688,24 +690,8 @@ class IfAnalyzer
// get all the var ids that were referened in the conditional, but not assigned in it
$cond_referenced_var_ids = array_diff_key($cond_referenced_var_ids, $cond_assigned_var_ids);
// remove all newly-asserted var ids too
$cond_referenced_var_ids = array_filter(
$cond_referenced_var_ids,
/**
* @param string $var_id
*
* @return bool
*/
function ($var_id) use ($pre_condition_vars_in_scope) {
return isset($pre_condition_vars_in_scope[$var_id]);
},
ARRAY_FILTER_USE_KEY
);
$cond_referenced_var_ids = array_merge($newish_var_ids, $cond_referenced_var_ids);
$if_conditional_context->inside_conditional = false;
return new \Psalm\Internal\Scope\IfConditionalScope(
$if_context,
$original_context,

View File

@ -2563,7 +2563,6 @@ class AssertionFinder
$second_arg = $stmt->args[1]->value;
if ($second_arg instanceof PhpParser\Node\Expr\ConstFetch
&& $second_arg->name instanceof PhpParser\Node\Name
&& strtolower($second_arg->name->parts[0]) === 'true'
) {
return 2;
@ -2592,7 +2591,6 @@ class AssertionFinder
$second_arg = $stmt->args[1]->value;
if ($second_arg instanceof PhpParser\Node\Expr\ConstFetch
&& $second_arg->name instanceof PhpParser\Node\Name
&& strtolower($second_arg->name->parts[0]) === 'true'
) {
return 2;
@ -2726,7 +2724,6 @@ class AssertionFinder
$second_arg = $stmt->args[2]->value;
if ($second_arg instanceof PhpParser\Node\Expr\ConstFetch
&& $second_arg->name instanceof PhpParser\Node\Name
&& strtolower($second_arg->name->parts[0]) === 'true'
) {
return true;

View File

@ -86,7 +86,6 @@ class BinaryOpAnalyzer
);
$pre_referenced_var_ids = $context->referenced_var_ids;
$original_vars_in_scope = $context->vars_in_scope;
$pre_assigned_var_ids = $context->assigned_var_ids;
@ -116,20 +115,6 @@ class BinaryOpAnalyzer
$new_referenced_var_ids = array_diff_key($new_referenced_var_ids, $new_assigned_var_ids);
// remove all newly-asserted var ids too
$new_referenced_var_ids = array_filter(
$new_referenced_var_ids,
/**
* @param string $var_id
*
* @return bool
*/
function ($var_id) use ($original_vars_in_scope) {
return isset($original_vars_in_scope[$var_id]);
},
ARRAY_FILTER_USE_KEY
);
$context_clauses = array_merge($left_context->clauses, $left_clauses);
if ($left_context->reconciled_expression_clauses) {

View File

@ -120,6 +120,7 @@ class StatementsProvider
$existing_statements = $this->parser_cache_provider->loadExistingStatementsFromCache($file_path);
/** @psalm-suppress DocblockTypeContradiction */
if ($existing_statements && !$existing_statements[0] instanceof PhpParser\Node\Stmt) {
$existing_statements = null;
}

View File

@ -1565,12 +1565,8 @@ class AssertionReconciler extends \Psalm\Type\Reconciler
if ($type->hasTraversableInterface($codebase)) {
$traversable_types[] = $type;
} elseif ($type instanceof Atomic\TIterable) {
if ($type->type_params) {
$clone_type = clone $type;
$traversable_types[] = new Atomic\TGenericObject('Traversable', $clone_type->type_params);
} else {
$traversable_types[] = new Atomic\TNamedObject('Traversable');
}
$clone_type = clone $type;
$traversable_types[] = new Atomic\TGenericObject('Traversable', $clone_type->type_params);
$did_remove_type = true;
} elseif ($type instanceof TObject) {
$traversable_types[] = new TNamedObject('Traversable');
@ -1644,12 +1640,8 @@ class AssertionReconciler extends \Psalm\Type\Reconciler
$did_remove_type = true;
} elseif ($type instanceof Atomic\TIterable) {
if ($type->type_params) {
$clone_type = clone $type;
$array_types[] = new TArray($clone_type->type_params);
} else {
$array_types[] = new TArray([Type::getArrayKey(), Type::getMixed()]);
}
$clone_type = clone $type;
$array_types[] = new TArray($clone_type->type_params);
$did_remove_type = true;
} else {
@ -1736,12 +1728,8 @@ class AssertionReconciler extends \Psalm\Type\Reconciler
$did_remove_type = true;
} elseif ($type instanceof Atomic\TIterable) {
if ($type->type_params) {
$clone_type = clone $type;
$array_types[] = new TList($clone_type->type_params[1]);
} else {
$array_types[] = new TList(Type::getMixed());
}
$clone_type = clone $type;
$array_types[] = new TList($clone_type->type_params[1]);
$did_remove_type = true;
} else {

View File

@ -229,7 +229,6 @@ class NegatedAssertionReconciler extends Reconciler
$existing_var_type->removeType('iterable');
if ($iterable instanceof Atomic\TIterable
&& $iterable->type_params
&& (!$iterable->type_params[0]->isMixed() || !$iterable->type_params[1]->isMixed())
) {
$traversable = new Atomic\TGenericObject('Traversable', $iterable->type_params);

View File

@ -310,14 +310,10 @@ class UnionTemplateHandler
return $atomic_input_type;
}
if ($atomic_type->type_params) {
$atomic_type = new Atomic\TGenericObject(
'Traversable',
$atomic_type->type_params
);
} else {
$atomic_type = new Atomic\TNamedObject('Traversable');
}
$atomic_type = new Atomic\TGenericObject(
'Traversable',
$atomic_type->type_params
);
}
try {

View File

@ -2700,7 +2700,6 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
$is_nullable = $param->default !== null &&
$param->default instanceof PhpParser\Node\Expr\ConstFetch &&
$param->default->name instanceof PhpParser\Node\Name &&
strtolower($param->default->name->parts[0]) === 'null';
$param_typehint = $param->type;

View File

@ -582,7 +582,16 @@ class Algebra
$new_clause_possibilities[$var] = [$impossible_type];
}
$new_clause = new Clause($new_clause_possibilities, false, true, true);
$new_clause = new Clause(
$new_clause_possibilities,
false,
true,
true,
[],
$clause->creating_object_id === $grouped_clause->creating_object_id
? $clause->creating_object_id
: null
);
$new_clauses[] = $new_clause;
@ -597,7 +606,14 @@ class Algebra
foreach ($clause->impossibilities as $var => $impossible_types) {
foreach ($impossible_types as $impossible_type) {
$new_clause = new Clause([$var => [$impossible_type]]);
$new_clause = new Clause(
[$var => [$impossible_type]],
false,
true,
false,
[],
$clause->creating_object_id
);
$new_clauses[] = $new_clause;

View File

@ -988,7 +988,7 @@ class ArrayAssignmentTest extends TestCase
/**
* @psalm-suppress MixedOperand
*/
$a[$key] += 5;
$a[$key] += rand(0, 10);
}
$a["four"] = true;

View File

@ -2616,23 +2616,14 @@ class ConditionalTest extends \Psalm\Tests\TestCase
}',
'error_message' => 'RedundantCondition',
],
'SKIPPED-catchRedundantConditionOnBinaryOpForwards' => [
'catchRedundantConditionOnBinaryOpForwards' => [
'<?php
class App {}
function test(App $app) : void {
if ($app || rand(0, 1)) {}
}',
'error_message' => 'RedundantCondition',
],
'SKIPPED-catchRedundantConditionOnBinaryOpBackwards' => [
'<?php
class App {}
function test(App $app) : void {
if (rand(0, 1) || $app) {}
}',
'error_message' => 'RedundantCondition',
'error_message' => 'TypeDoesNotContainType',
],
];
}

View File

@ -1136,7 +1136,43 @@ class RedundantConditionTest extends \Psalm\Tests\TestCase
}
}',
'error_message' => 'RedundantCondition',
]
],
'prohibitFalsyChecksOnPropertiesWithMethodCall' => [
'<?php
class RequestHeaders {
public function has(string $s) : bool {
return true;
}
}
class Request {
public RequestHeaders $headers;
public function __construct(RequestHeaders $headers) {
$this->headers = $headers;
}
}
function lag(Request $req) : void {
if ($req->headers && $req->headers->has("foo")) {}
}',
'error_message' => 'RedundantCondition',
],
'prohibitFalsyChecksOnPropertiesWithoutMethodCall' => [
'<?php
class RequestHeaders {}
class Request {
public RequestHeaders $headers;
public function __construct(RequestHeaders $headers) {
$this->headers = $headers;
}
}
function lag(Request $req) : void {
if ($req->headers) {}
}',
'error_message' => 'RedundantCondition',
],
];
}
}