Skip to content

RequiredPhpVersionVisitor: report native union types as requiring PHP 8.0#5872

Merged
VincentLanglet merged 1 commit into
phpstan:2.2.xfrom
phpstan-bot:create-pull-request/patch-o0qkfvy
Jun 14, 2026
Merged

RequiredPhpVersionVisitor: report native union types as requiring PHP 8.0#5872
VincentLanglet merged 1 commit into
phpstan:2.2.xfrom
phpstan-bot:create-pull-request/patch-o0qkfvy

Conversation

@phpstan-bot

Copy link
Copy Markdown
Collaborator

Summary

RequiredPhpVersionVisitor detects the minimum PHP version a test fixture needs to parse, so CI can skip fixtures on older PHP versions instead of hitting hard parse errors. It already reported intersection types (PHP 8.1), DNF types (8.2) and the standalone true type (8.2), but it did not report native union types (A|B), which are a parse error on PHP 7.4 and lower. This change makes the visitor report a PHP 8.0 requirement for native union types.

Changes

  • build/PHPStan/Build/RequiredPhpVersionVisitor.php: when visiting a Node\UnionType, call require(self::PHP_8_0, 'union types', $node). The pre-existing per-inner-type checks (intersection inside a union -> DNF -> 8.2, standalone true -> 8.2) still win because require() keeps the highest version.
  • tests/PHPStan/Build/RequiredPhpVersionCommentTest.php: add union type, union return type and nullable union type (A|null) cases asserting 80000.
  • Added // lint >= 8.0 comments to the 26 fixtures that newly trip the detector because they use native union types in signatures without advertising a minimum PHP version (under tests/PHPStan/Analyser/data, tests/PHPStan/Analyser/nsrt, and several tests/PHPStan/Rules/*/data directories).
    • For data fixtures the comment is appended to line 1 (the established pattern).
    • For nsrt fixtures the comment must immediately follow <?php (enforced by TypeInferenceTestCase). For the ones that had declare(strict_types = 1) on line 1, declare was moved onto the formerly-blank line 2 so no line-sensitive assertType() assertions shift.
  • Intersection types were already handled (Node\IntersectionType -> 8.1), so the second half of the issue needed no code change. The visitor's other type-syntax families (standalone null/false/true, mixed) were probed and are already correct.

Root cause

The visitor enumerated several type-declaration syntactic features but omitted the most basic PHP 8.0 type-declaration feature: native union types. A Node\UnionType node corresponds to syntax (A|B) that does not exist before PHP 8.0, so any fixture using it must be skipped on older PHP. Because the visitor never emitted a requirement for the union node itself (only for special inner types), fixtures relying solely on plain union types could slip through without a // lint >= 8.0 guard and would have parse-errored on PHP 7.4 in CI.

Test

  • RequiredPhpVersionCommentTest::testDetectedVersion gains three cases proving a native union type — in a parameter, in a return type, and the nullable A|null form — is detected as requiring PHP 8.0. These fail without the visitor change.
  • RequiredPhpVersionCommentTest::testFixtureHasRequiredLintComment now passes for the whole fixture corpus after the 26 fixtures gained their // lint >= 8.0 comment; it would fail (26 fixtures) without those comments, proving the detector now catches the previously-missed syntax.
  • NodeScopeResolverTest and the affected rule tests still pass, confirming the relocated declare / appended comments did not shift any line-sensitive assertions.

Fixes phpstan/phpstan#14824

… 8.0

- Emit a PHP 8.0 requirement for every `Node\UnionType` (native union type
  declarations such as `A|B` are a parse error on PHP 7.4 and lower).
- The existing inner-type checks (DNF intersections -> 8.2, standalone `true`
  -> 8.2) still take precedence via the highest-version-wins `require()` logic.
- Intersection types were already covered (`Node\IntersectionType` -> 8.1), so
  no change was needed there; the visitor's other type-syntax families
  (standalone null/false/true, mixed) are likewise unchanged.
- Add `union type`, `union return type` and `nullable union type` cases to
  RequiredPhpVersionCommentTest::dataDetectedVersion.
- Add `// lint >= 8.0` comments to the fixtures that newly trip the detector
  because they use native union types without advertising a minimum version.
  For nsrt fixtures that carried `declare(strict_types = 1)` on line 1, the
  comment is placed immediately after `<?php` (as TypeInferenceTestCase
  requires) and `declare` moves onto the previously-blank line 2, so existing
  line-sensitive assertions are preserved.
@VincentLanglet VincentLanglet requested a review from staabm June 14, 2026 18:02
@VincentLanglet VincentLanglet self-assigned this Jun 14, 2026

@staabm staabm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need another PR for intersection types on 8.1

@VincentLanglet

Copy link
Copy Markdown
Contributor

We need another PR for intersection types on 8.1

It was already supported I think, cf

if ($node instanceof Node\IntersectionType) {
			$this->require(self::PHP_8_1, 'pure intersection types', $node);
		}

@VincentLanglet VincentLanglet merged commit 8698991 into phpstan:2.2.x Jun 14, 2026
464 of 466 checks passed
@VincentLanglet VincentLanglet deleted the create-pull-request/patch-o0qkfvy branch June 14, 2026 19:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RequiredPhpVersionVisitor should check for Union and Intersection types

3 participants