Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Immutable unions #8627

Merged
merged 90 commits into from Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
28d4b03
Immutable CodeLocation
danog Oct 11, 2022
48d4e65
Remove excess clones
danog Oct 12, 2022
32d58cd
Remove external clones
danog Oct 12, 2022
7263cbd
Remove leftover clones
danog Oct 12, 2022
15f7dbd
Fix final clone issue
danog Oct 12, 2022
e75f390
Immutable storages
danog Oct 12, 2022
477a0c4
Refactoring
danog Oct 13, 2022
9015dbe
Fixes
danog Oct 13, 2022
9e10e00
Fixes
danog Oct 13, 2022
ef8b613
Fix
danog Oct 17, 2022
c2ab160
Fix
danog Oct 18, 2022
955b69f
Fixes
danog Oct 18, 2022
75a8ef7
Simplify
danog Oct 18, 2022
a53f6cc
Fixes
danog Oct 18, 2022
64d3b9b
Fix
danog Oct 18, 2022
5bffe8c
Fixes
danog Oct 18, 2022
7b4c9ee
Update
danog Oct 18, 2022
c169f94
Fix
danog Oct 18, 2022
5eba409
Cache global types
danog Oct 18, 2022
94d214b
Fix
danog Oct 18, 2022
00ea583
Update
danog Oct 18, 2022
fc0fe2e
Update
danog Oct 18, 2022
f7341d6
Fixes
danog Oct 18, 2022
d131ada
Fixes
danog Oct 18, 2022
1284ef9
Refactor
danog Oct 18, 2022
4fc8318
Fixes
danog Oct 19, 2022
9aec377
Fix
danog Oct 19, 2022
aeb9d38
Fix
danog Oct 19, 2022
0c8d83f
More caching
danog Oct 19, 2022
d398883
Fix
danog Oct 19, 2022
a2bcba3
Fix
danog Oct 19, 2022
ee70da6
Update
danog Oct 19, 2022
f8d86c2
Update
danog Oct 19, 2022
5c91b47
Fix
danog Oct 19, 2022
68d76d5
Fixes
danog Oct 19, 2022
b45bcd9
Update
danog Oct 19, 2022
1d5b4e3
Refactor
danog Oct 19, 2022
ca86d28
Update
danog Oct 19, 2022
d4f2e9b
Fixes
danog Oct 19, 2022
c4450be
Break one more test
danog Oct 19, 2022
10ab6c8
Fix
danog Oct 19, 2022
94b3af4
FIx
danog Oct 20, 2022
4bc47af
Fix
danog Oct 20, 2022
47ad0ff
Fix
danog Oct 20, 2022
c022617
Fix
danog Oct 20, 2022
00c8d5e
Fix
danog Oct 20, 2022
db401b4
Improve performance and readability
danog Oct 20, 2022
9f1f205
Equivalent logic
danog Oct 20, 2022
7650781
Fixes
danog Oct 20, 2022
f917510
Revert
danog Oct 20, 2022
0a2596c
Revert "Revert"
danog Oct 20, 2022
84a1bab
Fix
danog Oct 20, 2022
3d4b64e
Fix reference bug
danog Oct 22, 2022
6dc4dcc
Make default TypeVisitor immutable
danog Oct 22, 2022
5ad7ce5
Bugfix
danog Oct 22, 2022
1e5c3e6
Remove clones
danog Oct 22, 2022
b9dc64e
Partial refactoring
danog Oct 22, 2022
9eb031e
Refactoring
danog Oct 22, 2022
5305934
Merge branch 'uwu' into immutable_readonly_4
danog Oct 22, 2022
7090d03
Fixes
danog Oct 22, 2022
2a1bc83
Fix
danog Oct 22, 2022
81c345f
Fixes
danog Oct 22, 2022
76eb09c
Fixes
danog Oct 22, 2022
d66593a
cs-fix
danog Oct 22, 2022
ea8634f
Fix final bugs
danog Oct 24, 2022
01eb5ed
Add test
danog Oct 24, 2022
4ffc25c
Misc fixes
danog Oct 24, 2022
3ad0f95
Update
danog Oct 25, 2022
bbd5db8
Fixes
danog Oct 25, 2022
ac1156e
Experiment with removing different property
danog Oct 25, 2022
3a6b975
revert "Experiment with removing different property"
danog Oct 25, 2022
ec0c6a3
Uniform naming
danog Oct 25, 2022
d6ccae2
Uniform naming
danog Oct 25, 2022
a4c2565
Hack hotfix
danog Oct 26, 2022
64e683f
Clean up $_FILES ref #8621
danog Oct 26, 2022
1f90e18
Undo hack, try fixing properly
danog Oct 27, 2022
ecf6fff
Helper method
danog Oct 27, 2022
b12f013
Merge remote-tracking branch 'origin/master' into immutable_readonly_4
danog Oct 27, 2022
afa748f
Remove redundant call
danog Oct 28, 2022
d89d26e
Partially fix bugs
danog Oct 28, 2022
4f5f1f9
Cleanup
danog Oct 28, 2022
05ad20c
Change defaults
danog Oct 31, 2022
b22f754
Fix bug
danog Oct 31, 2022
e782141
Fix (?, hope this doesn't break anything else)
danog Oct 31, 2022
5aa9967
cs-fix
danog Oct 31, 2022
fc2acce
Review fixes
danog Oct 31, 2022
c72980d
Bugfix
danog Nov 1, 2022
b0bc942
Bugfix
danog Nov 1, 2022
50924b2
Improve logic
danog Nov 1, 2022
3da9148
Update
danog Nov 1, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Expand Up @@ -50,7 +50,7 @@
"phpspec/prophecy": "^1.15",
"phpspec/prophecy-phpunit": "^2.0",
"phpunit/phpunit": "^9.5",
"psalm/plugin-phpunit": "^0.16",
"psalm/plugin-phpunit": "^0.18",
"slevomat/coding-standard": "^7.0",
"phpstan/phpdoc-parser": "1.6.4",
"squizlabs/php_codesniffer": "^3.6",
Expand Down
1 change: 1 addition & 0 deletions examples/plugins/ClassUnqualifier.php
Expand Up @@ -37,6 +37,7 @@ public static function afterClassLikeExistenceCheck(
$type_token[0] = $aliases[strtolower($fq_class_name)];
}
}
unset($type_token);

$new_candidate_type = implode(
'',
Expand Down
68 changes: 53 additions & 15 deletions psalm-baseline.xml
Expand Up @@ -46,6 +46,10 @@
</PossiblyUndefinedIntArrayOffset>
</file>
<file src="src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php">
<ConflictingReferenceConstraint occurrences="2">
<code>if (AtomicTypeComparator::isContainedBy(</code>
<code>if (AtomicTypeComparator::isContainedBy(</code>
</ConflictingReferenceConstraint>
<PossiblyUndefinedIntArrayOffset occurrences="1">
<code>$iterator_atomic_type-&gt;type_params[1]</code>
</PossiblyUndefinedIntArrayOffset>
Expand Down Expand Up @@ -150,6 +154,18 @@
<code>$callable_arg-&gt;items[1]</code>
</PossiblyUndefinedIntArrayOffset>
</file>
<file src="src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php">
<ReferenceConstraintViolation occurrences="3">
<code>$stmt_type</code>
<code>$stmt_type</code>
<code>$stmt_type</code>
</ReferenceConstraintViolation>
</file>
<file src="src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php">
<ReferenceConstraintViolation occurrences="1">
<code>$stmt_type</code>
</ReferenceConstraintViolation>
</file>
<file src="src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php">
<PossiblyUndefinedIntArrayOffset occurrences="1">
<code>$invalid_fetch_types[0]</code>
Expand Down Expand Up @@ -293,16 +309,10 @@
</MoreSpecificReturnType>
</file>
<file src="src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php">
<ImpureMethodCall occurrences="5">
<code>get</code>
<code>get</code>
<code>get</code>
<ImpureMethodCall occurrences="2">
<code>getClassTemplateTypes</code>
<code>has</code>
</ImpureMethodCall>
<ImpurePropertyAssignment occurrences="1">
<code>$candidate_param_type-&gt;from_template_default</code>
</ImpurePropertyAssignment>
</file>
<file src="src/Psalm/Internal/Type/TypeCombiner.php">
<PossiblyUndefinedIntArrayOffset occurrences="6">
Expand All @@ -326,8 +336,21 @@
<code>array_keys($template_type_map[$template_param_name])[0]</code>
</PossiblyUndefinedIntArrayOffset>
</file>
<file src="src/Psalm/Storage/ClassConstantStorage.php">
<MutableDependency occurrences="1">
<code>CustomMetadataTrait</code>
</MutableDependency>
</file>
<file src="src/Psalm/Storage/FunctionLikeParameter.php">
<ImpureMethodCall occurrences="4">
<code>traverse</code>
<code>traverse</code>
<code>traverse</code>
<code>traverse</code>
</ImpureMethodCall>
</file>
<file src="src/Psalm/Type/Atomic.php">
<ImpureMethodCall occurrences="10">
<ImpureMethodCall occurrences="12">
<code>classExtendsOrImplements</code>
<code>classExtendsOrImplements</code>
<code>classExtendsOrImplements</code>
Expand All @@ -338,10 +361,15 @@
<code>interfaceExtends</code>
<code>interfaceExtends</code>
<code>interfaceExtends</code>
<code>traverse</code>
<code>traverse</code>
</ImpureMethodCall>
<PossiblyUndefinedIntArrayOffset occurrences="1">
<code>array_keys($template_type_map[$value])[0]</code>
</PossiblyUndefinedIntArrayOffset>
<ReferenceConstraintViolation occurrences="1">
<code>$value</code>
</ReferenceConstraintViolation>
</file>
<file src="src/Psalm/Type/Atomic/CallableTrait.php">
<ImpureMethodCall occurrences="4">
Expand Down Expand Up @@ -400,10 +428,8 @@
<code>replace</code>
<code>replace</code>
</ImpureMethodCall>
<ImpurePropertyAssignment occurrences="3">
<ImpurePropertyAssignment occurrences="1">
<code>$key_type-&gt;possibly_undefined</code>
<code>$value_type-&gt;possibly_undefined</code>
<code>$value_type-&gt;possibly_undefined</code>
</ImpurePropertyAssignment>
</file>
<file src="src/Psalm/Type/Atomic/TList.php">
Expand All @@ -420,10 +446,6 @@
<code>replace</code>
<code>replace</code>
</ImpureMethodCall>
<ImpurePropertyFetch occurrences="2">
<code>$type-&gt;possibly_undefined</code>
<code>$type-&gt;possibly_undefined</code>
</ImpurePropertyFetch>
</file>
<file src="src/Psalm/Type/Atomic/TTemplateKeyOf.php">
<ImpureMethodCall occurrences="1">
Expand Down Expand Up @@ -457,12 +479,28 @@
<code>$type[0][0]</code>
</PossiblyUndefinedIntArrayOffset>
</file>
<file src="src/Psalm/Type/TypeNode.php">
<ReferenceConstraintViolation occurrences="1">
<code>$node</code>
</ReferenceConstraintViolation>
</file>
<file src="src/Psalm/Type/TypeVisitor.php">
<ImpureMethodCall occurrences="1">
<code>visit</code>
</ImpureMethodCall>
</file>
<file src="src/Psalm/Type/Union.php">
<PossiblyUnusedProperty occurrences="1">
<code>$ignore_isset</code>
</PossiblyUnusedProperty>
</file>
<file src="src/Psalm/Type/UnionTrait.php">
<ImpureMethodCall occurrences="4">
<code>traverse</code>
<code>traverse</code>
<code>traverseArray</code>
<code>traverseArray</code>
</ImpureMethodCall>
<PossiblyUnusedMethod occurrences="2">
<code>allFloatLiterals</code>
<code>allFloatLiterals</code>
Expand Down
2 changes: 2 additions & 0 deletions src/Psalm/Aliases.php
Expand Up @@ -54,6 +54,8 @@ final class Aliases
* @param array<lowercase-string, string> $functions_flipped
* @param array<string, string> $constants_flipped
* @internal
*
* @psalm-mutation-free
*/
public function __construct(
?string $namespace = null,
Expand Down
34 changes: 30 additions & 4 deletions src/Psalm/CodeLocation.php
Expand Up @@ -7,6 +7,7 @@
use PhpParser;
use Psalm\Internal\Analyzer\CommentAnalyzer;
use Psalm\Internal\Analyzer\ProjectAnalyzer;
use Psalm\Storage\ImmutableNonCloneableTrait;
use UnexpectedValueException;

use function explode;
Expand All @@ -25,8 +26,13 @@

use const PREG_OFFSET_CAPTURE;

/**
* @psalm-immutable
*/
class CodeLocation
{
use ImmutableNonCloneableTrait;

/** @var string */
public $file_path;

Expand Down Expand Up @@ -85,7 +91,7 @@ class CodeLocation
private $docblock_start_line_number;

/** @var int|null */
private $docblock_line_number;
protected $docblock_line_number;

/** @var null|int */
private $regex_type;
Expand All @@ -111,9 +117,12 @@ public function __construct(
?CodeLocation $previous_location = null,
bool $single_line = false,
?int $regex_type = null,
?string $selected_text = null
?string $selected_text = null,
?int $comment_line = null
) {
/** @psalm-suppress ImpureMethodCall Actually mutation-free just not marked */
$this->file_start = (int)$stmt->getAttribute('startFilePos');
/** @psalm-suppress ImpureMethodCall Actually mutation-free just not marked */
$this->file_end = (int)$stmt->getAttribute('endFilePos');
$this->raw_file_start = $this->file_start;
$this->raw_file_end = $this->file_end;
Expand All @@ -124,21 +133,38 @@ public function __construct(
$this->previous_location = $previous_location;
$this->text = $selected_text;

/** @psalm-suppress ImpureMethodCall Actually mutation-free just not marked */
danog marked this conversation as resolved.
Show resolved Hide resolved
$doc_comment = $stmt->getDocComment();

$this->docblock_start = $doc_comment ? $doc_comment->getStartFilePos() : null;
$this->docblock_start_line_number = $doc_comment ? $doc_comment->getStartLine() : null;

$this->preview_start = $this->docblock_start ?: $this->file_start;

/** @psalm-suppress ImpureMethodCall Actually mutation-free just not marked */
$this->raw_line_number = $stmt->getLine();

$this->docblock_line_number = $comment_line;
}

public function setCommentLine(int $line): void
/**
* @psalm-suppress PossiblyUnusedMethod Part of public API
* @return static
*/
public function setCommentLine(?int $line): self
{
$this->docblock_line_number = $line;
if ($line === $this->docblock_line_number) {
return $this;
}
$cloned = clone $this;
$cloned->docblock_line_number = $line;
return $cloned;
}

/**
* @psalm-external-mutation-free
* @psalm-suppress InaccessibleProperty Mainly used for caching
*/
private function calculateRealLocation(): void
{
if ($this->have_recalculated) {
Expand Down
3 changes: 3 additions & 0 deletions src/Psalm/CodeLocation/DocblockTypeLocation.php
Expand Up @@ -5,6 +5,7 @@
use Psalm\CodeLocation;
use Psalm\FileSource;

/** @psalm-immutable */
class DocblockTypeLocation extends CodeLocation
{
public function __construct(
Expand All @@ -26,5 +27,7 @@ public function __construct(
$this->single_line = false;

$this->preview_start = $this->file_start;

$this->docblock_line_number = $line_number;
}
}
5 changes: 3 additions & 2 deletions src/Psalm/CodeLocation/ParseErrorLocation.php
Expand Up @@ -8,6 +8,7 @@
use function substr;
use function substr_count;

/** @psalm-immutable */
class ParseErrorLocation extends CodeLocation
{
public function __construct(
Expand All @@ -16,9 +17,9 @@ public function __construct(
string $file_path,
string $file_name
) {
/** @psalm-suppress PossiblyUndefinedStringArrayOffset */
/** @psalm-suppress PossiblyUndefinedStringArrayOffset, ImpureMethodCall */
$this->file_start = (int)$error->getAttributes()['startFilePos'];
/** @psalm-suppress PossiblyUndefinedStringArrayOffset */
/** @psalm-suppress PossiblyUndefinedStringArrayOffset, ImpureMethodCall */
$this->file_end = (int)$error->getAttributes()['endFilePos'];
$this->raw_file_start = $this->file_start;
$this->raw_file_end = $this->file_end;
Expand Down
1 change: 1 addition & 0 deletions src/Psalm/CodeLocation/Raw.php
Expand Up @@ -7,6 +7,7 @@
use function substr;
use function substr_count;

/** @psalm-immutable */
class Raw extends CodeLocation
{
public function __construct(
Expand Down
7 changes: 4 additions & 3 deletions src/Psalm/Codebase.php
Expand Up @@ -507,6 +507,7 @@ public function scanFiles(int $threads = 1): void
}
}

/** @psalm-mutation-free */
public function getFileContents(string $file_path): string
{
return $this->file_provider->getContents($file_path);
Expand Down Expand Up @@ -1951,9 +1952,9 @@ public function addTaintSource(
string $taint_id,
array $taints = TaintKindGroup::ALL_INPUT,
?CodeLocation $code_location = null
): void {
): Union {
if (!$this->taint_flow_graph) {
return;
return $expr_type;
}

$source = new TaintSource(
Expand All @@ -1966,7 +1967,7 @@ public function addTaintSource(

$this->taint_flow_graph->addSource($source);

$expr_type->parent_nodes[$source->id] = $source;
return $expr_type->addParentNodes([$source->id => $source]);
}

/**
Expand Down
24 changes: 8 additions & 16 deletions src/Psalm/Context.php
Expand Up @@ -432,17 +432,6 @@ public function __destruct()
$this->case_scope = null;
}

public function __clone()
{
foreach ($this->clauses as &$clause) {
$clause = clone $clause;
}

foreach ($this->constants as &$constant) {
$constant = clone $constant;
}
}

/**
* Updates the parent context, looking at the changes within a block and then applying those changes, where
* necessary, to the parent context
Expand Down Expand Up @@ -471,15 +460,13 @@ public function update(

if (!$existing_type) {
if ($new_type) {
$this->vars_in_scope[$var_id] = clone $new_type;
$this->vars_in_scope[$var_id] = $new_type;
$updated_vars[$var_id] = true;
}

continue;
}

$existing_type = clone $existing_type;

// if the type changed within the block of statements, process the replacement
// also never allow ourselves to remove all types from a union
if ((!$new_type || !$old_type->equals($new_type))
Expand Down Expand Up @@ -543,7 +530,12 @@ public function getRedefinedVars(array $new_vars_in_scope, bool $include_new_var

$new_type = $new_vars_in_scope[$var_id];

if (!$this_type->equals($new_type)) {
if (!$this_type->equals(
$new_type,
true,
!($this_type->propagate_parent_nodes || $new_type->propagate_parent_nodes)
)
) {
$redefined_vars[$var_id] = $this_type;
}
}
Expand Down Expand Up @@ -720,7 +712,7 @@ public static function filterClauses(

$result_type = AssertionReconciler::reconcile(
$assertion,
clone $new_type,
$new_type,
null,
$statements_analyzer,
false,
Expand Down
1 change: 1 addition & 0 deletions src/Psalm/ErrorBaseline.php
Expand Up @@ -230,6 +230,7 @@ static function (array $carry, IssueData $issue): array {
foreach ($groupedIssues as &$issues) {
ksort($issues);
}
unset($issues);

return $groupedIssues;
}
Expand Down