mirror of
https://github.com/danog/psalm.git
synced 2024-12-13 17:57:37 +01:00
commit
f86e3b1bf9
@ -942,26 +942,21 @@ class Codebase
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$storage = $this->methods->getStorage($declaring_method_id);
|
return $this->methods->getStorage($declaring_method_id);
|
||||||
return $storage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$function_id = strtolower(substr($symbol, 0, -2));
|
$function_id = strtolower(substr($symbol, 0, -2));
|
||||||
$file_storage = $this->file_storage_provider->get($file_path);
|
$file_storage = $this->file_storage_provider->get($file_path);
|
||||||
|
|
||||||
if (isset($file_storage->functions[$function_id])) {
|
if (isset($file_storage->functions[$function_id])) {
|
||||||
$function_storage = $file_storage->functions[$function_id];
|
return $file_storage->functions[$function_id];
|
||||||
|
|
||||||
return $function_storage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$function_id) {
|
if (!$function_id) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$function = $this->functions->getStorage(null, $function_id);
|
return $this->functions->getStorage(null, $function_id);
|
||||||
|
|
||||||
return $function;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1166,13 +1161,10 @@ class Codebase
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$function = $this->functions->getStorage(null, $function_id);
|
return $this->functions->getStorage(null, $function_id)->location;
|
||||||
return $function->location;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$storage = $this->classlike_storage_provider->get($symbol);
|
return $this->classlike_storage_provider->get($symbol)->location;
|
||||||
|
|
||||||
return $storage->location;
|
|
||||||
} catch (\UnexpectedValueException $e) {
|
} catch (\UnexpectedValueException $e) {
|
||||||
error_log($e->getMessage());
|
error_log($e->getMessage());
|
||||||
|
|
||||||
@ -1477,8 +1469,8 @@ class Codebase
|
|||||||
if (!$function_storage || !$function_storage->params) {
|
if (!$function_storage || !$function_storage->params) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$parameter = $function_storage->params[$argument_num];
|
|
||||||
return $parameter->type;
|
return $function_storage->params[$argument_num]->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -57,8 +57,6 @@ class FileBasedPluginAdapter implements Plugin\PluginEntryPointInterface
|
|||||||
|
|
||||||
$declared_classes = ClassLikeAnalyzer::getClassesForFile($codebase, $path);
|
$declared_classes = ClassLikeAnalyzer::getClassesForFile($codebase, $path);
|
||||||
|
|
||||||
$fq_class_name = reset($declared_classes);
|
return reset($declared_classes);
|
||||||
|
|
||||||
return $fq_class_name;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1036,7 +1036,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($property->type && $property->type->isNullable() && $property->type->from_docblock) {
|
if ($property->type && $property->type->from_docblock && $property->type->isNullable()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1359,131 +1359,131 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
}
|
||||||
if (!$codebase->traitHasCorrectCase($fq_trait_name)) {
|
|
||||||
|
if (!$codebase->traitHasCorrectCase($fq_trait_name)) {
|
||||||
|
if (IssueBuffer::accepts(
|
||||||
|
new UndefinedTrait(
|
||||||
|
'Trait ' . $fq_trait_name . ' has wrong casing',
|
||||||
|
new CodeLocation($previous_trait_analyzer ?: $this, $trait_name)
|
||||||
|
),
|
||||||
|
$storage->suppressed_issues + $this->getSuppressedIssues()
|
||||||
|
)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fq_trait_name_resolved = $codebase->classlikes->getUnAliasedName($fq_trait_name);
|
||||||
|
$trait_storage = $codebase->classlike_storage_provider->get($fq_trait_name_resolved);
|
||||||
|
|
||||||
|
if ($trait_storage->deprecated) {
|
||||||
|
if (IssueBuffer::accepts(
|
||||||
|
new DeprecatedTrait(
|
||||||
|
'Trait ' . $fq_trait_name . ' is deprecated',
|
||||||
|
new CodeLocation($previous_trait_analyzer ?: $this, $trait_name)
|
||||||
|
),
|
||||||
|
$storage->suppressed_issues + $this->getSuppressedIssues()
|
||||||
|
)) {
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($trait_storage->extension_requirement !== null) {
|
||||||
|
$extension_requirement = $codebase->classlikes->getUnAliasedName(
|
||||||
|
$trait_storage->extension_requirement
|
||||||
|
);
|
||||||
|
$extensionRequirementMet = in_array($extension_requirement, $storage->parent_classes);
|
||||||
|
|
||||||
|
if (!$extensionRequirementMet) {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new UndefinedTrait(
|
new ExtensionRequirementViolation(
|
||||||
'Trait ' . $fq_trait_name . ' has wrong casing',
|
$fq_trait_name . ' requires using class to extend ' . $extension_requirement
|
||||||
|
. ', but ' . $storage->name . ' does not',
|
||||||
new CodeLocation($previous_trait_analyzer ?: $this, $trait_name)
|
new CodeLocation($previous_trait_analyzer ?: $this, $trait_name)
|
||||||
),
|
),
|
||||||
$storage->suppressed_issues + $this->getSuppressedIssues()
|
$storage->suppressed_issues + $this->getSuppressedIssues()
|
||||||
)) {
|
)) {
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($trait_storage->implementation_requirements as $implementation_requirement) {
|
||||||
|
$implementation_requirement = $codebase->classlikes->getUnAliasedName($implementation_requirement);
|
||||||
|
$implementationRequirementMet = in_array($implementation_requirement, $storage->class_implements);
|
||||||
|
|
||||||
|
if (!$implementationRequirementMet) {
|
||||||
|
if (IssueBuffer::accepts(
|
||||||
|
new ImplementationRequirementViolation(
|
||||||
|
$fq_trait_name . ' requires using class to implement '
|
||||||
|
. $implementation_requirement . ', but ' . $storage->name . ' does not',
|
||||||
|
new CodeLocation($previous_trait_analyzer ?: $this, $trait_name)
|
||||||
|
),
|
||||||
|
$storage->suppressed_issues + $this->getSuppressedIssues()
|
||||||
|
)) {
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($storage->mutation_free && !$trait_storage->mutation_free) {
|
||||||
|
if (IssueBuffer::accepts(
|
||||||
|
new MutableDependency(
|
||||||
|
$storage->name . ' is marked @psalm-immutable but ' . $fq_trait_name . ' is not',
|
||||||
|
new CodeLocation($previous_trait_analyzer ?: $this, $trait_name)
|
||||||
|
),
|
||||||
|
$storage->suppressed_issues + $this->getSuppressedIssues()
|
||||||
|
)) {
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$trait_file_analyzer = $project_analyzer->getFileAnalyzerForClassLike($fq_trait_name_resolved);
|
||||||
|
$trait_node = $codebase->classlikes->getTraitNode($fq_trait_name_resolved);
|
||||||
|
$trait_aliases = $trait_storage->aliases;
|
||||||
|
if ($trait_aliases === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$trait_analyzer = new TraitAnalyzer(
|
||||||
|
$trait_node,
|
||||||
|
$trait_file_analyzer,
|
||||||
|
$fq_trait_name_resolved,
|
||||||
|
$trait_aliases
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($trait_node->stmts as $trait_stmt) {
|
||||||
|
if ($trait_stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
|
||||||
|
$trait_method_analyzer = $this->analyzeClassMethod(
|
||||||
|
$trait_stmt,
|
||||||
|
$storage,
|
||||||
|
$trait_analyzer,
|
||||||
|
$class_context,
|
||||||
|
$global_context
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($trait_stmt->name->name === '__construct') {
|
||||||
|
$constructor_analyzer = $trait_method_analyzer;
|
||||||
|
}
|
||||||
|
} elseif ($trait_stmt instanceof PhpParser\Node\Stmt\TraitUse) {
|
||||||
|
if ($this->analyzeTraitUse(
|
||||||
|
$trait_aliases,
|
||||||
|
$trait_stmt,
|
||||||
|
$project_analyzer,
|
||||||
|
$storage,
|
||||||
|
$class_context,
|
||||||
|
$global_context,
|
||||||
|
$constructor_analyzer,
|
||||||
|
$trait_analyzer
|
||||||
|
) === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$fq_trait_name_resolved = $codebase->classlikes->getUnAliasedName($fq_trait_name);
|
|
||||||
$trait_storage = $codebase->classlike_storage_provider->get($fq_trait_name_resolved);
|
|
||||||
|
|
||||||
if ($trait_storage->deprecated) {
|
|
||||||
if (IssueBuffer::accepts(
|
|
||||||
new DeprecatedTrait(
|
|
||||||
'Trait ' . $fq_trait_name . ' is deprecated',
|
|
||||||
new CodeLocation($previous_trait_analyzer ?: $this, $trait_name)
|
|
||||||
),
|
|
||||||
$storage->suppressed_issues + $this->getSuppressedIssues()
|
|
||||||
)) {
|
|
||||||
// fall through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($trait_storage->extension_requirement !== null) {
|
|
||||||
$extension_requirement = $codebase->classlikes->getUnAliasedName(
|
|
||||||
$trait_storage->extension_requirement
|
|
||||||
);
|
|
||||||
$extensionRequirementMet = in_array($extension_requirement, $storage->parent_classes);
|
|
||||||
|
|
||||||
if (!$extensionRequirementMet) {
|
|
||||||
if (IssueBuffer::accepts(
|
|
||||||
new ExtensionRequirementViolation(
|
|
||||||
$fq_trait_name . ' requires using class to extend ' . $extension_requirement
|
|
||||||
. ', but ' . $storage->name . ' does not',
|
|
||||||
new CodeLocation($previous_trait_analyzer ?: $this, $trait_name)
|
|
||||||
),
|
|
||||||
$storage->suppressed_issues + $this->getSuppressedIssues()
|
|
||||||
)) {
|
|
||||||
// fall through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($trait_storage->implementation_requirements as $implementation_requirement) {
|
|
||||||
$implementation_requirement = $codebase->classlikes->getUnAliasedName($implementation_requirement);
|
|
||||||
$implementationRequirementMet = in_array($implementation_requirement, $storage->class_implements);
|
|
||||||
|
|
||||||
if (!$implementationRequirementMet) {
|
|
||||||
if (IssueBuffer::accepts(
|
|
||||||
new ImplementationRequirementViolation(
|
|
||||||
$fq_trait_name . ' requires using class to implement '
|
|
||||||
. $implementation_requirement . ', but ' . $storage->name . ' does not',
|
|
||||||
new CodeLocation($previous_trait_analyzer ?: $this, $trait_name)
|
|
||||||
),
|
|
||||||
$storage->suppressed_issues + $this->getSuppressedIssues()
|
|
||||||
)) {
|
|
||||||
// fall through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($storage->mutation_free && !$trait_storage->mutation_free) {
|
|
||||||
if (IssueBuffer::accepts(
|
|
||||||
new MutableDependency(
|
|
||||||
$storage->name . ' is marked @psalm-immutable but ' . $fq_trait_name . ' is not',
|
|
||||||
new CodeLocation($previous_trait_analyzer ?: $this, $trait_name)
|
|
||||||
),
|
|
||||||
$storage->suppressed_issues + $this->getSuppressedIssues()
|
|
||||||
)) {
|
|
||||||
// fall through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$trait_file_analyzer = $project_analyzer->getFileAnalyzerForClassLike($fq_trait_name_resolved);
|
|
||||||
$trait_node = $codebase->classlikes->getTraitNode($fq_trait_name_resolved);
|
|
||||||
$trait_aliases = $trait_storage->aliases;
|
|
||||||
if ($trait_aliases === null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$trait_analyzer = new TraitAnalyzer(
|
|
||||||
$trait_node,
|
|
||||||
$trait_file_analyzer,
|
|
||||||
$fq_trait_name_resolved,
|
|
||||||
$trait_aliases
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($trait_node->stmts as $trait_stmt) {
|
|
||||||
if ($trait_stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
|
|
||||||
$trait_method_analyzer = $this->analyzeClassMethod(
|
|
||||||
$trait_stmt,
|
|
||||||
$storage,
|
|
||||||
$trait_analyzer,
|
|
||||||
$class_context,
|
|
||||||
$global_context
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($trait_stmt->name->name === '__construct') {
|
|
||||||
$constructor_analyzer = $trait_method_analyzer;
|
|
||||||
}
|
|
||||||
} elseif ($trait_stmt instanceof PhpParser\Node\Stmt\TraitUse) {
|
|
||||||
if ($this->analyzeTraitUse(
|
|
||||||
$trait_aliases,
|
|
||||||
$trait_stmt,
|
|
||||||
$project_analyzer,
|
|
||||||
$storage,
|
|
||||||
$class_context,
|
|
||||||
$global_context,
|
|
||||||
$constructor_analyzer,
|
|
||||||
$trait_analyzer
|
|
||||||
) === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$trait_file_analyzer->clearSourceBeforeDestruction();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$trait_file_analyzer->clearSourceBeforeDestruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
$class_context->include_location = $previous_context_include_location;
|
$class_context->include_location = $previous_context_include_location;
|
||||||
|
@ -130,7 +130,7 @@ class ClosureAnalyzer extends FunctionLikeAnalyzer
|
|||||||
|
|
||||||
// insert the ref into the current context if passed by ref, as whatever we're passing
|
// insert the ref into the current context if passed by ref, as whatever we're passing
|
||||||
// the closure to could execute it straight away.
|
// the closure to could execute it straight away.
|
||||||
if (!$context->hasVariable($use_var_id) && $use->byRef) {
|
if ($use->byRef && !$context->hasVariable($use_var_id)) {
|
||||||
$context->vars_in_scope[$use_var_id] = Type::getMixed();
|
$context->vars_in_scope[$use_var_id] = Type::getMixed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,23 +312,30 @@ class ReturnTypeCollector
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [Type::getMixed()];
|
return [Type::getMixed()];
|
||||||
} elseif ($stmt instanceof PhpParser\Node\Expr\YieldFrom) {
|
}
|
||||||
|
|
||||||
|
if ($stmt instanceof PhpParser\Node\Expr\YieldFrom) {
|
||||||
if ($stmt_expr_type = $nodes->getType($stmt->expr)) {
|
if ($stmt_expr_type = $nodes->getType($stmt->expr)) {
|
||||||
return [$stmt_expr_type];
|
return [$stmt_expr_type];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [Type::getMixed()];
|
return [Type::getMixed()];
|
||||||
} elseif ($stmt instanceof PhpParser\Node\Expr\BinaryOp) {
|
}
|
||||||
|
|
||||||
|
if ($stmt instanceof PhpParser\Node\Expr\BinaryOp) {
|
||||||
return array_merge(
|
return array_merge(
|
||||||
self::getYieldTypeFromExpression($stmt->left, $nodes),
|
self::getYieldTypeFromExpression($stmt->left, $nodes),
|
||||||
self::getYieldTypeFromExpression($stmt->right, $nodes)
|
self::getYieldTypeFromExpression($stmt->right, $nodes)
|
||||||
);
|
);
|
||||||
} elseif ($stmt instanceof PhpParser\Node\Expr\Assign) {
|
}
|
||||||
|
|
||||||
|
if ($stmt instanceof PhpParser\Node\Expr\Assign) {
|
||||||
return self::getYieldTypeFromExpression($stmt->expr, $nodes);
|
return self::getYieldTypeFromExpression($stmt->expr, $nodes);
|
||||||
} elseif ($stmt instanceof PhpParser\Node\Expr\MethodCall
|
}
|
||||||
|
|
||||||
|
if ($stmt instanceof PhpParser\Node\Expr\MethodCall
|
||||||
|| $stmt instanceof PhpParser\Node\Expr\FuncCall
|
|| $stmt instanceof PhpParser\Node\Expr\FuncCall
|
||||||
|| $stmt instanceof PhpParser\Node\Expr\StaticCall
|
|| $stmt instanceof PhpParser\Node\Expr\StaticCall) {
|
||||||
) {
|
|
||||||
$yield_types = [];
|
$yield_types = [];
|
||||||
|
|
||||||
foreach ($stmt->args as $arg) {
|
foreach ($stmt->args as $arg) {
|
||||||
|
@ -86,9 +86,7 @@ class ScopeAnalyzer
|
|||||||
|
|
||||||
$control_actions = [];
|
$control_actions = [];
|
||||||
|
|
||||||
for ($i = 0, $c = count($stmts); $i < $c; ++$i) {
|
foreach ($stmts as $stmt) {
|
||||||
$stmt = $stmts[$i];
|
|
||||||
|
|
||||||
if ($stmt instanceof PhpParser\Node\Stmt\Return_ ||
|
if ($stmt instanceof PhpParser\Node\Stmt\Return_ ||
|
||||||
$stmt instanceof PhpParser\Node\Stmt\Throw_ ||
|
$stmt instanceof PhpParser\Node\Stmt\Throw_ ||
|
||||||
($stmt instanceof PhpParser\Node\Stmt\Expression && $stmt->expr instanceof PhpParser\Node\Expr\Exit_)
|
($stmt instanceof PhpParser\Node\Stmt\Expression && $stmt->expr instanceof PhpParser\Node\Expr\Exit_)
|
||||||
|
@ -95,11 +95,7 @@ class ForAnalyzer
|
|||||||
|
|
||||||
foreach ($stmt->cond as $cond) {
|
foreach ($stmt->cond as $cond) {
|
||||||
if ($cond_type = $statements_analyzer->node_data->getType($cond)) {
|
if ($cond_type = $statements_analyzer->node_data->getType($cond)) {
|
||||||
foreach ($cond_type->getAtomicTypes() as $iterator_type) {
|
$always_enters_loop = $cond_type->isAlwaysTruthy();
|
||||||
$always_enters_loop = $iterator_type instanceof Type\Atomic\TTrue;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\count($stmt->init) === 1
|
if (\count($stmt->init) === 1
|
||||||
|
@ -369,7 +369,9 @@ class ForeachAnalyzer
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} elseif ($iterator_type->isNullable() && !$iterator_type->ignore_nullable_issues) {
|
}
|
||||||
|
|
||||||
|
if ($iterator_type->isNullable() && !$iterator_type->ignore_nullable_issues) {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new PossiblyNullIterator(
|
new PossiblyNullIterator(
|
||||||
'Cannot iterate over nullable var ' . $iterator_type,
|
'Cannot iterate over nullable var ' . $iterator_type,
|
||||||
|
@ -1155,9 +1155,10 @@ class AssertionFinder
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [$instanceof_class];
|
return [$instanceof_class];
|
||||||
} elseif ($this_class_name
|
}
|
||||||
&& (in_array(strtolower($stmt->class->parts[0]), ['self', 'static'], true))
|
|
||||||
) {
|
if ($this_class_name
|
||||||
|
&& (in_array(strtolower($stmt->class->parts[0]), ['self', 'static'], true))) {
|
||||||
if ($stmt->class->parts[0] === 'static') {
|
if ($stmt->class->parts[0] === 'static') {
|
||||||
return ['=' . $this_class_name . '&static'];
|
return ['=' . $this_class_name . '&static'];
|
||||||
}
|
}
|
||||||
|
@ -616,7 +616,7 @@ class InstancePropertyAssignmentAnalyzer
|
|||||||
Context $context,
|
Context $context,
|
||||||
bool $direct_assignment,
|
bool $direct_assignment,
|
||||||
\Psalm\Codebase $codebase,
|
\Psalm\Codebase $codebase,
|
||||||
Type\Union &$assignment_value_type,
|
Type\Union $assignment_value_type,
|
||||||
string $prop_name,
|
string $prop_name,
|
||||||
?string &$var_id
|
?string &$var_id
|
||||||
): array {
|
): array {
|
||||||
|
@ -11,6 +11,7 @@ use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
|
|||||||
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ArrayFetchAnalyzer;
|
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ArrayFetchAnalyzer;
|
||||||
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
||||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||||
|
use Psalm\Internal\Codebase\Functions;
|
||||||
use Psalm\Internal\Codebase\InternalCallMapHandler;
|
use Psalm\Internal\Codebase\InternalCallMapHandler;
|
||||||
use Psalm\Internal\Codebase\TaintFlowGraph;
|
use Psalm\Internal\Codebase\TaintFlowGraph;
|
||||||
use Psalm\Internal\DataFlow\TaintSink;
|
use Psalm\Internal\DataFlow\TaintSink;
|
||||||
@ -476,7 +477,7 @@ class ArgumentsAnalyzer
|
|||||||
if ($function_storage) {
|
if ($function_storage) {
|
||||||
$is_variadic = $function_storage->variadic;
|
$is_variadic = $function_storage->variadic;
|
||||||
} elseif (is_string($method_id)) {
|
} elseif (is_string($method_id)) {
|
||||||
$is_variadic = $codebase->functions->isVariadic(
|
$is_variadic = Functions::isVariadic(
|
||||||
$codebase,
|
$codebase,
|
||||||
strtolower($method_id),
|
strtolower($method_id),
|
||||||
$statements_analyzer->getRootFilePath()
|
$statements_analyzer->getRootFilePath()
|
||||||
|
@ -770,7 +770,9 @@ class ArrayFunctionArgumentsAnalyzer
|
|||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} elseif ($required_param_count > $max_closure_param_count) {
|
}
|
||||||
|
|
||||||
|
if ($required_param_count > $max_closure_param_count) {
|
||||||
$argument_text = $max_closure_param_count === 1 ? 'one argument' : $max_closure_param_count . ' arguments';
|
$argument_text = $max_closure_param_count === 1 ? 'one argument' : $max_closure_param_count . ' arguments';
|
||||||
|
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
|
@ -434,9 +434,9 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $function_call_info;
|
return $function_call_info;
|
||||||
} else {
|
|
||||||
$function_call_info->function_exists = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$function_call_info->function_exists = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$function_call_info->function_exists = true;
|
$function_call_info->function_exists = true;
|
||||||
|
@ -169,14 +169,15 @@ class ExpressionIdentifier
|
|||||||
|
|
||||||
if ($stmt->name instanceof PhpParser\Node\Identifier) {
|
if ($stmt->name instanceof PhpParser\Node\Identifier) {
|
||||||
return $object_id . '->' . $stmt->name;
|
return $object_id . '->' . $stmt->name;
|
||||||
} elseif ($source instanceof StatementsAnalyzer
|
|
||||||
&& ($stmt_name_type = $source->node_data->getType($stmt->name))
|
|
||||||
&& $stmt_name_type->isSingleStringLiteral()
|
|
||||||
) {
|
|
||||||
return $object_id . '->' . $stmt_name_type->getSingleStringLiteral()->value;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($source instanceof StatementsAnalyzer
|
||||||
|
&& ($stmt_name_type = $source->node_data->getType($stmt->name))
|
||||||
|
&& $stmt_name_type->isSingleStringLiteral()) {
|
||||||
|
return $object_id . '->' . $stmt_name_type->getSingleStringLiteral()->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($stmt instanceof PhpParser\Node\Expr\ClassConstFetch
|
if ($stmt instanceof PhpParser\Node\Expr\ClassConstFetch
|
||||||
|
@ -1086,16 +1086,14 @@ class ArrayFetchAnalyzer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$array_access_type) {
|
if ($array_access_type) {
|
||||||
return Type::getMixed(
|
|
||||||
$type instanceof TEmpty
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Type::combineUnionTypes(
|
return Type::combineUnionTypes(
|
||||||
$array_access_type,
|
$array_access_type,
|
||||||
Type::getMixed($type instanceof TEmpty)
|
Type::getMixed($type instanceof TEmpty)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Type::getMixed($type instanceof TEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -557,9 +557,7 @@ class VariableFetchAnalyzer
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (self::isSuperGlobal($var_id)) {
|
if (self::isSuperGlobal($var_id)) {
|
||||||
$type = Type::getArray();
|
return Type::getArray();
|
||||||
|
|
||||||
return $type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Type::getMixed();
|
return Type::getMixed();
|
||||||
|
@ -189,13 +189,20 @@ class SimpleTypeInferer
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
|
if ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
|
||||||
if (strtolower($stmt->name->parts[0]) === 'false') {
|
$name = strtolower($stmt->name->parts[0]);
|
||||||
|
if ($name === 'false') {
|
||||||
return Type::getFalse();
|
return Type::getFalse();
|
||||||
} elseif (strtolower($stmt->name->parts[0]) === 'true') {
|
}
|
||||||
|
|
||||||
|
if ($name === 'true') {
|
||||||
return Type::getTrue();
|
return Type::getTrue();
|
||||||
} elseif (strtolower($stmt->name->parts[0]) === 'null') {
|
}
|
||||||
|
|
||||||
|
if ($name === 'null') {
|
||||||
return Type::getNull();
|
return Type::getNull();
|
||||||
} elseif ($stmt->name->parts[0] === '__NAMESPACE__') {
|
}
|
||||||
|
|
||||||
|
if ($stmt->name->parts[0] === '__NAMESPACE__') {
|
||||||
return Type::getString($aliases->namespace);
|
return Type::getString($aliases->namespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,7 +698,9 @@ class SimpleTypeInferer
|
|||||||
if ($unpacked_atomic_type->type_params[0]->hasString()) {
|
if ($unpacked_atomic_type->type_params[0]->hasString()) {
|
||||||
// string keys are not supported in unpacked arrays
|
// string keys are not supported in unpacked arrays
|
||||||
return false;
|
return false;
|
||||||
} elseif ($unpacked_atomic_type->type_params[0]->hasInt()) {
|
}
|
||||||
|
|
||||||
|
if ($unpacked_atomic_type->type_params[0]->hasInt()) {
|
||||||
$array_creation_info->item_key_atomic_types[] = new Type\Atomic\TInt();
|
$array_creation_info->item_key_atomic_types[] = new Type\Atomic\TInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,14 +215,13 @@ class UnusedAssignmentRemover
|
|||||||
|| $rhs_exp instanceof PhpParser\Node\Expr\AssignOp
|
|| $rhs_exp instanceof PhpParser\Node\Expr\AssignOp
|
||||||
|| $rhs_exp instanceof PhpParser\Node\Expr\AssignRef
|
|| $rhs_exp instanceof PhpParser\Node\Expr\AssignRef
|
||||||
) {
|
) {
|
||||||
$rhs_removable = $this->checkRemovableChainAssignment($rhs_exp, $var_loc_map);
|
return $this->checkRemovableChainAssignment($rhs_exp, $var_loc_map);
|
||||||
return $rhs_removable;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $curr_removable;
|
return $curr_removable;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -344,9 +343,9 @@ class UnusedAssignmentRemover
|
|||||||
$rhs_exp = $current_node->expr;
|
$rhs_exp = $current_node->expr;
|
||||||
$rhs_search_result = $this->findAssignExp($rhs_exp, $var_id, $var_start_loc, $search_level + 1);
|
$rhs_search_result = $this->findAssignExp($rhs_exp, $var_id, $var_start_loc, $search_level + 1);
|
||||||
return [$rhs_search_result[0], $rhs_search_result[1]];
|
return [$rhs_search_result[0], $rhs_search_result[1]];
|
||||||
} else {
|
|
||||||
return [null, $search_level];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return [null, $search_level];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkIfVarRemoved(string $var_id, CodeLocation $var_loc): bool
|
public function checkIfVarRemoved(string $var_id, CodeLocation $var_loc): bool
|
||||||
|
@ -10,6 +10,7 @@ use Psalm\Internal\Analyzer\ProjectAnalyzer;
|
|||||||
use Psalm\Internal\CliUtils;
|
use Psalm\Internal\CliUtils;
|
||||||
use Psalm\Internal\Composer;
|
use Psalm\Internal\Composer;
|
||||||
use Psalm\Internal\ErrorHandler;
|
use Psalm\Internal\ErrorHandler;
|
||||||
|
use Psalm\Internal\Fork\PsalmRestarter;
|
||||||
use Psalm\Internal\IncludeCollector;
|
use Psalm\Internal\IncludeCollector;
|
||||||
use Psalm\Internal\Provider;
|
use Psalm\Internal\Provider;
|
||||||
use Psalm\IssueBuffer;
|
use Psalm\IssueBuffer;
|
||||||
@ -902,7 +903,7 @@ final class Psalm
|
|||||||
// If Xdebug is enabled, restart without it
|
// If Xdebug is enabled, restart without it
|
||||||
$ini_handler->check();
|
$ini_handler->check();
|
||||||
|
|
||||||
if ($config->load_xdebug_stub === null && '' !== $ini_handler->getSkippedVersion()) {
|
if ($config->load_xdebug_stub === null && PsalmRestarter::getSkippedVersion() !== '') {
|
||||||
$config->load_xdebug_stub = true;
|
$config->load_xdebug_stub = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ final class CliUtils
|
|||||||
require_once __DIR__ . '/../../../vendor/netresearch/jsonmapper/src/JsonMapper/Exception.php';
|
require_once __DIR__ . '/../../../vendor/netresearch/jsonmapper/src/JsonMapper/Exception.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (realpath($psalm_dir) !== realpath($current_dir) && !$in_phar) {
|
if (!$in_phar && realpath($psalm_dir) !== realpath($current_dir)) {
|
||||||
$autoload_roots[] = $psalm_dir;
|
$autoload_roots[] = $psalm_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,13 +241,14 @@ final class CliUtils
|
|||||||
/** @var string */
|
/** @var string */
|
||||||
$input_path = $input_paths[$i];
|
$input_path = $input_paths[$i];
|
||||||
|
|
||||||
if (realpath($input_path) === realpath(dirname(__DIR__, 5) . DIRECTORY_SEPARATOR . 'bin'
|
$real_input_path = realpath($input_path);
|
||||||
|
if ($real_input_path === realpath(dirname(__DIR__, 5) . DIRECTORY_SEPARATOR . 'bin'
|
||||||
. DIRECTORY_SEPARATOR . 'psalm')
|
. DIRECTORY_SEPARATOR . 'psalm')
|
||||||
|| realpath($input_path) === realpath(dirname(__DIR__, 5) . DIRECTORY_SEPARATOR . 'bin'
|
|| $real_input_path === realpath(dirname(__DIR__, 5) . DIRECTORY_SEPARATOR . 'bin'
|
||||||
. DIRECTORY_SEPARATOR . 'psalter')
|
. DIRECTORY_SEPARATOR . 'psalter')
|
||||||
|| realpath($input_path) === realpath(dirname(__DIR__, 3) . DIRECTORY_SEPARATOR . 'psalm')
|
|| $real_input_path === realpath(dirname(__DIR__, 3) . DIRECTORY_SEPARATOR . 'psalm')
|
||||||
|| realpath($input_path) === realpath(dirname(__DIR__, 3) . DIRECTORY_SEPARATOR . 'psalter')
|
|| $real_input_path === realpath(dirname(__DIR__, 3) . DIRECTORY_SEPARATOR . 'psalter')
|
||||||
|| realpath($input_path) === realpath(Phar::running(false))
|
|| $real_input_path === realpath(Phar::running(false))
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -545,13 +545,8 @@ class ClassLikes
|
|||||||
?string $calling_fq_class_name = null,
|
?string $calling_fq_class_name = null,
|
||||||
?string $calling_method_id = null
|
?string $calling_method_id = null
|
||||||
): bool {
|
): bool {
|
||||||
if (!$this->classExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id)
|
return $this->classExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id)
|
||||||
&& !$this->interfaceExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id)
|
|| $this->interfaceExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id);
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -563,14 +558,9 @@ class ClassLikes
|
|||||||
?string $calling_fq_class_name = null,
|
?string $calling_fq_class_name = null,
|
||||||
?string $calling_method_id = null
|
?string $calling_method_id = null
|
||||||
): bool {
|
): bool {
|
||||||
if (!$this->classExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id)
|
return $this->classExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id)
|
||||||
&& !$this->interfaceExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id)
|
|| $this->interfaceExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id)
|
||||||
&& !$this->enumExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id)
|
|| $this->enumExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id);
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -721,9 +711,7 @@ class ClassLikes
|
|||||||
{
|
{
|
||||||
$fq_interface_name = strtolower($fq_interface_name);
|
$fq_interface_name = strtolower($fq_interface_name);
|
||||||
|
|
||||||
$storage = $this->classlike_storage_provider->get($fq_interface_name);
|
return $this->classlike_storage_provider->get($fq_interface_name)->parent_interfaces;
|
||||||
|
|
||||||
return $storage->parent_interfaces;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function traitExists(string $fq_trait_name, ?CodeLocation $code_location = null): bool
|
public function traitExists(string $fq_trait_name, ?CodeLocation $code_location = null): bool
|
||||||
|
@ -301,16 +301,24 @@ class ConstantTypeResolver
|
|||||||
{
|
{
|
||||||
if (\is_string($value)) {
|
if (\is_string($value)) {
|
||||||
return new Type\Atomic\TLiteralString($value);
|
return new Type\Atomic\TLiteralString($value);
|
||||||
} elseif (\is_int($value)) {
|
|
||||||
return new Type\Atomic\TLiteralInt($value);
|
|
||||||
} elseif (\is_float($value)) {
|
|
||||||
return new Type\Atomic\TLiteralFloat($value);
|
|
||||||
} elseif ($value === false) {
|
|
||||||
return new Type\Atomic\TFalse;
|
|
||||||
} elseif ($value === true) {
|
|
||||||
return new Type\Atomic\TTrue;
|
|
||||||
} else {
|
|
||||||
return new Type\Atomic\TNull;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (\is_int($value)) {
|
||||||
|
return new Type\Atomic\TLiteralInt($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\is_float($value)) {
|
||||||
|
return new Type\Atomic\TLiteralFloat($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value === false) {
|
||||||
|
return new Type\Atomic\TFalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value === true) {
|
||||||
|
return new Type\Atomic\TTrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Type\Atomic\TNull;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -531,8 +531,7 @@ class Functions
|
|||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$method_storage = $codebase->methods->getStorage($count_method_id);
|
return $codebase->methods->getStorage($count_method_id)->mutation_free;
|
||||||
return $method_storage->mutation_free;
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
@ -955,9 +955,7 @@ class Methods
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$storage = $this->getStorage($method_id);
|
return $this->getStorage($method_id)->returns_by_ref;
|
||||||
|
|
||||||
return $storage->returns_by_ref;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -147,8 +147,8 @@ class Populator
|
|||||||
|
|
||||||
$this->progress->debug('FileStorage is populated' . "\n");
|
$this->progress->debug('FileStorage is populated' . "\n");
|
||||||
|
|
||||||
$this->classlike_storage_provider->populated();
|
ClassLikeStorageProvider::populated();
|
||||||
$this->file_storage_provider->populated();
|
FileStorageProvider::populated();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function populateClassLikeStorage(ClassLikeStorage $storage, array $dependent_classlikes = []): void
|
private function populateClassLikeStorage(ClassLikeStorage $storage, array $dependent_classlikes = []): void
|
||||||
|
@ -5,6 +5,7 @@ use Psalm\Codebase;
|
|||||||
use Psalm\Config;
|
use Psalm\Config;
|
||||||
use Psalm\Internal\Analyzer\IssueData;
|
use Psalm\Internal\Analyzer\IssueData;
|
||||||
use Psalm\Internal\ErrorHandler;
|
use Psalm\Internal\ErrorHandler;
|
||||||
|
use Psalm\Internal\Provider\ClassLikeStorageProvider;
|
||||||
use Psalm\Internal\Provider\FileProvider;
|
use Psalm\Internal\Provider\FileProvider;
|
||||||
use Psalm\Internal\Provider\FileReferenceProvider;
|
use Psalm\Internal\Provider\FileReferenceProvider;
|
||||||
use Psalm\Internal\Provider\FileStorageProvider;
|
use Psalm\Internal\Provider\FileStorageProvider;
|
||||||
@ -360,8 +361,8 @@ class Scanner
|
|||||||
$statements_provider = $codebase->statements_provider;
|
$statements_provider = $codebase->statements_provider;
|
||||||
|
|
||||||
$codebase->scanner->isForked();
|
$codebase->scanner->isForked();
|
||||||
$codebase->file_storage_provider->deleteAll();
|
FileStorageProvider::deleteAll();
|
||||||
$codebase->classlike_storage_provider->deleteAll();
|
ClassLikeStorageProvider::deleteAll();
|
||||||
|
|
||||||
$statements_provider->resetDiffs();
|
$statements_provider->resetDiffs();
|
||||||
|
|
||||||
|
@ -229,7 +229,7 @@ class FunctionDocblockManipulator
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($chars[$i] === '\\' || preg_match('/\w/', $char)) {
|
if ($char === '\\' || preg_match('/\w/', $char)) {
|
||||||
if ($this->return_typehint_start === null) {
|
if ($this->return_typehint_start === null) {
|
||||||
$this->return_typehint_start = $i + $end_bracket_position + 1;
|
$this->return_typehint_start = $i + $end_bracket_position + 1;
|
||||||
}
|
}
|
||||||
|
@ -48,9 +48,9 @@ class CheckTrivialExprVisitor extends PhpParser\NodeVisitorAbstract
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function enterNode(PhpParser\Node $node): ?int
|
public function enterNode(PhpParser\Node $node): ?int
|
||||||
@ -60,12 +60,13 @@ class CheckTrivialExprVisitor extends PhpParser\NodeVisitorAbstract
|
|||||||
if ($this->checkNonTrivialExpr($node)) {
|
if ($this->checkNonTrivialExpr($node)) {
|
||||||
$this->non_trivial_expr[] = $node;
|
$this->non_trivial_expr[] = $node;
|
||||||
return PhpParser\NodeTraverser::STOP_TRAVERSAL;
|
return PhpParser\NodeTraverser::STOP_TRAVERSAL;
|
||||||
} elseif ($node instanceof PhpParser\Node\Expr\ClassConstFetch
|
}
|
||||||
|
|
||||||
|
if ($node instanceof PhpParser\Node\Expr\ClassConstFetch
|
||||||
|| $node instanceof PhpParser\Node\Expr\ConstFetch
|
|| $node instanceof PhpParser\Node\Expr\ConstFetch
|
||||||
|| $node instanceof PhpParser\Node\Expr\Error
|
|| $node instanceof PhpParser\Node\Expr\Error
|
||||||
|| $node instanceof PhpParser\Node\Expr\PropertyFetch
|
|| $node instanceof PhpParser\Node\Expr\PropertyFetch
|
||||||
|| $node instanceof PhpParser\Node\Expr\StaticPropertyFetch
|
|| $node instanceof PhpParser\Node\Expr\StaticPropertyFetch) {
|
||||||
) {
|
|
||||||
return PhpParser\NodeTraverser::STOP_TRAVERSAL;
|
return PhpParser\NodeTraverser::STOP_TRAVERSAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,11 +117,17 @@ class ExpressionResolver
|
|||||||
if ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
|
if ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
|
||||||
if (strtolower($stmt->name->parts[0]) === 'false') {
|
if (strtolower($stmt->name->parts[0]) === 'false') {
|
||||||
return new UnresolvedConstant\ScalarValue(false);
|
return new UnresolvedConstant\ScalarValue(false);
|
||||||
} elseif (strtolower($stmt->name->parts[0]) === 'true') {
|
}
|
||||||
|
|
||||||
|
if (strtolower($stmt->name->parts[0]) === 'true') {
|
||||||
return new UnresolvedConstant\ScalarValue(true);
|
return new UnresolvedConstant\ScalarValue(true);
|
||||||
} elseif (strtolower($stmt->name->parts[0]) === 'null') {
|
}
|
||||||
|
|
||||||
|
if (strtolower($stmt->name->parts[0]) === 'null') {
|
||||||
return new UnresolvedConstant\ScalarValue(null);
|
return new UnresolvedConstant\ScalarValue(null);
|
||||||
} elseif ($stmt->name->parts[0] === '__NAMESPACE__') {
|
}
|
||||||
|
|
||||||
|
if ($stmt->name->parts[0] === '__NAMESPACE__') {
|
||||||
return new UnresolvedConstant\ScalarValue($aliases->namespace);
|
return new UnresolvedConstant\ScalarValue($aliases->namespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -967,26 +967,26 @@ class FunctionLikeNodeScanner
|
|||||||
$duplicate_method_storage->has_visitor_issues = true;
|
$duplicate_method_storage->has_visitor_issues = true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
}
|
||||||
// skip methods based on @since docblock tag
|
|
||||||
$doc_comment = $stmt->getDocComment();
|
|
||||||
|
|
||||||
if ($doc_comment) {
|
// skip methods based on @since docblock tag
|
||||||
$docblock_info = null;
|
$doc_comment = $stmt->getDocComment();
|
||||||
try {
|
|
||||||
$docblock_info = FunctionLikeDocblockParser::parse($doc_comment);
|
if ($doc_comment) {
|
||||||
} catch (IncorrectDocblockException|DocblockParseException $e) {
|
$docblock_info = null;
|
||||||
}
|
try {
|
||||||
if ($docblock_info) {
|
$docblock_info = FunctionLikeDocblockParser::parse($doc_comment);
|
||||||
if ($docblock_info->since_php_major_version && !$this->aliases->namespace) {
|
} catch (IncorrectDocblockException|DocblockParseException $e) {
|
||||||
if ($docblock_info->since_php_major_version > $this->codebase->php_major_version) {
|
}
|
||||||
return false;
|
if ($docblock_info) {
|
||||||
}
|
if ($docblock_info->since_php_major_version && !$this->aliases->namespace) {
|
||||||
if ($docblock_info->since_php_major_version === $this->codebase->php_major_version
|
if ($docblock_info->since_php_major_version > $this->codebase->php_major_version) {
|
||||||
&& $docblock_info->since_php_minor_version > $this->codebase->php_minor_version
|
return false;
|
||||||
) {
|
}
|
||||||
return false;
|
if ($docblock_info->since_php_major_version === $this->codebase->php_major_version
|
||||||
}
|
&& $docblock_info->since_php_minor_version > $this->codebase->php_minor_version
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -429,7 +429,7 @@ class FileReferenceProvider
|
|||||||
|
|
||||||
public function removeDeletedFilesFromReferences(): void
|
public function removeDeletedFilesFromReferences(): void
|
||||||
{
|
{
|
||||||
$deleted_files = self::getDeletedReferencedFiles();
|
$deleted_files = $this->getDeletedReferencedFiles();
|
||||||
|
|
||||||
if ($deleted_files) {
|
if ($deleted_files) {
|
||||||
foreach ($deleted_files as $file) {
|
foreach ($deleted_files as $file) {
|
||||||
|
@ -126,7 +126,7 @@ class NodeDataProvider implements \Psalm\NodeTypeProvider
|
|||||||
|
|
||||||
public function isPureCompatible(PhpParser\Node\Expr $node) : bool
|
public function isPureCompatible(PhpParser\Node\Expr $node) : bool
|
||||||
{
|
{
|
||||||
$node_type = self::getType($node);
|
$node_type = $this->getType($node);
|
||||||
|
|
||||||
return ($node_type && $node_type->reference_free) || isset($node->pure);
|
return ($node_type && $node_type->reference_free) || isset($node->pure);
|
||||||
}
|
}
|
||||||
|
@ -51,9 +51,10 @@ class ExplodeReturnTypeProvider implements \Psalm\Plugin\EventHandler\FunctionRe
|
|||||||
? new Type\Atomic\TList($inner_type)
|
? new Type\Atomic\TList($inner_type)
|
||||||
: new Type\Atomic\TNonEmptyList($inner_type)
|
: new Type\Atomic\TNonEmptyList($inner_type)
|
||||||
]);
|
]);
|
||||||
} elseif (($first_arg_type = $statements_source->node_data->getType($call_args[0]->value))
|
}
|
||||||
&& $first_arg_type->hasString()
|
|
||||||
) {
|
if (($first_arg_type = $statements_source->node_data->getType($call_args[0]->value))
|
||||||
|
&& $first_arg_type->hasString()) {
|
||||||
$can_be_false = true;
|
$can_be_false = true;
|
||||||
if ($first_arg_type->isString()) {
|
if ($first_arg_type->isString()) {
|
||||||
$can_be_false = false;
|
$can_be_false = false;
|
||||||
|
@ -42,7 +42,9 @@ class GetObjectVarsReturnTypeProvider implements FunctionReturnTypeProviderInter
|
|||||||
return new Type\Union([
|
return new Type\Union([
|
||||||
new Type\Atomic\TKeyedArray($object_type->properties)
|
new Type\Atomic\TKeyedArray($object_type->properties)
|
||||||
]);
|
]);
|
||||||
} elseif ($object_type instanceof Type\Atomic\TNamedObject) {
|
}
|
||||||
|
|
||||||
|
if ($object_type instanceof Type\Atomic\TNamedObject) {
|
||||||
if (strtolower($object_type->value) === strtolower(stdClass::class)) {
|
if (strtolower($object_type->value) === strtolower(stdClass::class)) {
|
||||||
return Type::parseString('array<string, mixed>');
|
return Type::parseString('array<string, mixed>');
|
||||||
}
|
}
|
||||||
|
@ -1335,7 +1335,9 @@ class AssertionReconciler extends \Psalm\Type\Reconciler
|
|||||||
|
|
||||||
if ($existing_has_object && !$existing_has_string) {
|
if ($existing_has_object && !$existing_has_string) {
|
||||||
return Type::parseString($assertion, null, $template_type_map);
|
return Type::parseString($assertion, null, $template_type_map);
|
||||||
} elseif ($existing_has_string && !$existing_has_object) {
|
}
|
||||||
|
|
||||||
|
if ($existing_has_string && !$existing_has_object) {
|
||||||
if (!$allow_string_comparison && $code_location) {
|
if (!$allow_string_comparison && $code_location) {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new TypeDoesNotContainType(
|
new TypeDoesNotContainType(
|
||||||
|
@ -53,14 +53,16 @@ class IntegerRangeComparator
|
|||||||
if (isset($container_atomic_types['int'])) {
|
if (isset($container_atomic_types['int'])) {
|
||||||
if (get_class($container_atomic_types['int']) === TInt::class) {
|
if (get_class($container_atomic_types['int']) === TInt::class) {
|
||||||
return true;
|
return true;
|
||||||
} elseif (get_class($container_atomic_types['int']) === TPositiveInt::class) {
|
}
|
||||||
|
|
||||||
|
if (get_class($container_atomic_types['int']) === TPositiveInt::class) {
|
||||||
if ($input_type_part->isPositive()) {
|
if ($input_type_part->isPositive()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
//every positive integer is satisfied by the positive-int int container so we reduce the range
|
|
||||||
$reduced_range->max_bound = 0;
|
|
||||||
unset($container_atomic_types['int']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//every positive integer is satisfied by the positive-int int container so we reduce the range
|
||||||
|
$reduced_range->max_bound = 0;
|
||||||
|
unset($container_atomic_types['int']);
|
||||||
} else {
|
} else {
|
||||||
throw new \UnexpectedValueException('Should not happen: unknown int key');
|
throw new \UnexpectedValueException('Should not happen: unknown int key');
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ class ObjectComparator
|
|||||||
->getStorageFor($intersection_input_type->defining_class);
|
->getStorageFor($intersection_input_type->defining_class);
|
||||||
|
|
||||||
if ($codebase->classlikes->traitExists($container_class)
|
if ($codebase->classlikes->traitExists($container_class)
|
||||||
&& !\is_null($input_class_like)
|
&& $input_class_like !== null
|
||||||
&& isset(
|
&& isset(
|
||||||
$input_class_like->template_extended_params[$container_class][$container_param]
|
$input_class_like->template_extended_params[$container_class][$container_param]
|
||||||
)) {
|
)) {
|
||||||
|
@ -153,9 +153,13 @@ class NegatedAssertionReconciler extends Reconciler
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Type::getNull();
|
return Type::getNull();
|
||||||
} elseif ($assertion === 'array-key-exists') {
|
}
|
||||||
|
|
||||||
|
if ($assertion === 'array-key-exists') {
|
||||||
return Type::getEmpty();
|
return Type::getEmpty();
|
||||||
} elseif (substr($assertion, 0, 9) === 'in-array-') {
|
}
|
||||||
|
|
||||||
|
if (substr($assertion, 0, 9) === 'in-array-') {
|
||||||
$assertion = substr($assertion, 9);
|
$assertion = substr($assertion, 9);
|
||||||
$new_var_type = null;
|
$new_var_type = null;
|
||||||
try {
|
try {
|
||||||
@ -189,7 +193,9 @@ class NegatedAssertionReconciler extends Reconciler
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $existing_var_type;
|
return $existing_var_type;
|
||||||
} elseif (substr($assertion, 0, 14) === 'has-array-key-') {
|
}
|
||||||
|
|
||||||
|
if (substr($assertion, 0, 14) === 'has-array-key-') {
|
||||||
return $existing_var_type;
|
return $existing_var_type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -435,6 +435,7 @@ class TypeParser
|
|||||||
*/
|
*/
|
||||||
public static function getComputedIntsFromMask(array $potential_ints) : array
|
public static function getComputedIntsFromMask(array $potential_ints) : array
|
||||||
{
|
{
|
||||||
|
/** @var list<int> */
|
||||||
$potential_values = [];
|
$potential_values = [];
|
||||||
|
|
||||||
foreach ($potential_ints as $ith) {
|
foreach ($potential_ints as $ith) {
|
||||||
@ -443,8 +444,8 @@ class TypeParser
|
|||||||
$new_values[] = $ith;
|
$new_values[] = $ith;
|
||||||
|
|
||||||
if ($ith !== 0) {
|
if ($ith !== 0) {
|
||||||
for ($j = 0; $j < count($potential_values); $j++) {
|
foreach ($potential_values as $potential_value) {
|
||||||
$new_values[] = $ith | $potential_values[$j];
|
$new_values[] = $ith | $potential_value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,9 @@ class CompactReport extends Report
|
|||||||
foreach ($this->issues_data as $i => $issue_data) {
|
foreach ($this->issues_data as $i => $issue_data) {
|
||||||
if (!$this->show_info && $issue_data->severity === Config::REPORT_INFO) {
|
if (!$this->show_info && $issue_data->severity === Config::REPORT_INFO) {
|
||||||
continue;
|
continue;
|
||||||
} elseif ($current_file === null || $current_file !== $issue_data->file_name) {
|
}
|
||||||
|
|
||||||
|
if ($current_file === null || $current_file !== $issue_data->file_name) {
|
||||||
// If we're processing a new file, then wrap up the last table and render it out.
|
// If we're processing a new file, then wrap up the last table and render it out.
|
||||||
if ($buffer !== null) {
|
if ($buffer !== null) {
|
||||||
$table->render();
|
$table->render();
|
||||||
|
@ -546,8 +546,10 @@ abstract class Type
|
|||||||
Codebase $codebase
|
Codebase $codebase
|
||||||
): ?Union {
|
): ?Union {
|
||||||
$intersection_performed = false;
|
$intersection_performed = false;
|
||||||
|
$type_1_mixed = $type_1->isMixed();
|
||||||
|
$type_2_mixed = $type_2->isMixed();
|
||||||
|
|
||||||
if ($type_1->isMixed() && $type_2->isMixed()) {
|
if ($type_1_mixed && $type_2_mixed) {
|
||||||
$combined_type = Type::getMixed();
|
$combined_type = Type::getMixed();
|
||||||
} else {
|
} else {
|
||||||
$both_failed_reconciliation = false;
|
$both_failed_reconciliation = false;
|
||||||
@ -562,10 +564,10 @@ abstract class Type
|
|||||||
return $type_1;
|
return $type_1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($type_1->isMixed() && !$type_2->isMixed()) {
|
if ($type_1_mixed && !$type_2_mixed) {
|
||||||
$combined_type = clone $type_2;
|
$combined_type = clone $type_2;
|
||||||
$intersection_performed = true;
|
$intersection_performed = true;
|
||||||
} elseif (!$type_1->isMixed() && $type_2->isMixed()) {
|
} elseif ($type_2_mixed) {
|
||||||
$combined_type = clone $type_1;
|
$combined_type = clone $type_1;
|
||||||
$intersection_performed = true;
|
$intersection_performed = true;
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user