From 51bfb3666b0059782f4ae815d6af9ae199e2eb4b Mon Sep 17 00:00:00 2001 From: Brown Date: Tue, 25 Aug 2020 11:24:57 -0400 Subject: [PATCH] Fix #4052 - foreach over an iterable is impure --- .../Statements/Block/ForeachAnalyzer.php | 25 +++++++++++++++++++ tests/PureAnnotationTest.php | 19 ++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php index 662085d99..62544c7c7 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php @@ -16,6 +16,7 @@ use Psalm\Internal\FileManipulation\FileManipulationBuffer; use Psalm\CodeLocation; use Psalm\Context; use Psalm\Exception\DocblockParseException; +use Psalm\Issue\ImpureMethodCall; use Psalm\Issue\InvalidDocblock; use Psalm\Issue\InvalidIterator; use Psalm\Issue\NullIterator; @@ -563,6 +564,30 @@ class ForeachAnalyzer } $has_valid_iterator = true; + + if (!$context->pure) { + $project_analyzer = $statements_analyzer->getProjectAnalyzer(); + + if ($codebase->alter_code + && (isset($project_analyzer->getIssuesToFix()['MissingPureAnnotation']) + || isset($project_analyzer->getIssuesToFix()['MissingImmutableAnnotation'])) + && $statements_analyzer->getSource() + instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer + ) { + $statements_analyzer->getSource()->inferred_has_mutation = true; + $statements_analyzer->getSource()->inferred_impure = true; + } + } else { + if (IssueBuffer::accepts( + new ImpureMethodCall( + 'Cannot call a possibly-mutating Traversable::getIterator from a pure context', + new CodeLocation($statements_analyzer, $stmt) + ), + $statements_analyzer->getSuppressedIssues() + )) { + // fall through + } + } } elseif ($iterator_atomic_type instanceof Type\Atomic\TNamedObject) { if ($iterator_atomic_type->value !== 'Traversable' && $iterator_atomic_type->value !== $statements_analyzer->getClassName() diff --git a/tests/PureAnnotationTest.php b/tests/PureAnnotationTest.php index 7c1c82ccc..048b556c0 100644 --- a/tests/PureAnnotationTest.php +++ b/tests/PureAnnotationTest.php @@ -732,6 +732,25 @@ class PureAnnotationTest extends TestCase }', 'error_message' => 'ImpureVariable', ], + 'iterableIsNotPure' => [ + ' $pieces + * + * @psalm-pure + */ + function foo(iterable $pieces): string + { + foreach ($pieces as $piece) { + return $piece; + } + + return "jello"; + }', + 'error_message' => 'ImpureMethodCall', + ], ]; } }