You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Removed the unused miette dependency, which pulled about 70 crates into every build (php-rs-parser).
php-rs-parser no longer has a build script; it ran php -r on every build only to configure its own tests, which now detect PHP at run time (php-rs-parser).
Every published crate now ships its LICENSE file, as BSD-3-Clause requires.
Cargo.lock is committed, so cargo publish --locked and the MSRV job build against known dependency versions.
Deeply nested input no longer overflows the stack. Statements, expressions and the code inside string interpolation now share one recursion budget of 512 levels, and input nested deeper reports the new ParseError::NestingTooDeep instead of aborting the process. Statement nesting had no limit at all and interpolated code restarted the count at zero, so about 10 KB of { or if (1) { was enough to crash. The budget is sized so that the costliest paths stay within about 1 MiB of stack in release builds (php-rs-parser).
Expressions nested more than 50 levels deep are no longer rejected. Most constructs ([, (, f(, match () take one level of the 512-level budget; some take two, e.g. ($x + (...)) (php-rs-parser).
Nested strings inside interpolation ("{$a["{$b[...]}"]}", nested heredocs) no longer take quadratic time. Every nested string is re-lexed by its own sub-parser, so each one now takes a larger share of the budget, which keeps string nesting under 32 levels (php-rs-parser).
The lexer skips nested strings inside {$...} with an explicit stack instead of recursion; deeply nested double-quoted strings overflowed the stack before the parser's limit applied (php-lexer).
parse() no longer overflows the stack on long left-associative or postfix chains such as a 20,000-term concatenation or method chain: converting the AST to its owned form and back, and dropping the owned form, walk chains iteratively (php-ast).
The printer prints such chains without recursing per link, and its depth guard now sits well above anything the parser accepts, so a 300-term concatenation is no longer printed as /* max depth */ (php-printer).
Sources over 4 GiB are refused in release builds too. The size guard was a debug_assert!, so release builds wrapped the u32 span offsets and could slice the wrong text or panic. The parse_* functions now return an empty program with the new ParseError::SourceTooLarge, and Lexer::new/Lexer::new_at yield only Eof with a LexerErrorKind::FileTooLarge error (php-lexer, php-rs-parser).
The WebAssembly build reserves an 8 MiB stack instead of the 1 MiB default; wasm has no guard page, so an overflow there would corrupt memory rather than trap (php-wasm).
errors_truncated is true only when errors were actually dropped; it was also set when exactly 100 errors were reported (php-rs-parser).
ParseError::UnclosedDelimiter reads "expected ')' to close the delimiter opened at byte 3074" instead of "unclosed '')'' opened at Span { start: 3074, end: 3075 }", and its delimiter field holds ')' rather than '')'' (php-rs-parser).
SourceMap::line_col_to_offset returns None for a column past the end of its line instead of an offset on a later line, and no longer overflows on a huge column (php-rs-parser).
Added
php_ast::fingerprint::structural_fingerprint hashes any AST node (arena or owned) while ignoring every Span, so a declaration that only moved keeps its fingerprint. It is meant for incremental analyzers: comparing fingerprints lets a salsa query backdate a function or method whose code did not change when the file around it was edited (php-ast).
Param::new and MethodDecl::new in the arena and owned AST (php-ast).
TokenKind::description (php-lexer).
ScopeVisitor and OwnedScopeVisitor gain visit_name and visit_block, and FoldOwned gains fold_trait_use and fold_trait_adaptation (with fold_owned_trait_use/fold_owned_trait_adaptation), matching the plain visitors and the arena Fold. All have default bodies (php-ast).
NameStr::new_in builds a NameStr in an arena without the hidden __arena constructor (php-ast).
php_ast::bumpalo and php_rs_parser::bumpalo re-export the arena allocator that appears in their APIs (php-ast, php-rs-parser).
Pull requests run cargo-semver-checks against their base branch (informational), and the release workflow runs it against crates.io before publishing.
Lexer::is_verbatim_string reports whether a quoted-string token contains no escape sequence (and, for a double-quoted string, no $), as recorded while lexing it (php-lexer).
ParseResult, SourceMap, PrinterConfig and Indent implement Debug and Clone (SourceMap, PrinterConfig and Indent also PartialEq/Eq, Indent also Copy); ArenaParseResult and ParserContext implement Debug (php-rs-parser, php-printer).
Changed
Breaking: every public enum is #[non_exhaustive] — all AST enums (ExprKind, StmtKind, BinaryOp, TypeHintKind, …, arena and owned), PhpVersion, ParseError, Severity, TokenKind, LexerErrorKind, Indent and TextSegment — so new PHP syntax and versions can be added without a major release. Matches on them outside these crates need a _ arm. The structs most likely to grow fields are #[non_exhaustive] too: Param, MethodDecl (arena and owned), ClassModifiers, ParseResult, ArenaParseResult and PrinterConfig. Build them with Param::new/MethodDecl::new or Default::default() and assign the remaining public fields, e.g. let mut config = PrinterConfig::default(); config.indent = Indent::Spaces(2); (all crates).
The printer writes /* unsupported … */ for a node kind it does not know, rather than dropping it silently (php-printer).
Breaking:ParseError::Expected::found is a Cow<'static, str> description (e.g. "';'", "identifier") instead of a php_lexer::TokenKind, so new lexer tokens are no longer a breaking change for php-rs-parser. The error message text is unchanged. TokenKind::description returns the same text as its Display (php-rs-parser, php-lexer).
Breaking: the printer-only offsets are Spans of the token they mark: IfStmt::else_kw_start → else_kw_span, TryCatchStmt::finally_kw_start → finally_kw_span, MatchExpr::brace_start → brace_span, TraitUseDecl::adaptations_brace_start → adaptations_brace_span, in both the arena and owned AST. They are still not serialized (php-ast, php-rs-parser, php-printer).
Breaking:php_rs_parser::instrument is public only with the instrument feature, and owned::from_owned_program is #[doc(hidden)]: both are internals, not stable API (php-rs-parser, php-ast).
Breaking:ExprKind::String holds a NameStr instead of &'arena str, so literals without escape sequences borrow the source instead of being copied into the arena. NameStr derefs to str; use .as_str() where a &str is needed (php-ast, php-rs-parser).
Faster parsing, measured on the Laravel framework with its vendor directory (95 MB), the workload of the mir analyzer: arena parsing is ~14% faster and parse() ~10% faster. String literals are no longer copied into the arena, the parser no longer rescans each quoted string for escapes or interpolation that the lexer already saw, double-quoted strings are lexed with a vectorised search instead of byte by byte, and array literals start with room for two elements instead of growing from zero. Arena memory use is ~7% lower (php-lexer, php-rs-parser).
PHPDoc parsing is ~27% faster with identical output: lines are borrowed from the comment rather than copied, and text is appended directly instead of being built and merged (phpdoc-parser).
ParseError has new NestingTooDeep and SourceTooLarge variants; exhaustive matches need extra arms. Exceeding the nesting limit used to produce ParseError::Forbidden with the message "maximum expression nesting depth exceeded" (php-rs-parser).
php_ast::owned::Expr implements Drop, so its fields can no longer be moved out by destructuring; take them with std::mem::replace(&mut expr.kind, ExprKind::Error) instead (php-ast).
Breaking:ParseError::VersionTooLow::required and ::used are PhpVersion values instead of strings. The message text is unchanged (php-rs-parser).
Breaking:ResolvedName::Special holds a SpecialClass enum (Self_, Static, Parent) instead of a &'static str (php-ast).
Breaking:ExprKind::ThrowExpr is renamed to ExprKind::Throw, matching StmtKind::Throw, in the arena and owned AST and in serialized output (php-ast).
Breaking:ExprKind::StaticDynMethodCall and StaticDynMethodCallExpr are renamed to StaticMethodCallDynamic and StaticMethodCallDynamicExpr, matching ClassConstAccessDynamic and StaticPropertyAccessDynamic (php-ast).
Breaking:StmtKind::Label holds an Ident borrowed from the source, like StmtKind::Goto, instead of an arena-allocated &str (Box<str> in the owned AST). Serialized output is unchanged (php-ast, php-rs-parser).
Breaking:owned::Ident is a struct instead of an alias for Option<Box<str>>, with the same API as the arena Ident: Ident::name, Ident::ERROR, as_str, is_error, or_error, Display and PartialEq<&str>. Serialized output is unchanged (php-ast).
Breaking:phpdoc_parser::Span is a re-export of php_ast::Span instead of its own type, so PHPDoc and AST spans share one type; PHPDoc offsets are still relative to the docblock text. phpdoc-parser now depends on php-ast (phpdoc-parser).
Breaking:phpdoc_parser::body_text takes Option<&PhpDocText> instead of &Option<PhpDocText> (call it with tag.body.as_ref()), and find_tags and inline_tags return iterators instead of Vecs (phpdoc-parser).
Breaking: the parse entry points take a ParseOptions (target version, whether to build the source_map) instead of coming in one function per combination. parse_versioned(src, v) becomes parse_with(src, &ParseOptions::default().version(v)), parse_arena_versioned becomes parse_arena_with, and parse_arena_raw becomes parse_arena_with with .source_map(false). ParserContext::with_options and set_options replace reparse_versioned and reparse_owned_versioned (php-rs-parser).
Breaking: the ten pretty_print* functions are replaced by pretty_print and a PrettyPrinter builder; both accept the arena and the owned program. pretty_print_owned(p) becomes pretty_print(p), and the _with_config, _with_comments and _file variants become PrettyPrinter::new().config(c).comments(source, &comments).trailing_newline(true).print(p). The trailing newline follows PrinterConfig::newline (php-printer).
The serde/JSON shape of the AST is documented as outside the semver guarantee: it may change in any release, and Deserialize is not implemented (php-ast, phpdoc-parser).
Breaking:PrinterConfig::newline is a Newline enum (Lf, the default, or CrLf) instead of a &'static str (php-printer).