Skip to content

v0.26.0

Choose a tag to compare

@github-actions github-actions released this 24 Oct 16:32
· 116 commits to main since this release
cefbeda

Error messages and recovery follow PHP more closely, spans are ordered and stay inside their parent, and the printer keeps invalid code invalid.

Added

  • PropertyDecl::empty_hook_list and Param::empty_hook_list for an empty hook list {} (php-ast).
  • FunctionDecl::missing_param_list, MethodDecl::missing_param_list, ClosureExpr::missing_param_list and ArrowFunctionExpr::missing_param_list for a parameter list missing its ( (php-ast).
  • LexerErrorKind::UnexpectedCharacter (php-lexer) and ForbiddenRule::UnexpectedCharacter (php-rs-parser) for a control character in code.
  • PrinterConfig::php_version, the PHP version the output is for (php-printer).
  • ForbiddenRule::CallResultWrite and ForbiddenRule::ListAsForeachKey (php-rs-parser).
  • ParseError::UnmatchedDelimiter for a } that closes nothing (php-rs-parser).
  • ForeachStmt::by_ref_span, the & of a by-reference value (php-ast).
  • Modifiers::is_var for a property declared with var (php-ast).

Changed

  • The messages for a positional or unpacked argument after a named one, an unparenthesized nested ternary and an unparenthesized arrow function after |> match PHP's (php-rs-parser).
  • A write to a call result, as in f() = 1, $a->f()++, unset(f()) or [f()] = $a, reports PHP's Can't use function return value in write context or Can't use method return value in write context, and a list as a foreach key Cannot use list as key element (php-rs-parser).
  • From PHP 8.4, a void or never return error in a method, or in a closure or arrow function inside a class, says "method" as PHP does (A void method must not return a value), and return null; from a void function adds PHP's (did you mean "return;" instead of "return null;"?) (php-rs-parser).
  • break or continue outside a loop or switch reports PHP's 'break' not in the 'loop' or 'switch' context instead of Cannot 'break' 1 level (php-rs-parser).
  • A repeated member or promoted-parameter modifier reports PHP's message, such as Multiple access type modifiers are not allowed for public public or public private(set) protected(set) and Multiple static modifiers are not allowed (php-rs-parser).
  • A repeated class modifier, as in readonly readonly class or abstract readonly abstract class, reports PHP's Multiple readonly modifiers are not allowed instead of a syntax error, and abstract final class PHP's Cannot use the final modifier on an abstract class (php-rs-parser).
  • A } at the top level that closes nothing reports unmatched '}', as PHP's Unmatched '}', instead of expected expression, found '}' and No code may exist outside of namespace {} (php-rs-parser).
  • A try whose body is missing its { or } no longer also reports a missing catch or finally clause, and compile errors inside an unclosed parameter list, such as Cannot declare promoted property outside a constructor for function f(int $x { }, are dropped, since PHP stops at the syntax error (php-rs-parser).
  • Fewer recovery cascades: a <?php or <?= inside PHP code is one error and the statement after it parses; a match arm missing its comma reports only that; the rest of a broken for header, as in for (;;;), is skipped; and a class with neither a name nor a body, as in class\Foo::x(); before PHP 8.0, no longer reads the following statements as class members (php-rs-parser).
  • A dynamic property fetch spans its closing }, as in $o->{'x'}; a group use item's name spans the name as written, D in A\{B, D}, instead of the prefix and its siblings; "${a}" spans the whole ${a} like "${a[0]}"; and a statement or enum member with attributes starts at its first attribute, like a class member (php-rs-parser).
  • Each constant or property of a grouped declaration, as in const A = 1, B = 2; or public $a, $b;, spans its own item, the first one from the modifiers, instead of the whole declaration or every earlier item, and a later item keeps its own doc comment, as PHP does (php-rs-parser).
  • A namespaced name starting with a keyword where it is not expected, as in class A implements\B {}, is reported over the whole name, as PHP lexes it (php-rs-parser).
  • An if, while or switch missing its ( before the {, as in if { }, reports only the missing ( instead of also reading the body as a curly-brace offset and an unclosed ) opened at the {; for and foreach no longer report an unclosed ) whose ( was missing (php-rs-parser).
  • A repeated property hook reports PHP's message, Cannot redeclare property hook "get", with the hook name as written, instead of duplicate 'get' hook (php-rs-parser).
  • PhpVersion lives in php-ast; php-rs-parser re-exports it at the same paths (php-ast, php-rs-parser).
  • A ?> that is a statement of its own, as after ;, }, : or another tag, is a Nop with the span of the ?>; a ?> that ends the statement before it adds nothing, and ?><?php no longer adds a Nop (php-rs-parser).
  • Empty statements are dropped, except where the target PHP version rejects them before a declare, and so is a ?> with nothing but its consumed newline before the next <?php (php-printer).
  • The CloseTag token includes the one newline after ?> (\n, \r\n or \r), as PHP's T_CLOSE_TAG does, so the InlineHtml after it holds only the output and the Nop for ?> spans the newline (php-lexer, php-rs-parser).
  • <?= ?> and <?= at the end of the file are an Echo holding an Error, like <?= ; (php-rs-parser).
  • A short echo's Echo span starts at <?= (php-rs-parser).
  • An elseif branch spans from elseif, and an alternative-syntax then, elseif or else block from its : instead of from if (php-rs-parser).
  • An alternative-syntax while, for, foreach, declare or switch body spans from its : to its last statement, without the closing keyword, like an if block; a catch clause spans from catch instead of ( (php-rs-parser).
  • An unclosed statement or declaration body reports missing closing '}', and a body missing its { reports only that, not also its } (php-rs-parser).
  • A syntax error at the same token as the previous one is dropped as a recovery cascade, as in <?php if } or a body missing at the end of the file (php-rs-parser).
  • The asymmetric-visibility version error spans the set-visibility modifier, such as private(set) (php-rs-parser).
  • A nested __halt_compiler() reports PHP's message, __HALT_COMPILER() can only be used from the outermost scope, and is a statement of its own: parsing goes on after it, as in PHP, so it no longer reports missing closing '}' and its node ends at its ; (php-rs-parser).
  • The printer writes a newline after a ?> before HTML when the source has one there or when the HTML starts with a newline that ?> would swallow (php-printer).

Fixed

  • An empty hook list {} prints as {} instead of ;, so the printed code keeps PHP's error (php-printer).
  • A class property with a fully qualified type and no modifier, such as \Foo $a;, is a syntax error instead of being accepted (php-rs-parser).
  • An enum property with a fully qualified type, such as public \Foo\Bar $a;, reports Enum E cannot include properties instead of a syntax error (php-rs-parser).
  • An unclosed exit(, die( or clone( call no longer panics (php-rs-parser).
  • A promoted parameter with only readonly, final or a set-visibility modifier, such as private(set) int $a, reports that promotion requires PHP 8.0 (php-rs-parser).
  • No span ends before it starts: the version errors for set-only visibility on a promoted parameter and for a non-capturing catch, and the spans of error statements, bodies missing their {, use items, parameters, attributes, short echo tags, static variables and alternative-syntax elseif branches after a missing token (php-rs-parser).
  • A node recovered from a missing or unexpected token, such as the name and parameter of public function @return f() {}, is empty and placed right after the last consumed token, so it lies inside its parent's span and no longer covers a token it did not consume (php-rs-parser).
  • A missing operand before an operator, as in * 2;, sits at the operator, so the expression and statement no longer start at the whitespace before it (php-rs-parser).
  • A missing item in a group use, as in use A\{B, , C};, has an Error name instead of the prefix with a span covering A\{B, (php-rs-parser).
  • A missing static variable after a trailing comma lies inside its statement's span (php-rs-parser).
  • __halt_compiler() ?> followed by \r leaves the \r out of the data, as PHP does (php-rs-parser).
  • A control character in code, including form feed, is an error from PHP 8.0 instead of being skipped (php-lexer, php-rs-parser).
  • <?php or <?= inside PHP code lexes as <, so it is a syntax error instead of being ignored (php-lexer).
  • A ?> where a statement is expected, as in if ($a) ?>x<?php, is the empty body and the HTML after it runs unconditionally, as in PHP (php-rs-parser).
  • Printing keeps whether a declare(encoding=...) or declare(strict_types=...) after an empty statement is valid, instead of making a ?> that ends a statement before one an error (php-printer).
  • A method missing its ( has no parameters instead of an empty placeholder (php-rs-parser).
  • A function missing its (, as in function f {}, has no parameters, so its body is no longer read as a parameter's property hook list (php-rs-parser).
  • A closure missing its (, as in function {}, has no parameters, so its body is no longer read as a parameter's property hook list (php-rs-parser).
  • An arrow function missing its (, as in fn => 1, has no parameters instead of an empty placeholder (php-rs-parser).
  • A grouped constant or property is split into declarations that keep their own doc comments, instead of moving /** q */ in public $p, /** q */ $q; to the next member (php-printer).
  • A comment before a property hook, such as a doc comment on set(int $value) {} or // c before get, prints before the hook instead of between its parameter list or keyword and its body (php-printer).
  • A heredoc line whose escapes decode to the closing label, as \x45OT in <<<EOT, prints with its first byte escaped instead of ending the heredoc early (php-printer).
  • A control character in a string, backtick or heredoc literal prints as \xNN instead of the raw byte, so "a\0b" prints as "a\x00b" instead of a single-quoted string holding a NUL, consistent with \e, \f and \v (php-printer).
  • An empty alternative-syntax body, as in foreach ($a as &$v): endforeach; or if ($a): endif;, prints without a blank line (php-printer).
  • A blank line follows a function, class-like or namespace declaration as it precedes one, instead of only before it (php-printer).
  • A property declared with var prints as var instead of public (php-printer).
  • With the source given, a nested ternary or an arrow function after |> that the source writes without parentheses prints without them, so $a ? $b : $c ? $d : $e and 1 |> fn($x) => $x keep PHP's error instead of becoming valid; without the source, the printer still adds the parentheses (php-printer).
  • A function, method, closure or arrow function missing its ( prints without (), so the printed code keeps PHP's error instead of becoming valid (php-printer).
  • A syntax error at the closing } of a {$...} interpolation, as in "{$a->}", reports found '}' at the } instead of found end of file (php-rs-parser).
  • Recovery from an invalid class or enum member skips over a { ... } block, so the block's } no longer ends the class, as in public function @return f() {} (php-rs-parser).
  • A spread in list(), as in list(...$x) = $a, or in a foreach destructuring target reports PHP's Spread operator is not supported in assignments instead of a syntax error or Assignments can only happen to writable values; an element of a nested list is reported at the element (php-rs-parser).