Tools

ast-grep: tìm và refactor code theo cấu trúc cú pháp thay vì regex

Tóm tắt

ast-grep tìm và thay code theo cây AST của tree-sitter, nên không vỡ khi code xuống dòng hay lồng ngoặc. -p cho mẫu, -r để thay, -i để xác nhận từng chỗ; ast-grep scan chạy quy tắc lint riêng của project.

Bạn cần đổi mọi lời gọi oldApi(a, b) thành newApi({ a, b }) trên cả codebase. Tìm và thay bằng regex thì vỡ khi tham số xuống dòng, có comment, hay lồng ngoặc. Sửa tay thì mất cả ngày. ast-grep tìm và thay code theo cấu trúc cú pháp (cây AST do tree-sitter phân tích) thay vì theo chuỗi ký tự. Bạn viết mẫu trông giống code thật, ast-grep hiểu đâu là biểu thức, tham số, khối lệnh, và thay thế chính xác.

Trang GitHub của ast-grep/ast-grep
Trang GitHub của ast-grep/ast-grep (ảnh chụp tháng 11/2026)

GitHub: https://github.com/ast-grep/ast-grep · khoảng 16 nghìn sao · Rust · MIT

Cài đặt

brew install ast-grep
cargo install ast-grep --locked
pip install ast-grep-cli
npm install --global @ast-grep/cli

Công cụ cũng cài kèm alias ngắn sg, nhưng trên Linux sg trùng một lệnh hệ thống có sẵn; dùng tên đầy đủ ast-grep cho chắc.

Tìm kiếm theo cấu trúc

# Tìm mọi console.log, bất kể tham số gì
ast-grep -p 'console.log($$$ARGS)' -l ts

# Tìm useEffect không có mảng phụ thuộc
ast-grep -p 'useEffect($FN)' -l tsx

# Tìm so sánh với chính nó (thường là lỗi)
ast-grep -p '$A == $A' -l py
Cú pháp mẫuKhớp với
$AĐúng một nút AST (một biểu thức, một tên), có thể tham chiếu lại
$$$ARGSKhông hoặc nhiều nút (danh sách tham số, câu lệnh)
$_Một nút bất kỳ, không cần nhớ
$A xuất hiện hai lầnHai vị trí phải giống hệt nhau

Thay thế (rewrite)

# Ví dụ trong README: dùng optional chaining
ast-grep -p '$A && $A()' -l ts -r '$A?.()'

# Đổi chữ ký API
ast-grep -p 'oldApi($A, $B)' -r 'newApi({ a: $A, b: $B })' -l ts

# Xem và xác nhận từng thay đổi
ast-grep -p 'oldApi($A, $B)' -r 'newApi({ a: $A, b: $B })' -l ts -i

# Áp dụng tất cả không hỏi (sau khi đã kiểm tra)
ast-grep -p 'oldApi($A, $B)' -r 'newApi({ a: $A, b: $B })' -l ts -U

Biến mẫu thành quy tắc lint

ast-grep new          # tạo project quy tắc: sgconfig.yml, thư mục rules/
# rules/no-console-log.yml
id: no-console-log
language: TypeScript
severity: warning
message: Xoá console.log trước khi merge
rule:
  pattern: console.log($$$ARGS)
fix: ''
ast-grep scan              # chạy mọi quy tắc trong project
ast-grep scan --update-all # áp dụng mọi fix

Lưu ý

  • Mẫu phải là code hợp lệ của ngôn ngữ đó; luôn chỉ định -l để ast-grep phân tích đúng.
  • Chạy rewrite trên nhánh riêng và xem git diff trước khi commit; dùng -i cho lần đầu.
  • ast-grep không hiểu kiểu dữ liệu (type); hai hàm cùng tên ở module khác nhau vẫn bị khớp. Thu hẹp bằng đường dẫn hoặc điều kiện trong quy tắc.

Áp dụng thế nào?

  • Dev: Dùng ast-grep cho refactor hàng loạt: đổi API, nâng cấp thư viện có thay đổi chữ ký hàm, xoá code chết theo mẫu.
  • Tech Lead: Viết quy tắc ast-grep cho những mẫu cấm riêng của dự án và chạy ast-grep scan trong CI; quy tắc ngắn, dễ đọc hơn plugin linter tự viết.
  • PM: Các đợt nâng cấp thư viện lớn trước đây tốn nhiều ngày sửa tay có thể rút ngắn đáng kể, ít lỗi sót hơn.

Nguồn & tham khảo

  1. ast-grep/ast-grep — ast-grep (GitHub)
  2. ast-grep documentation — ast-grep