1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-17 03:47:04 +01:00
psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php

1831 lines
69 KiB
PHP
Raw Normal View History

<?php
2018-11-06 03:57:36 +01:00
namespace Psalm\Internal\Analyzer;
2016-02-04 15:22:46 +01:00
use PhpParser;
use Psalm\Aliases;
2018-11-06 03:57:36 +01:00
use Psalm\Internal\Analyzer\Statements\Block\DoAnalyzer;
use Psalm\Internal\Analyzer\Statements\Block\ForAnalyzer;
use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
use Psalm\Internal\Analyzer\Statements\Block\IfAnalyzer;
use Psalm\Internal\Analyzer\Statements\Block\SwitchAnalyzer;
use Psalm\Internal\Analyzer\Statements\Block\TryAnalyzer;
use Psalm\Internal\Analyzer\Statements\Block\WhileAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\Assignment\PropertyAssignmentAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\BinaryOpAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ConstFetchAnalyzer;
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
use Psalm\Internal\Analyzer\Statements\ReturnAnalyzer;
use Psalm\Internal\Analyzer\Statements\ThrowAnalyzer;
use Psalm\Codebase;
use Psalm\CodeLocation;
2016-11-02 07:29:00 +01:00
use Psalm\Config;
use Psalm\Context;
2018-11-06 03:57:36 +01:00
use Psalm\DocComment;
use Psalm\Exception\DocblockParseException;
use Psalm\FileManipulation;
use Psalm\Internal\FileManipulation\FileManipulationBuffer;
use Psalm\Issue\ContinueOutsideLoop;
use Psalm\Issue\ForbiddenCode;
use Psalm\Issue\ForbiddenEcho;
use Psalm\Issue\InvalidDocblock;
use Psalm\Issue\InvalidGlobal;
use Psalm\Issue\UnevaluatedCode;
2016-11-06 01:53:39 +01:00
use Psalm\Issue\UnrecognizedStatement;
use Psalm\Issue\UnusedVariable;
2016-11-02 07:29:00 +01:00
use Psalm\IssueBuffer;
use Psalm\StatementsSource;
2016-10-22 19:23:18 +02:00
use Psalm\Type;
use function strtolower;
use function fwrite;
use const STDERR;
use function array_filter;
use function array_map;
use function preg_split;
use function array_diff;
use function is_string;
use function get_class;
use function in_array;
use function strrpos;
use function strlen;
use function substr;
use function array_key_exists;
use function count;
use function array_shift;
use function explode;
use function array_pop;
use function implode;
use function array_change_key_case;
/**
* @internal
*/
2018-11-06 03:57:36 +01:00
class StatementsAnalyzer extends SourceAnalyzer implements StatementsSource
{
/**
2018-11-06 03:57:36 +01:00
* @var SourceAnalyzer
*/
2016-08-11 00:10:12 +02:00
protected $source;
/**
2018-11-06 03:57:36 +01:00
* @var FileAnalyzer
*/
2018-11-11 18:01:14 +01:00
protected $file_analyzer;
2018-11-06 03:57:36 +01:00
/**
* @var Codebase
*/
protected $codebase;
2016-11-01 05:39:41 +01:00
/**
* @var array<string, CodeLocation>
2016-11-01 05:39:41 +01:00
*/
private $all_vars = [];
2016-11-01 05:39:41 +01:00
/**
* @var array<string, int>
*/
private $var_branch_points = [];
/**
* Possibly undefined variables should be initialised if we're altering code
*
* @var array<string, int>|null
*/
private $vars_to_initialize;
/**
2018-11-06 03:57:36 +01:00
* @var array<string, FunctionAnalyzer>
*/
2018-11-11 18:01:14 +01:00
private $function_analyzers = [];
/**
* @var array<string, array{0: string, 1: CodeLocation}>
*/
private $unused_var_locations = [];
2018-06-17 02:01:33 +02:00
/**
* @var array<string, bool>
*/
private $used_var_locations = [];
/**
* @var ?array<string, bool>
*/
private $byref_uses;
/**
* @var array{description:string, specials:array<string, array<int, string>>}|null
*/
private $parsed_docblock = null;
2016-11-02 07:29:00 +01:00
/**
2018-11-06 03:57:36 +01:00
* @param SourceAnalyzer $source
2016-11-02 07:29:00 +01:00
*/
2018-11-06 03:57:36 +01:00
public function __construct(SourceAnalyzer $source)
{
2016-08-11 00:10:12 +02:00
$this->source = $source;
2018-11-11 18:01:14 +01:00
$this->file_analyzer = $source->getFileAnalyzer();
2018-11-06 03:57:36 +01:00
$this->codebase = $source->getCodebase();
}
2016-06-18 20:45:55 +02:00
/**
* Checks an array of statements for validity
*
* @param array<PhpParser\Node\Stmt> $stmts
2016-12-12 19:50:46 +01:00
* @param Context $context
* @param Context|null $global_context
* @param bool $root_scope
2017-05-27 02:16:18 +02:00
*
2016-06-18 20:45:55 +02:00
* @return null|false
*/
public function analyze(
2017-01-07 20:35:07 +01:00
array $stmts,
Context $context,
Context $global_context = null,
$root_scope = false
2017-01-07 20:35:07 +01:00
) {
$has_returned = false;
2016-08-15 06:58:30 +02:00
// hoist functions to the top
foreach ($stmts as $stmt) {
if ($stmt instanceof PhpParser\Node\Stmt\Function_) {
try {
$function_analyzer = new FunctionAnalyzer($stmt, $this->source);
$this->function_analyzers[strtolower($stmt->name->name)] = $function_analyzer;
} catch (\UnexpectedValueException $e) {
// do nothing
}
2016-08-15 06:58:30 +02:00
}
}
2018-11-11 18:01:14 +01:00
$project_analyzer = $this->getFileAnalyzer()->project_analyzer;
$codebase = $project_analyzer->getCodebase();
if ($codebase->config->hoist_constants) {
foreach ($stmts as $stmt) {
if ($stmt instanceof PhpParser\Node\Stmt\Const_) {
foreach ($stmt->consts as $const) {
$this->setConstType(
$const->name->name,
2018-06-28 03:53:25 +02:00
self::getSimpleType($codebase, $const->value, $this->getAliases(), $this)
?: Type::getMixed(),
$context
);
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\Expression
&& $stmt->expr instanceof PhpParser\Node\Expr\FuncCall
&& $stmt->expr->name instanceof PhpParser\Node\Name
&& $stmt->expr->name->parts === ['define']
&& isset($stmt->expr->args[1])
) {
$const_name = static::getConstName($stmt->expr->args[0]->value, $codebase, $this->getAliases());
2019-02-18 19:17:08 +01:00
if ($const_name !== null) {
$this->setConstType(
$const_name,
self::getSimpleType($codebase, $stmt->expr->args[1]->value, $this->getAliases(), $this)
?: Type::getMixed(),
$context
);
}
}
}
}
2017-12-03 00:28:18 +01:00
$original_context = null;
if ($context->loop_scope) {
$original_context = clone $context->loop_scope->loop_parent_context;
2017-12-03 00:28:18 +01:00
}
$plugin_classes = $codebase->config->after_statement_checks;
foreach ($stmts as $stmt) {
$ignore_variable_property = false;
$ignore_variable_method = false;
2016-11-02 07:29:00 +01:00
if ($has_returned && !($stmt instanceof PhpParser\Node\Stmt\Nop) &&
2017-01-07 20:35:07 +01:00
!($stmt instanceof PhpParser\Node\Stmt\InlineHTML)
) {
if ($context->collect_references) {
if (IssueBuffer::accepts(
new UnevaluatedCode(
'Expressions after return/throw/continue',
new CodeLocation($this->source, $stmt)
),
$this->source->getSuppressedIssues()
)) {
return false;
}
}
break;
}
2018-11-11 18:01:14 +01:00
if ($project_analyzer->debug_lines) {
fwrite(STDERR, $this->getFilePath() . ':' . $stmt->getLine() . "\n");
2018-03-26 15:08:55 +02:00
}
2017-01-26 01:09:31 +01:00
/*
if (isset($context->vars_in_scope['$array']) && !$stmt instanceof PhpParser\Node\Stmt\Nop) {
var_dump($stmt->getLine(), $context->vars_in_scope['$array']);
}
2017-01-26 01:09:31 +01:00
*/
$new_issues = null;
if ($docblock = $stmt->getDocComment()) {
try {
$this->parsed_docblock = DocComment::parsePreservingLength($docblock);
} catch (DocblockParseException $e) {
if (IssueBuffer::accepts(
new InvalidDocblock(
(string)$e->getMessage(),
new CodeLocation($this->getSource(), $stmt, null, true)
)
)) {
// fall through
}
$this->parsed_docblock = null;
}
$comments = $this->parsed_docblock;
if (isset($comments['specials']['psalm-suppress'])) {
$suppressed = array_filter(
array_map(
/**
* @param string $line
*
* @return string
*/
function ($line) {
2019-06-03 21:02:28 +02:00
return preg_split('/[\s]+/', $line)[0];
},
$comments['specials']['psalm-suppress']
)
);
if ($suppressed) {
$new_issues = array_diff($suppressed, $this->source->getSuppressedIssues());
/** @psalm-suppress MixedTypeCoercion */
$this->addSuppressedIssues($new_issues);
}
}
if (isset($comments['specials']['psalm-ignore-variable-method'])) {
$context->ignore_variable_method = $ignore_variable_method = true;
}
if (isset($comments['specials']['psalm-ignore-variable-property'])) {
$context->ignore_variable_property = $ignore_variable_property = true;
}
} else {
$this->parsed_docblock = null;
}
if ($stmt instanceof PhpParser\Node\Stmt\If_) {
2018-11-06 03:57:36 +01:00
if (IfAnalyzer::analyze($this, $stmt, $context) === false) {
return false;
}
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\TryCatch) {
2018-11-06 03:57:36 +01:00
if (TryAnalyzer::analyze($this, $stmt, $context) === false) {
return false;
}
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\For_) {
2018-11-06 03:57:36 +01:00
if (ForAnalyzer::analyze($this, $stmt, $context) === false) {
return false;
}
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Foreach_) {
2018-11-06 03:57:36 +01:00
if (ForeachAnalyzer::analyze($this, $stmt, $context) === false) {
return false;
}
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\While_) {
2018-11-06 03:57:36 +01:00
if (WhileAnalyzer::analyze($this, $stmt, $context) === false) {
return false;
}
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Do_) {
2018-11-06 03:57:36 +01:00
DoAnalyzer::analyze($this, $stmt, $context);
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Const_) {
$this->analyzeConstAssignment($stmt, $context);
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Unset_) {
2018-12-20 22:03:21 +01:00
$this->analyzeUnset($stmt, $context);
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Return_) {
$has_returned = true;
2018-11-06 03:57:36 +01:00
ReturnAnalyzer::analyze($this, $stmt, $context);
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Throw_) {
$has_returned = true;
2018-11-06 03:57:36 +01:00
ThrowAnalyzer::analyze($this, $stmt, $context);
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Switch_) {
2018-11-06 03:57:36 +01:00
SwitchAnalyzer::analyze($this, $stmt, $context);
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Break_) {
$loop_scope = $context->loop_scope;
2017-12-03 00:28:18 +01:00
if ($loop_scope && $original_context) {
if ($context->inside_case && !$stmt->num) {
2018-11-06 03:57:36 +01:00
$loop_scope->final_actions[] = ScopeAnalyzer::ACTION_LEAVE_SWITCH;
} else {
2018-11-06 03:57:36 +01:00
$loop_scope->final_actions[] = ScopeAnalyzer::ACTION_BREAK;
}
2017-12-03 00:28:18 +01:00
$redefined_vars = $context->getRedefinedVars($loop_scope->loop_parent_context->vars_in_scope);
if ($loop_scope->possibly_redefined_loop_parent_vars === null) {
$loop_scope->possibly_redefined_loop_parent_vars = $redefined_vars;
} else {
foreach ($redefined_vars as $var => $type) {
if ($type->hasMixed()) {
2017-12-03 00:28:18 +01:00
$loop_scope->possibly_redefined_loop_parent_vars[$var] = $type;
} elseif (isset($loop_scope->possibly_redefined_loop_parent_vars[$var])) {
$loop_scope->possibly_redefined_loop_parent_vars[$var] = Type::combineUnionTypes(
$type,
$loop_scope->possibly_redefined_loop_parent_vars[$var]
);
} else {
$loop_scope->possibly_redefined_loop_parent_vars[$var] = $type;
}
}
}
2018-06-17 02:01:33 +02:00
2018-11-10 20:06:31 +01:00
if ($loop_scope->iteration_count === 0) {
foreach ($context->vars_in_scope as $var_id => $type) {
if (!isset($loop_scope->loop_parent_context->vars_in_scope[$var_id])) {
if (isset($loop_scope->possibly_defined_loop_parent_vars[$var_id])) {
$loop_scope->possibly_defined_loop_parent_vars[$var_id] = Type::combineUnionTypes(
$type,
$loop_scope->possibly_defined_loop_parent_vars[$var_id]
);
} else {
$loop_scope->possibly_defined_loop_parent_vars[$var_id] = $type;
}
}
}
}
if ($context->collect_references && (!$context->case_scope || $stmt->num)) {
2018-06-17 02:01:33 +02:00
foreach ($context->unreferenced_vars as $var_id => $locations) {
if (isset($loop_scope->unreferenced_vars[$var_id])) {
$loop_scope->unreferenced_vars[$var_id] += $locations;
} else {
$loop_scope->unreferenced_vars[$var_id] = $locations;
}
}
}
2017-12-03 00:28:18 +01:00
}
$case_scope = $context->case_scope;
if ($case_scope) {
foreach ($context->vars_in_scope as $var_id => $type) {
if ($case_scope->parent_context !== $context) {
if ($case_scope->break_vars === null) {
$case_scope->break_vars = [];
}
2019-05-13 05:13:27 +02:00
if (isset($case_scope->break_vars[$var_id])) {
$case_scope->break_vars[$var_id] = Type::combineUnionTypes(
$type,
$case_scope->break_vars[$var_id]
);
} else {
$case_scope->break_vars[$var_id] = $type;
}
}
}
if ($context->collect_references) {
foreach ($context->unreferenced_vars as $var_id => $locations) {
if (isset($case_scope->unreferenced_vars[$var_id])) {
$case_scope->unreferenced_vars[$var_id] += $locations;
} else {
$case_scope->unreferenced_vars[$var_id] = $locations;
}
}
}
}
2017-12-03 00:28:18 +01:00
$has_returned = true;
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Continue_) {
$loop_scope = $context->loop_scope;
2017-12-03 00:28:18 +01:00
if ($loop_scope === null) {
if (!$context->inside_case) {
if (IssueBuffer::accepts(
new ContinueOutsideLoop(
'Continue call outside loop context',
new CodeLocation($this->source, $stmt)
),
$this->source->getSuppressedIssues()
)) {
return false;
}
}
2017-12-03 00:28:18 +01:00
} elseif ($original_context) {
if ($context->inside_case && !$stmt->num) {
2018-11-06 03:57:36 +01:00
$loop_scope->final_actions[] = ScopeAnalyzer::ACTION_LEAVE_SWITCH;
} else {
2018-11-06 03:57:36 +01:00
$loop_scope->final_actions[] = ScopeAnalyzer::ACTION_CONTINUE;
}
2017-12-03 00:28:18 +01:00
$redefined_vars = $context->getRedefinedVars($original_context->vars_in_scope);
if ($loop_scope->redefined_loop_vars === null) {
$loop_scope->redefined_loop_vars = $redefined_vars;
} else {
foreach ($loop_scope->redefined_loop_vars as $redefined_var => $type) {
if (!isset($redefined_vars[$redefined_var])) {
unset($loop_scope->redefined_loop_vars[$redefined_var]);
} else {
$loop_scope->redefined_loop_vars[$redefined_var] = Type::combineUnionTypes(
$redefined_vars[$redefined_var],
$type
);
}
}
}
foreach ($redefined_vars as $var => $type) {
if ($type->hasMixed()) {
$loop_scope->possibly_redefined_loop_vars[$var] = $type;
} elseif (isset($loop_scope->possibly_redefined_loop_vars[$var])) {
$loop_scope->possibly_redefined_loop_vars[$var] = Type::combineUnionTypes(
$type,
$loop_scope->possibly_redefined_loop_vars[$var]
);
} else {
$loop_scope->possibly_redefined_loop_vars[$var] = $type;
2017-12-03 00:28:18 +01:00
}
}
2018-06-17 02:01:33 +02:00
if ($context->collect_references && (!$context->case_scope || $stmt->num)) {
2018-06-17 02:01:33 +02:00
foreach ($context->unreferenced_vars as $var_id => $locations) {
if (isset($loop_scope->possibly_unreferenced_vars[$var_id])) {
$loop_scope->possibly_unreferenced_vars[$var_id] += $locations;
} else {
$loop_scope->possibly_unreferenced_vars[$var_id] = $locations;
}
}
}
}
$case_scope = $context->case_scope;
if ($case_scope && $context->collect_references) {
foreach ($context->unreferenced_vars as $var_id => $locations) {
if (isset($case_scope->unreferenced_vars[$var_id])) {
$case_scope->unreferenced_vars[$var_id] += $locations;
} else {
$case_scope->unreferenced_vars[$var_id] = $locations;
}
}
}
$has_returned = true;
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Static_) {
$this->analyzeStatic($stmt, $context);
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Echo_) {
foreach ($stmt->exprs as $i => $expr) {
2018-11-06 03:57:36 +01:00
ExpressionAnalyzer::analyze($this, $expr, $context);
if (isset($expr->inferredType)) {
2018-11-06 03:57:36 +01:00
if (CallAnalyzer::checkFunctionArgumentType(
$this,
$expr->inferredType,
Type::getString(),
null,
'echo',
2016-12-09 19:27:59 +01:00
(int)$i,
new CodeLocation($this->getSource(), $expr),
$expr,
$context
) === false) {
return false;
}
}
}
if ($codebase->config->forbid_echo) {
if (IssueBuffer::accepts(
new ForbiddenEcho(
'Use of echo',
new CodeLocation($this->source, $stmt)
),
$this->source->getSuppressedIssues()
)) {
return false;
}
} elseif (isset($codebase->config->forbidden_functions['echo'])) {
if (IssueBuffer::accepts(
new ForbiddenCode(
'Use of echo',
new CodeLocation($this->source, $stmt)
),
$this->source->getSuppressedIssues()
)) {
2019-03-23 15:58:26 +01:00
// continue
}
}
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Function_) {
foreach ($stmt->stmts as $function_stmt) {
if ($function_stmt instanceof PhpParser\Node\Stmt\Global_) {
foreach ($function_stmt->vars as $var) {
if ($var instanceof PhpParser\Node\Expr\Variable) {
if (is_string($var->name)) {
$var_id = '$' . $var->name;
// registers variable in global context
$context->hasVariable($var_id, $this);
}
}
}
} elseif (!$function_stmt instanceof PhpParser\Node\Stmt\Nop) {
break;
}
}
2018-11-06 03:57:36 +01:00
if (!$codebase->register_stub_files
&& !$codebase->register_autoload_files
) {
$function_id = strtolower($stmt->name->name);
$function_context = new Context($context->self);
$config = Config::getInstance();
2018-11-06 03:57:36 +01:00
$function_context->collect_references = $codebase->collect_references;
$function_context->collect_exceptions = $config->check_for_throws_docblock;
if (isset($this->function_analyzers[$function_id])) {
$this->function_analyzers[$function_id]->analyze($function_context, $context);
if ($config->reportIssueInFile('InvalidReturnType', $this->getFilePath())) {
$method_id = $this->function_analyzers[$function_id]->getMethodId();
$function_storage = $codebase->functions->getStorage(
$this,
$method_id
);
$return_type = $function_storage->return_type;
$return_type_location = $function_storage->return_type_location;
$this->function_analyzers[$function_id]->verifyReturnType(
$this,
$return_type,
$this->getFQCLN(),
$return_type_location
);
}
}
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\Expression) {
2018-11-06 03:57:36 +01:00
if (ExpressionAnalyzer::analyze($this, $stmt->expr, $context, false, $global_context) === false) {
return false;
}
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\InlineHTML) {
// do nothing
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Global_) {
if (!$context->collect_initializations && !$global_context) {
if (IssueBuffer::accepts(
new InvalidGlobal(
'Cannot use global scope here',
new CodeLocation($this->source, $stmt)
),
2017-01-07 20:35:07 +01:00
$this->source->getSuppressedIssues()
)) {
// fall through
}
}
$source = $this->getSource();
2018-11-06 03:57:36 +01:00
$function_storage = $source instanceof FunctionLikeAnalyzer
? $source->getFunctionLikeStorage($this)
: null;
foreach ($stmt->vars as $var) {
if ($var instanceof PhpParser\Node\Expr\Variable) {
if (is_string($var->name)) {
$var_id = '$' . $var->name;
2018-05-31 04:56:46 +02:00
if ($var->name === 'argv' || $var->name === 'argc') {
2019-03-14 01:15:29 +01:00
$context->vars_in_scope[$var_id] = $this->getGlobalType($var_id);
} elseif (isset($function_storage->global_types[$var_id])) {
$context->vars_in_scope[$var_id] = clone $function_storage->global_types[$var_id];
$context->vars_possibly_in_scope[$var_id] = true;
2018-05-31 04:56:46 +02:00
} else {
$context->vars_in_scope[$var_id] =
$global_context && $global_context->hasVariable($var_id, $this)
? clone $global_context->vars_in_scope[$var_id]
2019-03-14 01:15:29 +01:00
: $this->getGlobalType($var_id);
2018-05-31 04:56:46 +02:00
$context->vars_possibly_in_scope[$var_id] = true;
}
}
}
}
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Property) {
2016-01-26 20:11:56 +01:00
foreach ($stmt->props as $prop) {
if ($prop->default) {
2018-11-06 03:57:36 +01:00
ExpressionAnalyzer::analyze($this, $prop->default, $context);
2016-06-24 00:45:46 +02:00
if (isset($prop->default->inferredType)) {
if (!$stmt->isStatic()) {
2018-11-06 03:57:36 +01:00
if (PropertyAssignmentAnalyzer::analyzeInstance(
2016-11-02 07:29:00 +01:00
$this,
$prop,
$prop->name->name,
2016-12-07 00:27:22 +01:00
$prop->default,
2016-11-02 07:29:00 +01:00
$prop->default->inferredType,
$context
) === false) {
// fall through
}
2016-06-24 00:45:46 +02:00
}
}
2016-01-26 20:11:56 +01:00
}
}
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\ClassConst) {
$const_visibility = \ReflectionProperty::IS_PUBLIC;
if ($stmt->isProtected()) {
$const_visibility = \ReflectionProperty::IS_PROTECTED;
}
if ($stmt->isPrivate()) {
$const_visibility = \ReflectionProperty::IS_PRIVATE;
}
foreach ($stmt->consts as $const) {
2018-11-06 03:57:36 +01:00
ExpressionAnalyzer::analyze($this, $const->value, $context);
if (isset($const->value->inferredType) && !$const->value->inferredType->hasMixed()) {
$codebase->classlikes->setConstantType(
2017-01-07 20:35:07 +01:00
(string)$this->getFQCLN(),
$const->name->name,
$const->value->inferredType,
$const_visibility
2016-11-02 07:29:00 +01:00
);
}
}
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Class_) {
try {
2018-11-11 18:01:14 +01:00
$class_analyzer = new ClassAnalyzer($stmt, $this->source, $stmt->name ? $stmt->name->name : null);
$class_analyzer->analyze(null, $global_context);
} catch (\InvalidArgumentException $e) {
// disregard this exception, we'll likely see it elsewhere in the form
// of an issue
}
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Nop) {
if (($doc_comment = $stmt->getDocComment()) && $this->parsed_docblock) {
$var_comments = [];
2018-01-24 19:52:34 +01:00
try {
$var_comments = CommentAnalyzer::arrayToDocblocks(
$doc_comment,
$this->parsed_docblock,
$this->getSource(),
$this->getSource()->getAliases(),
$this->getSource()->getTemplateTypeMap()
);
} catch (DocblockParseException $e) {
if (IssueBuffer::accepts(
new InvalidDocblock(
(string)$e->getMessage(),
new CodeLocation($this->getSource(), $stmt, null, true)
)
)) {
// fall through
}
}
foreach ($var_comments as $var_comment) {
if (!$var_comment->var_id) {
continue;
}
2018-11-06 03:57:36 +01:00
$comment_type = ExpressionAnalyzer::fleshOutType(
$codebase,
$var_comment->type,
$context->self,
$context->self,
$this->getParentFQCLN()
);
$context->vars_in_scope[$var_comment->var_id] = $comment_type;
}
2017-03-02 04:27:52 +01:00
}
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Goto_) {
2016-10-22 19:23:18 +02:00
// do nothing
2016-11-02 07:29:00 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Label) {
2016-10-22 19:23:18 +02:00
// do nothing
2016-11-20 08:52:34 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\Declare_) {
foreach ($stmt->declares as $declaration) {
if ((string) $declaration->key === 'strict_types'
&& $declaration->value instanceof PhpParser\Node\Scalar\LNumber
&& $declaration->value->value === 1
) {
$context->strict_types = true;
}
}
2019-01-06 15:18:41 +01:00
} elseif ($stmt instanceof PhpParser\Node\Stmt\HaltCompiler) {
$has_returned = true;
2016-11-02 07:29:00 +01:00
} else {
2016-11-06 01:53:39 +01:00
if (IssueBuffer::accepts(
new UnrecognizedStatement(
'Psalm does not understand ' . get_class($stmt),
new CodeLocation($this->source, $stmt)
2016-11-06 01:53:39 +01:00
),
$this->getSuppressedIssues()
)) {
return false;
}
}
if ($context->loop_scope
&& $context->loop_scope->final_actions
2018-11-06 03:57:36 +01:00
&& !in_array(ScopeAnalyzer::ACTION_NONE, $context->loop_scope->final_actions, true)
2017-12-03 00:28:18 +01:00
) {
//$has_returned = true;
}
if ($plugin_classes) {
$file_manipulations = [];
foreach ($plugin_classes as $plugin_fq_class_name) {
2018-11-06 03:57:36 +01:00
if ($plugin_fq_class_name::afterStatementAnalysis(
$stmt,
$context,
2018-11-06 03:57:36 +01:00
$this->getSource(),
$codebase,
$file_manipulations
) === false) {
return false;
}
}
if ($file_manipulations) {
/** @psalm-suppress MixedTypeCoercion */
FileManipulationBuffer::add($this->getFilePath(), $file_manipulations);
}
}
if ($new_issues) {
/** @psalm-suppress MixedTypeCoercion */
$this->removeSuppressedIssues($new_issues);
}
if ($ignore_variable_property) {
$context->ignore_variable_property = false;
}
if ($ignore_variable_method) {
$context->ignore_variable_method = false;
}
}
2016-11-02 07:29:00 +01:00
if ($root_scope
&& $context->collect_references
&& !$context->collect_initializations
&& $codebase->find_unused_variables
&& $context->check_variables
) {
$this->checkUnreferencedVars();
}
2018-11-06 03:57:36 +01:00
if ($codebase->alter_code && $root_scope && $this->vars_to_initialize) {
$file_contents = $codebase->getFileContents($this->getFilePath());
foreach ($this->vars_to_initialize as $var_id => $branch_point) {
$newline_pos = (int)strrpos($file_contents, "\n", $branch_point - strlen($file_contents)) + 1;
$indentation = substr($file_contents, $newline_pos, $branch_point - $newline_pos);
FileManipulationBuffer::add($this->getFilePath(), [
new FileManipulation($branch_point, $branch_point, $var_id . ' = null;' . "\n" . $indentation),
]);
}
}
2016-11-02 07:29:00 +01:00
return null;
}
/**
* @return void
*/
public function checkUnreferencedVars()
{
$source = $this->getSource();
2018-11-06 03:57:36 +01:00
$function_storage = $source instanceof FunctionLikeAnalyzer ? $source->getFunctionLikeStorage($this) : null;
2018-06-17 02:01:33 +02:00
foreach ($this->unused_var_locations as $hash => list($var_id, $original_location)) {
if ($var_id === '$_' || isset($this->used_var_locations[$hash])) {
continue;
}
if ((!$function_storage
|| !array_key_exists(substr($var_id, 1), $function_storage->param_types))
&& !isset($this->byref_uses[$var_id])
2019-03-20 04:16:00 +01:00
&& !$this->isSuperGlobal($var_id)
) {
if (IssueBuffer::accepts(
new UnusedVariable(
'Variable ' . $var_id . ' is never referenced',
$original_location
),
$this->getSuppressedIssues()
)) {
// fall through
}
}
}
}
2018-12-20 22:03:21 +01:00
/**
* @return void
*/
private function analyzeUnset(PhpParser\Node\Stmt\Unset_ $stmt, Context $context)
{
$context->inside_unset = true;
foreach ($stmt->vars as $var) {
ExpressionAnalyzer::analyze($this, $var, $context);
$var_id = ExpressionAnalyzer::getArrayVarId(
$var,
$this->getFQCLN(),
$this
);
if ($var_id) {
$context->remove($var_id);
}
if ($var instanceof PhpParser\Node\Expr\ArrayDimFetch && $var->dim) {
$root_var_id = ExpressionAnalyzer::getArrayVarId(
$var->var,
$this->getFQCLN(),
$this
);
if ($root_var_id && isset($context->vars_in_scope[$root_var_id])) {
$root_type = clone $context->vars_in_scope[$root_var_id];
foreach ($root_type->getTypes() as $atomic_root_type) {
if ($atomic_root_type instanceof Type\Atomic\ObjectLike) {
if ($var->dim instanceof PhpParser\Node\Scalar\String_
|| $var->dim instanceof PhpParser\Node\Scalar\LNumber
) {
if (isset($atomic_root_type->properties[$var->dim->value])) {
unset($atomic_root_type->properties[$var->dim->value]);
}
if (!$atomic_root_type->properties) {
if ($atomic_root_type->had_mixed_value) {
$root_type->addType(
new Type\Atomic\TArray([
new Type\Union([new Type\Atomic\TArrayKey]),
new Type\Union([new Type\Atomic\TMixed]),
])
);
} else {
$root_type->addType(
new Type\Atomic\TArray([
new Type\Union([new Type\Atomic\TEmpty]),
new Type\Union([new Type\Atomic\TEmpty]),
])
);
}
2018-12-20 22:03:21 +01:00
}
} else {
2019-05-13 07:35:29 +02:00
$atomic_root_type->sealed = false;
2018-12-20 22:03:21 +01:00
$root_type->addType(
$atomic_root_type->getGenericArrayType()
);
}
} elseif ($atomic_root_type instanceof Type\Atomic\TNonEmptyArray) {
$root_type->addType(
new Type\Atomic\TArray($atomic_root_type->type_params)
);
} elseif ($atomic_root_type instanceof Type\Atomic\TNonEmptyMixed) {
$root_type->addType(
new Type\Atomic\TMixed()
);
2018-12-20 22:03:21 +01:00
}
}
$context->vars_in_scope[$root_var_id] = $root_type;
$context->removeVarFromConflictingClauses(
$root_var_id,
$context->vars_in_scope[$root_var_id],
$this
);
}
}
}
$context->inside_unset = false;
}
/**
2016-11-02 07:29:00 +01:00
* @param PhpParser\Node\Stmt\Static_ $stmt
* @param Context $context
2017-05-27 02:16:18 +02:00
*
2016-11-02 07:29:00 +01:00
* @return false|null
*/
private function analyzeStatic(PhpParser\Node\Stmt\Static_ $stmt, Context $context)
{
$codebase = $this->getCodebase();
2016-10-22 19:23:18 +02:00
foreach ($stmt->vars as $var) {
if (!is_string($var->var->name)) {
continue;
}
$var_id = '$' . $var->var->name;
$doc_comment = $stmt->getDocComment();
$comment_type = null;
if ($doc_comment && $this->parsed_docblock) {
$var_comments = [];
try {
$var_comments = CommentAnalyzer::arrayToDocblocks(
$doc_comment,
$this->parsed_docblock,
$this->getSource(),
$this->getSource()->getAliases(),
$this->getSource()->getTemplateTypeMap()
);
} catch (\Psalm\Exception\IncorrectDocblockException $e) {
if (IssueBuffer::accepts(
new \Psalm\Issue\MissingDocblockType(
(string)$e->getMessage(),
new CodeLocation($this, $var)
)
)) {
// fall through
}
} catch (DocblockParseException $e) {
if (IssueBuffer::accepts(
new InvalidDocblock(
(string)$e->getMessage(),
new CodeLocation($this->getSource(), $var)
)
)) {
// fall through
}
}
foreach ($var_comments as $var_comment) {
try {
$var_comment_type = ExpressionAnalyzer::fleshOutType(
$codebase,
$var_comment->type,
$context->self,
$context->self,
$this->getParentFQCLN()
);
$var_comment_type->setFromDocblock();
$var_comment_type->check(
$this,
new CodeLocation($this->getSource(), $var),
$this->getSuppressedIssues()
);
2019-06-06 20:27:49 +02:00
if ($codebase->alter_code
&& $var_comment->type_start
2019-06-01 18:25:57 +02:00
&& $var_comment->type_end
&& $var_comment->line_number
) {
2019-06-04 22:36:32 +02:00
$type_location = new CodeLocation\DocblockTypeLocation(
$this,
$var_comment->type_start,
$var_comment->type_end,
$var_comment->line_number
);
2019-06-01 18:25:57 +02:00
2019-06-04 22:36:32 +02:00
$codebase->classlikes->handleDocblockTypeInMigration(
$codebase,
$this,
2019-06-01 18:25:57 +02:00
$var_comment_type,
2019-06-04 22:36:32 +02:00
$type_location,
$context->calling_method_id
2019-06-01 18:25:57 +02:00
);
}
if (!$var_comment->var_id || $var_comment->var_id === $var_id) {
$comment_type = $var_comment_type;
continue;
}
$context->vars_in_scope[$var_comment->var_id] = $var_comment_type;
} catch (\UnexpectedValueException $e) {
if (IssueBuffer::accepts(
new InvalidDocblock(
(string)$e->getMessage(),
new CodeLocation($this, $var)
)
)) {
// fall through
}
}
}
if ($comment_type) {
$context->byref_constraints[$var_id] = new \Psalm\Internal\ReferenceConstraint($comment_type);
}
}
2016-10-22 19:23:18 +02:00
if ($var->default) {
2018-11-06 03:57:36 +01:00
if (ExpressionAnalyzer::analyze($this, $var->default, $context) === false) {
2016-10-22 19:23:18 +02:00
return false;
}
if ($comment_type
&& isset($var->default->inferredType)
&& !TypeAnalyzer::isContainedBy(
$codebase,
$var->default->inferredType,
$comment_type
)
) {
if (IssueBuffer::accepts(
new \Psalm\Issue\ReferenceConstraintViolation(
$var_id . ' of type ' . $comment_type->getId() . ' cannot be assigned type '
. $var->default->inferredType->getId(),
new CodeLocation($this, $var)
)
)) {
// fall through
}
}
}
if ($context->check_variables) {
$context->vars_in_scope[$var_id] = $comment_type ? clone $comment_type : Type::getMixed();
$context->vars_possibly_in_scope[$var_id] = true;
$context->assigned_var_ids[$var_id] = true;
2019-03-20 04:26:46 +01:00
$this->byref_uses[$var_id] = true;
$location = new CodeLocation($this, $var);
if ($context->collect_references) {
2018-06-17 02:01:33 +02:00
$context->unreferenced_vars[$var_id] = [$location->getHash() => $location];
}
$this->registerVariable(
$var_id,
$location,
$context->branch_point
);
2016-10-22 19:23:18 +02:00
}
}
2016-11-02 07:29:00 +01:00
return null;
2016-10-22 19:23:18 +02:00
}
2016-10-22 19:23:18 +02:00
/**
2016-11-02 07:29:00 +01:00
* @param PhpParser\Node\Expr $stmt
2018-06-28 03:53:25 +02:00
* @param ?array<string, Type\Union> $existing_class_constants
* @param string $fq_classlike_name
2017-05-27 02:16:18 +02:00
*
2016-11-02 07:29:00 +01:00
* @return Type\Union|null
2016-10-22 19:23:18 +02:00
*/
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
public static function getSimpleType(
2018-06-28 03:53:25 +02:00
\Psalm\Codebase $codebase,
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
PhpParser\Node\Expr $stmt,
2018-06-28 03:53:25 +02:00
\Psalm\Aliases $aliases,
\Psalm\FileSource $file_source = null,
array $existing_class_constants = null,
$fq_classlike_name = null
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
) {
if ($stmt instanceof PhpParser\Node\Expr\BinaryOp) {
if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Concat) {
$left = self::getSimpleType(
$codebase,
$stmt->left,
$aliases,
$file_source,
$existing_class_constants,
$fq_classlike_name
);
$right = self::getSimpleType(
$codebase,
$stmt->right,
$aliases,
$file_source,
$existing_class_constants,
$fq_classlike_name
);
if ($left
&& $right
&& $left->isSingleStringLiteral()
&& $right->isSingleStringLiteral()
) {
$result = $left->getSingleStringLiteral()->value . $right->getSingleStringLiteral()->value;
if (strlen($result) < 50) {
return Type::getString($result);
}
}
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
return Type::getString();
}
if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\LogicalAnd
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\LogicalOr
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\Equal
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\NotEqual
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\Identical
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\Greater
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\GreaterOrEqual
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\Smaller
|| $stmt instanceof PhpParser\Node\Expr\BinaryOp\SmallerOrEqual
) {
return Type::getBool();
}
if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Coalesce) {
return null;
}
if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Spaceship) {
return Type::getInt();
}
$stmt->left->inferredType = self::getSimpleType(
2018-06-28 03:53:25 +02:00
$codebase,
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
$stmt->left,
2018-06-28 03:53:25 +02:00
$aliases,
$file_source,
$existing_class_constants,
$fq_classlike_name
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
);
$stmt->right->inferredType = self::getSimpleType(
2018-06-28 03:53:25 +02:00
$codebase,
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
$stmt->right,
2018-06-28 03:53:25 +02:00
$aliases,
$file_source,
$existing_class_constants,
$fq_classlike_name
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
);
if (!$stmt->left->inferredType || !$stmt->right->inferredType) {
return null;
}
if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Plus ||
$stmt instanceof PhpParser\Node\Expr\BinaryOp\Minus ||
$stmt instanceof PhpParser\Node\Expr\BinaryOp\Mod ||
$stmt instanceof PhpParser\Node\Expr\BinaryOp\Mul ||
$stmt instanceof PhpParser\Node\Expr\BinaryOp\Pow
) {
BinaryOpAnalyzer::analyzeNonDivArithmeticOp(
$file_source instanceof StatementsSource ? $file_source : null,
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
$stmt->left,
$stmt->right,
$stmt,
$result_type
);
if ($result_type) {
return $result_type;
}
return null;
}
if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Div
&& ($stmt->left->inferredType->hasInt() || $stmt->left->inferredType->hasFloat())
&& ($stmt->right->inferredType->hasInt() || $stmt->right->inferredType->hasFloat())
) {
return Type::combineUnionTypes(Type::getFloat(), Type::getInt());
}
}
2016-10-22 19:23:18 +02:00
if ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
if (strtolower($stmt->name->parts[0]) === 'false') {
return Type::getFalse();
} elseif (strtolower($stmt->name->parts[0]) === 'true') {
return Type::getTrue();
} elseif (strtolower($stmt->name->parts[0]) === 'null') {
return Type::getNull();
} elseif ($stmt->name->parts[0] === '__NAMESPACE__') {
return Type::getString($aliases->namespace);
}
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
return null;
}
if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Namespace_) {
return Type::getString($aliases->namespace);
}
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
if ($stmt instanceof PhpParser\Node\Expr\ClassConstFetch) {
if ($stmt->class instanceof PhpParser\Node\Name
&& $stmt->name instanceof PhpParser\Node\Identifier
&& $fq_classlike_name
&& $stmt->class->parts !== ['static']
&& $stmt->class->parts !== ['parent']
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
) {
2018-06-28 03:53:25 +02:00
if (isset($existing_class_constants[$stmt->name->name])) {
if ($stmt->class->parts === ['self']) {
return clone $existing_class_constants[$stmt->name->name];
}
}
if ($stmt->class->parts === ['self']) {
2018-06-28 03:53:25 +02:00
$const_fq_class_name = $fq_classlike_name;
} else {
2018-11-06 03:57:36 +01:00
$const_fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
2018-06-28 03:53:25 +02:00
$stmt->class,
$aliases
);
}
if (strtolower($const_fq_class_name) === strtolower($fq_classlike_name)
&& isset($existing_class_constants[$stmt->name->name])
) {
return clone $existing_class_constants[$stmt->name->name];
}
2018-06-28 03:53:25 +02:00
if (strtolower($stmt->name->name) === 'class') {
return Type::getLiteralClassString($const_fq_class_name);
2018-06-28 03:53:25 +02:00
}
2018-06-28 03:53:25 +02:00
if ($existing_class_constants === null) {
try {
$foreign_class_constants = $codebase->classlikes->getConstantsForClass(
$const_fq_class_name,
\ReflectionProperty::IS_PRIVATE
);
if (isset($foreign_class_constants[$stmt->name->name])) {
return clone $foreign_class_constants[$stmt->name->name];
}
return null;
} catch (\InvalidArgumentException $e) {
return null;
}
}
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
if ($stmt->name instanceof PhpParser\Node\Identifier && strtolower($stmt->name->name) === 'class') {
return Type::getClassString();
}
2018-06-28 03:53:25 +02:00
return null;
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
if ($stmt instanceof PhpParser\Node\Scalar\String_) {
return Type::getString(strlen($stmt->value) < 30 ? $stmt->value : null);
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
if ($stmt instanceof PhpParser\Node\Scalar\LNumber) {
return Type::getInt(false, $stmt->value);
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
if ($stmt instanceof PhpParser\Node\Scalar\DNumber) {
return Type::getFloat($stmt->value);
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
if ($stmt instanceof PhpParser\Node\Expr\Array_) {
if (count($stmt->items) === 0) {
return Type::getEmptyArray();
}
2017-12-19 15:48:01 +01:00
$item_key_type = null;
$item_value_type = null;
$property_types = [];
2018-08-21 23:59:06 +02:00
$class_strings = [];
2017-12-19 15:48:01 +01:00
$can_create_objectlike = true;
foreach ($stmt->items as $int_offset => $item) {
2018-01-14 00:33:32 +01:00
if ($item === null) {
continue;
}
$single_item_key_type = null;
2017-12-19 15:48:01 +01:00
if ($item->key) {
$single_item_key_type = self::getSimpleType(
2018-06-28 03:53:25 +02:00
$codebase,
2017-12-19 15:48:01 +01:00
$item->key,
2018-06-28 03:53:25 +02:00
$aliases,
$file_source,
$existing_class_constants,
$fq_classlike_name
2017-12-19 15:48:01 +01:00
);
if ($single_item_key_type) {
if ($item_key_type) {
$item_key_type = Type::combineUnionTypes(
$single_item_key_type,
$item_key_type,
$codebase,
false,
true,
30
);
2017-12-19 15:48:01 +01:00
} else {
$item_key_type = $single_item_key_type;
}
}
} else {
$item_key_type = Type::getInt();
}
2018-06-28 03:53:25 +02:00
if ($item_value_type && !$can_create_objectlike) {
2017-12-19 15:48:01 +01:00
continue;
}
$single_item_value_type = self::getSimpleType(
2018-06-28 03:53:25 +02:00
$codebase,
2017-12-19 15:48:01 +01:00
$item->value,
2018-06-28 03:53:25 +02:00
$aliases,
$file_source,
$existing_class_constants,
$fq_classlike_name
2017-12-19 15:48:01 +01:00
);
2018-06-28 03:53:25 +02:00
if (!$single_item_value_type) {
return null;
}
if ($item->key instanceof PhpParser\Node\Scalar\String_
|| $item->key instanceof PhpParser\Node\Scalar\LNumber
|| !$item->key
) {
if (count($property_types) <= 50) {
$property_types[$item->key ? $item->key->value : $int_offset] = $single_item_value_type;
} else {
$can_create_objectlike = false;
}
2018-06-28 03:53:25 +02:00
} else {
$dim_type = $single_item_key_type;
2018-06-28 03:53:25 +02:00
if (!$dim_type) {
return null;
}
2018-06-28 03:53:25 +02:00
$dim_atomic_types = $dim_type->getTypes();
if (count($dim_atomic_types) > 1 || $dim_type->hasMixed() || count($property_types) > 50) {
2018-06-28 03:53:25 +02:00
$can_create_objectlike = false;
} else {
$atomic_type = array_shift($dim_atomic_types);
if ($atomic_type instanceof Type\Atomic\TLiteralInt
|| $atomic_type instanceof Type\Atomic\TLiteralString
) {
2018-08-21 23:59:06 +02:00
if ($atomic_type instanceof Type\Atomic\TLiteralClassString) {
$class_strings[$atomic_type->value] = true;
}
2018-06-28 03:53:25 +02:00
$property_types[$atomic_type->value] = $single_item_value_type;
} else {
$can_create_objectlike = false;
}
2017-12-19 15:48:01 +01:00
}
2018-06-28 03:53:25 +02:00
}
2017-12-19 15:48:01 +01:00
2018-06-28 03:53:25 +02:00
if ($item_value_type) {
$item_value_type = Type::combineUnionTypes(
$single_item_value_type,
$item_value_type,
$codebase,
false,
true,
30
);
2017-12-19 15:48:01 +01:00
} else {
2018-06-28 03:53:25 +02:00
$item_value_type = $single_item_value_type;
2017-12-19 15:48:01 +01:00
}
}
// if this array looks like an object-like array, let's return that instead
if ($item_value_type
&& $item_key_type
&& ($item_key_type->hasString() || $item_key_type->hasInt())
&& $can_create_objectlike
) {
$objectlike = new Type\Atomic\ObjectLike($property_types, $class_strings);
$objectlike->sealed = true;
return new Type\Union([$objectlike]);
2017-12-19 15:48:01 +01:00
}
2018-06-28 03:53:25 +02:00
if (!$item_key_type || !$item_value_type) {
return null;
}
2017-12-19 15:48:01 +01:00
return new Type\Union([
new Type\Atomic\TNonEmptyArray([
2018-06-28 03:53:25 +02:00
$item_key_type,
$item_value_type,
2017-12-19 15:48:01 +01:00
]),
]);
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
if ($stmt instanceof PhpParser\Node\Expr\Cast\Int_) {
2016-10-22 19:23:18 +02:00
return Type::getInt();
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
if ($stmt instanceof PhpParser\Node\Expr\Cast\Double) {
2016-10-22 19:23:18 +02:00
return Type::getFloat();
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
if ($stmt instanceof PhpParser\Node\Expr\Cast\Bool_) {
2016-10-22 19:23:18 +02:00
return Type::getBool();
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
if ($stmt instanceof PhpParser\Node\Expr\Cast\String_) {
2016-10-22 19:23:18 +02:00
return Type::getString();
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
if ($stmt instanceof PhpParser\Node\Expr\Cast\Object_) {
2016-10-22 19:23:18 +02:00
return Type::getObject();
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
if ($stmt instanceof PhpParser\Node\Expr\Cast\Array_) {
2016-10-22 19:23:18 +02:00
return Type::getArray();
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
}
if ($stmt instanceof PhpParser\Node\Expr\UnaryMinus || $stmt instanceof PhpParser\Node\Expr\UnaryPlus) {
$type_to_invert = self::getSimpleType(
2018-06-28 03:53:25 +02:00
$codebase,
$stmt->expr,
2018-06-28 03:53:25 +02:00
$aliases,
$file_source,
$existing_class_constants,
$fq_classlike_name
);
if (!$type_to_invert) {
return null;
}
foreach ($type_to_invert->getTypes() as $type_part) {
if ($type_part instanceof Type\Atomic\TLiteralInt
&& $stmt instanceof PhpParser\Node\Expr\UnaryMinus
) {
$type_part->value = -$type_part->value;
} elseif ($type_part instanceof Type\Atomic\TLiteralFloat
&& $stmt instanceof PhpParser\Node\Expr\UnaryMinus
) {
$type_part->value = -$type_part->value;
}
}
return $type_to_invert;
2016-06-28 21:28:05 +02:00
}
2016-10-30 17:46:18 +01:00
return null;
2016-10-22 19:23:18 +02:00
}
2016-11-02 07:29:00 +01:00
/**
* @param PhpParser\Node\Stmt\Const_ $stmt
* @param Context $context
2017-05-27 02:16:18 +02:00
*
2016-11-02 07:29:00 +01:00
* @return void
*/
private function analyzeConstAssignment(PhpParser\Node\Stmt\Const_ $stmt, Context $context)
2016-10-22 19:23:18 +02:00
{
foreach ($stmt->consts as $const) {
2018-11-06 03:57:36 +01:00
ExpressionAnalyzer::analyze($this, $const->value, $context);
2016-10-19 03:54:08 +02:00
2016-11-02 07:29:00 +01:00
$this->setConstType(
$const->name->name,
isset($const->value->inferredType) ? $const->value->inferredType : Type::getMixed(),
$context
2016-11-02 07:29:00 +01:00
);
2016-08-11 23:36:22 +02:00
}
2016-10-22 19:23:18 +02:00
}
2016-08-11 23:36:22 +02:00
2016-11-02 07:29:00 +01:00
/**
* @param string $const_name
2016-11-21 03:49:06 +01:00
* @param bool $is_fully_qualified
* @param Context $context
2017-05-27 02:16:18 +02:00
*
2016-11-05 02:14:04 +01:00
* @return Type\Union|null
2016-11-02 07:29:00 +01:00
*/
public function getConstType(
$const_name,
$is_fully_qualified,
Context $context
) {
Refactor scanning and analysis, introducing multithreading (#191) * Add failing test * Add visitor to soup up classlike references * Move a whole bunch of code into the visitor * Move some methods back, move onto analysis stage * Use the getAliases method everywhere * Fix refs * Fix more refs * Fix some tests * Fix more tests * Fix include tests * Shift config class finding to project checker and fix bugs * Fix a few more tests * transition test to new syntax * Remove var_dump * Delete a bunch of code and fix mutation test * Remove unnecessary visitation * Transition to better mocked out file provider, breaking some cached statement loading * Use different scheme for naming anonymous classes * Fix anonymous class issues * Refactor file/statement loading * Add specific property types * Fix mapped property assignment * Improve how we deal with traits * Fix trait checking * Pass Psalm checks * Add multi-process support * Delay console output until the end * Remove PHP 7 syntax * Update file storage with classes * Fix scanning individual files and add reflection return types * Always turn XDebug off * Add quicker method of getting method mutations * Queue return types for crawling * Interpret all strings as possible classes once we see a `get_class` call * Check invalid return types again * Fix template namespacing issues * Default to class-insensitive file names for includes * Don’t overwrite existing issues data * Add var docblocks for scanning * Add null check * Fix loading of external classes in templates * Only try to populate class when we haven’t yet seen it’s not a class * Fix trait property accessibility * Only ever improve docblock param type * Make param replacement more robust * Fix static const missing inferred type * Fix a few more tests * Register constant definitions * Fix trait aliasing * Skip constant type tests for now * Fix linting issues * Make sure caching is off for tests * Remove unnecessary return * Use emulative parser if on PHP 5.6 * Cache parser for faster first-time parse * Fix constant resolution when scanning classes * Remove test that’s beyond a practical scope * Add back --diff support * Add --help for --threads * Remove unused vars
2017-07-25 22:11:02 +02:00
$aliased_constants = $this->getAliases()->constants;
2017-01-07 20:35:07 +01:00
if (isset($aliased_constants[$const_name])) {
$fq_const_name = $aliased_constants[$const_name];
} elseif ($is_fully_qualified) {
2016-11-21 03:49:06 +01:00
$fq_const_name = $const_name;
} else {
$fq_const_name = Type::getFQCLNFromString($const_name, $this->getAliases());
2016-11-21 03:49:06 +01:00
}
if ($fq_const_name) {
$const_name_parts = explode('\\', $fq_const_name);
$const_name = array_pop($const_name_parts);
$namespace_name = implode('\\', $const_name_parts);
2018-11-06 03:57:36 +01:00
$namespace_constants = NamespaceAnalyzer::getConstantsForNamespace(
2017-01-07 20:35:07 +01:00
$namespace_name,
\ReflectionProperty::IS_PUBLIC
);
2016-11-21 03:49:06 +01:00
if (isset($namespace_constants[$const_name])) {
return $namespace_constants[$const_name];
}
}
2019-03-03 22:43:24 +01:00
if ($context->hasVariable($fq_const_name, $this)) {
return $context->vars_in_scope[$fq_const_name];
2016-11-21 03:49:06 +01:00
}
2019-03-03 22:45:35 +01:00
$file_path = $this->getRootFilePath();
$codebase = $this->getCodebase();
2018-11-06 03:57:36 +01:00
$file_storage_provider = $codebase->file_storage_provider;
$file_storage = $file_storage_provider->get($file_path);
if (isset($file_storage->declaring_constants[$const_name])) {
$constant_file_path = $file_storage->declaring_constants[$const_name];
return $file_storage_provider->get($constant_file_path)->constants[$const_name];
}
if (isset($file_storage->declaring_constants[$fq_const_name])) {
$constant_file_path = $file_storage->declaring_constants[$fq_const_name];
return $file_storage_provider->get($constant_file_path)->constants[$fq_const_name];
}
return ConstFetchAnalyzer::getGlobalConstType($codebase, $fq_const_name, $const_name)
?? ConstFetchAnalyzer::getGlobalConstType($codebase, $const_name, $const_name);
2016-10-22 19:23:18 +02:00
}
2016-11-02 07:29:00 +01:00
/**
* @param string $const_name
* @param Type\Union $const_type
* @param Context $context
2017-05-27 02:16:18 +02:00
*
2016-11-02 07:29:00 +01:00
* @return void
*/
public function setConstType($const_name, Type\Union $const_type, Context $context)
2016-10-22 19:23:18 +02:00
{
$context->vars_in_scope[$const_name] = $const_type;
$context->constants[$const_name] = $const_type;
2018-11-06 03:57:36 +01:00
if ($this->source instanceof NamespaceAnalyzer) {
2016-11-21 03:49:06 +01:00
$this->source->setConstType($const_name, $const_type);
}
}
/**
* @param string $var_name
2017-05-27 02:16:18 +02:00
*
* @return bool
*/
public function hasVariable($var_name)
{
return isset($this->all_vars[$var_name]);
}
/**
* @param string $var_id
* @param CodeLocation $location
* @param int|null $branch_point
2017-05-27 02:16:18 +02:00
*
* @return void
*/
public function registerVariable($var_id, CodeLocation $location, $branch_point)
{
$this->all_vars[$var_id] = $location;
if ($branch_point) {
$this->var_branch_points[$var_id] = $branch_point;
}
$this->registerVariableAssignment($var_id, $location);
}
/**
* @param string $var_id
* @param CodeLocation $location
*
* @return void
*/
public function registerVariableAssignment($var_id, CodeLocation $location)
{
2018-06-17 02:01:33 +02:00
$this->unused_var_locations[$location->getHash()] = [$var_id, $location];
}
/**
2018-06-17 02:01:33 +02:00
* @param array<string, CodeLocation> $locations
* @return void
*/
2018-06-17 02:01:33 +02:00
public function registerVariableUses(array $locations)
{
2018-06-17 02:01:33 +02:00
foreach ($locations as $hash => $_) {
unset($this->unused_var_locations[$hash]);
$this->used_var_locations[$hash] = true;
}
}
/**
* @return array<string, array{0: string, 1: CodeLocation}>
*/
public function getUnusedVarLocations()
{
return $this->unused_var_locations;
}
2016-10-22 19:23:18 +02:00
/**
* The first appearance of the variable in this set of statements being evaluated
2016-11-02 07:29:00 +01:00
*
* @param string $var_id
2017-05-27 02:16:18 +02:00
*
* @return CodeLocation|null
2016-10-22 19:23:18 +02:00
*/
public function getFirstAppearance($var_id)
{
return isset($this->all_vars[$var_id]) ? $this->all_vars[$var_id] : null;
}
/**
* @param string $var_id
*
* @return int|null
*/
public function getBranchPoint($var_id)
{
return isset($this->var_branch_points[$var_id]) ? $this->var_branch_points[$var_id] : null;
}
/**
* @param string $var_id
* @param int $branch_point
*
* @return void
*/
public function addVariableInitialization($var_id, $branch_point)
2016-10-02 19:05:49 +02:00
{
$this->vars_to_initialize[$var_id] = $branch_point;
2016-10-02 19:05:49 +02:00
}
2016-10-21 00:16:17 +02:00
2018-11-06 03:57:36 +01:00
public function getFileAnalyzer() : FileAnalyzer
{
2018-11-11 18:01:14 +01:00
return $this->file_analyzer;
}
2018-11-06 03:57:36 +01:00
public function getCodebase() : Codebase
{
return $this->codebase;
}
/**
2018-11-06 03:57:36 +01:00
* @return array<string, FunctionAnalyzer>
*/
2018-11-06 03:57:36 +01:00
public function getFunctionAnalyzers()
{
2018-11-11 18:01:14 +01:00
return $this->function_analyzers;
}
2019-02-18 19:17:08 +01:00
/**
2019-02-18 19:51:27 +01:00
* @param PhpParser\Node\Expr $first_arg_value
2019-02-18 19:17:08 +01:00
*
* @return null|string
*/
public static function getConstName($first_arg_value, Codebase $codebase, Aliases $aliases)
2019-02-18 19:17:08 +01:00
{
$const_name = null;
if ($first_arg_value instanceof PhpParser\Node\Scalar\String_) {
$const_name = $first_arg_value->value;
} elseif (isset($first_arg_value->inferredType)) {
2019-02-19 00:25:07 +01:00
if ($first_arg_value->inferredType->isSingleStringLiteral()) {
$const_name = $first_arg_value->inferredType->getSingleStringLiteral()->value;
2019-02-18 19:17:08 +01:00
}
} else {
$simple_type = self::getSimpleType($codebase, $first_arg_value, $aliases);
if ($simple_type && $simple_type->isSingleStringLiteral()) {
$const_name = $simple_type->getSingleStringLiteral()->value;
2019-02-18 19:17:08 +01:00
}
}
return $const_name;
}
2019-03-06 00:08:41 +01:00
2019-03-14 01:15:29 +01:00
public function isSuperGlobal(string $var_id) : bool
{
return in_array(
$var_id,
[
'$GLOBALS',
'$_SERVER',
'$_GET',
'$_POST',
'$_FILES',
'$_COOKIE',
'$_SESSION',
'$_REQUEST',
'$_ENV',
'$http_response_header'
],
true
);
}
public function getGlobalType(string $var_id) : Type\Union
2019-03-06 00:08:41 +01:00
{
$config = Config::getInstance();
2019-03-14 01:15:29 +01:00
if (isset($config->globals[$var_id])) {
return Type::parseString($config->globals[$var_id]);
2019-03-06 00:08:41 +01:00
}
2019-03-14 01:15:29 +01:00
if ($var_id === '$argv') {
2019-03-06 00:08:41 +01:00
return new Type\Union([
new Type\Atomic\TArray([Type::getInt(), Type::getString()]),
]);
}
2019-03-14 01:15:29 +01:00
if ($var_id === '$argc') {
2019-03-06 00:08:41 +01:00
return Type::getInt();
}
2019-03-14 01:15:29 +01:00
if ($this->isSuperGlobal($var_id)) {
return Type::getArray();
}
return Type::getMixed();
2019-03-06 00:08:41 +01:00
}
/**
* @param array<string, bool> $byref_uses
* @return void
*/
public function setByRefUses(array $byref_uses)
{
$this->byref_uses = $byref_uses;
}
/**
2019-04-03 01:42:23 +02:00
* @return array<string, array<array-key, CodeLocation>>
*/
public function getUncaughtThrows(Context $context)
{
$uncaught_throws = [];
if ($context->collect_exceptions) {
if ($context->possibly_thrown_exceptions) {
$config = $this->codebase->config;
$ignored_exceptions = array_change_key_case(
$context->is_global ?
$config->ignored_exceptions_in_global_scope :
$config->ignored_exceptions
);
$ignored_exceptions_and_descendants = array_change_key_case(
$context->is_global ?
$config->ignored_exceptions_and_descendants_in_global_scope :
$config->ignored_exceptions_and_descendants
);
foreach ($context->possibly_thrown_exceptions as $possibly_thrown_exception => $codelocations) {
if (isset($ignored_exceptions[strtolower($possibly_thrown_exception)])) {
continue;
}
$is_expected = false;
foreach ($ignored_exceptions_and_descendants as $expected_exception => $_) {
if ($expected_exception === $possibly_thrown_exception
|| $this->codebase->classExtends($possibly_thrown_exception, $expected_exception)
) {
$is_expected = true;
break;
}
}
if (!$is_expected) {
$uncaught_throws[$possibly_thrown_exception] = $codelocations;
}
}
}
}
return $uncaught_throws;
}
/**
* @return array{description:string, specials:array<string, array<int, string>>}|null
*/
public function getParsedDocblock() : ?array
{
return $this->parsed_docblock;
}
}