From d5b713e4391e6339bd0c69f664ae15c3ccd96e01 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Mon, 18 Dec 2023 14:51:53 +0100 Subject: [PATCH 01/12] Fix https://github.com/vimeo/psalm/issues/10501 - report error for non-strict comparison on truthy+falsy union --- .../Block/IfConditionalAnalyzer.php | 33 +++++++ .../Expression/BooleanNotAnalyzer.php | 35 ++++++++ tests/TypeReconciliation/ConditionalTest.php | 86 +++++++++++++++++++ 3 files changed, 154 insertions(+) diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php index a306cca4ca0..8db97e794a3 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php @@ -17,6 +17,7 @@ use Psalm\Issue\RedundantConditionGivenDocblockType; use Psalm\Issue\TypeDoesNotContainType; use Psalm\IssueBuffer; +use Psalm\Type\Atomic\TBool; use Psalm\Type\Reconciler; use function array_diff_key; @@ -366,6 +367,38 @@ public static function handleParadoxicalCondition( $statements_analyzer->getSuppressedIssues(), ); } + } elseif (!($stmt instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) + && !($stmt instanceof PhpParser\Node\Expr\BinaryOp\Identical) + && !($stmt instanceof PhpParser\Node\Expr\BooleanNot)) { + $has_both = false; + $both_types = $type->getBuilder(); + if (count($type->getAtomicTypes()) > 1) { + foreach ($both_types->getAtomicTypes() as $key => $atomic_type) { + if ($atomic_type->isTruthy() + || $atomic_type->isFalsy() + || $atomic_type instanceof TBool) { + $both_types->removeType($key); + continue; + } + + $has_both = true; + } + } + + if ($has_both) { + $both_types = $both_types->freeze(); + IssueBuffer::maybeAdd( + new TypeDoesNotContainType( + 'Operand of type ' . $type->getId() . ' contains ' . + 'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' . + $both_types->getId() . ', which can be falsy and truthy. ' . + 'This can cause possibly unexpected behavior. Use strict comparison instead.', + new CodeLocation($statements_analyzer, $stmt), + $type->getId() . ' truthy-falsy', + ), + $statements_analyzer->getSuppressedIssues(), + ); + } } } } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php index 3c75dd9efca..bfe8d209e02 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php @@ -3,15 +3,20 @@ namespace Psalm\Internal\Analyzer\Statements\Expression; use PhpParser; +use Psalm\CodeLocation; use Psalm\Context; use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer; use Psalm\Internal\Analyzer\StatementsAnalyzer; +use Psalm\Issue\TypeDoesNotContainType; +use Psalm\IssueBuffer; use Psalm\Type; use Psalm\Type\Atomic\TBool; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TTrue; use Psalm\Type\Union; +use function count; + /** * @internal */ @@ -40,6 +45,36 @@ public static function analyze( } elseif ($expr_type->isAlwaysFalsy()) { $stmt_type = new TTrue($expr_type->from_docblock); } else { + $has_both = false; + $both_types = $expr_type->getBuilder(); + if (count($expr_type->getAtomicTypes()) > 1) { + foreach ($both_types->getAtomicTypes() as $key => $atomic_type) { + if ($atomic_type->isTruthy() + || $atomic_type->isFalsy() + || $atomic_type instanceof TBool) { + $both_types->removeType($key); + continue; + } + + $has_both = true; + } + } + + if ($has_both) { + $both_types = $both_types->freeze(); + IssueBuffer::maybeAdd( + new TypeDoesNotContainType( + 'Operand of type ' . $expr_type->getId() . ' contains ' . + 'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' . + $both_types->getId() . ', which can be falsy and truthy. ' . + 'This can cause possibly unexpected behavior. Use strict comparison instead.', + new CodeLocation($statements_analyzer, $stmt), + $expr_type->getId() . ' truthy-falsy', + ), + $statements_analyzer->getSuppressedIssues(), + ); + } + $stmt_type = new TBool(); } diff --git a/tests/TypeReconciliation/ConditionalTest.php b/tests/TypeReconciliation/ConditionalTest.php index 700ba17c621..bb16a20043e 100644 --- a/tests/TypeReconciliation/ConditionalTest.php +++ b/tests/TypeReconciliation/ConditionalTest.php @@ -38,6 +38,32 @@ function foo($a): void { if ($b === $a) { } }', ], + 'nonStrictConditionTruthyFalsyNoOverlap' => [ + 'code' => ' [ 'code' => ' 'TypeDoesNotContainType', ], + 'nonStrictConditionTruthyFalsy' => [ + 'code' => ' 'TypeDoesNotContainType', + ], + 'nonStrictConditionTruthyFalsyNegated' => [ + 'code' => ' 'TypeDoesNotContainType', + ], + 'nonStrictConditionTruthyFalsyFuncCall' => [ + 'code' => ' 'TypeDoesNotContainType', + ], + 'nonStrictConditionTruthyFalsyFuncCallNegated' => [ + 'code' => ' 'TypeDoesNotContainType', + ], 'redundantConditionForNonEmptyString' => [ 'code' => ' Date: Fri, 12 Jan 2024 22:40:47 +0100 Subject: [PATCH 02/12] add the fix for empty() too and fix empty returning bool on true/false only cases hiding errors when functions called --- .../Statements/Expression/EmptyAnalyzer.php | 82 +++++++++++++++---- tests/TypeReconciliation/EmptyTest.php | 32 ++++++++ 2 files changed, 100 insertions(+), 14 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php index 3b9014f85b4..40bf489ea5e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php @@ -8,8 +8,15 @@ use Psalm\Internal\Analyzer\StatementsAnalyzer; use Psalm\Issue\ForbiddenCode; use Psalm\Issue\InvalidArgument; +use Psalm\Issue\TypeDoesNotContainType; use Psalm\IssueBuffer; use Psalm\Type; +use Psalm\Type\Atomic\TBool; +use Psalm\Type\Atomic\TFalse; +use Psalm\Type\Atomic\TTrue; +use Psalm\Type\Union; + +use function count; /** * @internal @@ -35,21 +42,68 @@ public static function analyze( ); } - if (($stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr)) - && $stmt_expr_type->hasBool() - && $stmt_expr_type->isSingle() - && !$stmt_expr_type->from_docblock - ) { - IssueBuffer::maybeAdd( - new InvalidArgument( - 'Calling empty on a boolean value is almost certainly unintended', - new CodeLocation($statements_analyzer->getSource(), $stmt->expr), - 'empty', - ), - $statements_analyzer->getSuppressedIssues(), - ); + $expr_type = $statements_analyzer->node_data->getType($stmt->expr); + + if ($expr_type) { + if ($expr_type->hasBool() + && $expr_type->isSingle() + && !$expr_type->from_docblock + ) { + IssueBuffer::maybeAdd( + new InvalidArgument( + 'Calling empty on a boolean value is almost certainly unintended', + new CodeLocation($statements_analyzer->getSource(), $stmt->expr), + 'empty', + ), + $statements_analyzer->getSuppressedIssues(), + ); + } + + if ($expr_type->isAlwaysTruthy() && $expr_type->possibly_undefined === false) { + $stmt_type = new TFalse($expr_type->from_docblock); + } elseif ($expr_type->isAlwaysFalsy()) { + $stmt_type = new TTrue($expr_type->from_docblock); + } else { + $has_both = false; + $both_types = $expr_type->getBuilder(); + if (count($expr_type->getAtomicTypes()) > 1) { + foreach ($both_types->getAtomicTypes() as $key => $atomic_type) { + if ($atomic_type->isTruthy() + || $atomic_type->isFalsy() + || $atomic_type instanceof TBool) { + $both_types->removeType($key); + continue; + } + + $has_both = true; + } + } + + if ($has_both) { + $both_types = $both_types->freeze(); + IssueBuffer::maybeAdd( + new TypeDoesNotContainType( + 'Operand of type ' . $expr_type->getId() . ' contains ' . + 'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' . + $both_types->getId() . ', which can be falsy and truthy. ' . + 'This can cause possibly unexpected behavior. Use strict comparison instead.', + new CodeLocation($statements_analyzer, $stmt), + $expr_type->getId() . ' truthy-falsy', + ), + $statements_analyzer->getSuppressedIssues(), + ); + } + + $stmt_type = new TBool(); + } + + $stmt_type = new Union([$stmt_type], [ + 'parent_nodes' => $expr_type->parent_nodes, + ]); + } else { + $stmt_type = Type::getBool(); } - $statements_analyzer->node_data->setType($stmt, Type::getBool()); + $statements_analyzer->node_data->setType($stmt, $stmt_type); } } diff --git a/tests/TypeReconciliation/EmptyTest.php b/tests/TypeReconciliation/EmptyTest.php index c7991804454..944ae2218b6 100644 --- a/tests/TypeReconciliation/EmptyTest.php +++ b/tests/TypeReconciliation/EmptyTest.php @@ -612,6 +612,14 @@ function test(string $s): void { '$GLOBALS[\'sql_query\']===' => 'string', ], ], + 'emptyLiteralTrueFalse' => [ + 'code' => ' [ + '$x===' => 'true', + ], + ], ]; } @@ -720,6 +728,30 @@ function nonEmptyString(string $str): string { }', 'error_message' => 'LessSpecificReturnStatement', ], + 'impossibleEmptyOnFalsyFunctionCall' => [ + 'code' => ' 'DocblockTypeContradiction', + ], + 'redundantEmptyOnFalsyFunctionCall' => [ + 'code' => ' 'RedundantConditionGivenDocblockType', + ], ]; } } From fb93aede1279db1a34c6477a1d5aa56a99bcbfa4 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Fri, 12 Jan 2024 22:52:26 +0100 Subject: [PATCH 03/12] create a separate issue type --- config.xsd | 1 + docs/running_psalm/error_levels.md | 1 + docs/running_psalm/issues.md | 1 + .../issues/RiskyTruthyFalsyComparison.md | 29 +++++++++++++++++++ .../Block/IfConditionalAnalyzer.php | 5 ++-- .../Expression/BooleanNotAnalyzer.php | 6 ++-- .../Statements/Expression/EmptyAnalyzer.php | 6 ++-- .../Issue/RiskyTruthyFalsyComparison.php | 17 +++++++++++ tests/TypeReconciliation/ConditionalTest.php | 8 ++--- 9 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 docs/running_psalm/issues/RiskyTruthyFalsyComparison.md create mode 100644 src/Psalm/Issue/RiskyTruthyFalsyComparison.php diff --git a/config.xsd b/config.xsd index 5c176821e24..0f3e88916c8 100644 --- a/config.xsd +++ b/config.xsd @@ -427,6 +427,7 @@ + diff --git a/docs/running_psalm/error_levels.md b/docs/running_psalm/error_levels.md index 55a18b8fa61..df53f5227f6 100644 --- a/docs/running_psalm/error_levels.md +++ b/docs/running_psalm/error_levels.md @@ -173,6 +173,7 @@ Level 5 and above allows a more non-verifiable code, and higher levels are even - [TooManyArguments](issues/TooManyArguments.md) - [TypeDoesNotContainNull](issues/TypeDoesNotContainNull.md) - [TypeDoesNotContainType](issues/TypeDoesNotContainType.md) +- [RiskyTruthyFalsyComparison](issues/RiskyTruthyFalsyComparison.md) - [UndefinedMagicMethod](issues/UndefinedMagicMethod.md) - [UndefinedMagicPropertyAssignment](issues/UndefinedMagicPropertyAssignment.md) - [UndefinedMagicPropertyFetch](issues/UndefinedMagicPropertyFetch.md) diff --git a/docs/running_psalm/issues.md b/docs/running_psalm/issues.md index ac8135c7142..179f9bf7b53 100644 --- a/docs/running_psalm/issues.md +++ b/docs/running_psalm/issues.md @@ -229,6 +229,7 @@ - [ReferenceReusedFromConfusingScope](issues/ReferenceReusedFromConfusingScope.md) - [ReservedWord](issues/ReservedWord.md) - [RiskyCast](issues/RiskyCast.md) + - [RiskyTruthyFalsyComparison](issues/RiskyTruthyFalsyComparison.md) - [StringIncrement](issues/StringIncrement.md) - [TaintedCallable](issues/TaintedCallable.md) - [TaintedCookie](issues/TaintedCookie.md) diff --git a/docs/running_psalm/issues/RiskyTruthyFalsyComparison.md b/docs/running_psalm/issues/RiskyTruthyFalsyComparison.md new file mode 100644 index 00000000000..8d60969633e --- /dev/null +++ b/docs/running_psalm/issues/RiskyTruthyFalsyComparison.md @@ -0,0 +1,29 @@ +# RiskyTruthyFalsyComparison + +Emitted when comparing a value with multiple types that can both contain truthy and falsy values. + +```php +freeze(); IssueBuffer::maybeAdd( - new TypeDoesNotContainType( + new RiskyTruthyFalsyComparison( 'Operand of type ' . $type->getId() . ' contains ' . 'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' . $both_types->getId() . ', which can be falsy and truthy. ' . 'This can cause possibly unexpected behavior. Use strict comparison instead.', new CodeLocation($statements_analyzer, $stmt), - $type->getId() . ' truthy-falsy', + $type->getId(), ), $statements_analyzer->getSuppressedIssues(), ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php index bfe8d209e02..71f6e19324f 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php @@ -7,7 +7,7 @@ use Psalm\Context; use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer; use Psalm\Internal\Analyzer\StatementsAnalyzer; -use Psalm\Issue\TypeDoesNotContainType; +use Psalm\Issue\RiskyTruthyFalsyComparison; use Psalm\IssueBuffer; use Psalm\Type; use Psalm\Type\Atomic\TBool; @@ -63,13 +63,13 @@ public static function analyze( if ($has_both) { $both_types = $both_types->freeze(); IssueBuffer::maybeAdd( - new TypeDoesNotContainType( + new RiskyTruthyFalsyComparison( 'Operand of type ' . $expr_type->getId() . ' contains ' . 'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' . $both_types->getId() . ', which can be falsy and truthy. ' . 'This can cause possibly unexpected behavior. Use strict comparison instead.', new CodeLocation($statements_analyzer, $stmt), - $expr_type->getId() . ' truthy-falsy', + $expr_type->getId(), ), $statements_analyzer->getSuppressedIssues(), ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php index 40bf489ea5e..02fae12fd44 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php @@ -8,7 +8,7 @@ use Psalm\Internal\Analyzer\StatementsAnalyzer; use Psalm\Issue\ForbiddenCode; use Psalm\Issue\InvalidArgument; -use Psalm\Issue\TypeDoesNotContainType; +use Psalm\Issue\RiskyTruthyFalsyComparison; use Psalm\IssueBuffer; use Psalm\Type; use Psalm\Type\Atomic\TBool; @@ -82,13 +82,13 @@ public static function analyze( if ($has_both) { $both_types = $both_types->freeze(); IssueBuffer::maybeAdd( - new TypeDoesNotContainType( + new RiskyTruthyFalsyComparison( 'Operand of type ' . $expr_type->getId() . ' contains ' . 'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' . $both_types->getId() . ', which can be falsy and truthy. ' . 'This can cause possibly unexpected behavior. Use strict comparison instead.', new CodeLocation($statements_analyzer, $stmt), - $expr_type->getId() . ' truthy-falsy', + $expr_type->getId(), ), $statements_analyzer->getSuppressedIssues(), ); diff --git a/src/Psalm/Issue/RiskyTruthyFalsyComparison.php b/src/Psalm/Issue/RiskyTruthyFalsyComparison.php new file mode 100644 index 00000000000..9150aa30b8c --- /dev/null +++ b/src/Psalm/Issue/RiskyTruthyFalsyComparison.php @@ -0,0 +1,17 @@ +dupe_key = $dupe_key; + } +} diff --git a/tests/TypeReconciliation/ConditionalTest.php b/tests/TypeReconciliation/ConditionalTest.php index bb16a20043e..2209c2f163d 100644 --- a/tests/TypeReconciliation/ConditionalTest.php +++ b/tests/TypeReconciliation/ConditionalTest.php @@ -3528,7 +3528,7 @@ function foo($arg) { if ($arg) { } }', - 'error_message' => 'TypeDoesNotContainType', + 'error_message' => 'RiskyTruthyFalsyComparison', ], 'nonStrictConditionTruthyFalsyNegated' => [ 'code' => ' 'TypeDoesNotContainType', + 'error_message' => 'RiskyTruthyFalsyComparison', ], 'nonStrictConditionTruthyFalsyFuncCall' => [ 'code' => ' 'TypeDoesNotContainType', + 'error_message' => 'RiskyTruthyFalsyComparison', ], 'nonStrictConditionTruthyFalsyFuncCallNegated' => [ 'code' => ' 'TypeDoesNotContainType', + 'error_message' => 'RiskyTruthyFalsyComparison', ], 'redundantConditionForNonEmptyString' => [ 'code' => ' Date: Sat, 13 Jan 2024 00:53:32 +0100 Subject: [PATCH 04/12] fix shepherd issue --- src/Psalm/PluginFileExtensionsSocket.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Psalm/PluginFileExtensionsSocket.php b/src/Psalm/PluginFileExtensionsSocket.php index 1e698e89cc5..0b5ac1333fa 100644 --- a/src/Psalm/PluginFileExtensionsSocket.php +++ b/src/Psalm/PluginFileExtensionsSocket.php @@ -55,8 +55,8 @@ public function addFileTypeScanner(string $fileExtension, string $className): vo 1_622_727_271, ); } - if (!empty($this->config->getFiletypeScanners()[$fileExtension]) - || !empty($this->additionalFileTypeScanners[$fileExtension]) + if (isset($this->config->getFiletypeScanners()[$fileExtension]) + || isset($this->additionalFileTypeScanners[$fileExtension]) ) { throw new LogicException( sprintf('Cannot redeclare scanner for file-type %s', $fileExtension), @@ -91,8 +91,8 @@ public function addFileTypeAnalyzer(string $fileExtension, string $className): v 1_622_727_281, ); } - if (!empty($this->config->getFiletypeAnalyzers()[$fileExtension]) - || !empty($this->additionalFileTypeAnalyzers[$fileExtension]) + if (isset($this->config->getFiletypeAnalyzers()[$fileExtension]) + || isset($this->additionalFileTypeAnalyzers[$fileExtension]) ) { throw new LogicException( sprintf('Cannot redeclare analyzer for file-type %s', $fileExtension), From 19b1a33a20609fbb8b8ababfe899de6af122fa3c Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Sat, 13 Jan 2024 11:18:48 +0100 Subject: [PATCH 05/12] fix possibly undefined array key in keyed array doesnt include null when not validated Fix https://psalm.dev/r/b153d0d248 to return 'a'|null instead of 'a' - this is required as otherwise empty would report RedundantCondition errors now which would bring back https://github.com/vimeo/psalm/issues/2681 --- .../Expression/Fetch/ArrayFetchAnalyzer.php | 8 ++++++-- tests/ArrayAccessTest.php | 13 +++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php index 4a3a1f5c903..753e2891920 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php @@ -313,14 +313,18 @@ public static function analyze( && !$context->inside_unset && ($stmt_var_type && !$stmt_var_type->hasMixed()) ) { - IssueBuffer::maybeAdd( + if (IssueBuffer::accepts( new PossiblyUndefinedArrayOffset( 'Possibly undefined array key ' . $keyed_array_var_id . ' on ' . $stmt_var_type->getId(), new CodeLocation($statements_analyzer->getSource(), $stmt), ), $statements_analyzer->getSuppressedIssues(), - ); + )) { + $stmt_type = $stmt_type->getBuilder()->addType(new TNull())->freeze(); + } + } elseif ($stmt_type->possibly_undefined) { + $stmt_type = $stmt_type->getBuilder()->addType(new TNull())->freeze(); } $stmt_type = $stmt_type->setPossiblyUndefined(false); diff --git a/tests/ArrayAccessTest.php b/tests/ArrayAccessTest.php index 8bc2a488fc1..91ef428f99b 100644 --- a/tests/ArrayAccessTest.php +++ b/tests/ArrayAccessTest.php @@ -654,6 +654,19 @@ function f(array $p): void '$x3===' => "array{b: 'value'}", ], ], + 'possiblyUndefinedArrayOffsetKeyedArray' => [ + 'code' => ' [ + '$x===' => '"a"|null', + ], + 'ignored_issues' => ['PossiblyUndefinedArrayOffset'], + ], 'domNodeListAccessible' => [ 'code' => ' Date: Fri, 12 Jan 2024 23:37:45 +0100 Subject: [PATCH 06/12] fix bugs in tests --- tests/ArrayAccessTest.php | 19 +++++++++-- tests/FunctionCallTest.php | 4 +-- tests/ImmutableAnnotationTest.php | 2 +- tests/JsonOutputTest.php | 3 +- tests/Loop/DoTest.php | 2 +- tests/Loop/WhileTest.php | 4 +-- tests/MethodCallTest.php | 14 ++++---- tests/MethodSignatureTest.php | 6 ++-- tests/PropertyTypeTest.php | 8 +++-- tests/ReturnTypeTest.php | 6 ++++ tests/SwitchTypeTest.php | 2 +- tests/Template/ClassTemplateTest.php | 2 ++ .../AssignmentInConditionalTest.php | 6 ++-- tests/TypeReconciliation/ConditionalTest.php | 10 ++++++ tests/TypeReconciliation/EmptyTest.php | 8 ++++- tests/TypeReconciliation/IssetTest.php | 2 +- .../RedundantConditionTest.php | 15 ++++++--- tests/TypeReconciliation/TypeAlgebraTest.php | 32 +++++++++++++++++++ tests/UnusedVariableTest.php | 18 +++++------ 19 files changed, 121 insertions(+), 42 deletions(-) diff --git a/tests/ArrayAccessTest.php b/tests/ArrayAccessTest.php index 91ef428f99b..af33aa025f8 100644 --- a/tests/ArrayAccessTest.php +++ b/tests/ArrayAccessTest.php @@ -663,7 +663,7 @@ function f(array $p): void $x = $d[0];', 'assertions' => [ - '$x===' => '"a"|null', + '$x===' => '\'a\'', ], 'ignored_issues' => ['PossiblyUndefinedArrayOffset'], ], @@ -1349,7 +1349,7 @@ public function __toString() { echo $a[new Foo];', 'error_message' => 'InvalidArrayOffset', ], - 'possiblyUndefinedIntArrayOffet' => [ + 'possiblyUndefinedIntArrayOffset' => [ 'code' => ' 'PossiblyUndefinedArrayOffset', ], - 'possiblyUndefinedStringArrayOffet' => [ + 'possiblyUndefinedStringArrayOffset' => [ 'code' => ' 0.5, "b" => 1.5, "c" => new Exception()]);', 'error_message' => 'InvalidArgument', ], + 'possiblyUndefinedArrayOffsetKeyedArray' => [ + 'code' => ' 'PossiblyUndefinedArrayOffset', + ], ]; } } diff --git a/tests/FunctionCallTest.php b/tests/FunctionCallTest.php index e63636dff43..4d997070211 100644 --- a/tests/FunctionCallTest.php +++ b/tests/FunctionCallTest.php @@ -1937,7 +1937,7 @@ function badpattern() { 'strposAllowDictionary' => [ 'code' => ' [ @@ -2138,7 +2138,7 @@ function foo(A $a1, A $a2 = null): void 'strposFirstParamAllowClassString' => [ 'code' => ' [ diff --git a/tests/ImmutableAnnotationTest.php b/tests/ImmutableAnnotationTest.php index da36f660df9..07994b59b44 100644 --- a/tests/ImmutableAnnotationTest.php +++ b/tests/ImmutableAnnotationTest.php @@ -300,7 +300,7 @@ public function getError(): ?string { $dto = new DTO("BOOM!"); - if ($dto->getError()) { + if ($dto->getError() !== null) { takesString($dto->getError()); }', ], diff --git a/tests/JsonOutputTest.php b/tests/JsonOutputTest.php index c20d7b9d721..0fefa3f6dec 100644 --- a/tests/JsonOutputTest.php +++ b/tests/JsonOutputTest.php @@ -138,6 +138,7 @@ function fooFoo() { ], 'singleIssueForTypeDifference' => [ 'code' => ' 1, 'message' => 'Operand of type non-falsy-string is always truthy', - 'line' => 4, + 'line' => 5, 'error' => '$b', ], ]; diff --git a/tests/Loop/DoTest.php b/tests/Loop/DoTest.php index 5c125de7fac..0c703985b25 100644 --- a/tests/Loop/DoTest.php +++ b/tests/Loop/DoTest.php @@ -245,7 +245,7 @@ function bar(?string &$i) : void {} $c = null; do { - if (!$c) { + if ($c === null || $c === "" || $c === "0") { foo($c); } else { bar($c); diff --git a/tests/Loop/WhileTest.php b/tests/Loop/WhileTest.php index 73dd8085bdf..3056bdc8660 100644 --- a/tests/Loop/WhileTest.php +++ b/tests/Loop/WhileTest.php @@ -155,7 +155,7 @@ function foo(): ?A { } while ($a = foo()) { - if ($a->bar) {} + if ($a->bar !== null) {} }', ], 'whileTrueWithBreak' => [ @@ -271,7 +271,7 @@ function bar(?string &$i) : void {} $c = null; while (rand(0, 1)) { - if (!$c) { + if ($c === null || $c === "" || $c === "0") { foo($c); } else { bar($c); diff --git a/tests/MethodCallTest.php b/tests/MethodCallTest.php index 9b8ba5b72b2..4c411170272 100644 --- a/tests/MethodCallTest.php +++ b/tests/MethodCallTest.php @@ -149,7 +149,7 @@ function printInt(int $int): void { $obj = new SomeClass(); - if ($obj->getInt()) { + if ($obj->getInt() !== null) { printInt($obj->getInt()); }', ); @@ -185,7 +185,7 @@ function printInt(int $int): void { $obj = new SomeClass(); - if ($obj->getInt()) { + if ($obj->getInt() !== null) { printInt($obj->getInt()); }', ); @@ -936,7 +936,7 @@ final public function getA() { $a = new A(); - if ($a->getA()) { + if ($a->getA() !== null) { echo strlen($a->getA()); }', ], @@ -1007,7 +1007,7 @@ function printInt(int $int): void { $obj = new SomeClass(); - if ($obj->getInt()) { + if ($obj->getInt() !== null) { printInt($obj->getInt()); }', ], @@ -1031,7 +1031,7 @@ function printInt(int $int): void { $obj = new SomeClass(); - if ($obj->getInt()) { + if ($obj->getInt() !== null) { printInt($obj->getInt()); }', ], @@ -1631,7 +1631,7 @@ function getA() { } function foo(A $a) : void { - if ($a->getA()) { + if ($a->getA() !== null) { echo strlen($a->getA()); } } @@ -1697,7 +1697,7 @@ function printInt(int $int): void { $obj = new SomeClass(); - if ($obj->getInt()) { + if ($obj->getInt() !== null) { printInt($obj->getInt()); }', 'error_message' => 'PossiblyNullArgument', diff --git a/tests/MethodSignatureTest.php b/tests/MethodSignatureTest.php index a740cf0f4a9..e973c0f7e77 100644 --- a/tests/MethodSignatureTest.php +++ b/tests/MethodSignatureTest.php @@ -311,7 +311,7 @@ public function foo(string $s): ?string { class B extends A { public function foo(?string $s): string { - return $s ?: "hello"; + return $s !== null ? $s : "hello"; } } @@ -327,7 +327,7 @@ public function foo(string $s): string { class B extends A { public function foo(string $s = null): string { - return $s ?: "hello"; + return $s !== null ? $s : "hello"; } } @@ -1044,7 +1044,7 @@ public function fooFoo(int $a, bool $c): void { 'code' => 'getX()) { + if (is_int($x->getX())) { XCollector::modify(); if ($x->getX() === null) {} } @@ -221,7 +221,7 @@ public function getX() : ?int { } function testX(X $x): void { - if ($x->getX()) { + if ($x->getX() !== null) { XCollector::modify(); if ($x->getX() === null) {} } @@ -255,7 +255,7 @@ public function __construct(?int $x) { } function testX(X $x): void { - if ($x->x) { + if ($x->x !== null) { XCollector::modify(); if ($x->x === null) {} } @@ -686,6 +686,8 @@ class A { } echo substr($a->aa, 1);', + 'assertions' => [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'nullableStaticPropertyWithIfCheck' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'returnTypeNotEmptyCheckInElseIf' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'returnTypeNotEmptyCheckInElse' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'returnTypeAfterIf' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'noCrashTemplateInsideGenerator' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'assignmentInIf' => [ 'code' => ' 5) { - } elseif (($a = rand(0, 1) ? new A : null) && $a->foo) {}', + } elseif (($a = rand(0, 1) ? new A : null) && is_string($a->foo)) {}', ], 'noParadoxAfterConditionalAssignment' => [ 'code' => ' 'InvalidReturnStatement', - 'ignored_issues' => [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], 'php_version' => '8.0', ], 'assignmentInBranchOfAndReferencedAfterIf' => [ diff --git a/tests/TypeReconciliation/ConditionalTest.php b/tests/TypeReconciliation/ConditionalTest.php index 2209c2f163d..5a070688870 100644 --- a/tests/TypeReconciliation/ConditionalTest.php +++ b/tests/TypeReconciliation/ConditionalTest.php @@ -594,6 +594,8 @@ function Foo($value = null) : bool { } return false; }', + 'assertions' => [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'numericStringAssertion' => [ 'code' => ' 5) {} } }', + 'assertions' => [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'arrayUnionTypeSwitching' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'propertySetOnElementInConditional' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'issetAssertionOnStaticProperty' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'nonEmptyStringAfterLiteralCheck' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'emptyExceptionReconciliationAfterIf' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'alwaysBoolResult' => [ 'code' => ' [], - 'ignored_issues' => ['MixedAssignment', 'MissingParamType', 'MixedArgument'], + 'ignored_issues' => ['MixedAssignment', 'MissingParamType', 'MixedArgument', 'RiskyTruthyFalsyComparison'], ], 'multipleEmptiesInCondition' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'reconcileEmptyTwiceWithoutReturn' => [ 'code' => ' [], - 'ignored_issues' => ['MixedAssignment', 'MixedArrayAccess'], + 'ignored_issues' => ['MixedAssignment', 'MixedArrayAccess', 'RiskyTruthyFalsyComparison'], ], 'mixedArrayIssetGetStringVar' => [ 'code' => ' [], + 'ignored_issues' => [ + 'RiskyTruthyFalsyComparison', + ], ], 'noRedundantConditionAfterAssignment' => [ 'code' => 'foo) {} + if ($i->foo !== null) {} break; default: @@ -180,7 +184,7 @@ function makeA() { } if ($a) {}', 'assertions' => [], - 'ignored_issues' => ['MixedAssignment', 'MixedArrayAccess'], + 'ignored_issues' => ['MixedAssignment', 'MixedArrayAccess', 'RiskyTruthyFalsyComparison'], ], 'noComplaintWithIsNumericThenIsEmpty' => [ 'code' => ' [], 'ignored_issues' => ['MixedAssignment', 'MixedArrayAccess'], ], @@ -539,7 +543,7 @@ function bar(string $b) : bool { exit; } - if ($i) {}', + if ($i !== array() && $i !== "" && $i !== "0") {}', ], 'emptyWithoutKnowingArrayType' => [ 'code' => ' ' 'RedundantCondition', + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'refineTypeInMethodCall' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'twoVarLogicNotNestedWithAllPathsReturning' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'twoVarLogicNotNestedWithAssignmentBeforeReturn' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'invertedTwoVarLogicNotNested' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'invertedTwoVarLogicNotNestedWithAssignmentBeforeReturn' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'twoVarLogicNotNestedWithElseifAndNoNegations' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'threeVarLogicNotNestedWithNoRedefinitionsWithClasses' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'threeVarLogicNotNestedAndOrWithNoRedefinitions' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'twoVarLogicNotNestedWithElseifCorrectlyNegatedInElseIf' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'lotsaTruthyStatements' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'cancelOutDifferentStatement' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'moreChecks' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'compareToIntInsideIfCNF' => [ 'code' => ' [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'ternaryAssertionOnBool' => [ 'code' => ' 'NullableReturnStatement', + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'invertedTwoVarLogicNotNestedWithElseif' => [ 'code' => ' 'NullableReturnStatement', + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'threeVarLogicWithElseifAndAnd' => [ 'code' => ' 'TypeDoesNotContainType', + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'twoVarLogicNotNestedWithElseifIncorrectlyReinforcedInIf' => [ 'code' => ' 'RedundantCondition', + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'repeatedIfStatements' => [ 'code' => ' 'TypeDoesNotContainType', + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'repeatedConditionals' => [ 'code' => ' 'RedundantCondition', + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], ], 'dependentTypeInvalidated' => [ 'code' => ' [ @@ -128,10 +128,10 @@ function foo(string $a): void { 'dummyByRefVar' => [ 'code' => 'getMessage(); } - if ($s) {} + if ($s !== null) {} }', ], 'throwWithMessageCallAndAssignmentInCatchAndReference' => [ @@ -940,7 +940,7 @@ function foo() : void { if ($foo) {} } catch (Exception $e) {} - if ($foo) {}', + if ($foo !== false && $foo !== 0) {}', ], 'useTryAssignedVariableInsideFinally' => [ 'code' => ' [], - 'ignored_issues' => [], + 'ignored_issues' => ['RiskyTruthyFalsyComparison'], 'php_version' => '8.0', ], 'concatWithUnknownProperty' => [ @@ -3165,7 +3165,7 @@ function bar() : void { $user = $user_id; } - if ($user) { + if ($user !== null && $user !== 0) { $a = 0; for ($i = 1; $i <= 10; $i++) { $a += $i; @@ -3185,7 +3185,7 @@ function bar() : void { $user = $user_id; } - if ($user) { + if ($user !== null && $user !== 0) { $a = 0; foreach ([1, 2, 3] as $i) { $a += $i; From dca17bcb6affce958f4650172db70379138a6d46 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Sat, 13 Jan 2024 13:05:43 +0100 Subject: [PATCH 07/12] unrelated fix spelling --- tests/ArrayAccessTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ArrayAccessTest.php b/tests/ArrayAccessTest.php index af33aa025f8..88eb7d15e52 100644 --- a/tests/ArrayAccessTest.php +++ b/tests/ArrayAccessTest.php @@ -691,7 +691,7 @@ function example(array $x, $y) : void { 'assertions' => [], 'ignored_issues' => ['MixedArgument', 'MixedArrayOffset', 'MissingParamType'], ], - 'suppressPossiblyUndefinedStringArrayOffet' => [ + 'suppressPossiblyUndefinedStringArrayOffset' => [ 'code' => ' Date: Sat, 13 Jan 2024 13:03:28 +0100 Subject: [PATCH 08/12] simplify and remove redundant variable --- .../Block/IfConditionalAnalyzer.php | 34 ++++++++----------- .../Expression/BooleanNotAnalyzer.php | 34 ++++++++----------- .../Statements/Expression/EmptyAnalyzer.php | 34 ++++++++----------- 3 files changed, 45 insertions(+), 57 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php index 23b88d0710e..ebbc0a66e4a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php @@ -371,34 +371,30 @@ public static function handleParadoxicalCondition( } elseif (!($stmt instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) && !($stmt instanceof PhpParser\Node\Expr\BinaryOp\Identical) && !($stmt instanceof PhpParser\Node\Expr\BooleanNot)) { - $has_both = false; - $both_types = $type->getBuilder(); if (count($type->getAtomicTypes()) > 1) { + $both_types = $type->getBuilder(); foreach ($both_types->getAtomicTypes() as $key => $atomic_type) { if ($atomic_type->isTruthy() || $atomic_type->isFalsy() || $atomic_type instanceof TBool) { $both_types->removeType($key); - continue; } - - $has_both = true; } - } - if ($has_both) { - $both_types = $both_types->freeze(); - IssueBuffer::maybeAdd( - new RiskyTruthyFalsyComparison( - 'Operand of type ' . $type->getId() . ' contains ' . - 'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' . - $both_types->getId() . ', which can be falsy and truthy. ' . - 'This can cause possibly unexpected behavior. Use strict comparison instead.', - new CodeLocation($statements_analyzer, $stmt), - $type->getId(), - ), - $statements_analyzer->getSuppressedIssues(), - ); + if (count($both_types->getAtomicTypes()) > 0) { + $both_types = $both_types->freeze(); + IssueBuffer::maybeAdd( + new RiskyTruthyFalsyComparison( + 'Operand of type ' . $type->getId() . ' contains ' . + 'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' . + $both_types->getId() . ', which can be falsy and truthy. ' . + 'This can cause possibly unexpected behavior. Use strict comparison instead.', + new CodeLocation($statements_analyzer, $stmt), + $type->getId(), + ), + $statements_analyzer->getSuppressedIssues(), + ); + } } } } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php index 71f6e19324f..93d17c3f7f5 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php @@ -45,34 +45,30 @@ public static function analyze( } elseif ($expr_type->isAlwaysFalsy()) { $stmt_type = new TTrue($expr_type->from_docblock); } else { - $has_both = false; - $both_types = $expr_type->getBuilder(); if (count($expr_type->getAtomicTypes()) > 1) { + $both_types = $expr_type->getBuilder(); foreach ($both_types->getAtomicTypes() as $key => $atomic_type) { if ($atomic_type->isTruthy() || $atomic_type->isFalsy() || $atomic_type instanceof TBool) { $both_types->removeType($key); - continue; } - - $has_both = true; } - } - if ($has_both) { - $both_types = $both_types->freeze(); - IssueBuffer::maybeAdd( - new RiskyTruthyFalsyComparison( - 'Operand of type ' . $expr_type->getId() . ' contains ' . - 'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' . - $both_types->getId() . ', which can be falsy and truthy. ' . - 'This can cause possibly unexpected behavior. Use strict comparison instead.', - new CodeLocation($statements_analyzer, $stmt), - $expr_type->getId(), - ), - $statements_analyzer->getSuppressedIssues(), - ); + if (count($both_types->getAtomicTypes()) > 0) { + $both_types = $both_types->freeze(); + IssueBuffer::maybeAdd( + new RiskyTruthyFalsyComparison( + 'Operand of type ' . $expr_type->getId() . ' contains ' . + 'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' . + $both_types->getId() . ', which can be falsy and truthy. ' . + 'This can cause possibly unexpected behavior. Use strict comparison instead.', + new CodeLocation($statements_analyzer, $stmt), + $expr_type->getId(), + ), + $statements_analyzer->getSuppressedIssues(), + ); + } } $stmt_type = new TBool(); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php index 02fae12fd44..8dbcca2f9bb 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php @@ -64,34 +64,30 @@ public static function analyze( } elseif ($expr_type->isAlwaysFalsy()) { $stmt_type = new TTrue($expr_type->from_docblock); } else { - $has_both = false; - $both_types = $expr_type->getBuilder(); if (count($expr_type->getAtomicTypes()) > 1) { + $both_types = $expr_type->getBuilder(); foreach ($both_types->getAtomicTypes() as $key => $atomic_type) { if ($atomic_type->isTruthy() || $atomic_type->isFalsy() || $atomic_type instanceof TBool) { $both_types->removeType($key); - continue; } - - $has_both = true; } - } - if ($has_both) { - $both_types = $both_types->freeze(); - IssueBuffer::maybeAdd( - new RiskyTruthyFalsyComparison( - 'Operand of type ' . $expr_type->getId() . ' contains ' . - 'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' . - $both_types->getId() . ', which can be falsy and truthy. ' . - 'This can cause possibly unexpected behavior. Use strict comparison instead.', - new CodeLocation($statements_analyzer, $stmt), - $expr_type->getId(), - ), - $statements_analyzer->getSuppressedIssues(), - ); + if (count($both_types->getAtomicTypes()) > 0) { + $both_types = $both_types->freeze(); + IssueBuffer::maybeAdd( + new RiskyTruthyFalsyComparison( + 'Operand of type ' . $expr_type->getId() . ' contains ' . + 'type' . (count($both_types->getAtomicTypes()) > 1 ? 's' : '') . ' ' . + $both_types->getId() . ', which can be falsy and truthy. ' . + 'This can cause possibly unexpected behavior. Use strict comparison instead.', + new CodeLocation($statements_analyzer, $stmt), + $expr_type->getId(), + ), + $statements_analyzer->getSuppressedIssues(), + ); + } } $stmt_type = new TBool(); From 5643cf53d4983b75a7772cd96b53b109c3a020a9 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Sat, 13 Jan 2024 16:00:57 +0100 Subject: [PATCH 09/12] fix mixed test not actually checking mixed (since superglobals have a more specific type now) --- tests/TypeReconciliation/ConditionalTest.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/TypeReconciliation/ConditionalTest.php b/tests/TypeReconciliation/ConditionalTest.php index 5a070688870..4d48add4f1d 100644 --- a/tests/TypeReconciliation/ConditionalTest.php +++ b/tests/TypeReconciliation/ConditionalTest.php @@ -1168,11 +1168,19 @@ function Foo($width, $height) : void { ], 'notEmptyCheckOnMixedInTernary' => [ 'code' => ' [ 'code' => ' Date: Mon, 15 Jan 2024 10:09:49 +0100 Subject: [PATCH 10/12] change error level to 2 --- docs/running_psalm/error_levels.md | 2 +- src/Psalm/Issue/RiskyTruthyFalsyComparison.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/running_psalm/error_levels.md b/docs/running_psalm/error_levels.md index df53f5227f6..f3df22adb45 100644 --- a/docs/running_psalm/error_levels.md +++ b/docs/running_psalm/error_levels.md @@ -173,7 +173,6 @@ Level 5 and above allows a more non-verifiable code, and higher levels are even - [TooManyArguments](issues/TooManyArguments.md) - [TypeDoesNotContainNull](issues/TypeDoesNotContainNull.md) - [TypeDoesNotContainType](issues/TypeDoesNotContainType.md) -- [RiskyTruthyFalsyComparison](issues/RiskyTruthyFalsyComparison.md) - [UndefinedMagicMethod](issues/UndefinedMagicMethod.md) - [UndefinedMagicPropertyAssignment](issues/UndefinedMagicPropertyAssignment.md) - [UndefinedMagicPropertyFetch](issues/UndefinedMagicPropertyFetch.md) @@ -245,6 +244,7 @@ Level 5 and above allows a more non-verifiable code, and higher levels are even - [RedundantConditionGivenDocblockType](issues/RedundantConditionGivenDocblockType.md) - [RedundantFunctionCallGivenDocblockType](issues/RedundantFunctionCallGivenDocblockType.md) - [ReferenceConstraintViolation](issues/ReferenceConstraintViolation.md) +- [RiskyTruthyFalsyComparison](issues/RiskyTruthyFalsyComparison.md) - [UndefinedTrace](issues/UndefinedTrace.md) - [UnresolvableInclude](issues/UnresolvableInclude.md) - [UnsafeInstantiation](issues/UnsafeInstantiation.md) diff --git a/src/Psalm/Issue/RiskyTruthyFalsyComparison.php b/src/Psalm/Issue/RiskyTruthyFalsyComparison.php index 9150aa30b8c..68ab4e1322b 100644 --- a/src/Psalm/Issue/RiskyTruthyFalsyComparison.php +++ b/src/Psalm/Issue/RiskyTruthyFalsyComparison.php @@ -6,7 +6,7 @@ final class RiskyTruthyFalsyComparison extends CodeIssue { - public const ERROR_LEVEL = 4; + public const ERROR_LEVEL = 2; public const SHORTCODE = 356; public function __construct(string $message, CodeLocation $code_location, ?string $dupe_key) From 4b418918299e9846ca95c2f9451a2a8fa8063135 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Mon, 15 Jan 2024 10:33:46 +0100 Subject: [PATCH 11/12] update baseline --- psalm-baseline.xml | 1874 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1868 insertions(+), 6 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 71cc2ef6324..85f358308e2 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,186 @@ - + + + + mapper]]> + + + + + $items + + + + + $returnType + attrGroups]]> + byRef]]> + expr]]> + params]]> + returnType]]> + static]]> + + + + + + + + $returnType + attrGroups]]> + byRef]]> + params]]> + returnType]]> + static]]> + stmts]]> + uses]]> + + + + + $items + + + + + $parts + + + + + $conds + + + + + $parts + $parts + $parts + + + + + $stmts + + + + + $stmts + + + + + $returnType + attrGroups]]> + byRef]]> + flags]]> + params]]> + returnType]]> + stmts]]> + + + + + attrGroups]]> + extends]]> + flags]]> + implements]]> + stmts]]> + + + + + $stmts + + + + + $stmts + + + + + $stmts + + + + + $stmts + + + + + cond]]> + init]]> + loop]]> + stmts]]> + + + + + byRef]]> + keyVar]]> + stmts]]> + + + + + $returnType + attrGroups]]> + byRef]]> + params]]> + returnType]]> + stmts]]> + + + + + else]]> + elseifs]]> + stmts]]> + + + + + attrGroups]]> + extends]]> + stmts]]> + + + + + $stmts + + + + + attrGroups]]> + stmts]]> + + + + + $stmts + + + + + $stmts + + + + + static::getDefaultDescription() + static::getDefaultDescription() + static::getDefaultDescription() + static::getDefaultName() + static::getDefaultName() + static::getDefaultName() + + + $name + + tags['variablesfrom'][0]]]> @@ -12,6 +193,25 @@ $matches[1] + + + cased_name]]> + + + + + !$appearing_method_id + + + + + docblock_line_number]]> + docblock_line_number]]> + docblock_start]]> + docblock_start_line_number]]> + text]]> + + $const_name @@ -19,11 +219,83 @@ $symbol_name $symbol_parts[1] + + !$function_name + namespace]]> + namespace]]> + namespace]]> + namespace_first_stmt_start]]> + uses_end]]> + $file_path + insertText]]> + symbol, '()')]]> + symbol, '()')]]> + symbol, '()')]]> + symbol, '()')]]> + symbol, '::')]]> + symbol, '::')]]> + symbol, '\\')]]> + + + + + + + + + + + + + !$composer_json + !$config_path + !$file_path + + $cwd + $dir + function_id]]> + + $issue_handler_children + $parent_issue_type + composer_class_loader->findFile($pluginClassName)]]> + autoloader]]> + localName, $offset)]]> + name, $offset - strlen($file_contents))]]> + + + + + $suggested_dir + file_path, 'stub')]]> + file_path, 'vendor')]]> + + + !$directory_path + !$file_path + !$glob_directory_path + !$glob_file_path + directory]]> + file]]> + referencedClass]]> + referencedConstant]]> + referencedFunction]]> + referencedMethod]]> + referencedProperty]]> + referencedVariable]]> + glob($parts[0], GLOB_NOSORT) + glob($parts[0], GLOB_ONLYDIR | GLOB_NOSORT) + + + + + + + @@ -32,6 +304,17 @@ $matches[3] + + + $creating_conditional_id + $creating_conditional_id + + + + + name->name ?? null !== "name"]]> + + $comments[0] @@ -39,11 +322,147 @@ props[0]]]> $uninitialized_variables[0] + + !$declaring_property_class + !$fq_class_name + self]]> + self]]> + self]]> + self]]> + template_extended_params]]> + template_types]]> + $class_template_params + initialized_class]]> + $parent_fq_class_name + getStmts()]]> + getStmts()]]> + template_extended_params]]> + template_types]]> + classlike_storage_provider->get($original_fq_classlike_name), + strtolower($stmt->name->name), + $this_object_type, + )]]> + $property_name + + !$appearing_property_class + self]]> + !$declaring_property_class + self]]> + template_types]]> + $resolved_name + template_covariants]]> + template_extended_params]]> + template_types]]> + template_types]]> + + + + + self]]> + self]]> + self]]> + + + + + !$original_type + description]]> + var_id]]> + !$var_type_tokens + $brackets + $template_type_map + $type_aliases + line_number]]> + type_end]]> + type_start]]> + + + + + $namespace_name + $namespace_name + root_file_name]]> + root_file_path]]> + + + + + $namespace + $namespace + getNamespace()]]> + + + + + getStmts()]]> + $class_template_params + self]]> + self]]> + $fq_class_name + $self_fq_class_name + + + + + calling_method_id]]> + cased_name]]> + cased_name]]> + + template_types]]> + template_types]]> + $cased_method_id + $cased_method_id + $cased_method_id + $cased_method_id + $cased_method_id + self]]> + self]]> + self]]> + self]]> + self]]> + $context_self + $hash + $namespace + $parent_fqcln + $parent_fqcln + cased_name]]> + template_types]]> + $template_types + function->getStmts()]]> + source->getTemplateTypeMap()]]> + storage->template_types]]> + + + + + + !$calling_method_id + self]]> + $appearing_method_class + $appearing_method_class + self]]> + $context_self + + + + + template_types]]> + cased_name]]> + cased_name]]> + cased_name]]> + template_extended_params]]> + template_extended_params]]> + template_extended_params]]> + defining_fqcln]]> + @@ -53,22 +472,86 @@ $php_minor_version $source_parts[1] + + self]]> + + $potential_file_path + + + + + + branch_point]]> + cond]]> + + branch_point]]> + if (AtomicTypeComparator::isContainedBy( if (AtomicTypeComparator::isContainedBy( + + var_id]]> + var_id]]> + $calling_type_params + branch_point]]> + template_types]]> + getTemplateTypeMap()]]> + line_number]]> + type_end]]> + type_start]]> + $var_id + $var_id + + + + + negatable_if_types]]> + getTemplateTypeMap()]]> + + + + + getTemplateTypeMap()]]> + getTemplateTypeMap()]]> + + + + + getTemplateTypeMap()]]> + + + + + branch_point]]> + branch_point]]> + branch_point]]> + assigned_var_ids]]> + new_vars]]> + redefined_vars]]> + getTemplateTypeMap()]]> + assigned_var_ids += $switch_scope->new_assigned_var_ids]]> + + !$switch_var_id + new_assigned_var_ids]]> + new_vars_in_scope]]> + possibly_redefined_vars]]> + possibly_redefined_vars]]> + redefined_vars]]> + $switch_var_id + @@ -76,6 +559,29 @@ leftover_statements[0]]]> traverse([$switch_condition])[0]]]> + + branch_point]]> + $nested_or_options + $switch_var_id + $switch_var_id + $switch_var_id + $type_statements + + + + + branch_point]]> + + + + + branch_point]]> + + + + + $var_id + @@ -108,23 +614,204 @@ getArgs()[0]]]> getArgs()[0]]]> + + !$var_name + !$var_type + ')]]> + + $array_root + $count_equality_position + $count_equality_position + $count_equality_position + $count_inequality_position + $count_inequality_position + $count_inequality_position + $false_position + $false_position + $first_var_name + $first_var_name + $first_var_name + $first_var_name + $first_var_name + $first_var_name + $first_var_name + $first_var_name + $first_var_name + $first_var_name + $first_var_name + $first_var_name + $first_var_name + $first_var_name_in_array_argument + $get_debug_type_position + $get_debug_type_position + $getclass_position + $getclass_position + $gettype_position + $gettype_position + $if_false_assertions + $if_true_assertions + $inferior_value_position + $other_var_name + $superior_value_position + $this_class_name + $this_class_name + $this_class_name + $true_position + $true_position + $typed_value_position + $typed_value_position + $var_id + $var_id + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name + $var_name_left + $var_name_right + $var_type + $var_type + $var_type + self::hasReconcilableNonEmptyCountEqualityCheck($conditional) + + + + + !$parent_var_id + $object_id + $parent_var_id + $parent_var_id + $root_var_id + $root_var_id + $root_var_id + $root_var_id + $root_var_id + $var_id + $var_var_id + + + + + self]]> + !$var_id + $appearing_property_class + $class_template_params + $class_template_params + calling_method_id]]> + calling_method_id]]> + self]]> + self]]> + self]]> + $declaring_property_class + getter_method]]> + $var_id + $var_id + $var_id + $var_id + $var_id + $var_id + $var_id + $var_id + $var_id + $var_property_id + $var_property_id + calling_method_id, '::__clone')]]> + calling_method_id, '::__construct')]]> + calling_method_id, '::__unserialize')]]> + calling_method_id, '::unserialize')]]> + $new_property_name + + calling_method_id]]> + $var_id + $var_id + + + var_id]]> + ')]]> + ')]]> + + $assign_value_id + calling_method_id]]> + $extended_var_id + $extended_var_id + $extended_var_id + $extended_var_id + $extended_var_id + $list_var_id + $list_var_id + $list_var_id + $prop_name + $root_var_id + line_number]]> + type_end]]> + type_start]]> + $var_id + $var_id + $var_id + $var_id + $var_id + $var_id + $var_id + $var_id + $var_id + vars_in_scope[$lhs_var_id] = &$context->vars_in_scope[$rhs_var_id]]]> + + + getTemplateTypeMap()]]> + + $invalid_left_messages[0] $invalid_right_messages[0] + + + branch_point]]> + $var_id + + verifyType @@ -134,6 +821,54 @@ $parts[1] + + !$container_class + $cased_method_id + $cased_method_id + $cased_method_id + $cased_method_id + $cased_method_id + $class_generic_params + calling_function_id]]> + calling_function_id]]> + calling_method_id]]> + $self_fq_class_name + $static_fq_class_name + $var_id + value, '::')]]> + value, '::')]]> + + + + + self]]> + $cased_method_id + $cased_method_id + $cased_method_id + $cased_method_id + $cased_method_id + $cased_method_id + calling_method_id]]> + calling_method_id]]> + calling_method_id]]> + calling_method_id]]> + calling_method_id]]> + calling_method_id]]> + calling_method_id]]> + sinks]]> + $function_params + $function_params + $function_params + template_types]]> + $method_id + $method_id + $method_id + $method_id + $var_id + $var_id + $var_id + getFQCLN())]]> + @@ -142,19 +877,117 @@ $args[1] $method_name + + !$container_class + calling_method_id]]> + $var_id + + + + + !$template_types + !$template_types + template_types]]> + $method_name + $overridden_template_types + template_extended_params]]> + template_types]]> + + + $function_name + $function_name + + + getArgs()[0]->value]]> + getArgs()[0]]]> $parts[1] + + function_id]]> + function_id]]> + function_id]]> + function_id]]> + function_id]]> + function_id]]> + getTemplateTypeMap()]]> + value, '::')]]> + $method + + self]]> + self]]> + self]]> + template_types]]> + template_types]]> + + + + + mixin_declaring_fqcln]]> + mixin_declaring_fqcln]]> + template_types]]> + template_types]]> + calling_method_id]]> + calling_method_id]]> + self]]> + $lhs_var_id + $mixin_class_template_params + + + + + $class_template_params + calling_method_id]]> + calling_method_id]]> + $lhs_var_id + template_types]]> + template_types]]> + + + + + $caller_identifier + + + + + this_property_mutations]]> + + + + + specialization_key]]> + $var_id + + + + + self]]> + self]]> + $appearing_method_name + + + + + $found_generic_params + $found_generic_params + $found_generic_params + $found_generic_params + $found_generic_params + $found_generic_params + $intersection_method_id + $intersection_method_id + @@ -165,23 +998,115 @@ non_existent_interface_method_ids[0]]]> non_existent_magic_method_ids[0]]]> + + getFQCLN()]]> + $lhs_var_id + $lhs_var_id + $lhs_var_id + + + + + getFQCLN()]]> + $path_to_file + $var_id + ')]]> + + + + + + calling_method_id]]> + self]]> + $fq_class_name + $fq_class_name + getFullyQualifiedFunctionMethodOrNamespaceName()]]> + template_extended_params]]> + template_types]]> + template_types]]> + template_types]]> + + + + + parent_class]]> + $child_fq_class_name + calling_method_id]]> + self]]> + self]]> + + + + + self]]> + !$fq_class_name + mixin_declaring_fqcln]]> + parent_class]]> + parent_class]]> + calling_method_id]]> + calling_method_id]]> + self]]> + $new_method_name + + self]]> + self]]> + self]]> + self]]> + $found_generic_params + $found_generic_params + template_extended_params]]> + items[0]]]> items[1]]]> + + !$arg_var_id + $arg_var_id + $assertion_var_id + template_extended_params]]> + self]]> + self]]> + self]]> + template_types]]> + template_types]]> + $new_const_name $new_const_name + + self]]> + calling_method_id]]> + calling_method_id]]> + self]]> + self]]> + self]]> + self]]> + + + + + !$lhs_var_name + !$object_id + !$object_id + !$this_class_name + $object_id + $property_root + $resolved_name + $resolved_name + $root_var_id + $this_class_name + @@ -189,31 +1114,143 @@ $stmt_type $stmt_type + + $dim_var_id + $dim_var_id + $extended_var_id + $extended_var_id + $keyed_array_var_id + $keyed_array_var_id + $keyed_array_var_id + $keyed_array_var_id + $stmt_type + + self]]> + self]]> + $declaring_property_class + $declaring_property_class + template_types]]> + template_types]]> + $var_id + $var_id + $var_property_id + $var_property_id + $invalid_fetch_types[0] + + !$prop_name + calling_method_id]]> + calling_method_id]]> + $declaring_property_class + $stmt_var_id + $var_id + $var_id + $new_property_name + + !$prop_name + calling_method_id]]> + calling_method_id]]> + calling_method_id]]> + self]]> + $string_type + $var_id + $var_id + + + + + $branch_point + $branch_point + + + + + $var_id + + + + + !$evaled_path + !$var_id + $include_path + $left_string + $path_to_file + $right_string + $var_id + + + + + self]]> + + + + + !$switch_var_id + $switch_var_id + + + + + $fq_classlike_name + + + + + branch_point]]> + getTemplateTypeMap()]]> + getTemplateTypeMap()]]> + type_params[2]]]> + + var_id]]> + $class_template_params + declaring_yield_fqcn]]> + self]]> + line_number]]> + type_end]]> + type_start]]> + $method_name + + calling_function_id]]> + calling_method_id]]> + var_id]]> + calling_function_id]]> + self]]> + $found_generic_params + line_number]]> + type_end]]> + type_start]]> + + + + + $root_var_id + $var_id + @@ -231,21 +1268,84 @@ expr->getArgs()[0]]]> + + $branch_point + $new_issues + getNamespace()]]> + $possible_traced_variable_names + fake_this_class]]> + vars_to_initialize]]> + + + + + UndefinedFunction + UndefinedFunction + + + + + !$root_path + + + + error_baseline]]> + !$paths_to_check + !$root_path + + + $baseline_file_path + $cache_directory + threads]]> + $find_references_to + empty($baselineFile) + + + + + + !$root_path + $paths_to_check + $identifier_name + + !$last_arg + !$last_arg + !$last_arg + !$root_path + + + + + + !$config_file + !$end_psalm_open_tag + !$path_to_check + error_baseline]]> + $f_paths + $path_to_config + $stdin = fgets(STDIN) + getPHPVersionFromComposerJson()]]> + getPhpVersionFromConfig()]]> + + $trait + + + @@ -255,11 +1355,92 @@ $source_const_name $stub + + !$calling_fq_class_name + !$insert_pos + !$insert_pos + !$insert_pos + $calling_fq_class_name + $calling_fq_class_name + $calling_fq_class_name + $calling_fq_class_name + $calling_fq_class_name + $calling_fq_class_name + $calling_fq_class_name + $calling_fq_class_name + $calling_fq_class_name + $calling_fq_class_name + $calling_method_id + $calling_method_id + $calling_method_id + $calling_method_id + $calling_method_id + $file_path + $file_path + $file_path + $file_path + $file_path + $migrated_source_fqcln + $migrated_source_fqcln + + + + + value]]> + $stub + + !$checked_file_path + !$root_file_path + $args + cased_name]]> + $namespace + + + + + !$return_type_string + + + + + !$calling_class_name + !$extends + $calling_method_id + $calling_method_id + $calling_method_id + $calling_method_id + $calling_method_id + $calling_method_id + $calling_method_id + $calling_method_id + $calling_method_id + $found_generic_params + $old_method_id + $source_file_path + $source_file_path + $source_file_path + $source_file_path + $source_file_path + $source_file_path + $source_file_path + $source_file_path + + + + + + $mapped_name + template_extended_params]]> + template_extended_params]]> + template_extended_params]]> + template_types]]> + template_extended_params]]> + @@ -270,6 +1451,38 @@ $property_name $property_name + + calling_method_id]]> + calling_method_id]]> + calling_method_id]]> + + + + + $composer_file_path + cased_name]]> + cased_name]]> + + + + + specialization_key]]> + unspecialized_id]]> + escaped_taints]]> + unescaped_taints]]> + specialization_key]]> + path_types)]]> + + + + + + + + + + $specialization_key + @@ -280,28 +1493,110 @@ stmts[0]]]> $b_stmt_comments[0] + + stmts]]> + stmts]]> + $b[$y] + + + readEnv['CI_PR_NUMBER']]]> + + $exploded[1] $url + + + $var_end + $var_start + + + + + + new_php_return_type]]> + $last_arg_position + new_php_return_type]]> + new_phpdoc_return_type]]> + return_typehint_colon_start]]> + return_typehint_end]]> + return_typehint_end]]> + return_typehint_start]]> + return_typehint_start]]> + $php_type + new_phpdoc_return_type]]> + new_psalm_return_type]]> + return_type_description]]> + return_type_description]]> + + props[0]]]> + + new_php_type]]> + new_php_type]]> + new_phpdoc_type]]> + typehint_end]]> + typehint_end]]> + typehint_start]]> + typehint_start]]> + $preceding_semicolon_pos + new_phpdoc_type]]> + new_psalm_type]]> + type_description]]> + + + + + !$sockets + + + + + tmpIni]]> + + + + + empty($message) + + + + + TCPServerAddress]]> + TCPServerAddress]]> + onchangeLineLimit]]> + empty($additional_info) + $method_id_parts[1] + + + $arg_var_id + $arg_var_id + $left_var_id + $left_var_id + $right_var_id + $right_var_id + $var_id + $var_id + + $cs[0] @@ -313,6 +1608,17 @@ $replacement_stmts[0] $replacement_stmts[0] + + !$method_contents + parser->parse( + $hacky_class_fix, + $error_handler, + )]]> + parser->parse( + $fake_class, + $error_handler, + )]]> + @@ -321,18 +1627,56 @@ children[0]]]> children[1]]]> + + !$method_entry + + $l[4] $r[4] + + !$var_line_parts + newModifier]]> + $class_name + description]]> + inheritors]]> + yield]]> + template_types]]> + template_types]]> + template_types]]> + template_types]]> + template_types]]> + template_types]]> + template_types]]> + aliases->namespace]]> + aliases->namespace]]> + line_number]]> + type_end]]> + type_start]]> + + + + + $fq_classlike_name + $string_value + $string_value + $string_value + getArgs()[0]]]> getArgs()[1]]]> + + !$skip_if_descendants + !$skip_if_descendants + $include_path + $path_to_file + @@ -346,16 +1690,75 @@ $source_param_string + + namespace]]> + template_types]]> + template_types]]> + description]]> + return_type_end]]> + return_type_line_number]]> + return_type_line_number]]> + return_type_start]]> + template_types]]> + template_types]]> + template_types]]> + template_types]]> + $template_types + $template_types + $template_types + stmts[0]]]> + + stmts]]> + aliases->namespace]]> + aliases->namespace]]> + template_types]]> + $fq_classlike_name + $function_id + $function_id + $method_name_lc + stmts]]> + stmts]]> + stmts]]> + stmts]]> + aliases->namespace]]> + aliases->namespace]]> + + + + + $type_string + + + + + aliases->uses_start]]> + aliases->uses_start]]> + skip_if_descendants]]> + skip_if_descendants]]> + skip_if_descendants]]> + skip_if_descendants]]> + skip_if_descendants]]> + skip_if_descendants]]> + skip_if_descendants]]> + code_location->file_path, 'CoreGenericClasses.phpstub')]]> + code_location->file_path, 'CoreGenericFunctions.phpstub')]]> + file_path, 'CoreGenericIterators.phpstub')]]> + $cs[0] + + $offset_map + end_change]]> + start_change]]> + @@ -383,6 +1786,87 @@ getOption('config')]]> + + + !$path + $explicit_path + psalm_header]]> + psalm_tag_end_pos]]> + + + + + enabled_plugins]]> + + + + + !$root_cache_directory + $file_contents + $file_path + + + + + !$cache_directory + !$cache_directory + !$cache_directory + $cache_directory + + + + + cache->getFileMapCache()]]> + + + + + !$root_cache_directory + + + + + $result + + + + + $called_method_name + + + + + $extended_var_id + + + + + !$cache_directory + !$root_cache_directory + !$root_cache_directory + !$root_cache_directory + + + + + !$cache_directory + !$cache_directory + composer_lock_hash]]> + $cache_directory + + + + + !$key_column_name + + + + + $callable_extended_var_id + getTemplateTypeMap()]]> + getTemplateTypeMap()]]> + + $callable_method_name @@ -398,6 +1882,73 @@ $method_name + + + $fetch_class_name + + + + + !$call_args + + + + + $existing_file_contents + $existing_file_contents + $existing_file_contents + $existing_statements + $existing_statements + $existing_statements + $existing_statements + $file_changes + $file_path + parse($file_contents, $error_handler)]]> + parse($file_contents, $error_handler)]]> + + + + + + $first_line_padding + + + + + !$resolved_name + $mapped_type = $map[$offset_arg_value] ?? null + $mapped_type = $map[$offset_arg_value] ?? null + + + + + + + cased_name]]> + template_types]]> + parent_class]]> + template_types]]> + + + + + cased_name]]> + cased_name]]> + template_types]]> + + + + + $key + $var_id + $var_id + $var_id + $var_id + $var_id + $var_id + $var_id + + isContainedBy @@ -414,6 +1965,36 @@ TCallable|TClosure|null + + !$class_name + $calling_method_id + $calling_method_id + $calling_method_id + $calling_method_id + $calling_method_id + params]]> + $file_name + $file_name + $input_variadic_param_idx + $member_id + + + + + !($container_type_params_covariant[$i] ?? false) + + + + + $intersection_container_type_lower + + + + + $key + $key + $key + @@ -422,11 +2003,83 @@ $properties[0] $properties[0] + + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $var_id + $var_id + $var_id + $var_id + + + + + + !$count + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $key + $var_id + $var_id + $var_id + $var_id + + + + + template_extended_params]]> + getClassTemplateTypes + + + + $input_template_types + template_extended_params[$container_class])]]> + template_extended_params[$base_type->as_type->value])]]> + template_extended_params[$base_type->value])]]> + + @@ -437,11 +2090,48 @@ array_type_params[1]]]> array_type_params[1]]]> + + class_string_types]]> + floats]]> + ints]]> + named_object_types]]> + strings]]> + array_counts]]> + array_min_counts]]> + array_min_counts]]> + class_string_types]]> + class_string_types]]> + floats]]> + ints]]> + ints]]> + ints]]> + named_object_types]]> + strings]]> + strings]]> + strings]]> + strings]]> + value_types['string'] instanceof TNonFalsyString + ? $type->value + : $type->value !== '']]> + $shared_classlikes + $fallback_params + + template_types]]> + $params + $parent_class + $self_class + $self_class + $self_class + $self_class + $self_class + $self_class + $static_class_type + @@ -455,6 +2145,11 @@ array_keys($template_type_map[$fq_classlike_name])[0] array_keys($template_type_map[$template_param_name])[0] + + $extra_params + value, '::')]]> + value, '::')]]> + @@ -463,6 +2158,59 @@ $type_tokens[$i - 1] $type_tokens[$i - 1] + + $parent_fqcln + $self_fqcln + + + + + + !$fq_classlike_name + template_types]]> + template_types]]> + calling_method_id]]> + + + + + $function_id + + + + + $function_id + + + + + $function_id + + + + + output_path]]> + $parent_issue_type + + + + + other_references]]> + taint_trace]]> + + + + + + other_references]]> + taint_trace]]> + + + + + + taint_trace]]> + @@ -477,6 +2225,19 @@ traverse + + + $this_var_id + + + + + !$namespace + $namespace + $namespace + + + classOrInterfaceExists @@ -495,6 +2256,9 @@ $value + + + @@ -503,6 +2267,10 @@ replace replace + + $params + $params + @@ -513,6 +2281,9 @@ type_params[1]]]> + + !($container_type_params_covariant[$offset] ?? true) + @@ -528,6 +2299,10 @@ replace + + !$namespace + $namespace + @@ -540,6 +2315,12 @@ value_param]]> + + + !$intersection + !$intersection + + replace @@ -550,6 +2331,17 @@ __construct + + + !$intersection + !$intersection + + + + + !$intersection + + TList @@ -591,6 +2383,18 @@ type_param]]> + + + !$namespace + $namespace + + + + + !$intersection + $intersection + + TList @@ -604,12 +2408,21 @@ replace replace + + !$intersection + !$intersection + replace + + + !$intersection + + replace @@ -620,6 +2433,11 @@ replace + + + extra_types]]> + + $allow_mutations @@ -630,11 +2448,24 @@ $initialized_class $reference_free + + + $const_name + + + + $array_key_offset + $failed_reconciliation + + + ')]]> + + @@ -670,10 +2501,41 @@ hasLowercaseString hasLowercaseString - - - - - + + !$php_type + exact_id]]> + id]]> + exact_id]]> + exact_id]]> + id]]> + id]]> + + + + + + + + + + + $level + $php_version + + + + + + + + + + + $param_type_1 + $param_type_2 + $param_type_3 + $param_type_4 + $return_type + From a3a13241a76d19e7e7f0f51bef973f8c9266d594 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Wed, 17 Jan 2024 12:41:58 +0100 Subject: [PATCH 12/12] fix baseline somehow incorrect paths --- psalm-baseline.xml | 52 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 85f358308e2..ed55df4536a 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,16 +1,16 @@ - + mapper]]> - + $items - + $returnType attrGroups]]> @@ -24,7 +24,7 @@ - + $returnType attrGroups]]> @@ -36,39 +36,39 @@ uses]]> - + $items - + $parts - + $conds - + $parts $parts $parts - + $stmts - + $stmts - + $returnType attrGroups]]> @@ -79,7 +79,7 @@ stmts]]> - + attrGroups]]> extends]]> @@ -88,27 +88,27 @@ stmts]]> - + $stmts - + $stmts - + $stmts - + $stmts - + cond]]> init]]> @@ -116,14 +116,14 @@ stmts]]> - + byRef]]> keyVar]]> stmts]]> - + $returnType attrGroups]]> @@ -133,42 +133,42 @@ stmts]]> - + else]]> elseifs]]> stmts]]> - + attrGroups]]> extends]]> stmts]]> - + $stmts - + attrGroups]]> stmts]]> - + $stmts - + $stmts - + static::getDefaultDescription() static::getDefaultDescription()