Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ast-grep for openSUSE:Factory checked in at 2026-10-16 20:09:53 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ast-grep (Old) and /work/SRC/openSUSE:Factory/.ast-grep.new.3698736 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ast-grep" Fri Oct 16 20:09:53 2026 rev:80 rq:1385156 version:0.51.0 Changes: -------- --- /work/SRC/openSUSE:Factory/ast-grep/ast-grep.changes 2026-10-12 22:07:59.605238297 +0200 +++ /work/SRC/openSUSE:Factory/.ast-grep.new.3698736/ast-grep.changes 2026-10-16 20:13:47.946532396 +0200 @@ -1,0 +2,15 @@ +Fri Oct 16 08:50:05 UTC 2026 - Michael Vetter <[email protected]> + +- Update to version 0.51.0: + * 0.51.0 bump version + * fix(cli): use `ast-grep` name in project setup hint (#3001) + * Revise security policy for clarity and updates + * fix: replace deprecated serde_yaml with serde-saphyr (#2999) + * fix(deps): update rust crate tree-sitter-haskell to 0.24.0 (#2988) + * fix(cli): preserve files when update-all writes fail (#2995) + * fix: update wasm linting +- Drop because tree-sitter-haskell got updated to 0.24.0: + * tree-sitter-haskell-fix-heap-corruption-checksum.patch + * tree-sitter-haskell-fix-heap-corruption.patch + +------------------------------------------------------------------- @@ -42,0 +58,9 @@ + +------------------------------------------------------------------- +Fri Sep 25 07:04:20 UTC 2026 - Andrei Dziahel <[email protected]> + +- Fix heap corruption ("corrupted size vs. prev_size") in the vendored + Haskell parser caused by strict-aliasing UB in tree_sitter/array.h + (tree-sitter/tree-sitter-haskell#158, upstream fix from PR #157): + * tree-sitter-haskell-fix-heap-corruption.patch + * tree-sitter-haskell-fix-heap-corruption-checksum.patch Old: ---- ast-grep-0.50.0.tar.zst New: ---- ast-grep-0.51.0.tar.zst ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ast-grep.spec ++++++ --- /var/tmp/diff_new_pack.d8jJHh/_old 2026-10-16 20:13:49.304589426 +0200 +++ /var/tmp/diff_new_pack.d8jJHh/_new 2026-10-16 20:13:49.307589552 +0200 @@ -17,7 +17,7 @@ Name: ast-grep -Version: 0.50.0 +Version: 0.51.0 Release: 0 Summary: A CLI tool for code structural search, lint and rewriting License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.d8jJHh/_old 2026-10-16 20:13:49.373592324 +0200 +++ /var/tmp/diff_new_pack.d8jJHh/_new 2026-10-16 20:13:49.377592492 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/ast-grep/ast-grep.git</param> <param name="versionformat">@PARENT_TAG@</param> <param name="scm">git</param> - <param name="revision">0.50.0</param> + <param name="revision">0.51.0</param> <param name="match-tag">*</param> <param name="versionrewrite-pattern">v(\d+\.\d+\.\d+)</param> <param name="versionrewrite-replacement">\1</param> ++++++ ast-grep-0.50.0.tar.zst -> ast-grep-0.51.0.tar.zst ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/CHANGELOG.md new/ast-grep-0.51.0/CHANGELOG.md --- old/ast-grep-0.50.0/CHANGELOG.md 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/CHANGELOG.md 2026-10-15 17:05:11.000000000 +0200 @@ -4,8 +4,19 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [0.51.0](https://github.com/ast-grep/ast-grep/compare/0.50.0...0.51.0) + +- fix(cli): use `ast-grep` name in project setup hint [`#3001`](https://github.com/ast-grep/ast-grep/pull/3001) +- fix: replace deprecated serde_yaml with serde-saphyr [`#2999`](https://github.com/ast-grep/ast-grep/pull/2999) +- fix(deps): update rust crate tree-sitter-haskell to 0.24.0 [`#2988`](https://github.com/ast-grep/ast-grep/pull/2988) +- fix(cli): preserve files when update-all writes fail [`#2995`](https://github.com/ast-grep/ast-grep/pull/2995) +- Revise security policy for clarity and updates [`a7d3f69`](https://github.com/ast-grep/ast-grep/commit/a7d3f6935320f9859c3d522d133aabba21ccff4c) +- fix: update wasm linting [`ea55463`](https://github.com/ast-grep/ast-grep/commit/ea5546303f4aebe86c16a3fada2dae0372bd95c4) + #### [0.50.0](https://github.com/ast-grep/ast-grep/compare/0.45.3...0.50.0) +> 7 October 2026 + - feat(outline): add Java annotation type and element rules [`#2955`](https://github.com/ast-grep/ast-grep/pull/2955) - fix(outline): report Java private/protected fields in the outline [`#2950`](https://github.com/ast-grep/ast-grep/pull/2950) - feat(outline): add Java enum member rule [`#2954`](https://github.com/ast-grep/ast-grep/pull/2954) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/Cargo.lock new/ast-grep-0.51.0/Cargo.lock --- old/ast-grep-0.50.0/Cargo.lock 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/Cargo.lock 2026-10-15 17:05:11.000000000 +0200 @@ -22,6 +22,17 @@ ] [[package]] +name = "annotate-snippets" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f211a51805bc641f3ad5b7664c77d2547af685cc33b4cd8d31964027a46f13f1" +dependencies = [ + "anstyle", + "memchr", + "unicode-width 0.2.2", +] + +[[package]] name = "ansi_term" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -87,6 +98,12 @@ checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + +[[package]] name = "assert_cmd" version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -103,7 +120,7 @@ [[package]] name = "ast-grep" -version = "0.50.0" +version = "0.51.0" dependencies = [ "ansi_term", "anyhow", @@ -126,9 +143,9 @@ "predicates", "regex", "serde", + "serde-saphyr", "serde-sarif", "serde_json", - "serde_yaml", "similar", "smallvec", "tempfile", @@ -141,7 +158,7 @@ [[package]] name = "ast-grep-config" -version = "0.50.0" +version = "0.51.0" dependencies = [ "ast-grep-core", "bit-set", @@ -149,15 +166,16 @@ "regex", "schemars", "serde", + "serde-saphyr", + "serde-value", "serde_regex", - "serde_yaml", "thiserror 2.0.21", "tree-sitter-typescript", ] [[package]] name = "ast-grep-core" -version = "0.50.0" +version = "0.51.0" dependencies = [ "bit-set", "regex", @@ -168,13 +186,13 @@ [[package]] name = "ast-grep-dynamic" -version = "0.50.0" +version = "0.51.0" dependencies = [ "ast-grep-core", "ignore", "libloading", "serde", - "serde_yaml", + "serde-saphyr", "target-triple", "thiserror 2.0.21", "tree-sitter", @@ -182,7 +200,7 @@ [[package]] name = "ast-grep-language" -version = "0.50.0" +version = "0.51.0" dependencies = [ "ast-grep-core", "ignore", @@ -220,7 +238,7 @@ [[package]] name = "ast-grep-lsp" -version = "0.50.0" +version = "0.51.0" dependencies = [ "anyhow", "ast-grep-config", @@ -238,7 +256,7 @@ [[package]] name = "ast-grep-napi" -version = "0.50.0" +version = "0.51.0" dependencies = [ "ast-grep-config", "ast-grep-core", @@ -254,21 +272,21 @@ [[package]] name = "ast-grep-outline" -version = "0.50.0" +version = "0.51.0" dependencies = [ "ast-grep-config", "ast-grep-core", "ast-grep-language", "regex", "serde", + "serde-saphyr", "serde_json", - "serde_yaml", "thiserror 2.0.21", ] [[package]] name = "ast-grep-py" -version = "0.50.0" +version = "0.51.0" dependencies = [ "anyhow", "ast-grep-config", @@ -298,6 +316,12 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + +[[package]] name = "bit-set" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -497,6 +521,12 @@ ] [[package]] +name = "core_detect" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" + +[[package]] name = "crokey" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -690,6 +720,29 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] +name = "encoding_rs" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e985e0451871ad22fb8d2b6b076e2028a502a0d3950998c2c5c0a4f9b5d9679" +dependencies = [ + "cfg-if", + "core_detect", + "multiversion_no_op", + "rustversion", + "scopeguard", + "simdutf8", +] + +[[package]] +name = "encoding_rs_io" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba3fe847045ecff794b9c138293a80db914678c453ad63fbf0c6a9eb6e00b22" +dependencies = [ + "encoding_rs", +] + +[[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -875,6 +928,16 @@ ] [[package]] +name = "granit-parser" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da55feca5d1625466bae068f9fd4b87892caeae70c3fe671ddb0f50d41aebf37" +dependencies = [ + "arraydeque", + "smallvec", +] + +[[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1108,6 +1171,12 @@ ] [[package]] +name = "multiversion_no_op" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d" + +[[package]] name = "napi" version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1236,6 +1305,15 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] name = "parking_lot" version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1520,12 +1598,6 @@ checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1609,6 +1681,23 @@ ] [[package]] +name = "serde-saphyr" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a253edfdea34032da21af84e16fcc94f4c7a6ba237f9f3bc2dbf43cea8535c44" +dependencies = [ + "annotate-snippets", + "base64", + "encoding_rs_io", + "granit-parser", + "nohash-hasher", + "num-traits", + "serde_core", + "smallvec", + "zmij", +] + +[[package]] name = "serde-sarif" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1629,6 +1718,16 @@ ] [[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] name = "serde-wasm-bindgen" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1695,19 +1794,6 @@ ] [[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - -[[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1754,6 +1840,12 @@ ] [[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] name = "similar" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2252,9 +2344,9 @@ [[package]] name = "tree-sitter-haskell" -version = "0.23.1" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977c51e504548cba13fc27cb5a2edab2124cf6716a1934915d07ab99523b05a4" +checksum = "781de30e0d1ec8e6db4ed799d36ce1cedaf4684374dada45ebe2a8ea44c3f814" dependencies = [ "cc", "tree-sitter-language", @@ -2501,12 +2593,6 @@ checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - -[[package]] name = "uriparse" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2555,7 +2641,7 @@ [[package]] name = "wasm" -version = "0.50.0" +version = "0.51.0" dependencies = [ "ast-grep-config", "ast-grep-core", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/Cargo.toml new/ast-grep-0.51.0/Cargo.toml --- old/ast-grep-0.50.0/Cargo.toml 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/Cargo.toml 2026-10-15 17:05:11.000000000 +0200 @@ -10,30 +10,30 @@ lto = true [workspace.package] -version = "0.50.0" +version = "0.51.0" authors = ["Herrington Darkholme <[email protected]>"] edition = "2024" license = "MIT" documentation = "https://ast-grep.github.io/guide/introduction.html" homepage = "https://ast-grep.github.io/" repository = "https://github.com/ast-grep/ast-grep" -rust-version = "1.88.0" +rust-version = "1.90.0" readme = "README.md" [workspace.dependencies] -ast-grep-core = { path = "crates/core", version = "0.50.0", default-features = false } -ast-grep-config = { path = "crates/config", version = "0.50.0" } -ast-grep-dynamic = { path = "crates/dynamic", version = "0.50.0" } -ast-grep-language = { path = "crates/language", version = "0.50.0" } -ast-grep-lsp = { path = "crates/lsp", version = "0.50.0" } -ast-grep-outline = { path = "crates/outline", version = "0.50.0" } +ast-grep-core = { path = "crates/core", version = "0.51.0", default-features = false } +ast-grep-config = { path = "crates/config", version = "0.51.0" } +ast-grep-dynamic = { path = "crates/dynamic", version = "0.51.0" } +ast-grep-language = { path = "crates/language", version = "0.51.0" } +ast-grep-lsp = { path = "crates/lsp", version = "0.51.0" } +ast-grep-outline = { path = "crates/outline", version = "0.51.0" } bit-set = { version = "0.11.0" } ignore = { version = "0.4.22" } regex = { version = "1.10.4" } serde = { version = "1.0.200", features = ["derive"] } serde_regex = "1.2.0" -serde_yaml = "0.9.33" +serde-saphyr = { version = "2.0.0", default-features = false, features = ["serialize", "deserialize"] } tree-sitter = { version = "0.27.0" } thiserror = "2.0.0" schemars = "1.0.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/SECURITY.md new/ast-grep-0.51.0/SECURITY.md --- old/ast-grep-0.50.0/SECURITY.md 1970-01-01 01:00:00.000000000 +0100 +++ new/ast-grep-0.51.0/SECURITY.md 2026-10-15 17:05:11.000000000 +0200 @@ -0,0 +1,30 @@ +# Security Policy + +## Supported Versions + +Security fixes are provided for the latest stable release of ast-grep. Please +upgrade to the latest release before checking whether a suspected vulnerability +is still present. + +## Reporting a Vulnerability + +Please report suspected security vulnerabilities in ast-grep, including its CLI +and language bindings, through [GitHub's private vulnerability reporting form](https://github.com/ast-grep/ast-grep/security/advisories/new). + +Do not disclose vulnerability details in public issues, discussions, or pull +requests before maintainers have had an opportunity to investigate and address +the report. + +To help us assess your report, include: + +- The affected ast-grep version, component, and operating system. +- A description of the vulnerability and its potential impact. +- Steps to reproduce the issue, ideally with a minimal proof of concept. +- Any suggested mitigation or fix, if available. + +We will review the report and use the private reporting thread to discuss +findings, request additional information, and coordinate any fix and public +disclosure with you. + +For bugs that do not have a security impact, please use the +[public issue tracker](https://github.com/ast-grep/ast-grep/issues). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/cli/Cargo.toml new/ast-grep-0.51.0/crates/cli/Cargo.toml --- old/ast-grep-0.50.0/crates/cli/Cargo.toml 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/cli/Cargo.toml 2026-10-15 17:05:11.000000000 +0200 @@ -48,7 +48,7 @@ inquire = "0.9.0" serde.workspace = true serde_json = "1.0.116" -serde_yaml.workspace = true +serde-saphyr.workspace = true serde-sarif = "0.8.0" similar = { version = "3.0.0", features = ["inline"] } smallvec = "1.13.2" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/cli/src/lang/lang_globs.rs new/ast-grep-0.51.0/crates/cli/src/lang/lang_globs.rs --- old/ast-grep-0.50.0/crates/cli/src/lang/lang_globs.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/cli/src/lang/lang_globs.rs 2026-10-15 17:05:11.000000000 +0200 @@ -104,7 +104,7 @@ mod test { use super::*; use ast_grep_language::SupportLang; - use serde_yaml::from_str; + use serde_saphyr::from_str; const YAML: &str = r" js: ['.eslintrc'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/cli/src/new.rs new/ast-grep-0.51.0/crates/cli/src/new.rs --- old/ast-grep-0.50.0/crates/cli/src/new.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/cli/src/new.rs 2026-10-15 17:05:11.000000000 +0200 @@ -223,8 +223,8 @@ language_injections: vec![], // advanced feature }; let config_path = project_dir.join("sgconfig.yml"); - let f = File::create(config_path)?; - serde_yaml::to_writer(f, &root_config)?; + let mut f = File::create(config_path)?; + serde_saphyr::to_io_writer(&mut f, &root_config)?; println!("Your new ast-grep project has been created!"); Ok(ExitCode::SUCCESS) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/cli/src/print/interactive_print.rs new/ast-grep-0.51.0/crates/cli/src/print/interactive_print.rs --- old/ast-grep-0.50.0/crates/cli/src/print/interactive_print.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/cli/src/print/interactive_print.rs 2026-10-15 17:05:11.000000000 +0200 @@ -10,6 +10,7 @@ use smallvec::{SmallVec, smallvec}; use std::borrow::Cow; +use std::io::Write; use std::ops::Range; use std::path::{Path, PathBuf}; @@ -52,7 +53,7 @@ utils::prompt(VIEW_PROMPT, "qe", Some('\n')).expect("cannot fail") } - fn rewrite_action(&self, diffs: Diffs<()>, path: &PathBuf) -> Result<()> { + fn rewrite_action(&self, diffs: Diffs<()>, path: &Path) -> Result<()> { if diffs.contents.is_empty() { return Ok(()); } @@ -61,7 +62,7 @@ println!("{new_content}"); Ok(()) } else { - std::fs::write(path, new_content).with_context(|| EC::WriteFile(path.clone())) + write_atomically(path, &new_content).with_context(|| EC::WriteFile(path.to_path_buf())) } } @@ -100,6 +101,23 @@ } } +/// Replace a writable source file only after the replacement is fully written. +fn write_atomically(path: &Path, content: &str) -> Result<()> { + let path = path.canonicalize()?; + // Check source write access without truncating it, and close the handle before replacing it. + let permissions = std::fs::OpenOptions::new() + .write(true) + .open(&path)? + .metadata()? + .permissions(); + let parent = path.parent().unwrap_or_else(|| Path::new(".")); + let mut temp = tempfile::NamedTempFile::new_in(parent)?; + temp.write_all(content.as_bytes())?; + temp.as_file().set_permissions(permissions)?; + temp.persist(path).map_err(|error| error.error)?; + Ok(()) +} + impl Printer for InteractivePrinter { type Processed = Payload<InnerPrinter>; type Processor = InteractiveProcessor<InnerPrinter>; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/cli/src/utils/error_context.rs new/ast-grep-0.51.0/crates/cli/src/utils/error_context.rs --- old/ast-grep-0.50.0/crates/cli/src/utils/error_context.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/cli/src/utils/error_context.rs 2026-10-15 17:05:11.000000000 +0200 @@ -279,7 +279,7 @@ ), ProjectNotExist => Self::new( "No ast-grep project configuration is found.", - "You need to create an ast-grep project for this command. Try `sg new` to create one.", + "You need to create an ast-grep project for this command. Try `ast-grep new` to create one.", PROJECT_GUIDE, ), FileAlreadyExist(path) => Self::new( @@ -513,4 +513,4 @@ let error = anyhow::Error::new(io_err); assert!(exit_with_error(error).is_err()); } -} +} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/cli/src/verify/find_file.rs new/ast-grep-0.51.0/crates/cli/src/verify/find_file.rs --- old/ast-grep-0.50.0/crates/cli/src/verify/find_file.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/cli/src/verify/find_file.rs 2026-10-15 17:05:11.000000000 +0200 @@ -3,11 +3,10 @@ use crate::utils::ErrorContext as EC; use anyhow::{Context, Result}; -use ast_grep_config::from_str; +use ast_grep_config::{from_multiple, from_str}; use ast_grep_language::config_file_type; use ignore::WalkBuilder; use regex::Regex; -use serde_yaml::{Deserializer, with::singleton_map_recursive::deserialize}; use std::collections::HashMap; use std::fs::read_to_string; @@ -141,9 +140,9 @@ snapshot_path: &Path, builder: &mut HarnessBuilder<'_>, ) -> Result<()> { - for deser in Deserializer::from_str(&yaml) { - let test_case: TestCase = - deserialize(deser).with_context(|| EC::ParseTest(path.to_path_buf()))?; + for test_case in + from_multiple::<TestCase>(&yaml).with_context(|| EC::ParseTest(path.to_path_buf()))? + { if builder.included_in_filter(&test_case.id) { let harness = &mut builder.dest; harness diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/cli/src/verify/reporter.rs new/ast-grep-0.51.0/crates/cli/src/verify/reporter.rs --- old/ast-grep-0.50.0/crates/cli/src/verify/reporter.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/cli/src/verify/reporter.rs 2026-10-15 17:05:11.000000000 +0200 @@ -2,7 +2,7 @@ use ansi_term::{Color, Style}; use anyhow::Result; -use serde_yaml::to_string; +use serde_saphyr::to_string; use std::io::Write; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/cli/src/verify.rs new/ast-grep-0.51.0/crates/cli/src/verify.rs --- old/ast-grep-0.50.0/crates/cli/src/verify.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/cli/src/verify.rs 2026-10-15 17:05:11.000000000 +0200 @@ -13,7 +13,7 @@ use ast_grep_config::RuleCollection; use clap::Args; use regex::Regex; -use serde_yaml::to_string; +use serde_saphyr::to_string; use std::collections::HashMap; use std::io::Write; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/cli/tests/scan_test.rs new/ast-grep-0.51.0/crates/cli/tests/scan_test.rs --- old/ast-grep-0.50.0/crates/cli/tests/scan_test.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/cli/tests/scan_test.rs 2026-10-15 17:05:11.000000000 +0200 @@ -153,6 +153,94 @@ Ok(()) } +#[cfg(unix)] +#[test] +fn test_sg_scan_update_all_preserves_permissions() -> Result<()> { + use std::os::unix::fs::PermissionsExt; + + let dir = create_test_files([ + ("rules/no-var.yml", FIXABLE_JS_RULE), + ("test.js", "var x = 1;\n"), + ])?; + let path = dir.path().join("test.js"); + std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o751))?; + let mode = std::fs::metadata(&path)?.permissions().mode(); + Command::new(cargo_bin!()) + .current_dir(dir.path()) + .args([ + "scan", + "--rule", + "rules/no-var.yml", + "--update-all", + "test.js", + ]) + .assert() + .success(); + assert_eq!(std::fs::read_to_string(&path)?, "let x = 1;\n"); + assert_eq!(std::fs::metadata(&path)?.permissions().mode(), mode); + Ok(()) +} + +#[cfg(unix)] +#[test] +fn test_sg_scan_update_all_rejects_read_only_file() -> Result<()> { + use std::os::unix::fs::PermissionsExt; + + let dir = create_test_files([ + ("rules/no-var.yml", FIXABLE_JS_RULE), + ("test.js", "var x = 1;\n"), + ])?; + let path = dir.path().join("test.js"); + std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o444))?; + // Privileged users can still write read-only files, just as with std::fs::write. + if std::fs::OpenOptions::new().write(true).open(&path).is_ok() { + return Ok(()); + } + Command::new(cargo_bin!()) + .current_dir(dir.path()) + .args([ + "scan", + "--rule", + "rules/no-var.yml", + "--update-all", + "test.js", + ]) + .assert() + .failure() + .stderr(contains("Cannot rewrite file test.js")); + assert_eq!(std::fs::read_to_string(&path)?, "var x = 1;\n"); + assert_eq!( + std::fs::metadata(&path)?.permissions().mode() & 0o777, + 0o444 + ); + Ok(()) +} + +#[cfg(unix)] +#[test] +fn test_sg_scan_update_all_preserves_file_when_write_fails() -> Result<()> { + let dir = create_test_files([ + ("rules/no-var.yml", FIXABLE_JS_RULE), + ("test.js", "var x = 1;\n"), + ])?; + Command::new("/bin/sh") + .args(["-c", "ulimit -f 0; exec \"$@\"", "sh"]) + .arg(cargo_bin!()) + .args([ + "scan", + "--rule", + "rules/no-var.yml", + "--update-all", + "test.js", + ]) + .current_dir(dir.path()) + .assert() + .failure(); + let updated = std::fs::read_to_string(dir.path().join("test.js"))?; + assert_eq!(updated, "var x = 1;\n"); + Ok(()) +} + #[test] fn test_sg_scan_inline_rules() -> Result<()> { let inline_rules = "{id: test, language: ts, rule: {pattern: console.log($A)}}"; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/config/Cargo.toml new/ast-grep-0.51.0/crates/config/Cargo.toml --- old/ast-grep-0.50.0/crates/config/Cargo.toml 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/config/Cargo.toml 2026-10-15 17:05:11.000000000 +0200 @@ -22,7 +22,8 @@ regex.workspace = true serde.workspace = true serde_regex.workspace = true -serde_yaml = "0.9.33" +serde-saphyr.workspace = true +serde-value = "0.7.0" thiserror.workspace = true schemars.workspace = true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/config/src/fixer.rs new/ast-grep-0.51.0/crates/config/src/fixer.rs --- old/ast-grep-0.50.0/crates/config/src/fixer.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/config/src/fixer.rs 2026-10-15 17:05:11.000000000 +0200 @@ -247,7 +247,7 @@ } #[test] - fn test_deserialize_object() -> Result<(), serde_yaml::Error> { + fn test_deserialize_object() -> Result<(), serde_saphyr::Error> { let src = "{template: 'abc', expandEnd: {regex: ',', stopBy: neighbor}}"; let SerializableFixer::Config(cfg) = from_str(src)? else { panic!("wrong parsing") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/config/src/lib.rs new/ast-grep-0.51.0/crates/config/src/lib.rs --- old/ast-grep-0.50.0/crates/config/src/lib.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/config/src/lib.rs 2026-10-15 17:05:11.000000000 +0200 @@ -11,7 +11,7 @@ mod transform; use serde::Deserialize; -use serde_yaml::{Deserializer, Error as YamlError, with::singleton_map_recursive::deserialize}; +use serde_saphyr::{Error as YamlError, MergeKeyPolicy, NonFiniteFloatPolicy, Options}; use ast_grep_core::language::Language; @@ -31,9 +31,31 @@ pub use rule_core::{RuleCore, RuleCoreError, SerializableRuleCore}; pub use transform::Transformation; +fn yaml_options() -> Options { + let mut options = Options::default(); + // Preserve YAML 1.2 scalars (e.g. Y, on and off are strings), literal merge + // keys, and non-finite floats in arbitrary rule metadata. + options.strict_booleans = true; + options.merge_keys = MergeKeyPolicy::AsOrdinary; + options.non_finite_float_policy = NonFiniteFloatPolicy::PassThrough; + options.reject_unsupported_tags = true; + options +} + pub fn from_str<'de, T: Deserialize<'de>>(s: &'de str) -> Result<T, YamlError> { - let deserializer = Deserializer::from_str(s); - deserialize(deserializer) + serde_saphyr::from_str_with_options(s, yaml_options()) +} + +/// Deserialize a YAML document stream using the same options as rule configuration. +/// Null and empty documents are skipped. +pub fn from_multiple<'de, T: Deserialize<'de>>(s: &'de str) -> Result<Vec<T>, YamlError> { + let mut options = yaml_options(); + if let Some(budget) = &mut options.budget { + // Rule and test files can contain arbitrarily many documents. Keep the + // other parser resource limits while avoiding the default 1,024-document cap. + budget.max_documents = usize::MAX; + } + serde_saphyr::from_multiple_with_options(s, options) } pub fn from_yaml_string<'a, L: Language + Deserialize<'a>>( @@ -41,8 +63,8 @@ registration: &GlobalRules, ) -> Result<Vec<RuleConfig<L>>, RuleConfigError> { let mut ret = vec![]; - for yaml in Deserializer::from_str(yamls) { - let config = RuleConfig::deserialize(yaml, registration)?; + for yaml in from_multiple::<SerializableRuleConfig<L>>(yamls)? { + let config = RuleConfig::try_from(yaml, registration)?; ret.push(config); } Ok(ret) @@ -55,7 +77,75 @@ use ast_grep_core::tree_sitter::{LanguageExt, StrDoc, TSLanguage}; use std::path::Path; - #[derive(Clone, Deserialize, PartialEq, Eq)] + #[test] + fn test_yaml_boolean_resolution() { + use serde_value::Value; + let values: Vec<Value> = from_str("[Y, N, yes, no, on, off, true, FALSE]").unwrap(); + assert_eq!( + values, + vec![ + Value::String("Y".into()), + Value::String("N".into()), + Value::String("yes".into()), + Value::String("no".into()), + Value::String("on".into()), + Value::String("off".into()), + Value::Bool(true), + Value::Bool(false), + ] + ); + assert!(from_str::<bool>("yes").is_err()); + } + + #[test] + fn test_yaml_document_stream_borrows_and_preserves_scalars() { + let docs: Vec<&str> = from_multiple("Y\n---\noff").unwrap(); + assert_eq!(docs, ["Y", "off"]); + assert!(from_multiple::<String>("").unwrap().is_empty()); + assert!( + from_multiple::<String>("# no documents\n") + .unwrap() + .is_empty() + ); + } + + #[test] + fn test_yaml_document_stream_skips_null_and_empty_documents() { + let rule = make_yaml(" pattern: let a = 123"); + for null in [ + "null", + "~", + "Null", + "NULL", + "---", + "---\n# empty document\n", + ] { + assert!( + from_yaml_string::<TypeScript>(null, &GlobalRules::default()) + .unwrap() + .is_empty() + ); + let stream = format!("{rule}\n---\n{null}\n---\n{rule}"); + let rules = from_yaml_string::<TypeScript>(&stream, &GlobalRules::default()).unwrap(); + assert_eq!(rules.len(), 2); + } + let strings: Vec<String> = from_multiple("'null'\n---\n\"~\"").unwrap(); + assert_eq!(strings, ["null", "~"]); + let nested_null = format!("{rule}metadata:\n value: null\n"); + assert!(from_yaml_string::<TypeScript>(&nested_null, &GlobalRules::default()).is_ok()); + } + + #[test] + fn test_yaml_document_stream_over_1024_documents() { + let yaml = std::iter::repeat_n("Y", 1025) + .collect::<Vec<_>>() + .join("\n---\n"); + let docs: Vec<String> = from_multiple(&yaml).unwrap(); + assert_eq!(docs.len(), 1025); + assert!(docs.iter().all(|doc| doc == "Y")); + } + + #[derive(Clone, Deserialize, serde::Serialize, PartialEq, Eq)] pub enum TypeScript { Tsx, } @@ -121,6 +211,26 @@ } #[test] + fn test_rule_config_deserialize_with_saphyr() { + let yaml = make_yaml(" pattern: let a = 123"); + let globals = GlobalRules::default(); + let config = serde_saphyr::with_deserializer_from_str_with_options( + &yaml, + yaml_options(), + |deserializer| { + Ok(RuleConfig::<TypeScript>::deserialize( + deserializer, + &globals, + )) + }, + ) + .unwrap() + .unwrap(); + let grep = TypeScript::Tsx.ast_grep("let a = 123;"); + assert!(grep.root().find(&config.matcher).is_some()); + } + + #[test] fn test_deserialize_nested() { let yaml = &make_yaml( " diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/config/src/rule/stop_by.rs new/ast-grep-0.51.0/crates/config/src/rule/stop_by.rs --- old/ast-grep-0.50.0/crates/config/src/rule/stop_by.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/config/src/rule/stop_by.rs 2026-10-15 17:05:11.000000000 +0200 @@ -198,7 +198,7 @@ assert!(matches!(stop_by, SerializableStopBy::Neighbor)); } - fn to_stop_by(src: &str) -> Result<SerializableStopBy, serde_yaml::Error> { + fn to_stop_by(src: &str) -> Result<SerializableStopBy, serde_saphyr::Error> { from_str(src) } @@ -254,17 +254,17 @@ #[test] fn test_serialization_deserialization_symmetry() { let stop = to_stop_by("'neighbor'").expect("cannot parse stopBy"); - let serialized = serde_yaml::to_string(&stop).expect("cannot serialize stopBy"); + let serialized = serde_saphyr::to_string(&stop).expect("cannot serialize stopBy"); let deserialized = to_stop_by(&serialized).expect("cannot parse stopBy"); assert!(matches!(deserialized, SerializableStopBy::Neighbor)); let stop = to_stop_by("'end'").expect("cannot parse stopBy"); - let serialized = serde_yaml::to_string(&stop).expect("cannot serialize stopBy"); + let serialized = serde_saphyr::to_string(&stop).expect("cannot serialize stopBy"); let deserialized = to_stop_by(&serialized).expect("cannot parse stopBy"); assert!(matches!(deserialized, SerializableStopBy::End)); let stop = to_stop_by("kind: some-kind").expect("cannot parse stopBy"); - let serialized = serde_yaml::to_string(&stop).expect("cannot serialize stopBy"); + let serialized = serde_saphyr::to_string(&stop).expect("cannot serialize stopBy"); let deserialized = to_stop_by(&serialized).expect("cannot parse stopBy"); assert!(matches!(deserialized, SerializableStopBy::Rule(_))); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/config/src/rule_config.rs new/ast-grep-0.51.0/crates/config/src/rule_config.rs --- old/ast-grep-0.50.0/crates/config/src/rule_config.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/config/src/rule_config.rs 2026-10-15 17:05:11.000000000 +0200 @@ -15,8 +15,7 @@ use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema}; use serde::{Deserialize, Serialize}; -use serde_yaml::Error as YamlError; -use serde_yaml::{Deserializer, with::singleton_map_recursive::deserialize}; +use serde_saphyr::Error as YamlError; use thiserror::Error; use std::borrow::Cow; @@ -59,7 +58,7 @@ #[derive(Debug, Error)] pub enum RuleConfigError { #[error("Fail to parse yaml as RuleConfig")] - Yaml(#[from] YamlError), + Yaml(#[from] Box<YamlError>), #[error("Fail to parse yaml as Rule.")] Core(#[from] RuleCoreError), #[error("`fix` pattern is invalid.")] @@ -74,6 +73,12 @@ MissingPotentialKinds, } +impl From<YamlError> for RuleConfigError { + fn from(error: YamlError) -> Self { + Self::Yaml(Box::new(error)) + } +} + #[derive(Serialize, Deserialize, Clone, JsonSchema)] #[schemars(title = "ast-grep rule")] pub struct SerializableRuleConfig<L: Language> { @@ -127,10 +132,9 @@ }, } -/// A trivial wrapper around a HashMap to work around -/// the limitation of `serde_yaml::Value` not implementing `JsonSchema`. +/// Arbitrary YAML metadata, including non-string mapping keys and non-finite numbers. #[derive(Serialize, Deserialize, Clone)] -pub struct Metadata(HashMap<String, serde_yaml::Value>); +pub struct Metadata(HashMap<String, serde_value::Value>); impl JsonSchema for Metadata { fn schema_name() -> Cow<'static, str> { @@ -235,14 +239,15 @@ }) } - pub fn deserialize<'de>( - deserializer: Deserializer<'de>, + pub fn deserialize<'de, D>( + deserializer: D, globals: &GlobalRules, ) -> Result<Self, RuleConfigError> where L: Deserialize<'de>, + D: serde::Deserializer<'de, Error = YamlError>, { - let inner: SerializableRuleConfig<L> = deserialize(deserializer)?; + let inner = SerializableRuleConfig::deserialize(deserializer)?; Self::try_from(inner, globals) } @@ -890,6 +895,48 @@ } #[test] + fn test_yaml_metadata_round_trip() { + use serde_value::Value; + let config: SerializableRuleConfig<TypeScript> = from_str( + r" +id: metadata +language: Tsx +rule: {pattern: foo} +metadata: + values: [null, true, -1, 18446744073709551615, .inf, -.inf, .nan] + mapping: {[a, b]: value} +", + ) + .expect("YAML metadata should retain non-JSON values"); + let metadata = config.metadata.as_ref().unwrap(); + let Value::Seq(values) = &metadata.0["values"] else { + panic!("metadata should contain a sequence"); + }; + assert_eq!(values[0], Value::Unit); + assert_eq!(values[1], Value::Bool(true)); + assert_eq!(values[2], Value::I64(-1)); + assert_eq!(values[3], Value::U64(u64::MAX)); + assert_eq!(values[4], Value::F64(f64::INFINITY)); + assert_eq!(values[5], Value::F64(f64::NEG_INFINITY)); + assert!(matches!(values[6], Value::F64(f) if f.is_nan())); + let Value::Map(mapping) = &metadata.0["mapping"] else { + panic!("metadata should contain a mapping"); + }; + let key = Value::Seq(vec![Value::String("a".into()), Value::String("b".into())]); + assert_eq!(mapping[&key], Value::String("value".into())); + let yaml = serde_saphyr::to_string(&config).unwrap(); + let round_trip: SerializableRuleConfig<TypeScript> = from_str(&yaml).unwrap(); + assert_eq!(metadata.0, round_trip.metadata.unwrap().0); + } + + #[test] + fn test_yaml_metadata_rejects_duplicate_keys_and_unknown_tags() { + assert!(from_str::<Metadata>("value: {key: 1, key: 2}").is_err()); + assert!(from_str::<Metadata>("value: !Custom hello").is_err()); + assert!(from_str::<Metadata>("value: {nested: !Custom hello}").is_err()); + } + + #[test] fn test_label() { let src = r" id: test-rule diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/config/src/rule_core.rs new/ast-grep-0.51.0/crates/config/src/rule_core.rs --- old/ast-grep-0.50.0/crates/config/src/rule_core.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/config/src/rule_core.rs 2026-10-15 17:05:11.000000000 +0200 @@ -9,7 +9,7 @@ use ast_grep_core::meta_var::MetaVarEnv; use ast_grep_core::{Doc, Matcher, Node}; use serde::{Deserialize, Serialize}; -use serde_yaml::Error as YamlError; +use serde_saphyr::Error as YamlError; use bit_set::BitSet; use schemars::JsonSchema; @@ -22,7 +22,7 @@ #[derive(Debug, Error)] pub enum RuleCoreError { #[error("Fail to parse yaml as RuleConfig")] - Yaml(#[from] YamlError), + Yaml(#[from] Box<YamlError>), #[error("`utils` is not configured correctly.")] Utils(#[source] RuleSerializeError), #[error("`rule` is not configured correctly.")] @@ -35,6 +35,12 @@ UndefinedMetaVar(String, &'static str), } +impl From<YamlError> for RuleCoreError { + fn from(error: YamlError) -> Self { + Self::Yaml(Box::new(error)) + } +} + type RResult<T> = std::result::Result<T, RuleCoreError>; /// Used for global rules, rewriters, and pyo3/napi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/config/src/transform/parse.rs new/ast-grep-0.51.0/crates/config/src/transform/parse.rs --- old/ast-grep-0.50.0/crates/config/src/transform/parse.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/config/src/transform/parse.rs 2026-10-15 17:05:11.000000000 +0200 @@ -1,8 +1,8 @@ use super::Trans; use super::rewrite::Rewrite; use super::trans::{Convert, Replace, Substring}; +use crate::from_str as yaml_from_str; use regex::Regex; -use serde_yaml::from_str as yaml_from_str; use std::str::FromStr; use thiserror::Error; @@ -17,11 +17,17 @@ #[error("Argument `{0}` is required.")] RequiredArg(&'static str), #[error("Invalid argument value.")] - ArgValue(#[from] serde_yaml::Error), + ArgValue(#[from] Box<serde_saphyr::Error>), #[error("Invalid regex.")] Regex(#[from] regex::Error), } +impl From<serde_saphyr::Error> for ParseTransError { + fn from(error: serde_saphyr::Error) -> Self { + Self::ArgValue(Box::new(error)) + } +} + impl FromStr for Trans<String> { type Err = ParseTransError; @@ -112,11 +118,11 @@ } let replace = replace.ok_or(ParseTransError::RequiredArg("replace"))?; let by = by.ok_or(ParseTransError::RequiredArg("by"))?; - let replace: String = serde_yaml::from_str(replace)?; + let replace: String = yaml_from_str(replace)?; Ok(Replace { source: decomposed.source.to_string(), replace: Regex::new(&replace)?, - by: serde_yaml::from_str(by)?, + by: yaml_from_str(by)?, }) } fn to_substring(decomposed: DecomposedTransString) -> Result<Substring<String>, ParseTransError> { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/config/src/transform/trans.rs new/ast-grep-0.51.0/crates/config/src/transform/trans.rs --- old/ast-grep-0.50.0/crates/config/src/transform/trans.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/config/src/transform/trans.rs 2026-10-15 17:05:11.000000000 +0200 @@ -210,7 +210,6 @@ use crate::test::TypeScript; use crate::{DeserializeEnv, Transformation}; use ast_grep_core::tree_sitter::LanguageExt; - use serde_yaml::with::singleton_map_recursive; use std::collections::HashMap; type R = std::result::Result<(), ()>; @@ -228,8 +227,7 @@ } fn parse(trans: &str) -> Result<Trans<String>, ()> { - let deserializer = serde_yaml::Deserializer::from_str(trans); - singleton_map_recursive::deserialize(deserializer).map_err(|_| ()) + crate::from_str(trans).map_err(|_| ()) } #[test] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/dynamic/Cargo.toml new/ast-grep-0.51.0/crates/dynamic/Cargo.toml --- old/ast-grep-0.50.0/crates/dynamic/Cargo.toml 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/dynamic/Cargo.toml 2026-10-15 17:05:11.000000000 +0200 @@ -21,4 +21,4 @@ target-triple = "1.0.0" [dev-dependencies] -serde_yaml.workspace = true \ No newline at end of file +serde-saphyr.workspace = true \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/dynamic/src/custom_lang.rs new/ast-grep-0.51.0/crates/dynamic/src/custom_lang.rs --- old/ast-grep-0.50.0/crates/dynamic/src/custom_lang.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/dynamic/src/custom_lang.rs 2026-10-15 17:05:11.000000000 +0200 @@ -65,7 +65,7 @@ #[cfg(test)] mod test { use super::*; - use serde_yaml::from_str; + use serde_saphyr::from_str; #[test] fn test_custom_lang() { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/language/Cargo.toml new/ast-grep-0.51.0/crates/language/Cargo.toml --- old/ast-grep-0.50.0/crates/language/Cargo.toml 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/language/Cargo.toml 2026-10-15 17:05:11.000000000 +0200 @@ -25,7 +25,7 @@ tree-sitter-c = { version = "0.24.0", optional = true } tree-sitter-elixir = { version = "0.3.0", optional = true } tree-sitter-go = { version = "0.25.0", optional = true } -tree-sitter-haskell = { version = "0.23.0", optional = true } +tree-sitter-haskell = { version = "0.24.0", optional = true } tree-sitter-html = { version = "0.23.0", optional = true } tree-sitter-javascript = { version = "0.25.0", optional = true } tree-sitter-java = { version = "0.23.0", optional = true } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/napi/npm/darwin-arm64/package.json new/ast-grep-0.51.0/crates/napi/npm/darwin-arm64/package.json --- old/ast-grep-0.50.0/crates/napi/npm/darwin-arm64/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/napi/npm/darwin-arm64/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/napi-darwin-arm64", - "version": "0.50.0", + "version": "0.51.0", "os": [ "darwin" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/napi/npm/darwin-x64/package.json new/ast-grep-0.51.0/crates/napi/npm/darwin-x64/package.json --- old/ast-grep-0.50.0/crates/napi/npm/darwin-x64/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/napi/npm/darwin-x64/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/napi-darwin-x64", - "version": "0.50.0", + "version": "0.51.0", "os": [ "darwin" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/napi/npm/linux-arm64-gnu/package.json new/ast-grep-0.51.0/crates/napi/npm/linux-arm64-gnu/package.json --- old/ast-grep-0.50.0/crates/napi/npm/linux-arm64-gnu/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/napi/npm/linux-arm64-gnu/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/napi-linux-arm64-gnu", - "version": "0.50.0", + "version": "0.51.0", "os": [ "linux" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/napi/npm/linux-arm64-musl/package.json new/ast-grep-0.51.0/crates/napi/npm/linux-arm64-musl/package.json --- old/ast-grep-0.50.0/crates/napi/npm/linux-arm64-musl/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/napi/npm/linux-arm64-musl/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/napi-linux-arm64-musl", - "version": "0.50.0", + "version": "0.51.0", "os": [ "linux" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/napi/npm/linux-x64-gnu/package.json new/ast-grep-0.51.0/crates/napi/npm/linux-x64-gnu/package.json --- old/ast-grep-0.50.0/crates/napi/npm/linux-x64-gnu/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/napi/npm/linux-x64-gnu/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/napi-linux-x64-gnu", - "version": "0.50.0", + "version": "0.51.0", "os": [ "linux" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/napi/npm/linux-x64-musl/package.json new/ast-grep-0.51.0/crates/napi/npm/linux-x64-musl/package.json --- old/ast-grep-0.50.0/crates/napi/npm/linux-x64-musl/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/napi/npm/linux-x64-musl/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/napi-linux-x64-musl", - "version": "0.50.0", + "version": "0.51.0", "os": [ "linux" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/napi/npm/win32-arm64-msvc/package.json new/ast-grep-0.51.0/crates/napi/npm/win32-arm64-msvc/package.json --- old/ast-grep-0.50.0/crates/napi/npm/win32-arm64-msvc/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/napi/npm/win32-arm64-msvc/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/napi-win32-arm64-msvc", - "version": "0.50.0", + "version": "0.51.0", "os": [ "win32" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/napi/npm/win32-ia32-msvc/package.json new/ast-grep-0.51.0/crates/napi/npm/win32-ia32-msvc/package.json --- old/ast-grep-0.50.0/crates/napi/npm/win32-ia32-msvc/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/napi/npm/win32-ia32-msvc/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/napi-win32-ia32-msvc", - "version": "0.50.0", + "version": "0.51.0", "os": [ "win32" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/napi/npm/win32-x64-msvc/package.json new/ast-grep-0.51.0/crates/napi/npm/win32-x64-msvc/package.json --- old/ast-grep-0.50.0/crates/napi/npm/win32-x64-msvc/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/napi/npm/win32-x64-msvc/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/napi-win32-x64-msvc", - "version": "0.50.0", + "version": "0.51.0", "os": [ "win32" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/napi/package.json new/ast-grep-0.51.0/crates/napi/package.json --- old/ast-grep-0.50.0/crates/napi/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/napi/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/napi", - "version": "0.50.0", + "version": "0.51.0", "description": "Search and Rewrite code at large scale using precise AST pattern", "homepage": "https://ast-grep.github.io", "main": "index.js", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/outline/Cargo.toml new/ast-grep-0.51.0/crates/outline/Cargo.toml --- old/ast-grep-0.50.0/crates/outline/Cargo.toml 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/outline/Cargo.toml 2026-10-15 17:05:11.000000000 +0200 @@ -15,7 +15,7 @@ ast-grep-config.workspace = true serde.workspace = true -serde_yaml.workspace = true +serde-saphyr.workspace = true thiserror.workspace = true regex.workspace = true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/outline/src/extractor.rs new/ast-grep-0.51.0/crates/outline/src/extractor.rs --- old/ast-grep-0.50.0/crates/outline/src/extractor.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/outline/src/extractor.rs 2026-10-15 17:05:11.000000000 +0200 @@ -16,7 +16,7 @@ source::Content, }; use serde::{Deserialize, Serialize}; -use serde_yaml::{Deserializer, Error as YamlError, with::singleton_map_recursive::deserialize}; +use serde_saphyr::Error as YamlError; use std::borrow::Cow; use thiserror::Error; @@ -426,7 +426,7 @@ where L: Deserialize<'a>, { - Deserializer::from_str(src).map(deserialize).collect() + ast_grep_config::from_multiple(src) } #[cfg(test)] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/pyo3/pyproject.toml new/ast-grep-0.51.0/crates/pyo3/pyproject.toml --- old/ast-grep-0.50.0/crates/pyo3/pyproject.toml 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/pyo3/pyproject.toml 2026-10-15 17:05:11.000000000 +0200 @@ -5,7 +5,7 @@ [project] name = "ast-grep-py" requires-python = ">=3.8" -version = "0.50.0" +version = "0.51.0" description = "Structural Search and Rewrite code at large scale using precise AST pattern." authors = [{ name = "Herrington Darkholme", email = "[email protected]" }] maintainers = [{ name = "Herrington Darkholme", email = "[email protected]" }] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/crates/wasm/src/sg_node.rs new/ast-grep-0.51.0/crates/wasm/src/sg_node.rs --- old/ast-grep-0.50.0/crates/wasm/src/sg_node.rs 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/crates/wasm/src/sg_node.rs 2026-10-15 17:05:11.000000000 +0200 @@ -8,18 +8,19 @@ use crate::doc::{WasmConfig, WasmDoc, Wrapper}; #[derive(serde::Serialize, serde::Deserialize)] -#[wasm_bindgen(getter_with_clone)] +#[wasm_bindgen] pub struct WasmEdit { /// The start position of the edit (character offset) pub start_pos: u32, /// The end position of the edit (character offset) pub end_pos: u32, /// The text to be inserted + #[wasm_bindgen(getter_with_clone)] pub inserted_text: String, } #[derive(Clone)] -#[wasm_bindgen(getter_with_clone)] +#[wasm_bindgen] pub struct Pos { /// line number starting from 0 pub line: u32, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/npm/package.json new/ast-grep-0.51.0/npm/package.json --- old/ast-grep-0.50.0/npm/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/npm/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/cli", - "version": "0.50.0", + "version": "0.51.0", "publishConfig": { "access": "public" }, @@ -30,13 +30,13 @@ "postinstall": "node postinstall.js" }, "optionalDependencies": { - "@ast-grep/cli-win32-arm64-msvc": "0.50.0", - "@ast-grep/cli-win32-ia32-msvc": "0.50.0", - "@ast-grep/cli-win32-x64-msvc": "0.50.0", - "@ast-grep/cli-darwin-arm64": "0.50.0", - "@ast-grep/cli-darwin-x64": "0.50.0", - "@ast-grep/cli-linux-arm64-gnu": "0.50.0", - "@ast-grep/cli-linux-x64-gnu": "0.50.0" + "@ast-grep/cli-win32-arm64-msvc": "0.51.0", + "@ast-grep/cli-win32-ia32-msvc": "0.51.0", + "@ast-grep/cli-win32-x64-msvc": "0.51.0", + "@ast-grep/cli-darwin-arm64": "0.51.0", + "@ast-grep/cli-darwin-x64": "0.51.0", + "@ast-grep/cli-linux-arm64-gnu": "0.51.0", + "@ast-grep/cli-linux-x64-gnu": "0.51.0" }, "bin": { "sg": "sg", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/npm/platforms/darwin-arm64/package.json new/ast-grep-0.51.0/npm/platforms/darwin-arm64/package.json --- old/ast-grep-0.50.0/npm/platforms/darwin-arm64/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/npm/platforms/darwin-arm64/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/cli-darwin-arm64", - "version": "0.50.0", + "version": "0.51.0", "os": [ "darwin" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/npm/platforms/darwin-x64/package.json new/ast-grep-0.51.0/npm/platforms/darwin-x64/package.json --- old/ast-grep-0.50.0/npm/platforms/darwin-x64/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/npm/platforms/darwin-x64/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/cli-darwin-x64", - "version": "0.50.0", + "version": "0.51.0", "os": [ "darwin" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/npm/platforms/linux-arm64-gnu/package.json new/ast-grep-0.51.0/npm/platforms/linux-arm64-gnu/package.json --- old/ast-grep-0.50.0/npm/platforms/linux-arm64-gnu/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/npm/platforms/linux-arm64-gnu/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/cli-linux-arm64-gnu", - "version": "0.50.0", + "version": "0.51.0", "os": [ "linux" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/npm/platforms/linux-x64-gnu/package.json new/ast-grep-0.51.0/npm/platforms/linux-x64-gnu/package.json --- old/ast-grep-0.50.0/npm/platforms/linux-x64-gnu/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/npm/platforms/linux-x64-gnu/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/cli-linux-x64-gnu", - "version": "0.50.0", + "version": "0.51.0", "os": [ "linux" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/npm/platforms/win32-arm64-msvc/package.json new/ast-grep-0.51.0/npm/platforms/win32-arm64-msvc/package.json --- old/ast-grep-0.50.0/npm/platforms/win32-arm64-msvc/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/npm/platforms/win32-arm64-msvc/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/cli-win32-arm64-msvc", - "version": "0.50.0", + "version": "0.51.0", "os": [ "win32" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/npm/platforms/win32-ia32-msvc/package.json new/ast-grep-0.51.0/npm/platforms/win32-ia32-msvc/package.json --- old/ast-grep-0.50.0/npm/platforms/win32-ia32-msvc/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/npm/platforms/win32-ia32-msvc/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/cli-win32-ia32-msvc", - "version": "0.50.0", + "version": "0.51.0", "os": [ "win32" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/npm/platforms/win32-x64-msvc/package.json new/ast-grep-0.51.0/npm/platforms/win32-x64-msvc/package.json --- old/ast-grep-0.50.0/npm/platforms/win32-x64-msvc/package.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/npm/platforms/win32-x64-msvc/package.json 2026-10-15 17:05:11.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@ast-grep/cli-win32-x64-msvc", - "version": "0.50.0", + "version": "0.51.0", "os": [ "win32" ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/pyproject.toml new/ast-grep-0.51.0/pyproject.toml --- old/ast-grep-0.50.0/pyproject.toml 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/pyproject.toml 2026-10-15 17:05:11.000000000 +0200 @@ -4,7 +4,7 @@ [project] name = "ast-grep-cli" -version = "0.50.0" +version = "0.51.0" description = "Structural Search and Rewrite code at large scale using precise AST pattern." authors = [{ name = "Herrington Darkholme", email = "[email protected]" }] maintainers = [{ name = "Herrington Darkholme", email = "[email protected]" }] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ast-grep-0.50.0/schemas/haskell_rule.json new/ast-grep-0.51.0/schemas/haskell_rule.json --- old/ast-grep-0.50.0/schemas/haskell_rule.json 2026-10-07 16:13:05.000000000 +0200 +++ new/ast-grep-0.51.0/schemas/haskell_rule.json 2026-10-15 17:05:11.000000000 +0200 @@ -315,7 +315,7 @@ "type_params", "type_patterns", "type_role", - "type_synomym", + "type_synonym", "typed_quote", "unboxed_sum", "unboxed_tuple", @@ -724,7 +724,7 @@ "type_params", "type_patterns", "type_role", - "type_synomym", + "type_synonym", "typed_quote", "unboxed_sum", "unboxed_tuple", ++++++ vendor.tar.zst ++++++ /work/SRC/openSUSE:Factory/ast-grep/vendor.tar.zst /work/SRC/openSUSE:Factory/.ast-grep.new.3698736/vendor.tar.zst differ: char 7, line 1
