diff --git a/.github/workflows/windows-ci.yml b/.github/workflows/windows-ci.yml index f733c0beda7..ab61f84ea31 100644 --- a/.github/workflows/windows-ci.yml +++ b/.github/workflows/windows-ci.yml @@ -56,7 +56,7 @@ jobs: with: php-version: '8.5' #ini-values: zend.assertions=1, assert.exception=1, opcache.enable_cli=1, opcache.jit=function, opcache.jit_buffer_size=512M - ini-values: zend.assertions=1, assert.exception=1, opcache.enable_cli=1, opcache.jit=off + ini-values: zend.assertions=1, assert.exception=1 tools: composer:v2 coverage: none #extensions: none, curl, dom, filter, intl, json, libxml, mbstring, openssl, opcache, pcre, phar, reflection, simplexml, spl, tokenizer, xml, xmlwriter diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php index 3d70bc56bba..94531fa4d34 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php @@ -1110,6 +1110,8 @@ private static function getNewType( * Whether the constructor binds the given template through a `class-string` * (`T::class`) parameter position, which names the template's type exactly * rather than providing a value of it. + * + * @psalm-mutation-free */ private static function templateBoundThroughClassString( MethodStorage $method_storage, @@ -1139,6 +1141,7 @@ private static function templateBoundThroughClassString( * only have been fixed at the construction site. * * @return array + * @psalm-mutation-free */ private static function getUnconstrainableTemplates(ClassLikeStorage $storage): array { diff --git a/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php b/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php index 1bbf23c481e..a5aad70ea0e 100644 --- a/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php @@ -168,8 +168,16 @@ final class StatementsAnalyzer extends SourceAnalyzer */ public readonly bool $owns_type_variable_tracker; - public function __construct(protected SourceAnalyzer $source, public NodeDataProvider $node_data) - { + private int $depth = 0; + + /** + * @psalm-mutation-free + */ + public function __construct( + protected SourceAnalyzer $source, + public NodeDataProvider $node_data, + private readonly bool $root_scope, + ) { $this->file_analyzer = $source->getFileAnalyzer(); $this->codebase = $source->getCodebase(); @@ -185,10 +193,17 @@ public function __construct(protected SourceAnalyzer $source, public NodeDataPro $this->owns_type_variable_tracker = true; } - if ($this->codebase->taint_flow_graph) { - $this->data_flow_graph = new TaintFlowGraph(); - } elseif ($this->codebase->find_unused_variables) { - $this->data_flow_graph = new VariableUseGraph(); + if ($this->codebase->taint_flow_graph + && $root_scope + && $this->codebase->config->trackTaintsInPath($this->getFilePath()) + ) { + $this->data_flow_graph = $this->taint_flow_graph = $this->codebase->taint_flow_graph; + } + if ($this->codebase->find_unused_variables) { + $this->data_flow_graph = $this->variable_use_graph = new VariableUseGraph(); + } + if ($this->taint_flow_graph && $this->variable_use_graph) { + $this->data_flow_graph = new CombinedFlowGraph($this->variable_use_graph, $this->taint_flow_graph); } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterUtils.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterUtils.php index 6331724668a..0d395b965fd 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterUtils.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterUtils.php @@ -20,7 +20,6 @@ use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; use Psalm\Type\Atomic\TBool; -use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TInt; diff --git a/src/Psalm/Internal/Type/TypeVariableBounds.php b/src/Psalm/Internal/Type/TypeVariableBounds.php index 65597cb2d11..be941e8f795 100644 --- a/src/Psalm/Internal/Type/TypeVariableBounds.php +++ b/src/Psalm/Internal/Type/TypeVariableBounds.php @@ -12,6 +12,10 @@ * when the surrounding function-like has been analyzed. * * @internal + * @psalm-suppress MissingImmutableAnnotation TypeVariableTracker::addBounds() + * appends to lower_bounds/upper_bounds in place, on purpose: every + * TTypeVariable holding a reference to this object needs to see later + * constraints as they're recorded during analysis. */ final class TypeVariableBounds { @@ -24,6 +28,7 @@ final class TypeVariableBounds /** * @param list $lower_bounds * @param list $upper_bounds + * @psalm-mutation-free */ public function __construct( public array $lower_bounds = [], diff --git a/src/Psalm/Internal/Type/TypeVariableTracker.php b/src/Psalm/Internal/Type/TypeVariableTracker.php index dbe5d429258..b4433c0b8ce 100644 --- a/src/Psalm/Internal/Type/TypeVariableTracker.php +++ b/src/Psalm/Internal/Type/TypeVariableTracker.php @@ -40,6 +40,8 @@ final class TypeVariableTracker /** * Mints a fresh type variable name, registering its bound storage. + * + * @psalm-external-mutation-free */ public function addVariable(TypeVariableBounds $bounds): string { @@ -80,6 +82,8 @@ public function addBounds(array $lower, array $upper, ?CodeLocation $pos): void * below. Used where a concrete shape is required (property reads, method * call returns); the variable itself stays in the object's type params, * so later uses still constrain it. + * + * @psalm-external-mutation-free */ public static function resolveTypeVariables(Union $type, ?Codebase $codebase): Union { @@ -327,6 +331,7 @@ private static function reconcileLowerBoundsWithUpperBounds( * * @param list $lower_bounds * @return list + * @psalm-mutation-free */ private static function getRelevantBounds(array $lower_bounds): array { diff --git a/src/Psalm/Internal/TypeVisitor/TypeVariableResolver.php b/src/Psalm/Internal/TypeVisitor/TypeVariableResolver.php index 67446826ada..4dd48ce21a8 100644 --- a/src/Psalm/Internal/TypeVisitor/TypeVariableResolver.php +++ b/src/Psalm/Internal/TypeVisitor/TypeVariableResolver.php @@ -29,6 +29,9 @@ final class TypeVariableResolver extends MutableTypeVisitor { public bool $resolved_a_variable = false; + /** + * @psalm-mutation-free + */ public function __construct( private readonly ?Codebase $codebase, ) { diff --git a/src/Psalm/Issue/IncompatibleTypeParameters.php b/src/Psalm/Issue/IncompatibleTypeParameters.php index c8636c03b1b..7689a3f2cd7 100644 --- a/src/Psalm/Issue/IncompatibleTypeParameters.php +++ b/src/Psalm/Issue/IncompatibleTypeParameters.php @@ -7,5 +7,5 @@ final class IncompatibleTypeParameters extends CodeIssue { public const ERROR_LEVEL = 1; - public const SHORTCODE = 362; + public const SHORTCODE = 368; } diff --git a/src/Psalm/Type/Atomic/TTypeVariable.php b/src/Psalm/Type/Atomic/TTypeVariable.php index 06c5f733c58..2cbd1b14b96 100644 --- a/src/Psalm/Type/Atomic/TTypeVariable.php +++ b/src/Psalm/Type/Atomic/TTypeVariable.php @@ -66,7 +66,8 @@ public function getId(bool $exact = true, bool $nested = false): string } /** - * @param array $aliased_classes + * @param array $aliased_classes + * @psalm-pure */ #[Override] public function toPhpString( @@ -78,6 +79,9 @@ public function toPhpString( return null; } + /** + * @psalm-pure + */ #[Override] public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool { diff --git a/tests/Template/TypeVariableTest.php b/tests/Template/TypeVariableTest.php index c7561e96c17..70d1deda0bb 100644 --- a/tests/Template/TypeVariableTest.php +++ b/tests/Template/TypeVariableTest.php @@ -16,6 +16,9 @@ final class TypeVariableTest extends TestCase use InvalidCodeAnalysisTestTrait; use ValidCodeAnalysisTestTrait; + /** + * @psalm-pure + */ #[Override] public function providerValidCodeParse(): iterable { @@ -84,6 +87,9 @@ function probe(): void { ]; } + /** + * @psalm-pure + */ #[Override] public function providerInvalidCodeParse(): iterable {