# Ast Grep > Thank you for your interest in adding a new language to ast-grep! --- # Source: https://ast-grep.github.io/contributing/add-lang.md --- url: /contributing/add-lang.md --- # Add New Language to ast-grep Thank you for your interest in adding a new language to ast-grep! We appreciate your contribution to this project. Adding new languages will make the tool more useful and accessible to a wider range of users. However, there are some requirements and constraints that you need to consider before you start. This guide will help you understand the process and the standards of adding a new language to ast-grep. ## Requirements and Constraints To keep ast-grep lightweight and fast, we have several factors to consider when adding a new language. As a rule of thumb, we want to limit the binary size of ast-grep under 10MB after zip compression. * **Popularity of the language**. While the popularity of a language does not necessarily reflect its merits, our limited size budget allows us to only support languages that are widely used and have a large user base. Online sources like [TIOBE index](https://www.tiobe.com/tiobe-index/) or [GitHub Octoverse](https://octoverse.github.com/2022/top-programming-languages) can help one to check the popularity of the language. - **Quality of the Tree-sitter grammar**. ast-grep relies on [Tree-sitter](https://tree-sitter.github.io/tree-sitter/), a parser generator tool and a parsing library, to support different languages. The Tree-sitter grammar for the new language should be *well-written*, *up-to-date*, and *regularly maintained*. You can search [Tree-sitter on GitHub](https://github.com/search?q=tree-sitter\&type=repositories) or on [crates.io](https://crates.io/search?q=tree%20sitter). - **Size of the grammar**. The new language's grammar should not be too complicated. Otherwise it may take too much space from other languages. You can also check the current size of ast-grep in the [releases page](https://github.com/ast-grep/ast-grep/releases). - **Availability of the grammar on crates.io**. To ease the maintenance burden, we prefer to use grammars that are published on crates.io, Rust's package registry. If your grammar is not on crates.io, you need to publish it yourself or ask the author to do so. *** Don't worry if your language is not supported by ast-grep. You can try ast-grep's [custom language support](/advanced/custom-language.html) and register your own Tree-sitter parser! If your language satisfies the requirements above, congratulations! Let's see how to add it to ast-grep. ## Add to ast-grep Core ast-grep has several distinct use cases: [CLI tool](https://crates.io/crates/ast-grep), [n-api lib](https://www.npmjs.com/package/@ast-grep/napi) and [web playground](https://ast-grep.github.io/playground.html). Adding a language includes two steps. The first step is to add the language to ast-grep core. The core repository is multi-crate workspace hosted at [GitHub](https://github.com/ast-grep/ast-grep). The relevant crate is [language](https://github.com/ast-grep/ast-grep/tree/main/crates/language), which defines the supported languages and their tree-sitter grammars. We will use Ruby as an example to show how to add a new language to ast-grep core. You can see [the commit](https://github.com/ast-grep/ast-grep/commit/ffe14ceb8773c5d2b85559ff7455070e2a1a9388#diff-3590708789e9cdf7fa0421ecba544a69e9bbe8dd0915f0d9ff8344a9c899adfd) as a reference. ### Add Dependencies 1. Add `tree-sitter-[lang]` crate as `dependencies` to the [Cargo.toml](https://github.com/ast-grep/ast-grep/blob/main/crates/language/Cargo.toml#L13) in the `language` crate. ```toml # Cargo.toml [dependencies] ... tree-sitter-ruby = {version = "0.20.0", optional = true } // [!code ++] ... ``` *Note the `optional` attribute is required here.* 2. Add the `tree-sitter-[lang]` dependency in [`builtin-parser`](https://github.com/ast-grep/ast-grep/blob/e494500fc5d6994c20fe0102aa4b93d2108827bb/crates/language/Cargo.toml#L40) list. ```toml # Cargo.toml [features] builtin-parser = [ ... "tree-sitter-ruby", // [!code ++] ... ] ``` The `builtin-parser` feature is used for command line tool. Web playground is not using the builtin parser so the dependency must be optional. ### Implement Parser 3. Add the parser function in [parsers.rs](https://github.com/ast-grep/ast-grep/blob/main/crates/language/src/parsers.rs), where tree-sitter grammars are imported. ```rust #[cfg(feature = "builtin-parser")] mod parser_implementation { ... pub fn language_ruby() -> TSLanguage { // [!code ++] tree_sitter_ruby::language().into() // [!code ++] } // [!code ++] ... } #[cfg(not(feature = "builtin-parser"))] mod parser_implementation { impl_parsers!( ... language_ruby, // [!code ++] ... ); } ``` Note there are two places to add, one for `#[cfg(feature = "builtin-parser")]` and the other for `#[cfg(not(feature = "builtin-parser"))]`. 4. Implement `language` trait by using macro in [lib.rs](https://github.com/ast-grep/ast-grep/commit/ffe14ceb8773c5d2b85559ff7455070e2a1a9388#diff-1f2939360f8f95434ed23b53406eac0aa8b2f404171b63c6466bbdfda728c82d) ```rust // lib.rs impl_lang_expando!(Ruby, language_ruby, 'µ'); // [!code ++] ``` There are two macros, `impl_lang_expando` or `impl_lang`, to generate necessary methods required by ast-grep [`Language`](https://github.com/ast-grep/ast-grep/blob/e494500fc5d6994c20fe0102aa4b93d2108827bb/crates/core/src/language.rs#L12) trait. You need to choose one of them to use for the new language. If the language does not allow `$` as valid identifier character and you need to customize the expando\_char, use `impl_lang_expando`. You can reference the comment [here](https://github.com/ast-grep/ast-grep/blob/e494500fc5d6994c20fe0102aa4b93d2108827bb/crates/language/src/lib.rs#L1-L8) for more information. ### Register the New Language 6. Add new lang in [`SupportLang`](https://github.com/ast-grep/ast-grep/blob/e494500fc5d6994c20fe0102aa4b93d2108827bb/crates/language/src/lib.rs#L119) enum. ```rust // lib.rs pub enum SupportLang { ... Ruby, // [!code ++] ... } ``` 7. Add new lang in [`execute_lang_method`](https://github.com/ast-grep/ast-grep/blob/e494500fc5d6994c20fe0102aa4b93d2108827bb/crates/language/src/lib.rs#L229C14-L229C33) ```rust // lib.rs macro_rules! execute_lang_method { ($me: path, $method: ident, $($pname:tt),*) => { use SupportLang as S; match $me { ... S::Ruby => Ruby.$method($($pname,)*), // [!code ++] } } } ``` 7. Add new lang in [`all_langs`](https://github.com/ast-grep/ast-grep/blob/be10ff97d6d5adad4b524961d82e40ca76ab4259/crates/language/src/lib.rs#L143), [`alias`](https://github.com/ast-grep/ast-grep/blob/be10ff97d6d5adad4b524961d82e40ca76ab4259/crates/language/src/lib.rs#L188), [`extension`](https://github.com/ast-grep/ast-grep/blob/be10ff97d6d5adad4b524961d82e40ca76ab4259/crates/language/src/lib.rs#L281) and [`file_types`](https://github.com/ast-grep/ast-grep/blob/be10ff97d6d5adad4b524961d82e40ca76ab4259/crates/language/src/lib.rs#L331) See this [commit](https://github.com/ast-grep/ast-grep/commit/ffe14ceb8773c5d2b85559ff7455070e2a1a9388#diff-1f2939360f8f95434ed23b53406eac0aa8b2f404171b63c6466bbdfda728c82d) for the detailed code change. :::tip Find existing languages as reference The rule of thumb to add a new language is to find a reference language that is already included in the language crate. Then add your new language by searching and following the existing language. ::: ## Add to ast-grep Playground Adding new language to web playground is a little bit more complex. The playground has a standalone [repository](https://github.com/ast-grep/ast-grep.github.io) and we need to change code there. ### Prepare WASM 1. Set up Tree-sitter First, we need to set up Tree-sitter development tools like. You can refer to the Tree-sitter setup section in this [link](/advanced/custom-language.html#prepare-tree-sitter-tool-and-parser). 2. Build WASM file Then, in your parser repository, use this command to build a WASM file. ```bash tree-sitter generate # if grammar is not generated before tree-sitter build --wasm ``` Note you may need to install [docker](https://www.docker.com/) when building WASM files. 3. Move WASM file to the website [`public`](https://github.com/ast-grep/ast-grep.github.io/tree/main/website/public) folder. You can also see other languages' WASM files in the public directory. The file name is in the format of `tree-sitter-[lang].wasm`. The name will be used later in [`parserPaths`](https://github.com/ast-grep/ast-grep.github.io/blob/a2dce64dda67e1c0842b757fc692ffe05639e407/website/src/components/lang.ts#L4). ### Add language in Rust You need to add the language in the [wasm\_lang.rs](https://github.com/ast-grep/ast-grep.github.io/blob/main/src/wasm_lang.rs). More specifically, you need to add a new enum variant in [`WasmLang`](https://github.com/ast-grep/ast-grep.github.io/blob/a2dce64dda67e1c0842b757fc692ffe05639e407/src/wasm_lang.rs#L16), handle the new variant in [`execute_lang_method`](https://github.com/ast-grep/ast-grep.github.io/blob/a2dce64dda67e1c0842b757fc692ffe05639e407/src/wasm_lang.rs#L111) and implement [`FromStr`](https://github.com/ast-grep/ast-grep.github.io/blob/a2dce64dda67e1c0842b757fc692ffe05639e407/src/wasm_lang.rs#L48). ```rust // new variant pub enum WasmLang { // ... Swift, // [!code ++] } // handle variant in macro macro_rules! execute_lang_method { ($me: path, $method: ident, $($pname:tt),*) => { use WasmLang as W; match $me { W::Swift => L::Swift.$method($($pname,)*), // [!code ++] } } } // impl FromStr impl FromStr for WasmLang { // ... fn from_str(s: &str) -> Result { Ok(match s { "swift" => Swift, // [!code ++] }) } } ``` ### Add language in TypeScript Finally you need to add the language in TypeScript to make it available in playground. The file is [lang.ts](https://github.com/ast-grep/ast-grep.github.io/blob/main/website/src/components/lang.ts). There are two changes need to make. ```typescript // Add language parserPaths const parserPaths = { // ... swift: 'tree-sitter-swift.wasm', // [!code ++] } // Add language display name export const languageDisplayNames: Record = { // ... swift: 'Swift', } ``` You can see Swift's support as the [reference commit](https://github.com/ast-grep/ast-grep.github.io/commit/55a546535dee989ce5ee2582080e771d006d165e). --- # Source: https://ast-grep.github.io/guide/api-usage.md --- url: /guide/api-usage.md --- # API Usage ## ast-grep as Library ast-grep allows you to craft complicated rules, but it is not easy to do arbitrary AST manipulation. For example, you may struggle to: * replace a list of nodes individually, based on their content * replace a node conditionally, based on its content and surrounding nodes * count the number or order of nodes that match a certain pattern * compute the replacement string based on the matched nodes To solve these problems, you can use ast-grep's programmatic API! You can freely inspect and generate text patches based on syntax trees, using popular programming languages! :::tip Applying ast-grep's `fix` using JS/Python API is still experimental. See [this issue](https://github.com/ast-grep/ast-grep/issues/1172) for more information. ::: ## Language Bindings ast-grep provides support for these programming languages: * **JavaScript:** Powered by napi.rs, ast-grep's JavaScript API is the most robust and reliable. [Explore JavaScript API](/guide/api-usage/js-api.html) * **Python:** ast-grep's PyO3 interface is the latest addition to climb the syntax tree! [Discover Python API](/guide/api-usage/py-api.html) * **Rust:** ast-grep's Rust API is the most efficient way, but also the most challenging way, to use ast-grep. You can refer to [ast\_grep\_core](https://docs.rs/ast-grep-core/latest/ast_grep_core/) if you are familiar with Rust. ## Why and When to use API? ast-grep's API is designed to solve the problems that are hard to express in ast-grep's rule language. ast-grep's rule system is deliberately simple and not as powerful as a programming language. Other similar rewriting/query tools have complex features like conditional, loop, filter or function call. These features are hard to learn and use, and they cannot perform computation as well as a general purpose programming language. So ast-grep chooses to have a simple rule system that is easy to learn and use. But it also has its limitations. The API is created to overcome these limitations. If your code transformation requires complex logic, or if you need to change code that has no parser library in JavaScript or Python, ast-grep API is a good option to achieve your goal without writing a lot of complicated rules. --- # Source: https://ast-grep.github.io/reference/api.md --- url: /reference/api.md --- # API Reference ast-grep currently has an experimental API for [Node.js](https://nodejs.org/). You can see [API usage guide](/guide/api-usage.html) for more details. \[\[toc]] ## NAPI Please see the link for up-to-date type declaration. https://github.com/ast-grep/ast-grep/blob/main/crates/napi/index.d.ts ### Supported Languages `@ast-grep/napi` supports JS ecosystem languages by default. More custom languages can be loaded via [`registerDynamicLanguage`](https://github.com/search?q=repo%3Aast-grep%2Flangs%20registerDynamicLanguage\&type=code). #### Type ```ts export const enum Lang { Html = 'Html', JavaScript = 'JavaScript', Tsx = 'Tsx', Css = 'Css', TypeScript = 'TypeScript', } // More custom languages can be loaded // see https://github.com/ast-grep/langs type CustomLang = string & {} ``` `CustomLang` is not widely used now. If you have use case and needs support, please file an issue in the [@ast-grep/langs](https://github.com/ast-grep/langs?tab=readme-ov-file#packages) repository. ### Main functions You can use `parse` to transform a string to ast-grep's main object `SgRoot`. ast-grep also provides other utility for parse kind string and construct pattern. ```ts /** Parse a string to an ast-grep instance */ export function parse(lang: Lang, src: string): SgRoot /** Get the `kind` number from its string name. */ export function kind(lang: Lang, kindName: string): number /** Compile a string to ast-grep Pattern. */ export function pattern(lang: Lang, pattern: string): NapiConfig ``` #### Example ```ts import { parse, Lang } from '@ast-grep/napi' const ast = parse(Lang.JavaScript, source) const root = ast.root() root.find("console.log") ``` ### SgRoot You will get an `SgRoot` instance when you `parse(lang, string)`. `SgRoot` can also be accessed in `lang.findInFiles`'s callback by calling `node.getRoot()`. In the latter case, `sgRoot.filename()` will return the path of the matched file. #### Type ```ts /** Represents the parsed tree of code. */ class SgRoot { /** Returns the root SgNode of the ast-grep instance. */ root(): SgNode /** * Returns the path of the file if it is discovered by ast-grep's `findInFiles`. * Returns `"anonymous"` if the instance is created by `parse(lang, source)`. */ filename(): string } ``` #### Example ```ts import { parse, Lang } from '@ast-grep/napi' const ast = parse(Lang.JavaScript, source) const root = ast.root() root.find("console.log") ``` ### SgNode The main interface to traverse the AST. #### Type Most methods are self-explanatory. Please submit a new [issue](https://github.com/ast-grep/ast-grep/issues/new/choose) if you find something confusing. ```ts class SgNode { // Read node's information range(): Range isLeaf(): boolean isNamed(): boolean isNamedLeaf(): boolean kind(): string // check if node has kind is(kind: string): boolean // for TypeScript type narrow kindToRefine: string text(): string // Check if node meets certain patterns matches(m: string): boolean inside(m: string): boolean has(m: string): boolean precedes(m: string): boolean follows(m: string): boolean // Get nodes' matched meta variables getMatch(m: string): SgNode | null getMultipleMatches(m: string): Array // Get node's SgRoot getRoot(): SgRoot // Traverse node tree children(): Array find(matcher: string | number | NapiConfig): SgNode | null findAll(matcher: string | number | NapiConfig): Array field(name: string): SgNode | null parent(): SgNode | null child(nth: number): SgNode | null ancestors(): Array next(): SgNode | null nextAll(): Array prev(): SgNode | null prevAll(): Array // Edit replace(text: string): Edit commitEdits(edits: Edit[]): string } ``` Some methods have more sophisticated type signatures for the ease of use. See the [source code](https://github.com/ast-grep/ast-grep/blob/0999cdb542ff4431e3734dad38fcd648de972e6a/crates/napi/types/sgnode.d.ts#L38-L41) and our [tech blog](/blog/typed-napi.html) ### NapiConfig `NapiConfig` is used in `find` or `findAll`. #### Type `NapiConfig` has similar fields as the [rule config](/reference/yaml.html). ```ts interface NapiConfig { rule: object constraints?: object language?: FrontEndLanguage transform?: object utils?: object } ``` ### FindConfig `FindConfig` is used in `findInFiles`. #### Type ```ts interface FindConfig { // You can search multiple paths // ast-grep will recursively find all files under the paths. paths: Array // Specify what nodes will be matched matcher: NapiConfig } ``` ### Edit `Edit` is used in `replace` and `commitEdits`. ```ts interface Edit { startPos: number endPos: number insertedText: string } ``` ### Useful Examples * [Test Case Source](https://github.com/ast-grep/ast-grep/blob/main/crates/napi/__test__/index.spec.ts) for `@ast-grep/napi` * ast-grep usage in [vue-vine](https://github.com/vue-vine/vue-vine/blob/b661fd2dfb54f2945e7bf5f3691443e05a1ab8f8/packages/compiler/src/analyze.ts#L32) ### Language Object (deprecated) :::details language objects are deprecated `ast-grep/napi` also has special language objects for `html`, `js` and `css`. They are deprecated and will be removed in the next version. A language object has following methods. ```ts /** * @deprecated language specific objects are deprecated * use the equivalent functions like `parse` in @ast-grep/napi */ export declare namespace js { /** @deprecated use `parse(Lang.JavaScript, src)` instead */ export function parse(src: string): SgRoot /** @deprecated use `parseAsync(Lang.JavaScript, src)` instead */ export function parseAsync(src: string): Promise /** @deprecated use `kind(Lang.JavaScript, kindName)` instead */ export function kind(kindName: string): number /** @deprecated use `pattern(Lang.JavaScript, p)` instead */ export function pattern(pattern: string): NapiConfig /** @deprecated use `findInFiles(Lang.JavaScript, config, callback)` instead */ export function findInFiles( config: FindConfig, callback: (err: null | Error, result: SgNode[]) => void ): Promise } ``` #### Example ```ts import { js } from '@ast-grep/napi' const source = `console.log("hello world")` const ast = js.parse(source) ``` ::: ## Python API ### SgRoot The entry point object of ast-grep. You can use SgRoot to parse a string into a syntax tree. ```python class SgRoot: def __init__(self, src: str, language: str) -> None: ... def root(self) -> SgNode: ... ``` ### SgNode Most methods are self-explanatory. Please submit a new [issue](https://github.com/ast-grep/ast-grep/issues/new/choose) if you find something confusing. ```python class SgNode: # Node Inspection def range(self) -> Range: ... def is_leaf(self) -> bool: ... def is_named(self) -> bool: ... def is_named_leaf(self) -> bool: ... def kind(self) -> str: ... def text(self) -> str: ... # Refinement def matches(self, **rule: Unpack[Rule]) -> bool: ... def inside(self, **rule: Unpack[Rule]) -> bool: ... def has(self, **rule: Unpack[Rule]) -> bool: ... def precedes(self, **rule: Unpack[Rule]) -> bool: ... def follows(self, **rule: Unpack[Rule]) -> bool: ... def get_match(self, meta_var: str) -> Optional[SgNode]: ... def get_multiple_matches(self, meta_var: str) -> List[SgNode]: ... def get_transformed(self, meta_var: str) -> Optional[str]: ... def __getitem__(self, meta_var: str) -> SgNode: ... # Search @overload def find(self, config: Config) -> Optional[SgNode]: ... @overload def find(self, **kwargs: Unpack[Rule]) -> Optional[SgNode]: ... @overload def find_all(self, config: Config) -> List[SgNode]: ... @overload def find_all(self, **kwargs: Unpack[Rule]) -> List[SgNode]: ... # Tree Traversal def get_root(self) -> SgRoot: ... def field(self, name: str) -> Optional[SgNode]: ... def parent(self) -> Optional[SgNode]: ... def child(self, nth: int) -> Optional[SgNode]: ... def children(self) -> List[SgNode]: ... def ancestors(self) -> List[SgNode]: ... def next(self) -> Optional[SgNode]: ... def next_all(self) -> List[SgNode]: ... def prev(self) -> Optional[SgNode]: ... def prev_all(self) -> List[SgNode]: ... # Edit def replace(self, new_text: str) -> Edit: ... def commit_edits(self, edits: List[Edit]) -> str: ... ``` ### Rule The `Rule` object is a Python representation of the [YAML rule object](/guide/rule-config/atomic-rule.html) in the CLI. See the [reference](/reference/rule.html). ```python class Pattern(TypedDict): selector: str context: str class Rule(TypedDict, total=False): # atomic rule pattern: str | Pattern kind: str regex: str # relational rule inside: Relation has: Relation precedes: Relation follows: Relation # composite rule all: List[Rule] any: List[Rule] # pseudo code below for demo. "not": Rule # Python does not allow "not" keyword as attribute matches: str # Relational Rule Related StopBy = Union[Literal["neighbor"], Literal["end"], Rule] class Relation(Rule, total=False): stopBy: StopBy field: str ``` ### Config The Config object is similar to the [YAML rule config](/guide/rule-config.html) in the CLI. See the [reference](/reference/yaml.html). ```python class Config(TypedDict, total=False): rule: Rule constraints: Dict[str, Mapping] utils: Dict[str, Rule] transform: Dict[str, Mapping] ``` ### Edit `Edit` is used in `replace` and `commitEdits`. ```python class Edit: # The start position of the edit start_pos: int # The end position of the edit end_pos: int # The text to be inserted inserted_text: str ``` ## Rust API Rust API is not stable yet. The following link is only for those who are interested in modifying ast-grep's source. https://docs.rs/ast-grep-core/latest/ast\_grep\_core/ --- # Source: https://ast-grep.github.io/guide/rule-config/atomic-rule.md --- url: /guide/rule-config/atomic-rule.md --- # Atomic Rule ast-grep has three categories of rules. Let's start with the most basic one: atomic rule. Atomic rule defines the most basic matching rule that determines whether one syntax node matches the rule or not. There are five kinds of atomic rule: `pattern`, `kind`, `regex`, `nthChild` and `range`. ## `pattern` Pattern will match one single syntax node according to the [pattern syntax](/guide/pattern-syntax). ```yaml rule: pattern: console.log($GREETING) ``` The above rule will match code like `console.log('Hello World')`. By default, a *string* `pattern` is parsed and matched as a whole. ### Pattern Object It is not always possible to select certain code with a simple string pattern. A pattern code can be invalid, incomplete or ambiguous for the parser since it lacks context. For example, to select class field in JavaScript, writing `$FIELD = $INIT` will not work because it will be parsed as `assignment_expression`. See [playground](/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoiamF2YXNjcmlwdCIsInF1ZXJ5IjoiJEZJRUxEID0gJElOSVQiLCJyZXdyaXRlIjoiRGVidWcuYXNzZXJ0IiwiY29uZmlnIjoicnVsZTpcbiAgcGF0dGVybjogXG4gICAgY29udGV4dDogJ3sgJE06ICgkJCRBKSA9PiAkTUFUQ0ggfSdcbiAgICBzZWxlY3RvcjogcGFpclxuIiwic291cmNlIjoiYSA9IDEyM1xuY2xhc3MgQSB7XG4gIGEgPSAxMjNcbn0ifQ==). *** We can also use an *object* to specify a sub-syntax node to match within a larger context. It consists of an object with three properties: `context`, `selector` and `strictness`. * `context` (required): defines the surrounding code that helps to resolve any ambiguity in the syntax. * `selector` (optional): defines the sub-syntax node kind that is the actual matcher of the pattern. * `strictness` (optional): defines how strictly pattern will match against nodes. Let's see how pattern object can solve the ambiguity in the class field example above. The pattern object below instructs ast-grep to select the `field_definition` node as the pattern target. ```yaml pattern: selector: field_definition context: class A { $FIELD = $INIT } ``` ast-grep works like this: 1. First, the code in `context`, `class A { $FIELD = $INIT }`, is parsed as a class declaration. 2. Then, it looks for the `field_definition` node, specified by `selector`, in the parsed tree. 3. The selected `$FIELD = $INIT` is matched against code as the pattern. In this way, the pattern is parsed as `field_definition` instead of `assignment_expression`. See [playground](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6IiRGSUVMRCA9ICRJTklUIiwicmV3cml0ZSI6IkRlYnVnLmFzc2VydCIsImNvbmZpZyI6InJ1bGU6XG4gIHBhdHRlcm46XG4gICAgc2VsZWN0b3I6IGZpZWxkX2RlZmluaXRpb25cbiAgICBjb250ZXh0OiBjbGFzcyBBIHsgJEZJRUxEID0gJElOSVQgfVxuIiwic291cmNlIjoiYSA9IDEyM1xuY2xhc3MgQSB7XG4gIGEgPSAxMjNcbn0ifQ==) in action. Other examples are [function call in Go](https://github.com/ast-grep/ast-grep/issues/646) and [function parameter in Rust](https://github.com/ast-grep/ast-grep/issues/648). ### `strictness` You can also use pattern object to control the matching strategy with `strictness` field. By default, ast-grep uses a smart strategy to match pattern against the AST node. All nodes in the pattern must be matched, but it will skip unnamed nodes in target code. For the definition of ***named*** and ***unnamed*** nodes, please refer to the [core concepts](/advanced/core-concepts.html) doc. For example, the following pattern `function $A() {}` will match both plain function and async function in JavaScript. See [playground](/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoiamF2YXNjcmlwdCIsInF1ZXJ5IjoiZnVuY3Rpb24gJEEoKSB7fSIsInJld3JpdGUiOiJEZWJ1Zy5hc3NlcnQiLCJjb25maWciOiJydWxlOlxuICBwYXR0ZXJuOiBcbiAgICBjb250ZXh0OiAneyAkTTogKCQkJEEpID0+ICRNQVRDSCB9J1xuICAgIHNlbGVjdG9yOiBwYWlyXG4iLCJzb3VyY2UiOiJmdW5jdGlvbiBhKCkge31cbmFzeW5jIGZ1bmN0aW9uIGEoKSB7fSJ9) ```js // function $A() {} function foo() {} // matched async function bar() {} // matched ``` This is because the keyword `async` is an unnamed node in the AST, so the `async` in the code to search is skipped. As long as `function`, `$A` and `{}` are matched, the pattern is considered matched. However, this is not always the desired behavior. ast-grep provides `strictness` to control the matching strategy. At the moment, it provides these options, ordered from the most strict to the least strict: * `cst`: All nodes in the pattern and target code must be matched. No node is skipped. * `smart`: All nodes in the pattern must be matched, but it will skip unnamed nodes in target code. This is the default behavior. * `ast`: Only named AST nodes in both pattern and target code are matched. All unnamed nodes are skipped. * `relaxed`: Named AST nodes in both pattern and target code are matched. Comments and unnamed nodes are ignored. * `signature`: Only named AST nodes' kinds are matched. Comments, unnamed nodes and text are ignored. :::tip Deep Dive and More Examples `strictness` is an advanced feature that you may not need in most cases. If you are interested in more examples and details, please refer to the [deep dive](/advanced/match-algorithm.html) doc on ast-grep's match algorithm. ::: ## `kind` Sometimes it is not easy to write a pattern because it is hard to construct the valid syntax. For example, if we want to match class property declaration in JavaScript like `class A { a = 1 }`, writing `a = 1` will not match the property because it is parsed as assigning to a variable. Instead, we can use `kind` to specify the AST node type defined in [tree-sitter parser](https://tree-sitter.github.io/tree-sitter/using-parsers#named-vs-anonymous-nodes). `kind` rule accepts the tree-sitter node's name, like `if_statement` and `expression`. You can refer to [ast-grep playground](/playground) for relevant `kind` names. Back to our example, we can look up class property's kind from the playground. ```yaml rule: kind: field_definition ``` It will match the following code successfully ([playground link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6ImEgPSAxMjMiLCJyZXdyaXRlIjoibG9nZ2VyLmxvZygkTUFUQ0gpIiwiY29uZmlnIjoiIyBDb25maWd1cmUgUnVsZSBpbiBZQU1MXG5ydWxlOlxuICBraW5kOiBmaWVsZF9kZWZpbml0aW9uIiwic291cmNlIjoiY2xhc3MgVGVzdCB7XG4gIGEgPSAxMjNcbn0ifQ==)). ```js class Test { a = 123 // match this line } ``` Here are some situations that you can effectively use `kind`: 1. Pattern code is ambiguous to parse, e.g. `{}` in JavaScript can be either object or code block. 2. It is too hard to enumerate all patterns of an AST kind node, e.g. matching all Java/TypeScript class declaration will need including all modifiers, generics, `extends` and `implements`. 3. Patterns only appear within specific context, e.g. the class property definition. :::warning `kind` + `pattern` is different from pattern object You may want to use `kind` to change how `pattern` is parsed. However, ast-grep rules are independent of each other. To change the parsing behavior of `pattern`, you should use pattern object with `context` and `selector` field. See [this FAQ](/advanced/faq.html#kind-and-pattern-rules-are-not-working-together-why). ::: ### ESQuery style `kind` From ast-grep v0.39.1, you can also use ESQuery style selector in `kind` to match AST nodes. This is an experimental feature and may change in the future. ```yaml rule: kind: call_expression > identifier ``` This will match the `identifier` node that is a child of `call_expression` node. Internally, it will be converted to a [relational rule](/guide/rule-config/relational-rule.html) `has`. Currently, the ESQuery style `kind` only supports the following selectors: * node kind: `identifier` * `>`: direct child selectors * `+`: next sibling selector * `~`: following sibling selector * ` `: descendant selector If you want more selectors, please respond to [this issue on GitHub](https://github.com/ast-grep/ast-grep/issues/2127). ## `regex` The `regex` atomic rule will match the AST node by its text against a Rust regular expression. ```yaml rule: regex: "\w+" ``` :::tip The regular expression is written in [Rust syntax](https://docs.rs/regex/latest/regex/), not the popular [PCRE like syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions). So some features are not available like arbitrary look-ahead and back references. ::: You should almost always combine `regex` with other atomic rules to make sure the regular expression is applied to the correct AST node. Regex matching is quite expensive and cannot be optimized based on AST node kinds. While `kind` and `pattern` rules can be only applied to nodes with specific `kind_id` for optimized performance. :::tip You can use [Rust‑style inline flags](https://docs.rs/regex/latest/regex/#grouping-and-flags), for example: ```yaml rule: regex: "(?i)apple" ``` This matches Apple as well as apple or APPLE. ::: ## `nthChild` `nthChild` is a rule to find nodes based on their indexes in the parent node's children list. In other words, it selects nodes based on their position among all sibling nodes within a parent node. It is very helpful in finding nodes without children or nodes appearing in specific positions. `nthChild` is heavily inspired by CSS's [`nth-child` pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child), and it accepts similar forms of arguments. ```yaml # a number to match the exact nth child nthChild: 3 # An+B style string to match position based on formula nthChild: 2n+1 # object style nthChild rule nthChild: # accepts number or An+B style string position: 2n+1 # optional, count index from the end of sibling list reverse: true # default is false # optional, filter the sibling node list based on rule ofRule: kind: function_declaration # accepts ast-grep rule ``` :::tip * `nthChild`'s index is 1-based, not 0-based, as in the CSS selector. * `nthChild`'s node list only includes named nodes, not unnamed nodes. ::: **Example** The [following rule](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6IiRGSUVMRCA9ICRJTklUIiwicmV3cml0ZSI6IkRlYnVnLmFzc2VydCIsImNvbmZpZyI6InJ1bGU6XG4gIGtpbmQ6IG51bWJlclxuICBudGhDaGlsZDogMiIsInNvdXJjZSI6IlsxLDIsM10ifQ==) will match the second number in the JavaScript array. ```yaml rule: kind: number nthChild: 2 ``` It will match the following code: ```js const arr = [ 1, 2, 3, ] // |- match this number ``` ## `range` `range` is a rule to match nodes based on their position in the source code. It is useful when you want to integrate external tools like compilers or type checkers with ast-grep. External tools can provide the range information of the interested node, and ast-grep can use it to rewrite the code. `range` rule accepts a range object with `start` and `end` fields. Each field is an object with `line` and `column` fields. ```yaml rule: range: start: line: 0 column: 0 end: line: 1 column: 5 ``` The above example will match an AST node having the first three characters of the first line like `foo` in `foo.bar()`. `line` and `column` are 0-based and character-wise, and the `start` is inclusive while the `end` is exclusive. ## Tips for Writing Rules Since one rule will have *only one* AST node in one match, it is recommended to first write the atomic rule that matches the desired node. Suppose we want to write a rule which finds functions without a return type. For example, this code would trigger an error: ```ts const foo = () => { return 1; } ``` The first step to compose a rule is to find the target. In this case, we can first use kind: `arrow_function` to find function node. Then we can use other rules to filter candidate nodes that does have return type. Another trick to write cleaner rule is to use sub-rules as fields. Please refer to [composite rule](/guide/rule-config/composite-rule.html#combine-different-rules-as-fields) for more details. --- # Source: https://ast-grep.github.io/catalog/rust/avoid-duplicated-exports.md --- url: /catalog/rust/avoid-duplicated-exports.md --- ## Avoid Duplicated Exports * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InJ1c3QiLCJxdWVyeSI6IiIsImNvbmZpZyI6InJ1bGU6XG4gIGFsbDpcbiAgICAgLSBwYXR0ZXJuOiBwdWIgdXNlICRCOjokQztcbiAgICAgLSBpbnNpZGU6XG4gICAgICAgIGtpbmQ6IHNvdXJjZV9maWxlXG4gICAgICAgIGhhczpcbiAgICAgICAgICBwYXR0ZXJuOiBwdWIgbW9kICRBO1xuICAgICAtIGhhczpcbiAgICAgICAgcGF0dGVybjogJEFcbiAgICAgICAgc3RvcEJ5OiBlbmQiLCJzb3VyY2UiOiJwdWIgbW9kIGZvbztcbnB1YiB1c2UgZm9vOjpGb287XG5wdWIgdXNlIGZvbzo6QTo6QjtcblxuXG5wdWIgdXNlIGFhYTo6QTtcbnB1YiB1c2Ugd29vOjpXb287In0=) ### Description Generally, we don't encourage the use of re-exports. However, sometimes, to keep the interface exposed by a lib crate tidy, we use re-exports to shorten the path to specific items. When doing so, a pitfall is to export a single item under two different names. Consider: ```rs pub mod foo; pub use foo::Foo; ``` The issue with this code, is that `Foo` is now exposed under two different paths: `Foo`, `foo::Foo`. This unnecessarily increases the surface of your API. It can also cause issues on the client side. For example, it makes the usage of auto-complete in the IDE more involved. Instead, ensure you export only once with `pub`. ### YAML ```yaml id: avoid-duplicate-export language: rust rule: all: - pattern: pub use $B::$C; - inside: kind: source_file has: pattern: pub mod $A; - has: pattern: $A stopBy: end ``` ### Example ```rs {2,3} pub mod foo; pub use foo::Foo; pub use foo::A::B; pub use aaa::A; pub use woo::Woo; ``` ### Contributed by Julius Lungys([voidpumpkin](https://github.com/voidpumpkin)) --- # Source: https://ast-grep.github.io/catalog/tsx/avoid-jsx-short-circuit.md --- url: /catalog/tsx/avoid-jsx-short-circuit.md --- ## Avoid `&&` short circuit in JSX * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InRzeCIsInF1ZXJ5IjoiY29uc29sZS5sb2coJE1BVENIKSIsInJld3JpdGUiOiJsb2dnZXIubG9nKCRNQVRDSCkiLCJjb25maWciOiJpZDogZG8td2hhdC1icm9vb29vb2tseW4tc2FpZFxubGFuZ3VhZ2U6IFRzeFxuc2V2ZXJpdHk6IGVycm9yXG5ydWxlOlxuICBraW5kOiBqc3hfZXhwcmVzc2lvblxuICBoYXM6XG4gICAgcGF0dGVybjogJEEgJiYgJEJcbiAgbm90OlxuICAgIGluc2lkZTpcbiAgICAgIGtpbmQ6IGpzeF9hdHRyaWJ1dGVcbmZpeDogXCJ7JEEgPyAkQiA6IG51bGx9XCIiLCJzb3VyY2UiOiI8ZGl2PntcbiAgbnVtICYmIDxkaXYvPlxufTwvZGl2PiJ9) ### Description In [React](https://react.dev/learn/conditional-rendering), you can conditionally render JSX using JavaScript syntax like `if` statements, `&&`, and `? :` operators. However, you should almost never put numbers on the left side of `&&`. This is because React will render the number `0`, instead of the JSX element on the right side. A concrete example will be conditionally rendering a list when the list is not empty. This rule will find and fix any short-circuit rendering in JSX and rewrite it to a ternary operator. ### YAML ```yaml id: do-what-brooooooklyn-said language: Tsx rule: kind: jsx_expression has: pattern: $A && $B not: inside: kind: jsx_attribute fix: "{$A ? $B : null}" ``` ### Example ```tsx {1}
{ list.length && list.map(i =>

) }

``` ### Diff ```tsx
{ list.length && list.map(i =>

) }

// [!code --]
{ list.length ? list.map(i =>

) : null }

// [!code ++] ``` ### Contributed by [Herrington Darkholme](https://twitter.com/hd_nvim), inspired by [@Brooooook\_lyn](https://twitter.com/Brooooook_lyn/status/1666637274757595141) --- # Source: https://ast-grep.github.io/catalog/tsx/avoid-nested-links.md --- url: /catalog/tsx/avoid-nested-links.md --- ## Avoid nested links * [Playground Link](https://ast-grep.github.io/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InRzeCIsInF1ZXJ5IjoiaWYgKCRBKSB7ICQkJEIgfSIsInJld3JpdGUiOiJpZiAoISgkQSkpIHtcbiAgICByZXR1cm47XG59XG4kJCRCIiwic3RyaWN0bmVzcyI6InNtYXJ0Iiwic2VsZWN0b3IiOiIiLCJjb25maWciOiJpZDogbm8tbmVzdGVkLWxpbmtzXG5sYW5ndWFnZTogdHN4XG5zZXZlcml0eTogZXJyb3JcbnJ1bGU6XG4gIHBhdHRlcm46IDxhICQkJD4kJCRBPC9hPlxuICBoYXM6XG4gICAgcGF0dGVybjogPGEgJCQkPiQkJDwvYT5cbiAgICBzdG9wQnk6IGVuZCIsInNvdXJjZSI6ImZ1bmN0aW9uIENvbXBvbmVudCgpIHtcbiAgcmV0dXJuIDxhIGhyZWY9Jy9kZXN0aW5hdGlvbic+XG4gICAgPGEgaHJlZj0nL2Fub3RoZXJkZXN0aW5hdGlvbic+TmVzdGVkIGxpbmshPC9hPlxuICA8L2E+O1xufVxuZnVuY3Rpb24gT2theUNvbXBvbmVudCgpIHtcbiAgcmV0dXJuIDxhIGhyZWY9Jy9kZXN0aW5hdGlvbic+XG4gICAgSSBhbSBqdXN0IGEgbGluay5cbiAgPC9hPjtcbn0ifQ==) ### Description React will produce a warning message if you nest a link element inside of another link element. This rule will catch this mistake! ### YAML ```yaml id: no-nested-links language: tsx severity: error rule: pattern: $$$A has: pattern: $$$ stopBy: end ``` ### Example ```tsx {1-5} function Component() { return Nested link! ; } function OkayComponent() { return I am just a link. ; } ``` ### Contributed by [Tom MacWright](https://macwright.com/) --- # Source: https://ast-grep.github.io/catalog/rust/boshen-footgun.md --- url: /catalog/rust/boshen-footgun.md --- ## Beware of char offset when iterate over a string * [Playground Link](https://ast-grep.github.io/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoicnVzdCIsInF1ZXJ5IjoiJEEuY2hhcnMoKS5lbnVtZXJhdGUoKSIsInJld3JpdGUiOiIkQS5jaGFyX2luZGljZXMoKSIsImNvbmZpZyI6IiIsInNvdXJjZSI6ImZvciAoaSwgY2hhcikgaW4gc291cmNlLmNoYXJzKCkuZW51bWVyYXRlKCkge1xuICAgIHByaW50bG4hKFwiQm9zaGVuIGlzIGFuZ3J5IDopXCIpO1xufSJ9) ### Description It's a common pitfall in Rust that counting *character offset* is not the same as counting *byte offset* when iterating through a string. Rust string is represented by utf-8 byte array, which is a variable-length encoding scheme. `chars().enumerate()` will yield the character offset, while [`char_indices()`](https://doc.rust-lang.org/std/primitive.str.html#method.char_indices) will yield the byte offset. ```rs let yes = "y̆es"; let mut char_indices = yes.char_indices(); assert_eq!(Some((0, 'y')), char_indices.next()); // not (0, 'y̆') assert_eq!(Some((1, '\u{0306}')), char_indices.next()); // note the 3 here - the last character took up two bytes assert_eq!(Some((3, 'e')), char_indices.next()); assert_eq!(Some((4, 's')), char_indices.next()); ``` Depending on your use case, you may want to use `char_indices()` instead of `chars().enumerate()`. ### Pattern ```shell ast-grep -p '$A.chars().enumerate()' \ -r '$A.char_indices()' \ -l rs ``` ### Example ```rs {1} for (i, char) in source.chars().enumerate() { println!("Boshen is angry :)"); } ``` ### Diff ```rs for (i, char) in source.chars().enumerate() { // [!code --] for (i, char) in source.char_indices() { // [!code ++] println!("Boshen is angry :)"); } ``` ### Contributed by Inspired by [Boshen's Tweet](https://x.com/boshen_c/status/1719033308682870891) ![Boshen's footgun](https://pbs.twimg.com/media/F9s7mJHaYAEndnY?format=jpg\&name=medium) --- # Source: https://ast-grep.github.io/catalog/c.md --- url: /catalog/c.md --- # C This page curates a list of example ast-grep rules to check and to rewrite C code. :::tip C files can be parsed as Cpp You can parse C code as Cpp to avoid rewriting similar rules. The [`languageGlobs`](/reference/sgconfig.html#languageglobs) option can force ast-grep to parse `.c` files as Cpp. ::: ## Match Function Call in C * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImMiLCJxdWVyeSI6InRlc3QoJCQkKSIsInJld3JpdGUiOiIiLCJjb25maWciOiJydWxlOlxuICBwYXR0ZXJuOiBcbiAgICBjb250ZXh0OiAkTSgkJCQpO1xuICAgIHNlbGVjdG9yOiBjYWxsX2V4cHJlc3Npb24iLCJzb3VyY2UiOiIjZGVmaW5lIHRlc3QoeCkgKDIqeClcbmludCBhID0gdGVzdCgyKTtcbmludCBtYWluKCl7XG4gICAgaW50IGIgPSB0ZXN0KDIpO1xufSJ9) ### Description One of the common questions of ast-grep is to match function calls in C. A plain pattern like `test($A)` will not work. This is because [tree-sitter-c](https://github.com/tree-sitter/tree-sitter-c) parse the code snippet into `macro_type_specifier`, see the [pattern output](https://ast-grep.github.io/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoiYyIsInF1ZXJ5IjoidGVzdCgkJCQpIiwicmV3cml0ZSI6IiIsImNvbmZpZyI6InJ1bGU6XG4gIHBhdHRlcm46IFxuICAgIGNvbnRleHQ6ICRNKCQkJCk7XG4gICAgc2VsZWN0b3I6IGNhbGxfZXhwcmVzc2lvbiIsInNvdXJjZSI6IiNkZWZpbmUgdGVzdCh4KSAoMip4KVxuaW50IGEgPSB0ZXN0KDIpO1xuaW50IG1haW4oKXtcbiAgICBpbnQgYiA9IHRlc3QoMik7XG59In0=). To avoid this ambiguity, ast-grep lets us write a [contextual pattern](/guide/rule-config/atomic-rule.html#pattern), which is a pattern inside a larger code snippet. We can use `context` to write a pattern like this: `test($A);`. Then, we can use the selector `call_expression` to match only function calls. ### YAML ```yaml id: match-function-call language: c rule: pattern: context: $M($$$); selector: call_expression ``` ### Example ```c{2,4} #define test(x) (2*x) int a = test(2); int main(){ int b = test(2); } ``` ### Caveat Note, tree-sitter-c parses code differently when it receives code fragment. For example, * `test(a)` is parsed as `macro_type_specifier` * `test(a);` is parsed as `expression_statement -> call_expression` * `int b = test(a)` is parsed as `declaration -> init_declarator -> call_expression` The behavior is controlled by how the tree-sitter parser is written. And tree-sitter-c behaves differently from [tree-sitter-cpp](https://github.com/tree-sitter/tree-sitter-cpp). Please file issues on tree-sitter-c repo if you want to change the behavior. ast-grep will respect changes and decision from upstream authors. ## Rewrite Method to Function Call * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImMiLCJxdWVyeSI6IiRDT1VOVCA9ICRcbiIsInJld3JpdGUiOiIiLCJjb25maWciOiJpZDogbWV0aG9kX3JlY2VpdmVyXG5ydWxlOlxuICBwYXR0ZXJuOiAkUi4kTUVUSE9EKCQkJEFSR1MpXG50cmFuc2Zvcm06XG4gIE1BWUJFX0NPTU1BOlxuICAgIHJlcGxhY2U6XG4gICAgICBzb3VyY2U6ICQkJEFSR1NcbiAgICAgIHJlcGxhY2U6ICdeLisnXG4gICAgICBieTogJywgJ1xuZml4OlxuICAkTUVUSE9EKCYkUiRNQVlCRV9DT01NQSQkJEFSR1MpXG4iLCJzb3VyY2UiOiJ2b2lkIHRlc3RfZnVuYygpIHtcbiAgICBzb21lX3N0cnVjdC0+ZmllbGQubWV0aG9kKCk7XG4gICAgc29tZV9zdHJ1Y3QtPmZpZWxkLm90aGVyX21ldGhvZCgxLCAyLCAzKTtcbn0ifQ==) ### Description In C, there is no built-in support for object-oriented programming, but some programmers use structs and function pointers to simulate classes and methods. However, this style can have some drawbacks, such as: * extra memory allocation and deallocation for the struct and the function pointer. * indirection overhead when calling the function pointer. A possible alternative is to use a plain function call with the struct pointer as the first argument. ### YAML ```yaml id: method_receiver language: c rule: pattern: $R.$METHOD($$$ARGS) transform: MAYBE_COMMA: replace: source: $$$ARGS replace: '^.+' by: ', ' fix: $METHOD(&$R$MAYBE_COMMA$$$ARGS) ``` ### Example ```c {2-3} void test_func() { some_struct->field.method(); some_struct->field.other_method(1, 2, 3); } ``` ### Diff ```c void test_func() { some_struct->field.method(); // [!code --] method(&some_struct->field); // [!code ++] some_struct->field.other_method(1, 2, 3); // [!code --] other_method(&some_struct->field, 1, 2, 3); // [!code ++] } ``` ### Contributed by [Surma](https://twitter.com/DasSurma), adapted from the [original tweet](https://twitter.com/DasSurma/status/1706086320051794217) ## Rewrite Check to Yoda Condition * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImMiLCJxdWVyeSI6IiRDOiAkVCA9IHJlbGF0aW9uc2hpcCgkJCRBLCB1c2VsaXN0PVRydWUsICQkJEIpIiwicmV3cml0ZSI6IiRDOiBMaXN0WyRUXSA9IHJlbGF0aW9uc2hpcCgkJCRBLCB1c2VsaXN0PVRydWUsICQkJEIpIiwiY29uZmlnIjoiaWQ6IG1heS10aGUtZm9yY2UtYmUtd2l0aC15b3Vcbmxhbmd1YWdlOiBjXG5ydWxlOlxuICBwYXR0ZXJuOiAkQSA9PSAkQiBcbiAgaW5zaWRlOlxuICAgIGtpbmQ6IHBhcmVudGhlc2l6ZWRfZXhwcmVzc2lvblxuICAgIGluc2lkZToge2tpbmQ6IGlmX3N0YXRlbWVudH1cbmNvbnN0cmFpbnRzOlxuICBCOiB7IGtpbmQ6IG51bWJlcl9saXRlcmFsIH1cbmZpeDogJEIgPT0gJEEiLCJzb3VyY2UiOiJpZiAobXlOdW1iZXIgPT0gNDIpIHsgLyogLi4uICovfVxuaWYgKG5vdE1hdGNoID09IGFub3RoZXIpIHt9XG5pZiAobm90TWF0Y2gpIHt9In0=) ### Description In programming jargon, a [Yoda condition](https://en.wikipedia.org/wiki/Yoda_conditions) is a style that places the constant portion of the expression on the left side of the conditional statement. It is used to prevent assignment errors that may occur in languages like C. ### YAML ```yaml id: may-the-force-be-with-you language: c rule: pattern: $A == $B # Find equality comparison inside: # inside an if_statement kind: parenthesized_expression inside: {kind: if_statement} constraints: # with the constraint that B: { kind: number_literal } # right side is a number fix: $B == $A ``` The rule targets an equality comparison, denoted by the [pattern](/guide/pattern-syntax.html) `$A == $B`. This comparison must occur [inside](/reference/rule.html#inside) an `if_statement`. Additionally, there’s a [constraint](/reference/yaml.html#constraints) that the right side of the comparison, `$B`, must be a number\_literal like `42`. ### Example ```c {1} if (myNumber == 42) { /* ... */} if (notMatch == another) { /* ... */} if (notMatch) { /* ... */} ``` ### Diff ```c if (myNumber == 42) { /* ... */} // [!code --] if (42 == myNumber) { /* ... */} // [!code ++] if (notMatch == another) { /* ... */} if (notMatch) { /* ... */} ``` ### Contributed by Inspired by this [thread](https://x.com/cocoa1han/status/1763020689303581141) --- # Source: https://ast-grep.github.io/reference/cli.md --- url: /reference/cli.md --- # Command Line Reference You can always see up-to-date command line options using `ast-grep --help`. ast-grep has several subcommands as listed below. ## `ast-grep run` Run one time search or rewrite in command line. This is the default command when you run the CLI, so `ast-grep -p 'foo()'` is equivalent to `ast-grep run -p 'foo()'`. [View detailed reference.](/reference/cli/run.html) ### Usage ```shell ast-grep run [OPTIONS] --pattern [PATHS]... ``` ### Arguments `[PATHS]...` The paths to search. You can provide multiple paths separated by spaces \[default: .] ### Options | Short | Long | Description | |-------|------|-------------| | -p| --pattern `` | AST pattern to match. | | | --selector `` | AST kind to extract sub-part of pattern to match. | | -r| --rewrite `` | String to replace the matched AST node. | | -l| --lang `` | The language of the pattern query. ast-grep will infer the language based on file extension if this option is omitted. | | | --debug-query`[=]` | Print query pattern's tree-sitter AST. Requires lang be set explicitly. | | | --strictness `` | The strictness of the pattern \[possible values: cst, smart, ast, relaxed, signature] | | | --follow | Follow symbolic links | | | --no-ignore `` | Do not respect hidden file system or ignore files (.gitignore, .ignore, etc.) \[possible values: hidden, dot, exclude, global, parent, vcs] | | | --stdin | Enable search code from StdIn. See [link](/guide/tooling-overview.html#enable-stdin-mode) | | | --globs `` | Include or exclude file paths | -j| --threads `` | Set the approximate number of threads to use \[default: heuristic] | -i| --interactive | Start interactive edit session. Code rewrite only happens inside a session. | | -U| --update-all | Apply all rewrite without confirmation if true. | | | --json`[=

Hello World!

``` Running this ast-grep command will extract the matching CSS style code out of the HTML file! ```sh ast-grep run -p 'color: $COLOR' ``` ast-grep outputs this beautiful CLI report. ```shell test.html 2│ h1 { color: red; } ``` ast-grep works well even if just providing the pattern without specifying the pattern language! ### **Using `ast-grep scan`**: find JavaScript in HTML with rule files You can also use ast-grep's [rule file](https://ast-grep.github.io/guide/rule-config.html) to search injected languages. For example, we can warn the use of `alert` in JavaScript, even if it is inside the HTML file. ```yml id: no-alert language: JavaScript severity: warning rule: pattern: alert($MSG) message: Prefer use appropriate custom UI instead of obtrusive alert call. ``` The rule above will detect usage of `alert` in JavaScript. Running the rule via `ast-grep scan`. ```sh ast-grep scan --rule no-alert.yml ``` The command leverages built-in behaviors in ast-grep to handle language injection seamlessly. It will produce the following warning message for the HTML file above. ```sh warning[no-alert]: Prefer use appropriate custom UI instead of obtrusive alert call. ┌─ test.html:8:3 │ 8 │ alert('hello world!') │ ^^^^^^^^^^^^^^^^^^^^^ ``` ## How language injections work? ast-grep employs a multi-step process to handle language injections effectively. Here's a detailed breakdown of the workflow: 1. **File Discovery**: The CLI first discovers files on the disk via the venerable [ignore](https://crates.io/crates/ignore) crate, the same library under [ripgrep](https://github.com/BurntSushi/ripgrep)'s hood. 2. **Language Inference**: ast-grep infers the language of each discovered file based on file extensions. 3. **Injection Extraction**: For documents that contain code written in multiple languages (e.g., HTML with embedded JS), ast-grep extracts the injected language sub-regions. *At the moment, ast-grep handles HTML/JS/CSS natively*. 4. **Code Matching**: ast-grep matches the specified patterns or rules against these regions. Pattern code will be interpreted according to the injected language (e.g. JS/CSS), instead of the parent document language (e.g. HTML). ## Customize Language Injection: styled-components in JavaScript You can customize language injection via the `sgconfig.yml` [configuration file](https://ast-grep.github.io/reference/sgconfig.html). This allows you to specify how ast-grep handles multi-language documents based on your specific needs, without modifying ast-grep's built-in behaviors. Let's see an example of searching CSS code in JavaScript. [styled-components](https://styled-components.com/) is a library for styling React applications using [CSS-in-JS](https://bootcamp.uxdesign.cc/css-in-js-libraries-for-styling-react-components-a-comprehensive-comparison-56600605a5a1). It allows you to write CSS directly within your JavaScript via [tagged template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals), creating styled elements as React components. The example will configure ast-grep to detect styled-components' CSS. ### Injection Configuration You can add the `languageInjections` section in the project configuration file `sgconfig.yml`. ```yaml languageInjections: - hostLanguage: js rule: pattern: styled.$TAG`$CONTENT` injected: css ``` Let's break the configuration down. 1. `hostLanguage`: Specifies the main language of the document. In this example, it is set to `js` (JavaScript). 2. `rule`: Defines the ast-grep rule to identify the injected language region within the host language. * `pattern`: The pattern matches styled components syntax where `styled` is followed by a tag (e.g., `button`, `div`) and a template literal containing CSS. * the rule should have a meta variable `$CONTENT` to specify the subregion of injected language. In this case, it is the content inside the template string. 3. `injected`: Specifies the injected language within the identified regions. In this case, it is `css`. ### Example Match Consider a JSX file using styled components: ```js import styled from 'styled-components'; const Button = styled.button` background: red; color: white; padding: 10px 20px; border-radius: 3px; ` export default function App() { return } ``` With the above `languageInjections` configuration, ast-grep will: 1. Identify the `styled.button` block as a CSS region. 2. Extract the CSS code inside the template literal. 3. Apply any CSS-specific pattern searches within this extracted region. You can search the CSS inside JavaScript in the project configuration folder using this command: ```sh ast-grep -p 'background: $COLOR' -C 2 ``` It will produce the match result: ```shell styled.js 2│ 3│const Button = styled.button` 4│ background: red; 5│ color: white; 6│ padding: 10px 20px; ``` ## Using Custom Language with Injection Finally, let's look at an example of searching for GraphQL within JavaScript files. This demonstrates ast-grep's flexibility in handling custom language injections. ### Define graphql custom language in `sgconfig.yml`. First, we need to register graphql as a custom language in ast-grep. See [custom language reference](https://ast-grep.github.io/advanced/custom-language.html) for more details. ```yaml customLanguages: graphql: libraryPath: graphql.so # the graphql tree-sitter parser dynamic library extensions: [graphql] # graphql file extension expandoChar: $ # see reference above for explanation ``` ### Define graphql injection in `sgconfig.yml`. Next, we need to customize what region should be parsed as graphql string in JavaScript. This is similar to styled-components example above. ```yaml languageInjections: - hostLanguage: js rule: pattern: graphql`$CONTENT` injected: graphql ``` ### Search GraphQL in JavaScript Suppose we have this JavaScript file from [Relay](https://relay.dev/), a GraphQL client framework. ```js import React from "react" import { graphql } from "react-relay" const artistsQuery = graphql` query ArtistQuery($artistID: String!) { artist(id: $artistID) { name ...ArtistDescription_artist } } ` ``` We can search the GraphQL fragment via this `--inline-rules` scan. ```sh ast-grep scan --inline-rules="{id: test, language: graphql, rule: {kind: fragment_spread}}" ``` Output ```sh help[test]: ┌─ relay.js:8:7 │ 8 │ ...ArtistDescription_artist │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` ## More Possibility to be Unlocked... By following these steps, you can effectively use ast-grep to search and analyze code across multiple languages within the same document, enhancing your ability to manage and understand complex codebases. This feature extends to various frameworks like [Vue](https://vuejs.org/) and [Svelte](https://svelte.dev/), enables searching for [SQL in React server actions](https://x.com/peer_rich/status/1717609270475194466), and supports new patterns like [Vue-Vine](https://x.com/hd_nvim/status/1815300932793663658). Hope you enjoy the feature! Happy ast-grepping! --- # Source: https://ast-grep.github.io/reference/languages.md --- url: /reference/languages.md --- # List of Languages with Built-in Support The table below lists all languages that are supported by ast-grep. **Alias** is the name you can use as an argument in `ast-grep run --lang [alias]` or as a value in YAML rule with `language: [alias]`. **Extension** specifies the file extensions that ast-grep will look for when scanning the file system. By default, ast-grep uses the file extensions to determine the language. *** | Language Name | Alias | File Extension | |---|---|---| |Bash | `bash` | `bash`, `bats`, `cgi`, `command`, `env`, `fcgi`, `ksh`, `sh`, `sh.in`, `tmux`, `tool`, `zsh` | |C | `c` | `c`,`h`| |Cpp | `cc`, `c++`, `cpp`, `cxx` | `cc`, `hpp`, `cpp`, `c++`, `hh`, `cxx`, `cu`, `ino`| |CSharp | `cs`, `csharp` | `cs`| |Css | `css` | `css`| |Elixir | `ex`, `elixir` | `ex`, `exs`| |Go | `go`, `golang` | `go`| |Haskell | `hs`, `haskell` | `hs`| |Hcl | `hcl` | `hcl`| |Html | `html` | `html`, `htm`, `xhtml`| |Java | `java` | `java`| |JavaScript | `javascript`, `js`, `jsx` | `cjs`, `js`, `mjs`, `jsx`| |Json | `json` | `json` | |Kotlin | `kotlin`, `kt` | `kt`, `ktm`, `kts`| |Lua | `lua` | `lua`| |Nix | `nix` | `nix`| |Php | `php` | `php` | |Python | `py`, `python` | `py`, `py3`, `pyi`, `bzl`| |Ruby | `rb`, `ruby` | `rb`, `rbw`, `gemspec`| |Rust | `rs`, `rust` | `rs`| |Scala | `scala` | `scala`, `sc`, `sbt`| |Solidity | `solidity`, `sol` | `sol`| |Swift | `swift` | `swift`| |TypeScript | `ts`, `typescript` | `ts`, `cts`, `mts`| |Tsx | `tsx` | `tsx`| |Yaml | `yml` | `yml`, `yaml`| *** :::tip Pro Tips You can use [`languageGlobs`](/reference/sgconfig.html#languageglobs) to customize languages' extension mapping. ::: --- # Source: https://ast-grep.github.io/guide/project/lint-rule.md --- url: /guide/project/lint-rule.md --- # Lint Rule A lint rule is a configuration file that specifies how to find, report and fix issues in the codebase. Lint rule in ast-grep is natural extension of the core [rule object](/guide/rule-config.html). There are several additional fields to enable even more powerful code analysis and transformation. ## Rule Example A typical ast-grep rule file looks like this. It reports error when using `await` inside a loop since the loop can proceed *only after* the awaited Promise resolves first. See the [eslint rule](https://eslint.org/docs/latest/rules/no-await-in-loop). ```yaml id: no-await-in-loop language: TypeScript rule: pattern: await $_ inside: any: - kind: for_in_statement - kind: while_statement # Other linting related fields message: Don't use await inside of loops severity: warning note: | Performing an await as part of each operation is an indication that the program is not taking full advantage of the parallelization benefits of async/await. ``` The *TypeScript* rule, `no-await-in-loop`, will report a warning when it finds `await` **inside** a `for-in` or `while` loop. The linter rule file is a YAML file. It has fields identical to the [rule essentials](/guide/rule-config.html) plus some linter specific fields. `id`, `language` and `rule` are the same as in the rule essentials. `message`, `severity` and `note` are self-descriptive linter fields. They correspond to the similar concept `Diagnostic` in the [language server protocol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnostic) specification. ## Basic Workflow A full configured ast-grep rule may look like daunting and complex. But the basic workflow of ast-grep rule is simple. 1. *Find*: search the nodes in the AST that match the rewriter rules (hence the name ast-grep). 2. *Rewrite*: generate a new string based on the matched meta-variables. 3. *Patch*: optionally, replace the node text with the generated fix. The workflow above is called [*Find and Patch*](/advanced/find-n-patch.html), which is embodied in the lint rule fields: * **Find** * Find a target node based on the [`rule`](/reference/rule.html) * Filter the matched nodes based on [`constraints`](/guide/project/lint-rule.html#constraints) * **Patch** * Rewrite the matched meta-variable based on [`transform`](/guide/project/lint-rule.html#transform) * Replace the matched node with [`fix`](/guide/project/lint-rule.html#fix), which can use the transformed meta-variables. ## Core Rule Fields ### `rule` `rule` is exactly the same as the [rule object](/guide/rule-config.html) in the core ast-grep configuration. ### `constraints` We can constrain what kind of meta variables we should match. ```yaml rule: pattern: console.log($GREET) constraints: GREET: kind: identifier ``` The above rule will constraint the [`kind`](/guide/rule-config/atomic-rule.html#kind) of matched nodes to be only `identifier`. So `console.log(name)` will match the above rule, but `console.log('Rem')` will not because the matched variable `GREET` is string. See [playground](https://ast-grep.github.io/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6ImNvbnNvbGUubG9nKCRNQVRDSCkiLCJjb25maWciOiIjIENvbmZpZ3VyZSBSdWxlIGluIFlBTUxcbnJ1bGU6XG4gIHBhdHRlcm46IGNvbnNvbGUubG9nKCRHUkVFVClcbmNvbnN0cmFpbnRzOlxuICBHUkVFVDpcbiAgICBraW5kOiBpZGVudGlmaWVyIiwic291cmNlIjoiY29uc29sZS5sb2coJ0hlbGxvIFdvcmxkJylcbmNvbnNvbGUubG9nKGdyZWV0aW5nKVxuIn0=) in action. :::warning Note, constraints only applies to the single meta variable like `$ARG`, not multiple meta variable like `$$$ARGS`. ::: :::details `constraints` is applied after `rule` and does not work inside `not` `constraints` is a filter to further refine the matched nodes and is applied after the `rule` is matched. So the `constraints` field cannot be used inside `not`, for example ```yml rule: pattern: console.log($GREET) not: { pattern: console.log($STR) } constraints: STR: { kind: string} ``` The intent of the above rule is to match all `console.log` call except the one with string argument. But it will match nothing because `console.log($STR)` is exactly the same as `console.log($GREET)` before the `constraints` is applied. The `not` and `pattern` will conflict with each other. ::: ### `transform` `transform` is an advanced feature that allows you to transform the matched AST nodes into another string. It is useful when you combine `transform` and `fix` to rewrite the codebase. For example, you may want to capitalize the matched variable name, or extract a substring from the matched node. See the [transform](/guide/rewrite/transform.html) section in rewriting guide for more details. ### `fix` ast-grep can perform automatic rewriting to the codebase. The `fix` field in the rule configuration specifies how to rewrite the code. We can also use meta variables specified in the `rule` in `fix`. ast-grep will replace the meta-variables with the content of actual matched AST nodes. Example: ```yaml rule: pattern: console.log($GREET) fix: console.log('Hello ' + $GREET) ``` will rewrite `console.log('World')` to `console.log('Hello ' + 'World')`. :::warning `fix` is textual The `fix` field is a template string and is not parsed by tree-sitter parsers. Meta variables in `fix` will be replaced as long as they follow the meta variable syntax. ::: An example will be like this. The meta variable `$GREET` will be replaced both in the fix `alert($GREET)` and in the fix `nonMeta$GREET`, even though the latter cannot be parsed into valid code. ## Other Linting Fields * `message` is a concise description when the issue is reported. * `severity` is the issue's severity. See more in [severity](/guide/project/severity.html). * `note` is a detailed message to elaborate the message and preferably to provide actionable fix to end users. * `labels` is a dictionary of labels to customize error reporting's code highlighting. ### `files`/`ignores` Rules can be applied to only certain files in a codebase with `files`. `files` supports a list of glob patterns: ```yaml files: - "tests/**" - "integration_tests/test.py" ``` Similarly, you can use `ignores` to ignore applying a rule to certain files. `ignores` supports a list of glob patterns: ```yaml ignores: - "tests/config/**" ``` `ignores` and `files` can be used together. `ignores` will be tested before `files`. See [reference](/reference/yaml.html#ignores) for more details. :::warning Don't add `./` Be sure to remove `./` to the beginning of your rules. ast-grep will not recognize the paths if you add `./`. ::: Paths in both `files` and `ignores` are relative to the project root directory, that is, `sgconfig.yml`'s directory. ## Customize Code Highlighting ast-grep will report linting issues with highlighted code span called label. A label describes an underlined region of code associated with an issue. *By default, the matched target code and its surrounding code captured by [relational rules](/guide/rule-config/relational-rule.html)*. ast-grep further allows you to customize the highlighting style with the configuration `labels` in the rule to provide more context to the developer. **`labels` is a dictionary of which the keys are the meta-variable name without `$` and the values ares label config objects.** The label config object contains two fields: the required `style` and the optional `message`. * `style` specifies the category of the label. Available choices are `primary` and `secondary`. * `primary` describe the primary cause of an issue. * `secondary` provides additional context for a diagnostic. * `message` specifies the message to be displayed along with the label. Note, a `label` meta-variable must have a corresponding AST node in the matched code because highlighting requires a range in the code for label. That is, the **label meta-variables must be defined in `rule` or `constraints`**. Meta-variables in `transform` cannot be used in `labels` as they are not part of the matched AST node. *** Let's see an example. Suppose we have a [rule](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6IiIsInJld3JpdGUiOiIiLCJzdHJpY3RuZXNzIjoic21hcnQiLCJzZWxlY3RvciI6IiIsImNvbmZpZyI6InJ1bGU6XG4gIHBhdHRlcm46XG4gICAgY29udGV4dDogJ2NsYXNzIEggeyAkTUVUSE9EKCkgeyAkJCQgfSB9J1xuICAgIHNlbGVjdG9yOiBtZXRob2RfZGVmaW5pdGlvblxuICBpbnNpZGU6XG4gICAgcGF0dGVybjogY2xhc3MgJENMQVNTIHsgJCQkIH1cbiAgICBzdG9wQnk6IGVuZCIsInNvdXJjZSI6ImNsYXNzIE5vdENvbXBvbmVudCB7XG4gICAgbmdPbkluaXQoKSB7fVxufSJ9) that matches method declaration in a class. ```yaml rule: pattern: context: 'class H { $METHOD() { $$$ } }' selector: method_definition inside: pattern: class $CLASS { $$$ } stopBy: end ``` Without label customization, ast-grep will highlight the method declaration (target), and the whole class declaration, captured by relational rule. We can customize the highlighting with `labels`: ```yaml labels: METHOD: style: primary message: the method name CLASS: style: secondary message: The class name ``` Instead of highlighting the whole method declaration and class declaration, we are just highlighting the method name and class name. The `style` field specifies the highlighting style. The `message` field specifies the message to be displayed in the editor extension. See this post for a [demo](https://x.com/hd_nvim/status/1924120276939256154) and [the example](/catalog/typescript/missing-component-decorator.html) in catalog. :::tip VSCode Extension respects `labels` ast-grep's LSP diagnostic reporting also respects the labels configuration. Labels with messages are displayed in the editor extension as [diagnostic related information](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnosticRelatedInformation). Users can jump to the label by clicking the message in the editor. ::: ## Ignore Linting Error It is possible to ignore a single line of code in ast-grep's scanning. A developer can suppress ast-grep's error by adding `ast-grep-ignore` comment. For example, in JavaScript: ```javascript // ast-grep-ignore // ast-grep-ignore: , ``` The first comment will suppress the following line's diagnostic. The second comment will suppress one or more specific rules. There are more options to configure ast-grep's linting behavior, please see [severity](/guide/project/severity.html) for more deep dive. ## Test and Debug Rules After you have written your rule, you can test it with ast-grep's builtin `test` command. Let's see it in [next section](/guide/test-rule). :::tip Pro Tip You can write a standalone [rule file](/reference/rule.html) and the command `ast-grep scan -r rule.yml` to perform an [ad-hoc search](/guide/tooling-overview.html#run-one-single-query-or-one-single-rule). ::: --- # Source: https://ast-grep.github.io/advanced/match-algorithm.md --- url: /advanced/match-algorithm.md --- # Deep Dive into ast-grep's Match Algorithm By default, ast-grep uses a smart strategy to match pattern against the AST node. All nodes in the pattern must be matched, but it will skip unnamed nodes in target code. For background and the definition of ***named*** and ***unnamed*** nodes, please refer to the [core concepts](/advanced/core-concepts.html) doc. ## How ast-grep's Smart Matching Works Let's see an example in action. The following pattern `function $A() {}` will match both plain function and async function in JavaScript. See [playground](/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoiamF2YXNjcmlwdCIsInF1ZXJ5IjoiZnVuY3Rpb24gJEEoKSB7fSIsInJld3JpdGUiOiJEZWJ1Zy5hc3NlcnQiLCJjb25maWciOiJydWxlOlxuICBwYXR0ZXJuOiBcbiAgICBjb250ZXh0OiAneyAkTTogKCQkJEEpID0+ICRNQVRDSCB9J1xuICAgIHNlbGVjdG9yOiBwYWlyXG4iLCJzb3VyY2UiOiJmdW5jdGlvbiBhKCkge31cbmFzeW5jIGZ1bmN0aW9uIGEoKSB7fSJ9) ```js // function $A() {} function foo() {} // matched async function bar() {} // matched ``` This is because the keyword `async` is an unnamed node in the syntax tree, so the `async` in the code to search is skipped. As long as `function`, `$A` and `{}` are matched, the pattern is considered matched. However, if the `async` keyword appears in the pattern code, it will [not be skipped](/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoiamF2YXNjcmlwdCIsInF1ZXJ5IjoiYXN5bmMgZnVuY3Rpb24gJEEoKSB7fSIsInJld3JpdGUiOiJ1c2luZyBuYW1lc3BhY2UgZm9vOjokQTsiLCJjb25maWciOiJcbmlkOiB0ZXN0YmFzZV9pbml0aWFsaXplclxubGFuZ3VhZ2U6IENQUFxucnVsZTpcbiAgcGF0dGVybjpcbiAgICBzZWxlY3RvcjogY29tcG91bmRfc3RhdGVtZW50XG4gICAgY29udGV4dDogXCJ7ICQkJEIgfVwiXG5maXg6IHwtXG4gIHtcbiAgICBmKCk7XG4gICAgJCQkQlxuICB9Iiwic291cmNlIjoiLy8gYXN5bmMgZnVuY3Rpb24gJEEoKSB7fVxuZnVuY3Rpb24gZm9vKCkge30gICAgLy8gbm90IG1hdGNoZWRcbmFzeW5jIGZ1bmN0aW9uIGJhcigpIHt9IC8vIG1hdGNoZWRcbiJ9) and is required to match node in the code. ```js // async function $A() {} function foo() {} // not matched async function bar() {} // matched ``` The design principle here is that the less a pattern specifies, the more code it can match. Every nodes the pattern author spells out will be respected by ast-grep's matching algorithm by default. ## Smart is Sometimes Dumb The smart algorithm does not always behave as desired. There are cases where we need more flexibility in the matching algorithm. We may want to ignore all CST trivia nodes. Or even we want to ignore comment AST nodes. Suppose we want to write a pattern to match import statement in JavaScript. The pattern `import $A from 'lib'` will match only `import A from 'lib'`, but not `import A from "lib"`. This is because the import string has different quotation marks. We do want to ignore the trivial unnamed nodes here. To this end, ast-grep implements different pattern matching algorithms to provide more flexibility to the users, and every pattern can have their own matching algorithm to fine-tune the matching behavior. ## Matching Algorithm Strictness Different matching algorithm is controlled by **pattern strictness**. :::tip Strictness Strictness is defined in terms of what nodes can be *skipped* during matching. A *stricter* matching algorithm will *skip fewer nodes* and accordingly *produce fewer matches*. ::: Currently, ast-grep has these strictness levels. * `cst`: All nodes in the pattern and target code must be matched. No node is skipped. * `smart`: All nodes in the pattern must be matched, but it will skip unnamed nodes in target code. This is the default behavior. * `ast`: Only named AST nodes in both pattern and target code are matched. All unnamed nodes are skipped. * `relaxed`: Named AST nodes in both pattern and target code are matched. Comments and unnamed nodes are ignored. * `signature`: Only named AST nodes' kinds are matched. Comments, unnamed nodes and text are ignored. ## Strictness Examples Let's see how strictness `ast` will impact matching. In our previous import lib example, the pattern `import $A from 'lib'` will match both two statements. ```js import $A from 'lib' // pattern import A1 from 'lib' // match, quotation is ignored import A2 from "lib" // match, quotation is ignored import A3 from "not" // no match, string_fragment is checked ``` First, the pattern and code will be parsed as the tree below. Named The unnamed nodes are skipped during the matching. Nodes' namedness is annotated beside them. ``` import_statement // named import // unnamed import_clause // named identifier // named from // unnamed string // named " // unnamed string_fragment // named " // unnamed ``` Under the strictness of `ast`, the full syntax tree will be reduced to an Abstract Syntax Tree where only named nodes are kept. ``` import_statement import_clause identifier // $A string string_fragment // lib ``` As long as the tree structure matches and the meta-variable `$A` and string\_fragment `lib` are matched, the pattern and code are counted as a match. *** Another example will be matching the pattern `foo(bar)` across different strictness levels: ```ts // exact match in all levels foo(bar) // match in all levels except cst due to the trailing comma in code foo(bar,) // match in relaxed and signature because comment is skipped foo(/* comment */ bar) // match in signature because text content is ignored bar(baz) ``` ## Strictness Table Strictness considers both nodes' namedness and their locations, i.e, *is the node named* and *is the node in pattern or code* The table below summarize how nodes are skipped during matching. |Strictness|Named Node in Pattern|Named Node in Code to Search|Unnamed Node in Pattern| Unnamed Node in Code to Search| |---|----|---|---|---| |`cst`| Keep | Keep| Keep | Keep | |`smart`| Keep| Keep | Keep | Skip | |`ast`| Keep| Keep | Skip| Skip | |`relaxed`| Skip comment | Skip comment | Skip | Skip | |`signature`| Skip comment. Ignore text | Skip comment. Ignore text | Skip | Skip | ## Configure Strictness ast-grep has two ways to configure pattern strictness. 1. Using `--strictness` in `ast-grep run` You can use the `--strictness` flag in [`ast-grep run`](/reference/cli/run.html) ```bash ast-grep run -p '$FOO($BAR)' --strictness ast ``` 2. Using `strictness` in Pattern Object [Pattern object](/reference/rule.html#pattern) in YAML has an optional `strictness` field. ``` id: test-pattern-strictness language: JavaScript rule: pattern: context: $FOO($BAR) strictness: ast ``` --- # Source: https://ast-grep.github.io/catalog/go/match-function-call.md # Source: https://ast-grep.github.io/catalog/c/match-function-call.md --- url: /catalog/c/match-function-call.md --- ## Match Function Call in C * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImMiLCJxdWVyeSI6InRlc3QoJCQkKSIsInJld3JpdGUiOiIiLCJjb25maWciOiJydWxlOlxuICBwYXR0ZXJuOiBcbiAgICBjb250ZXh0OiAkTSgkJCQpO1xuICAgIHNlbGVjdG9yOiBjYWxsX2V4cHJlc3Npb24iLCJzb3VyY2UiOiIjZGVmaW5lIHRlc3QoeCkgKDIqeClcbmludCBhID0gdGVzdCgyKTtcbmludCBtYWluKCl7XG4gICAgaW50IGIgPSB0ZXN0KDIpO1xufSJ9) ### Description One of the common questions of ast-grep is to match function calls in C. A plain pattern like `test($A)` will not work. This is because [tree-sitter-c](https://github.com/tree-sitter/tree-sitter-c) parse the code snippet into `macro_type_specifier`, see the [pattern output](https://ast-grep.github.io/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoiYyIsInF1ZXJ5IjoidGVzdCgkJCQpIiwicmV3cml0ZSI6IiIsImNvbmZpZyI6InJ1bGU6XG4gIHBhdHRlcm46IFxuICAgIGNvbnRleHQ6ICRNKCQkJCk7XG4gICAgc2VsZWN0b3I6IGNhbGxfZXhwcmVzc2lvbiIsInNvdXJjZSI6IiNkZWZpbmUgdGVzdCh4KSAoMip4KVxuaW50IGEgPSB0ZXN0KDIpO1xuaW50IG1haW4oKXtcbiAgICBpbnQgYiA9IHRlc3QoMik7XG59In0=). To avoid this ambiguity, ast-grep lets us write a [contextual pattern](/guide/rule-config/atomic-rule.html#pattern), which is a pattern inside a larger code snippet. We can use `context` to write a pattern like this: `test($A);`. Then, we can use the selector `call_expression` to match only function calls. ### YAML ```yaml id: match-function-call language: c rule: pattern: context: $M($$$); selector: call_expression ``` ### Example ```c{2,4} #define test(x) (2*x) int a = test(2); int main(){ int b = test(2); } ``` ### Caveat Note, tree-sitter-c parses code differently when it receives code fragment. For example, * `test(a)` is parsed as `macro_type_specifier` * `test(a);` is parsed as `expression_statement -> call_expression` * `int b = test(a)` is parsed as `declaration -> init_declarator -> call_expression` The behavior is controlled by how the tree-sitter parser is written. And tree-sitter-c behaves differently from [tree-sitter-cpp](https://github.com/tree-sitter/tree-sitter-cpp). Please file issues on tree-sitter-c repo if you want to change the behavior. ast-grep will respect changes and decision from upstream authors. --- # Source: https://ast-grep.github.io/catalog/go/match-package-import.md --- url: /catalog/go/match-package-import.md --- ## Match package import in Golang * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImdvIiwicXVlcnkiOiIiLCJyZXdyaXRlIjoiIiwic3RyaWN0bmVzcyI6InNtYXJ0Iiwic2VsZWN0b3IiOiIiLCJjb25maWciOiJpZDogbWF0Y2gtcGFja2FnZS1pbXBvcnRcbmxhbmd1YWdlOiBnb1xucnVsZTpcbiAga2luZDogaW1wb3J0X3NwZWNcbiAgaGFzOlxuICAgIHJlZ2V4OiBnaXRodWIuY29tL2dvbGFuZy1qd3Qvand0Iiwic291cmNlIjoicGFja2FnZSBtYWluXG5cbmltcG9ydCAoXG5cdFwiZm10XCJcblx0XCJnaXRodWIuY29tL2dvbGFuZy1qd3Qvand0XCIgIC8vIFRoaXMgbWF0Y2hlcyB0aGUgQVNUIHJ1bGVcbilcblxuZnVuYyBtYWluKCkge1xuXHQvLyBDcmVhdGUgYSBuZXcgdG9rZW5cblx0dG9rZW4gOj0gand0Lk5ldyhqd3QuU2lnbmluZ01ldGhvZEhTMjU2KVxuXHRcblx0Ly8gQWRkIHNvbWUgY2xhaW1zXG5cdHRva2VuLkNsYWltcyA9IGp3dC5NYXBDbGFpbXN7XG5cdFx0XCJ1c2VyXCI6IFwiYWxpY2VcIixcblx0XHRcInJvbGVcIjogXCJhZG1pblwiLFxuXHR9XG5cdFxuXHQvLyBTaWduIHRoZSB0b2tlblxuXHR0b2tlblN0cmluZywgZXJyIDo9IHRva2VuLlNpZ25lZFN0cmluZyhbXWJ5dGUoXCJteS1zZWNyZXRcIikpXG5cdGlmIGVyciAhPSBuaWwge1xuXHRcdGZtdC5QcmludGYoXCJFcnJvciBzaWduaW5nIHRva2VuOiAldlxcblwiLCBlcnIpXG5cdFx0cmV0dXJuXG5cdH1cblx0XG5cdGZtdC5QcmludGYoXCJHZW5lcmF0ZWQgdG9rZW46ICVzXFxuXCIsIHRva2VuU3RyaW5nKVxufSJ9) ### Description A generic rule template for detecting imports of specific packages in Go source code. This rule can be customized to match any package by modifying the regex pattern, making it useful for security auditing, dependency management, and compliance checking. This rule identifies Go import statements based on the configured regex pattern, including: Direct imports: `import "package/name"`\ Versioned imports: `import "package/name/v4"`\ Subpackage imports: `import "package/name/subpkg"`\ Grouped imports within `import () blocks` ### YAML ```yaml id: match-package-import language: go rule: kind: import_spec has: regex: PACKAGE_PATTERN_HERE ``` ### Example JWT Library Detection ```go{5} package main import ( "fmt" "github.com/golang-jwt/jwt" // This matches the AST rule ) func main() { token := jwt.New(jwt.SigningMethodHS256) // Create a new token // Add some claims token.Claims = jwt.MapClaims{"user": "alice", "role": "admin"} tokenString, err := token.SignedString([]byte("my-secret")) // Sign the token if err != nil { fmt.Printf("Error signing token: %v\n", err) return } fmt.Printf("Generated token: %s\n", tokenString) } ``` ### Contributed by [Sudesh Gutta](https://github.com/sudeshgutta) --- # Source: https://ast-grep.github.io/catalog/ruby/migrate-action-filter.md --- url: /catalog/ruby/migrate-action-filter.md --- ## Migrate action\_filter in Ruby on Rails * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InJ1YnkiLCJxdWVyeSI6ImNvbnNvbGUubG9nKCRNQVRDSCkiLCJyZXdyaXRlIjoibG9nZ2VyLmxvZygkTUFUQ0gpIiwiY29uZmlnIjoiIyBhc3QtZ3JlcCBZQU1MIFJ1bGUgaXMgcG93ZXJmdWwgZm9yIGxpbnRpbmchXG4jIGh0dHBzOi8vYXN0LWdyZXAuZ2l0aHViLmlvL2d1aWRlL3J1bGUtY29uZmlnLmh0bWwjcnVsZVxucnVsZTpcbiAgYW55OlxuICAgIC0gcGF0dGVybjogYmVmb3JlX2ZpbHRlciAkJCRBQ1RJT05cbiAgICAtIHBhdHRlcm46IGFyb3VuZF9maWx0ZXIgJCQkQUNUSU9OXG4gICAgLSBwYXR0ZXJuOiBhZnRlcl9maWx0ZXIgJCQkQUNUSU9OXG4gIGhhczpcbiAgICBwYXR0ZXJuOiAkRklMVEVSXG4gICAgZmllbGQ6IG1ldGhvZFxuZml4OiBcbiAgJE5FV19BQ1RJT04gJCQkQUNUSU9OXG50cmFuc2Zvcm06XG4gIE5FV19BQ1RJT046XG4gICAgcmVwbGFjZTpcbiAgICAgIHNvdXJjZTogJEZJTFRFUlxuICAgICAgcmVwbGFjZTogX2ZpbHRlclxuICAgICAgYnk6IF9hY3Rpb24iLCJzb3VyY2UiOiJjbGFzcyBUb2Rvc0NvbnRyb2xsZXIgPCBBcHBsaWNhdGlvbkNvbnRyb2xsZXJcbiAgYmVmb3JlX2ZpbHRlciA6YXV0aGVudGljYXRlXG4gIGFyb3VuZF9maWx0ZXIgOndyYXBfaW5fdHJhbnNhY3Rpb24sIG9ubHk6IDpzaG93XG4gIGFmdGVyX2ZpbHRlciBkbyB8Y29udHJvbGxlcnxcbiAgICBmbGFzaFs6ZXJyb3JdID0gXCJZb3UgbXVzdCBiZSBsb2dnZWQgaW5cIlxuICBlbmRcblxuICBkZWYgaW5kZXhcbiAgICBAdG9kb3MgPSBUb2RvLmFsbFxuICBlbmRcbmVuZFxuIn0=) ### Description This rule is used to migrate `{before,after,around}_filter` to `{before,after,around}_action` in Ruby on Rails controllers. These are methods that run before, after or around an action is executed, and they can be used to check permissions, set variables, redirect requests, log events, etc. However, these methods are [deprecated](https://stackoverflow.com/questions/16519828/rails-4-before-filter-vs-before-action) in Rails 5.0 and will be removed in Rails 5.1. `{before,after,around}_action` are the new syntax for the same functionality. This rule will replace all occurrences of `{before,after,around}_filter` with `{before,after,around}_action` in the controller code. ### YAML ```yaml id: migration-action-filter language: ruby rule: any: - pattern: before_filter $$$ACTION - pattern: around_filter $$$ACTION - pattern: after_filter $$$ACTION has: pattern: $FILTER field: method fix: $NEW_ACTION $$$ACTION transform: NEW_ACTION: replace: source: $FILTER replace: _filter by: _action ``` ### Example ```rb {2-4} class TodosController < ApplicationController before_filter :authenticate around_filter :wrap_in_transaction, only: :show after_filter do |controller| flash[:error] = "You must be logged in" end def index @todos = Todo.all end end ``` ### Diff ```rb class TodosController < ApplicationController before_action :authenticate # [!code --] before_filter :authenticate # [!code ++] around_action :wrap_in_transaction, only: :show # [!code --] around_filter :wrap_in_transaction, only: :show # [!code ++] after_action do |controller| # [!code --] flash[:error] = "You must be logged in" # [!code --] end # [!code --] after_filter do |controller| # [!code ++] flash[:error] = "You must be logged in" # [!code ++] end # [!code ++] def index @todos = Todo.all end end ``` ### Contributed by [Herrington Darkholme](https://twitter.com/hd_nvim), inspired by [Future of Ruby - AST Tooling](https://dev.to/baweaver/future-of-ruby-ast-tooling-9i1). --- # Source: https://ast-grep.github.io/catalog/python/migrate-openai-sdk.md --- url: /catalog/python/migrate-openai-sdk.md --- ## Migrate OpenAI SDK * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiZGVmICRGVU5DKCQkJEFSR1MpOiAkJCRCT0RZIiwicmV3cml0ZSI6IiIsImNvbmZpZyI6InJ1bGU6XG4gIHBhdHRlcm46IGltcG9ydCBvcGVuYWlcbmZpeDogZnJvbSBvcGVuYWkgaW1wb3J0IENsaWVudFxuLS0tXG5ydWxlOlxuICBwYXR0ZXJuOiBvcGVuYWkuYXBpX2tleSA9ICRLRVlcbmZpeDogY2xpZW50ID0gQ2xpZW50KCRLRVkpXG4tLS1cbnJ1bGU6XG4gIHBhdHRlcm46IG9wZW5haS5Db21wbGV0aW9uLmNyZWF0ZSgkJCRBUkdTKVxuZml4OiB8LVxuICBjbGllbnQuY29tcGxldGlvbnMuY3JlYXRlKFxuICAgICQkJEFSR1NcbiAgKSIsInNvdXJjZSI6ImltcG9ydCBvc1xuaW1wb3J0IG9wZW5haVxuZnJvbSBmbGFzayBpbXBvcnQgRmxhc2ssIGpzb25pZnlcblxuYXBwID0gRmxhc2soX19uYW1lX18pXG5vcGVuYWkuYXBpX2tleSA9IG9zLmdldGVudihcIk9QRU5BSV9BUElfS0VZXCIpXG5cblxuQGFwcC5yb3V0ZShcIi9jaGF0XCIsIG1ldGhvZHM9KFwiUE9TVFwiKSlcbmRlZiBpbmRleCgpOlxuICAgIGFuaW1hbCA9IHJlcXVlc3QuZm9ybVtcImFuaW1hbFwiXVxuICAgIHJlc3BvbnNlID0gb3BlbmFpLkNvbXBsZXRpb24uY3JlYXRlKFxuICAgICAgICBtb2RlbD1cInRleHQtZGF2aW5jaS0wMDNcIixcbiAgICAgICAgcHJvbXB0PWdlbmVyYXRlX3Byb21wdChhbmltYWwpLFxuICAgICAgICB0ZW1wZXJhdHVyZT0wLjYsXG4gICAgKVxuICAgIHJldHVybiBqc29uaWZ5KHJlc3BvbnNlLmNob2ljZXMpIn0=) ### Description OpenAI has introduced some breaking changes in their API, such as using `Client` to initialize the service and renaming the `Completion` method to `completions` . This example shows how to use ast-grep to automatically update your code to the new API. API migration requires multiple related rules to work together. The example shows how to write [multiple rules](/reference/playground.html#test-multiple-rules) in a [single YAML](/guide/rewrite-code.html#using-fix-in-yaml-rule) file. The rules and patterns in the example are simple and self-explanatory, so we will not explain them further. ### YAML ```yaml id: import-openai language: python rule: pattern: import openai fix: from openai import Client --- id: rewrite-client language: python rule: pattern: openai.api_key = $KEY fix: client = Client($KEY) --- id: rewrite-chat-completion language: python rule: pattern: openai.Completion.create($$$ARGS) fix: |- client.completions.create( $$$ARGS ) ``` ### Example ```python {2,6,11-15} import os import openai from flask import Flask, jsonify app = Flask(__name__) openai.api_key = os.getenv("OPENAI_API_KEY") @app.route("/chat", methods=("POST")) def index(): animal = request.form["animal"] response = openai.Completion.create( model="text-davinci-003", prompt=generate_prompt(animal), temperature=0.6, ) return jsonify(response.choices) ``` ### Diff ```python import os import openai # [!code --] from openai import Client # [!code ++] from flask import Flask, jsonify app = Flask(__name__) openai.api_key = os.getenv("OPENAI_API_KEY") # [!code --] client = Client(os.getenv("OPENAI_API_KEY")) # [!code ++] @app.route("/chat", methods=("POST")) def index(): animal = request.form["animal"] response = openai.Completion.create( # [!code --] response = client.completions.create( # [!code ++] model="text-davinci-003", prompt=generate_prompt(animal), temperature=0.6, ) return jsonify(response.choices) ``` ### Contributed by [Herrington Darkholme](https://twitter.com/hd_nvim), inspired by [Morgante](https://twitter.com/morgantepell/status/1721668781246750952) from [grit.io](https://www.grit.io/) --- # Source: https://ast-grep.github.io/catalog/typescript/migrate-xstate-v5.md --- url: /catalog/typescript/migrate-xstate-v5.md --- ## Migrate XState to v5 from v4 * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6ImlmICgkQSkgeyAkJCRCIH0iLCJyZXdyaXRlIjoiaWYgKCEoJEEpKSB7XG4gICAgcmV0dXJuO1xufVxuJCQkQiIsImNvbmZpZyI6InV0aWxzOlxuICBGUk9NX1hTVEFURTogeyBraW5kOiBpbXBvcnRfc3RhdGVtZW50LCBoYXM6IHsga2luZDogc3RyaW5nLCByZWdleDogeHN0YXRlIH0gfVxuICBYU1RBVEVfRVhQT1JUOlxuICAgIGtpbmQ6IGlkZW50aWZpZXJcbiAgICBpbnNpZGU6IHsgaGFzOiB7IG1hdGNoZXM6IEZST01fWFNUQVRFIH0sIHN0b3BCeTogZW5kIH1cbnJ1bGU6IHsgcmVnZXg6IF5NYWNoaW5lfGludGVycHJldCQsIHBhdHRlcm46ICRJTVBPUlQsIG1hdGNoZXM6IFhTVEFURV9FWFBPUlQgfVxudHJhbnNmb3JtOlxuICBTVEVQMTogXG4gICAgcmVwbGFjZToge2J5OiBjcmVhdGUkMSwgcmVwbGFjZTogKE1hY2hpbmUpLCBzb3VyY2U6ICRJTVBPUlQgfVxuICBGSU5BTDpcbiAgICByZXBsYWNlOiB7IGJ5OiBjcmVhdGVBY3RvciwgcmVwbGFjZTogaW50ZXJwcmV0LCBzb3VyY2U6ICRTVEVQMSB9XG5maXg6ICRGSU5BTFxuLS0tIFxucnVsZTogeyBwYXR0ZXJuOiAkTUFDSElORS53aXRoQ29uZmlnIH1cbmZpeDogJE1BQ0hJTkUucHJvdmlkZVxuLS0tXG5ydWxlOlxuICBraW5kOiBwcm9wZXJ0eV9pZGVudGlmaWVyXG4gIHJlZ2V4OiBec2VydmljZXMkXG4gIGluc2lkZTogeyBwYXR0ZXJuOiAgJE0ud2l0aENvbmZpZygkJCRBUkdTKSwgc3RvcEJ5OiBlbmQgfVxuZml4OiBhY3RvcnMiLCJzb3VyY2UiOiJpbXBvcnQgeyBNYWNoaW5lLCBpbnRlcnByZXQgfSBmcm9tICd4c3RhdGUnO1xuXG5jb25zdCBtYWNoaW5lID0gTWFjaGluZSh7IC8qLi4uKi99KTtcblxuY29uc3Qgc3BlY2lmaWNNYWNoaW5lID0gbWFjaGluZS53aXRoQ29uZmlnKHtcbiAgYWN0aW9uczogeyAvKiAuLi4gKi8gfSxcbiAgZ3VhcmRzOiB7IC8qIC4uLiAqLyB9LFxuICBzZXJ2aWNlczogeyAvKiAuLi4gKi8gfSxcbn0pO1xuXG5jb25zdCBhY3RvciA9IGludGVycHJldChzcGVjaWZpY01hY2hpbmUsIHtcbi8qIGFjdG9yIG9wdGlvbnMgKi9cbn0pOyJ9) ### Description [XState](https://xstate.js.org/) is a state management/orchestration library based on state machines, statecharts, and the actor model. It allows you to model complex logic in event-driven ways, and orchestrate the behavior of many actors communicating with each other. XState's v5 version introduced some breaking changes and new features compared to v4. While the migration should be a straightforward process, it is a tedious process and requires knowledge of the differences between v4 and v5. ast-grep provides a way to automate the process and a way to encode valuable knowledge to executable rules. The following example picks up some migration items and demonstrates the power of ast-grep's rule system. ### YAML The rules below correspond to XState v5's [`createMachine`](https://stately.ai/docs/migration#use-createmachine-not-machine), [`createActor`](https://stately.ai/docs/migration#use-createactor-not-interpret), and [`machine.provide`](https://stately.ai/docs/migration#use-machineprovide-not-machinewithconfig). The example shows how ast-grep can use various features like [utility rule](/guide/rule-config/utility-rule.html), [transformation](/reference/yaml/transformation.html) and [multiple rule in single file](/reference/playground.html#test-multiple-rules) to automate the migration. Each rule has a clear and descriptive `id` field that explains its purpose. For more information, you can use [Codemod AI](https://app.codemod.com/studio?ai_thread_id=new) to provide more detailed explanation for each rule. ```yaml id: migrate-import-name utils: FROM_XS: {kind: import_statement, has: {kind: string, regex: xstate}} XS_EXPORT: kind: identifier inside: { has: { matches: FROM_XS }, stopBy: end } rule: { regex: ^Machine|interpret$, pattern: $IMPT, matches: XS_EXPORT } transform: STEP1: replace: {by: create$1, replace: (Machine), source: $IMPT } FINAL: replace: { by: createActor, replace: interpret, source: $STEP1 } fix: $FINAL --- id: migrate-to-provide rule: { pattern: $MACHINE.withConfig } fix: $MACHINE.provide --- id: migrate-to-actors rule: kind: property_identifier regex: ^services$ inside: { pattern: $M.withConfig($$$ARGS), stopBy: end } fix: actors ``` ### Example ```js {1,3,5,8,11} import { Machine, interpret } from 'xstate'; const machine = Machine({ /*...*/}); const specificMachine = machine.withConfig({ actions: { /* ... */ }, guards: { /* ... */ }, services: { /* ... */ }, }); const actor = interpret(specificMachine, { /* actor options */ }); ``` ### Diff ```js import { Machine, interpret } from 'xstate'; // [!code --] import { createMachine, createActor } from 'xstate'; // [!code ++] const machine = Machine({ /*...*/}); // [!code --] const machine = createMachine({ /*...*/}); // [!code ++] const specificMachine = machine.withConfig({ // [!code --] const specificMachine = machine.provide({ // [!code ++] actions: { /* ... */ }, guards: { /* ... */ }, services: { /* ... */ }, // [!code --] actors: { /* ... */ }, // [!code ++] }); const actor = interpret(specificMachine, { // [!code --] const actor = createActor(specificMachine, { // [!code ++] /* actor options */ }); ``` ### Contributed by Inspired by [XState's blog](https://stately.ai/blog/2023-12-01-xstate-v5). --- # Source: https://ast-grep.github.io/catalog/typescript/missing-component-decorator.md --- url: /catalog/typescript/missing-component-decorator.md --- ## Missing Component Decorator * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6ImltcG9ydCAkQSBmcm9tICdhbmltZWpzJyIsInJld3JpdGUiOiJpbXBvcnQgeyBhbmltZSBhcyAkQSB9IGZyb20gJ2FuaW1lJyIsInN0cmljdG5lc3MiOiJzbWFydCIsInNlbGVjdG9yIjoiIiwiY29uZmlnIjoiaWQ6IG1pc3NpbmctY29tcG9uZW50LWRlY29yYXRvclxubWVzc2FnZTogWW91J3JlIHVzaW5nIGFuIEFuZ3VsYXIgbGlmZWN5Y2xlIG1ldGhvZCwgYnV0IG1pc3NpbmcgYW4gQW5ndWxhciBAQ29tcG9uZW50KCkgZGVjb3JhdG9yLlxubGFuZ3VhZ2U6IFR5cGVTY3JpcHRcbnNldmVyaXR5OiB3YXJuaW5nXG5ydWxlOlxuICBwYXR0ZXJuOlxuICAgIGNvbnRleHQ6ICdjbGFzcyBIaSB7ICRNRVRIT0QoKSB7ICQkJF99IH0nXG4gICAgc2VsZWN0b3I6IG1ldGhvZF9kZWZpbml0aW9uXG4gIGluc2lkZTpcbiAgICBwYXR0ZXJuOiAnY2xhc3MgJEtMQVNTICQkJF8geyAkJCRfIH0nXG4gICAgc3RvcEJ5OiBlbmRcbiAgICBub3Q6XG4gICAgICBoYXM6XG4gICAgICAgIHBhdHRlcm46ICdAQ29tcG9uZW50KCQkJF8pJ1xuY29uc3RyYWludHM6XG4gIE1FVEhPRDpcbiAgICByZWdleDogbmdPbkluaXR8bmdPbkRlc3Ryb3lcbmxhYmVsczpcbiAgS0xBU1M6XG4gICAgc3R5bGU6IHByaW1hcnlcbiAgICBtZXNzYWdlOiBcIlRoaXMgY2xhc3MgaXMgbWlzc2luZyB0aGUgZGVjb3JhdG9yLlwiXG4gIE1FVEhPRDpcbiAgICBzdHlsZTogc2Vjb25kYXJ5XG4gICAgbWVzc2FnZTogXCJUaGlzIGlzIGFuIEFuZ3VsYXIgbGlmZWN5Y2xlIG1ldGhvZC5cIlxubWV0YWRhdGE6XG4gIGNvbnRyaWJ1dGVkQnk6IHNhbXdpZ2h0dCIsInNvdXJjZSI6ImNsYXNzIE5vdENvbXBvbmVudCB7XG4gICAgbmdPbkluaXQoKSB7fVxufVxuXG5AQ29tcG9uZW50KClcbmNsYXNzIEtsYXNzIHtcbiAgICBuZ09uSW5pdCgpIHt9XG59In0=) ### Description Angular lifecycle methods are a set of methods that allow you to hook into the lifecycle of an Angular component or directive. They must be used within a class that is decorated with the `@Component()` decorator. ### YAML This rule illustrates how to use custom labels to highlight specific parts of the code. ```yaml id: missing-component-decorator message: You're using an Angular lifecycle method, but missing an Angular @Component() decorator. language: TypeScript severity: warning rule: pattern: context: 'class Hi { $METHOD() { $$$_} }' selector: method_definition inside: pattern: 'class $KLASS $$$_ { $$$_ }' stopBy: end not: has: pattern: '@Component($$$_)' constraints: METHOD: regex: ngOnInit|ngOnDestroy labels: KLASS: style: primary message: "This class is missing the decorator." METHOD: style: secondary message: "This is an Angular lifecycle method." metadata: contributedBy: samwightt ``` ### Example ```ts {2} class NotComponent { ngOnInit() {} } @Component() class Klass { ngOnInit() {} } ``` ### Contributed by [Sam Wight](https://github.com/samwightt). --- # Source: https://ast-grep.github.io/reference/cli/new.md --- url: /reference/cli/new.md --- # `ast-grep new` Create new ast-grep project or items like rules/tests. Also see the step by step [guide](/guide/scan-project.html). ## Usage ```shell ast-grep new [COMMAND] [OPTIONS] [NAME] ``` ## Commands ### `project` Create an new project by scaffolding. By default, this command will create a root config file `sgconfig.yml`, a rule folder `rules`, a test case folder `rule-tests` and a utility rule folder `utils`. You can customize the folder names during the creation. ### `rule` Create a new rule. This command will create a new rule in one of the `rule_dirs`. You need to provide `name` and `language` either by interactive input or via command line arguments. ast-grep will ask you which `rule_dir` to use if multiple ones are configured in the `sgconfig.yml`. If `-y, --yes` flag is true, ast-grep will choose the first `rule_dir` to create the new rule. ### `test` Create a new test case. This command will create a new test in one of the `test_dirs`. You need to provide `name` either by interactive input or via command line arguments. ast-grep will ask you which `test_dir` to use if multiple ones are configured in the `sgconfig.yml`. If `-y, --yes` flag is true, ast-grep will choose the first `test_dir` to create the new test. ### `util` Create a new global utility rule. This command will create a new global utility rule in one of the `utils` folders. You need to provide `name` and `language` either by interactive input or via command line arguments. ast-grep will ask you which `util_dir` to use if multiple ones are configured in the `sgconfig.yml`. If `-y, --yes` flag is true, ast-grep will choose the first `util_dir` to create the new item. ### `help` Print this message or the help of the given subcommand(s) ## Arguments `[NAME]` The id of the item to create ## Options ### `-l, --lang ` The language of the item to create. This option is only available when creating rule and util. ### `-y, --yes` Accept all default options without interactive input during creation. You need to provide all required arguments via command line if this flag is true. Please see the command description for the what arguments are required. ### `-c, --config ` Path to ast-grep root config, default is sgconfig.yml ### `-h, --help` Print help (see a summary with '-h') --- # Source: https://ast-grep.github.io/catalog/typescript/no-await-in-promise-all.md --- url: /catalog/typescript/no-await-in-promise-all.md --- ## No `await` in `Promise.all` array * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6ImNvbnNvbGUubG9nKCRNQVRDSCkiLCJyZXdyaXRlIjoibG9nZ2VyLmxvZygkTUFUQ0gpIiwiY29uZmlnIjoiaWQ6IG5vLWF3YWl0LWluLXByb21pc2UtYWxsXG5zZXZlcml0eTogZXJyb3Jcbmxhbmd1YWdlOiBKYXZhU2NyaXB0XG5tZXNzYWdlOiBObyBhd2FpdCBpbiBQcm9taXNlLmFsbFxucnVsZTpcbiAgcGF0dGVybjogYXdhaXQgJEFcbiAgaW5zaWRlOlxuICAgIHBhdHRlcm46IFByb21pc2UuYWxsKCRfKVxuICAgIHN0b3BCeTpcbiAgICAgIG5vdDogeyBhbnk6IFt7a2luZDogYXJyYXl9LCB7a2luZDogYXJndW1lbnRzfV0gfVxuZml4OiAkQSIsInNvdXJjZSI6ImNvbnN0IFtmb28sIGJhcl0gPSBhd2FpdCBQcm9taXNlLmFsbChbXG4gIGF3YWl0IGdldEZvbygpLFxuICBnZXRCYXIoKSxcbiAgKGFzeW5jICgpID0+IHsgYXdhaXQgZ2V0QmF6KCl9KSgpLFxuXSkifQ==) ### Description Using `await` inside an inline `Promise.all` array is usually a mistake, as it defeats the purpose of running the promises in parallel. Instead, the promises should be created without `await` and passed to `Promise.all`, which can then be awaited. ### YAML ```yaml id: no-await-in-promise-all language: typescript rule: pattern: await $A inside: pattern: Promise.all($_) stopBy: not: { any: [{kind: array}, {kind: arguments}] } fix: $A ``` ### Example ```ts {2} const [foo, bar] = await Promise.all([ await getFoo(), getBar(), (async () => { await getBaz()})(), ]) ``` ### Diff ```ts const [foo, bar] = await Promise.all([ await getFoo(), // [!code --] getFoo(), // [!code ++] getBar(), (async () => { await getBaz()})(), ]) ``` ### Contributed by Inspired by [Alvar Lagerlöf](https://twitter.com/alvarlagerlof) --- # Source: https://ast-grep.github.io/catalog/typescript/no-console-except-catch.md --- url: /catalog/typescript/no-console-except-catch.md --- ## No `console` except in `catch` block * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6ImlmICRBLmhhc19mZWF0dXJlP1xuICAgICQkJEJcbmVsc2UgXG4gICAgJCQkQyBcbmVuZCAiLCJyZXdyaXRlIjoiJCQkQiIsImNvbmZpZyI6InJ1bGU6XG4gIGFueTpcbiAgICAtIHBhdHRlcm46IGNvbnNvbGUuZXJyb3IoJCQkKVxuICAgICAgbm90OlxuICAgICAgICBpbnNpZGU6XG4gICAgICAgICAga2luZDogY2F0Y2hfY2xhdXNlXG4gICAgICAgICAgc3RvcEJ5OiBlbmRcbiAgICAtIHBhdHRlcm46IGNvbnNvbGUuJE1FVEhPRCgkJCQpXG5jb25zdHJhaW50czpcbiAgTUVUSE9EOlxuICAgIHJlZ2V4OiAnbG9nfGRlYnVnfHdhcm4nXG5maXg6ICcnIiwic291cmNlIjoiY29uc29sZS5kZWJ1ZygnJylcbnRyeSB7XG4gICAgY29uc29sZS5sb2coJ2hlbGxvJylcbn0gY2F0Y2ggKGUpIHtcbiAgICBjb25zb2xlLmVycm9yKGUpXG59In0=) ### Description Using `console` methods is usually for debugging purposes and therefore not suitable to ship to the client. `console` can expose sensitive information, clutter the output, or affect the performance. The only exception is using `console.error` to log errors in the catch block, which can be useful for debugging production. ### YAML ```yaml id: no-console-except-error language: typescript rule: any: - pattern: console.error($$$) not: inside: kind: catch_clause stopBy: end - pattern: console.$METHOD($$$) constraints: METHOD: regex: 'log|debug|warn' ``` ### Example ```ts {1,3} console.debug('') try { console.log('hello') } catch (e) { console.error(e) // OK } ``` ### Diff ```ts console.debug('') // [!code --] try { console.log('hello') // [!code --] } catch (e) { console.error(e) // OK } ``` ### Contributed by Inspired by [Jerry Mouse](https://github.com/WWK563388548) --- # Source: https://ast-grep.github.io/catalog/java/no-unused-vars.md --- url: /catalog/java/no-unused-vars.md --- ## No Unused Vars in Java * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmEiLCJxdWVyeSI6ImlmKHRydWUpeyQkJEJPRFl9IiwicmV3cml0ZSI6IiRDOiBMaXN0WyRUXSA9IHJlbGF0aW9uc2hpcCgkJCRBLCB1c2VsaXN0PVRydWUsICQkJEIpIiwic3RyaWN0bmVzcyI6InNtYXJ0Iiwic2VsZWN0b3IiOiIiLCJjb25maWciOiJpZDogbm8tdW51c2VkLXZhcnNcbnJ1bGU6XG4gICAga2luZDogbG9jYWxfdmFyaWFibGVfZGVjbGFyYXRpb25cbiAgICBhbGw6XG4gICAgICAgIC0gaGFzOlxuICAgICAgICAgICAgaGFzOlxuICAgICAgICAgICAgICAgIGtpbmQ6IGlkZW50aWZpZXJcbiAgICAgICAgICAgICAgICBwYXR0ZXJuOiAkSURFTlRcbiAgICAgICAgLSBub3Q6XG4gICAgICAgICAgICBwcmVjZWRlczpcbiAgICAgICAgICAgICAgICBzdG9wQnk6IGVuZFxuICAgICAgICAgICAgICAgIGhhczpcbiAgICAgICAgICAgICAgICAgICAgc3RvcEJ5OiBlbmRcbiAgICAgICAgICAgICAgICAgICAgYW55OlxuICAgICAgICAgICAgICAgICAgICAgICAgLSB7IGtpbmQ6IGlkZW50aWZpZXIsIHBhdHRlcm46ICRJREVOVCB9XG4gICAgICAgICAgICAgICAgICAgICAgICAtIHsgaGFzOiB7a2luZDogaWRlbnRpZmllciwgcGF0dGVybjogJElERU5ULCBzdG9wQnk6IGVuZH19XG5maXg6ICcnXG4iLCJzb3VyY2UiOiJTdHJpbmcgdW51c2VkID0gXCJ1bnVzZWRcIjtcbk1hcDxTdHJpbmcsIFN0cmluZz4gZGVjbGFyZWRCdXROb3RJbnN0YW50aWF0ZWQ7XG5cblN0cmluZyB1c2VkMSA9IFwidXNlZFwiO1xuaW50IHVzZWQyID0gMztcbmJvb2xlYW4gdXNlZDMgPSBmYWxzZTtcbmludCB1c2VkNCA9IDQ7XG5TdHJpbmcgdXNlZDUgPSBcIjVcIjtcblxuXG5cbnVzZWQxO1xuU3lzdGVtLm91dC5wcmludGxuKHVzZWQyKTtcbmlmKHVzZWQzKXtcbiAgICBTeXN0ZW0ub3V0LnByaW50bG4oXCJzb21lIHZhcnMgYXJlIHVudXNlZFwiKTtcbiAgICBNYXA8U3RyaW5nLCBTdHJpbmc+IHVudXNlZE1hcCA9IG5ldyBIYXNoTWFwPD4oKSB7e1xuICAgICAgICBwdXQodXNlZDUsIFwidXNlZDVcIik7XG4gICAgfX07XG5cbiAgICAvLyBFdmVuIHRob3VnaCB3ZSBkb24ndCByZWFsbHkgZG8gYW55dGhpbmcgd2l0aCB0aGlzIG1hcCwgc2VwYXJhdGluZyB0aGUgZGVjbGFyYXRpb24gYW5kIGluc3RhbnRpYXRpb24gbWFrZXMgaXQgY291bnQgYXMgYmVpbmcgdXNlZFxuICAgIGRlY2xhcmVkQnV0Tm90SW5zdGFudGlhdGVkID0gbmV3IEhhc2hNYXA8PigpO1xuXG4gICAgcmV0dXJuIHVzZWQ0O1xufSJ9) ### Description Identifying unused variables is a common task in code refactoring. You should rely on a Java linter or IDE for this task rather than writing a custom rule in ast-grep, but for educational purposes, this rule demonstrates how to find unused variables in Java. This approach makes some simplifying assumptions. We only consider local variable declarations and ignore the other many ways variables can be declared: Method Parameters, Fields, Class Variables, Constructor Parameters, Loop Variables, Exception Handler Parameters, Lambda Parameters, Annotation Parameters, Enum Constants, and Record Components. Now you may see why it is recommended to use a rule from an established linter or IDE rather than writing your own. ### YAML ```yaml id: no-unused-vars rule: kind: local_variable_declaration all: - has: has: kind: identifier pattern: $IDENT - not: precedes: stopBy: end has: stopBy: end any: - { kind: identifier, pattern: $IDENT } - { has: {kind: identifier, pattern: $IDENT, stopBy: end}} fix: '' ``` First, we identify the local variable declaration and capture the pattern of the identifier inside of it. Then we use `not` and `precedes` to only match the local variable declaration if the identifier we captured does not appear later in the code. It is important to note that we use `all` here to force the ordering of the `has` rule to be before the `not` rule. This guarantees that the meta-variable `$IDENT` is captured by looking inside of the local variable declaration. Additionally, when looking ahead in the code, we can't just look for the identifier directly, but for any node that may contain the identifier. ### Example ```java String unused = "unused"; // [!code --] String used = "used"; System.out.println(used); ``` --- # Source: https://ast-grep.github.io/catalog/python/optional-to-none-union.md --- url: /catalog/python/optional-to-none-union.md --- ## Rewrite `Optional[Type]` to `Type | None` * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiIiwicmV3cml0ZSI6IiIsInN0cmljdG5lc3MiOiJzaWduYXR1cmUiLCJzZWxlY3RvciI6IiIsImNvbmZpZyI6InJ1bGU6XG4gIHBhdHRlcm46IFxuICAgIGNvbnRleHQ6ICdhOiBPcHRpb25hbFskVF0nXG4gICAgc2VsZWN0b3I6IGdlbmVyaWNfdHlwZVxuZml4OiAkVCB8IE5vbmUiLCJzb3VyY2UiOiJkZWYgYShhcmc6IE9wdGlvbmFsW0ludF0pOiBwYXNzIn0=) ### Description [PEP 604](https://peps.python.org/pep-0604/) recommends that `Type | None` is preferred over `Optional[Type]` for Python 3.10+. This rule performs such rewriting. Note `Optional[$T]` alone is interpreted as subscripting expression instead of generic type, we need to use [pattern object](/guide/rule-config/atomic-rule.html#pattern-object) to disambiguate it with more context code. ### YAML ```yaml id: optional-to-none-union language: python rule: pattern: context: 'a: Optional[$T]' selector: generic_type fix: $T | None ``` ### Example ```py {1} def a(arg: Optional[int]): pass ``` ### Diff ```py def a(arg: Optional[int]): pass # [!code --] def a(arg: int | None): pass # [!code ++] ``` ### Contributed by [Bede Carroll](https://github.com/ast-grep/ast-grep/discussions/1492) --- # Source: https://ast-grep.github.io/advanced/pattern-parse.md --- url: /advanced/pattern-parse.md --- # Deep Dive into ast-grep's Pattern Syntax ast-grep's pattern is easy to learn but hard to master. While it's easy to get started with, mastering its nuances can greatly enhance your code searching capabilities. This article aims to provide you with a deep understanding of how ast-grep's patterns are parsed, created, and effectively used in code matching. ## Steps to Create a Pattern Parsing a pattern in ast-grep involves these keys steps: 1. Preprocess the pattern text, e.g, replacing `$` with [expando\_char](/advanced/custom-language.html#register-language-in-sgconfig-yml). 2. Parse the preprocessed pattern text into AST. 3. Extract effective AST nodes based on builtin heuristics or user provided [selector](/reference/rule.html#pattern). 4. Detect AST with wildcard text and convert them into [meta variables](/guide/pattern-syntax.html#meta-variable). ![image](/image/parse-pattern.jpg) Let's dive deep into each of these steps. ## Pattern is AST based ***First and foremost, pattern is AST based**.* ast-grep's pattern code will be converted into the Abstract Syntax Tree (AST) format, which is a tree structure that represents the code snippet you want to match. Therefore pattern cannot be arbitrary text, but a valid code with meta variables as placeholders. If the pattern cannot be parsed by the underlying parser tree-sitter, ast-grep won't be able to find valid matching for it. There are several common pitfalls to avoid when creating patterns. ### Invalid Pattern Code ast-grep pattern must be parsable valid code. While this may seem obvious, newcomers sometimes make mistakes when creating patterns with meta-variables. ***Meta-variable is usually parsed as identifier in most languages.*** When using meta-variables, make sure they are placed in a valid context and not used as a keyword or an operator. For example, you may want to use `$OP` to match binary expressions like `a + b`. The pattern below will not work because parsers see it as three consecutive identifiers separated by spaces. ``` $LEFT $OP $RIGHT ``` You can instead use [atomic rule](/guide/rule-config/atomic-rule.html#kind) `kind: binary_expression` to [match binary expressions](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6IiIsInJld3JpdGUiOiIiLCJzdHJpY3RuZXNzIjoic21hcnQiLCJzZWxlY3RvciI6IiIsImNvbmZpZyI6InJ1bGU6XG4gIGtpbmQ6IGJpbmFyeV9leHByZXNzaW9uIiwic291cmNlIjoiYSArIGIgXHJcbmEgLSBiXHJcbmEgPT0gYiAifQ==). Similarly, in JavaScript you may want to match [object accessors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#method_definitions) like `{ get foo() {}, set bar() { } }`. The pattern below will not work because meta-variable is not parsed as the keywords `get` and `set`. ```js obj = { $KIND foo() { } } ``` Again [rule](/guide/rule-config.html) is more suitable for [this scenario](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6IiIsInJld3JpdGUiOiIiLCJzdHJpY3RuZXNzIjoic21hcnQiLCJzZWxlY3RvciI6IiIsImNvbmZpZyI6InJ1bGU6XG4gIGtpbmQ6IG1ldGhvZF9kZWZpbml0aW9uXG4gIHJlZ2V4OiAnXmdldHxzZXRcXHMnIiwic291cmNlIjoidmFyIGEgPSB7XHJcbiAgICBmb28oKSB7fVxyXG4gICAgZ2V0IGZvbygpIHt9LFxyXG4gICAgc2V0IGJhcigpIHt9LFxyXG59In0=). ```yaml rule: kind: method_definition regex: '^get|set\s' ``` ### Incomplete Pattern Code It is very common and even attempting to write incomplete code snippet in patterns. However, incomplete code does not *always* work. Consider the following JSON code snippet as pattern: ```json "a": 123 ``` While the intention here is clearly to match a key-value pair, tree-sitter does not treat it as valid JSON code because it is missing the enclosing `{}`. Consequently ast-grep will not be able to parse it. The solution here is to use [pattern object](/guide/rule-config/atomic-rule.html#pattern-object) to provide complete code snippet. ```yaml pattern: context: '{ "a": 123 }' selector: pair ``` You can use both ast-grep playground's [pattern tab](/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoianNvbiIsInF1ZXJ5IjoieyBcImFcIjogMTIzIH0iLCJyZXdyaXRlIjoiIiwic3RyaWN0bmVzcyI6InNtYXJ0Iiwic2VsZWN0b3IiOiJwYWlyIiwiY29uZmlnIjoicnVsZTpcbiAga2luZDogbWV0aG9kX2RlZmluaXRpb25cbiAgcmVnZXg6ICdeZ2V0fHNldFxccyciLCJzb3VyY2UiOiJ7IFwiYVwiOiAxMjMgfSAifQ==) or [rule tab](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6Impzb24iLCJxdWVyeSI6InsgXCJhXCI6IDEyMyB9IiwicmV3cml0ZSI6IiIsInN0cmljdG5lc3MiOiJzbWFydCIsInNlbGVjdG9yIjoicGFpciIsImNvbmZpZyI6InJ1bGU6XG4gIHBhdHRlcm46IFxuICAgIGNvbnRleHQ6ICd7XCJhXCI6IDEyM30nXG4gICAgc2VsZWN0b3I6IHBhaXIiLCJzb3VyY2UiOiJ7IFwiYVwiOiAxMjMgfSAifQ==) to verify it. ***Incomplete pattern code sometimes works fine due to error-tolerance.*** For better *user experience*, ast-grep parse pattern code as lenient as possible. ast-grep parsers will try recovering parsing errors and ignoring missing language constructs. For example, the pattern `foo(bar)` in Java cannot be parsed as valid code. However, ast-grep recover the parsing error, ignoring missing semicolon and treat it as a method call. So the pattern [still works](/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoiamF2YSIsInF1ZXJ5IjoiZm9vKGJhcikiLCJyZXdyaXRlIjoiIiwic3RyaWN0bmVzcyI6InNtYXJ0Iiwic2VsZWN0b3IiOiIiLCJjb25maWciOiJydWxlOlxuICBwYXR0ZXJuOiBcbiAgICBjb250ZXh0OiAne1wiYVwiOiAxMjN9J1xuICAgIHNlbGVjdG9yOiBwYWlyIiwic291cmNlIjoiY2xhc3MgQSB7XG4gICAgZm9vKCkge1xuICAgICAgICBmb28oYmFyKTtcbiAgICB9XG59In0=). ### Ambiguous Pattern Code Just as programming languages have ambiguous grammar, so ast-grep patterns can be ambiguous. Let's consider the JavaScript code snippet below: ```js a: 123 ``` It can be interpreted as an object key-value pair or a labeled statement. Without other hints, ast-grep will parse it as labeled statement by default. To match object key-value pair, we need to provide more context by [using pattern object](/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoiamF2YXNjcmlwdCIsInF1ZXJ5IjoieyBhOiAxMjMgfSIsInJld3JpdGUiOiIiLCJzdHJpY3RuZXNzIjoic21hcnQiLCJzZWxlY3RvciI6InBhaXIiLCJjb25maWciOiJydWxlOlxuICBwYXR0ZXJuOiBcbiAgICBjb250ZXh0OiAne1wiYVwiOiAxMjN9J1xuICAgIHNlbGVjdG9yOiBwYWlyIiwic291cmNlIjoiYSA9IHsgYTogIDEyMyB9In0=). ```yaml pattern: context: '{ a: 123 }' selector: pair ``` Other examples of ambiguous patterns include: * Match function call in [Golang](/catalog/go/#match-function-call-in-golang) and [C](/catalog/c/#match-function-call) * Match [class field](/guide/rule-config/atomic-rule.html#pattern-object) in JavaScript ### How ast-grep Handles Pattern Code? ast-grep uses best efforts to parse pattern code for best user experience. Here are some strategies ast-grep uses to handle code snippet: * **Replace `$` with expando\_char**: some languages use `$` as a special character, so ast-grep replace it with [expando\_char](/advanced/custom-language.html#register-language-in-sgconfig-yml) in order to make the pattern code parsable. * **Ignore missing nodes**: ast-grep will ignore missing nodes in pattern like trailing semicolon in Java/C/C++. * **Treat root error as normal node**: if the parser error has no siblings, ast-grep will treat it as a normal node. * If all above fails, users should provide more code via pattern object :::warning Pattern Error Recovery is useful, but not guaranteed ast-grep's recovery mechanism heavily depends on tree-sitter's behavior. We cannot guarantee invalid patterns will be parsed consistently between different versions. So using invalid pattern may lead to unexpected results after upgrading ast-grep. When in doubt, always use valid code snippets with pattern object. ::: ## Extract Effective AST for Pattern After parsing the pattern code, ast-grep needs to extract AST nodes to make the actual pattern. Normally, a code snippet generated by tree-sitter will be a full AST tree. Yet it is unlikely that the entire tree will be used as a pattern. The code `123` will produce a tree like `program -> expression_statement -> number` in many languages. But we want to match a number literal in the code, not a program containing just a number. ast-grep uses two strategies to extract **effective AST nodes** that will be used to match code. ### Builtin Heuristic ***By default, at-grep extracts the leaf node or the innermost node with more than one child.*** This heuristic extracts the most specific node while still keeping all structural information in the pattern. If a node has only one child, it is atomic and cannot be further decomposed. We can safely assume the node contains no structural information for matching. In contrast, a node with more than one child contains a structure that we want to search. Examples: * `123` will be extracted as `number` because it is the leaf node. ```yaml program expression_statement number <--- effective node ``` See [Playground](/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoiamF2YXNjcmlwdCIsInF1ZXJ5IjoiMTIzIiwicmV3cml0ZSI6IiIsInN0cmljdG5lc3MiOiJzbWFydCIsInNlbGVjdG9yIjoiIiwiY29uZmlnIjoiIiwic291cmNlIjoiIn0=). * `foo(bar)` will be extracted as `call_expression` because it is the innermost node that has more than one child. ```yaml program expression_statement call_expression <--- effective node identifier arguments identifier ``` See [Playground](/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoiamF2YXNjcmlwdCIsInF1ZXJ5IjoiZm9vKGJhcikiLCJyZXdyaXRlIjoiIiwic3RyaWN0bmVzcyI6InNtYXJ0Iiwic2VsZWN0b3IiOiJjYWxsX2V4cHJlc3Npb24iLCJjb25maWciOiIiLCJzb3VyY2UiOiIifQ==). ### User Defined Selector Sometimes the effective node extracted by the builtin heuristic may not be what you want. You can explicitly specify the node to extract using the [selector](/reference/rule.html#pattern) field in the rule configuration. For example, you may want to match the whole `console.log` statement in JavaScript code. The effective node extracted by the builtin heuristic is `call_expression`, but you want to match the whole `expression_statement`. Using `console.log($$$)` directly will not include the trailing `;` in the pattern, see [Playground](/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoiamF2YXNjcmlwdCIsInF1ZXJ5IjoiY29uc29sZS5sb2coJCQkKSIsInJld3JpdGUiOiIiLCJzdHJpY3RuZXNzIjoic2lnbmF0dXJlIiwic2VsZWN0b3IiOiJjYWxsX2V4cHJlc3Npb24iLCJjb25maWciOiIiLCJzb3VyY2UiOiJjb25zb2xlLmxvZyhmb28pXG5jb25zb2xlLmxvZyhiYXIpOyJ9). ```js console.log("Hello") console.log("World"); ``` You can use pattern object to explicitly specify the effective node to be `expression_statement`. [Playground](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6ImNvbnNvbGUubG9nKCQkJCkiLCJyZXdyaXRlIjoiIiwic3RyaWN0bmVzcyI6InNpZ25hdHVyZSIsInNlbGVjdG9yIjoiY2FsbF9leHByZXNzaW9uIiwiY29uZmlnIjoicnVsZTpcbiAgcGF0dGVybjpcbiAgICBjb250ZXh0OiBjb25zb2xlLmxvZygkJCQpXG4gICAgc2VsZWN0b3I6IGV4cHJlc3Npb25fc3RhdGVtZW50XG5maXg6ICcnIiwic291cmNlIjoiY29uc29sZS5sb2coZm9vKVxuY29uc29sZS5sb2coYmFyKTsifQ==) ```yaml pattern: context: console.log($$$) selector: expression_statement ``` Using `selector` is especially helpful when you are also using relational rules like `follows` and `precedes`. You want to match the statement instead of the default inner expression node, and [match other statements around it](https://github.com/ast-grep/ast-grep/issues/1427). :::tip When in doubt, try pattern object first. ::: ## Meta Variable Deep Dive ast-grep's meta variables are also AST based and are detected in the effective nodes extracted from the pattern code. ### Meta Variable Detection in Pattern Not all `$` prefixed strings will be detected as meta variables. Only AST nodes that match meta variable syntax will be detected. If meta variable text is not the only text in the node or it spans multiple nodes, it will not be detected as a meta variable. **Working meta variable examples:** * `$A` works * `$A` is one single `identifier` * `$A.$B` works * `$A` is `identifier` inside `member_expression` * `$B` is the `property_identifier`. * `$A.method($B)` works * `$A` is `identifier` inside `member_expression` * `$B` is `identifier` inside `arguments` **Non working meta variable examples:** * `obj.on$EVENT` does not work * `on$EVENT` is `property_identifier` but `$EVENT` is not the only text * `"Hello $WORLD"` does not work * `$WORLD` is inside `string_content` and is not the only text * `a $OP b` does not work * the whole pattern does not parse * `$jq` does not work * meta variable does not accept lower case letters See all examples in [Playground](/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoiamF2YXNjcmlwdCIsInF1ZXJ5IjoiIiwicmV3cml0ZSI6IiIsInN0cmljdG5lc3MiOiJzaWduYXR1cmUiLCJzZWxlY3RvciI6ImNhbGxfZXhwcmVzc2lvbiIsImNvbmZpZyI6IiIsInNvdXJjZSI6Ii8vIHdvcmtpbmdcbiRBXG4kQS4kQlxuJEEubWV0aG9kKCRCKVxuXG4vLyBub24gd29ya2luZ1xub2JqLm9uJEVWRU5UXG5cIkhlbGxvICRXT1JMRFwiXG5hICRPUCBiIn0=). ### Matching Unnamed Nodes A meta variable pattern `$META` will capture [named nodes](/advanced/core-concepts.html#named-vs-unnamed) by default. To capture [unnamed nodes](/advanced/core-concepts.html#named-vs-unnamed), you can use double dollar sign `$$VAR`. Let's go back to the binary expression example. It is impossible to match arbitrary binary expression in one single pattern. But we can combine `kind` and `has` to match the operator in binary expressions. Note, `$OP` cannot match the operator because operator is not a named node. We need to use `$$OP` instead. ```yaml rule: kind: binary_expression has: field: operator pattern: $$OP # pattern: $OP ``` See the above rule to match all arithmetic expressions in [action](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6ImNvbnNvbGUubG9nKCQkJCkiLCJyZXdyaXRlIjoiIiwic3RyaWN0bmVzcyI6InNpZ25hdHVyZSIsInNlbGVjdG9yIjoiY2FsbF9leHByZXNzaW9uIiwiY29uZmlnIjoicnVsZTpcbiAga2luZDogYmluYXJ5X2V4cHJlc3Npb25cbiAgaGFzOlxuICAgIGZpZWxkOiBvcGVyYXRvclxuICAgIHBhdHRlcm46ICQkT1BcbiAgICAjIHBhdHRlcm46ICRPUCIsInNvdXJjZSI6IjEgKyAxIn0=). ### How Multi Meta Variables Match Code Multiple meta variables like `$$$ARGS` has special matching behavior. It will match multiple nodes in the AST. `$$$ARGS` will match multiple nodes in source code when the meta variable starts to match. It will match as many nodes as possible until the first AST node after the meta var in pattern is matched. The behavior is like [non-greedy](https://stackoverflow.com/questions/11898998/how-can-i-write-a-regex-which-matches-non-greedy) matching in regex and template string literal `infer` in [TypeScript](https://github.com/microsoft/TypeScript/pull/40336). ## Use ast-grep playground to debug pattern ast-grep playground is a great tool to debug pattern code. The pattern tab and pattern panel can help you visualize the AST tree, effective nodes and meta variables. ![playground](/image/pattern-debugger.jpg) In next article, we will explain how ast-grep's pattern is used to match code, the pattern matching algorithm. --- # Source: https://ast-grep.github.io/guide/pattern-syntax.md --- url: /guide/pattern-syntax.md --- # Pattern Syntax In this guide we will walk through ast-grep's pattern syntax. The example will be written in JavaScript, but the basic principle will apply to other languages as well. ## Pattern Matching ast-grep uses pattern code to construct AST tree and match that against target code. The pattern code can search through the full syntax tree, so pattern can also match nested expression. For example, the pattern `a + 1` can match all the following code. ```javascript const b = a + 1 funcCall(a + 1) deeplyNested({ target: a + 1 }) ``` ::: warning Pattern code must be valid code that tree-sitter can parse. [ast-grep playground](/playground.html) is a useful tool to confirm pattern is parsed correctly. If ast-grep fails to parse code as expected, you can try give it more context by using [object-style pattern](/reference/rule.html#pattern). ::: ## Meta Variable It is usually desirable to write a pattern to match dynamic content. We can use meta variables to match sub expression in pattern. Meta variables start with the `$` sign, followed by a name composed of upper case letters `A-Z`, underscore `_` or digits `1-9`. `$META_VARIABLE` is a wildcard expression that can match any **single** AST node. Think it as REGEX dot `.`, except it is not textual. :::tip Valid meta variables `$META`, `$META_VAR`, `$META_VAR1`, `$_`, `$_123` ::: :::danger Invalid meta variables `$invalid`, `$Svalue`, `$123`, `$KEBAB-CASE`, `$` ::: The pattern `console.log($GREETING)` will match all the following. ```javascript function tryAstGrep() { console.log('Hello World') } const multiLineExpression = console .log('Also matched!') ``` But it will not match these. ```javascript // console.log(123) in comment is not matched 'console.log(123) in string' // is not matched as well console.log() // mismatch argument console.log(a, b) // too many arguments ``` Note, one meta variable `$MATCH` will match one **single** AST node, so the last two `console.log` calls do not match the pattern. Let's see how we can match multiple AST nodes. ## Multi Meta Variable We can use `$$$` to match zero or more AST nodes, including function arguments, parameters or statements. These variables can also be named, for example: `console.log($$$ARGS)`. ### Function Arguments For example, `console.log($$$)` can match ```javascript console.log() // matches zero AST node console.log('hello world') // matches one node console.log('debug: ', key, value) // matches multiple nodes console.log(...args) // it also matches spread ``` ### Function Parameters `function $FUNC($$$ARGS) { $$$ }` will match ```javascript function foo(bar) { return bar } function noop() {} function add(a, b, c) { return a + b + c } ``` :::details `ARGS` will be populated with a list of AST nodes. Click to see details. |Code|Match| |---|----| |`function foo(bar) { ... }` | \[`bar`] | |`function noop() {}` | \[] | |`function add(a, b, c) { ... }` | \[`a`, `b`, `c`] | ::: ## Meta Variable Capturing Meta variable is also similar to [capture group](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Groups_and_Backreferences) in regular expression. You can reuse same name meta variables to find previously occurred AST nodes. For example, the pattern `$A == $A` will have the following result. ```javascript // will match these patterns a == a 1 + 1 == 1 + 1 // but will not match these a == b 1 + 1 == 2 ``` ### Non Capturing Match You can also suppress meta variable capturing. All meta variables with name starting with underscore `_` will not be captured. ```javascript // Given this pattern $_FUNC($_FUNC) // it will match all function call with one argument or spread call test(a) testFunc(1 + 1) testFunc(...args) ``` Note in the example above, even if two meta variables have the same name `$_FUNC`, each occurrence of `$_FUNC` can match different content because they are not captured. :::info Why use non-capturing match? This is a useful trick to micro-optimize pattern matching speed, since we don't need to create a [HashMap](https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html) for bookkeeping. ::: ### Capture Unnamed Nodes A meta variable pattern `$META` will capture [named nodes](/advanced/core-concepts.html#named-vs-unnamed) by default. To capture [unnamed nodes](/advanced/core-concepts.html#named-vs-unnamed), you can use double dollar sign `$$VAR`. Namedness is an advanced topic in [Tree-sitter](https://tree-sitter.github.io/tree-sitter/using-parsers#named-vs-anonymous-nodes). You can read this [in-depth guide](/advanced/core-concepts.html) for more background. ## More Powerful Rule Pattern is a fast and easy way to match code. But it is not as powerful as [rule](/guide/rule-config.html#rule-file) which can match code with more [precise selector](/guide/rule-config/atomic-rule.html#kind) or [more context](/guide/rule-config/relational-rule.html). We will cover using rules in next chapter. :::tip Pro Tip Pattern can also be an object instead of string in YAML rule. It is very useful to avoid ambiguity in code snippet. See [here](/guide/rule-config/atomic-rule.html#pattern) for more details. Also see our FAQ for more [guidance](/advanced/faq.html) on writing patterns. ::: --- # Source: https://ast-grep.github.io/guide/api-usage/performance-tip.md --- url: /guide/api-usage/performance-tip.md --- # Performance Tip for napi usage Using `napi` to parse code and search for nodes [isn't always faster](https://medium.com/@hchan_nvim/benchmark-typescript-parsers-demystify-rust-tooling-performance-025ebfd391a3) than pure JavaScript implementations. There are a lot of tricks to improve performance when using `napi`. The mantra is to *reduce FFI (Foreign Function Interface) calls between Rust and JavaScript*, and to *take advantage of parallel computing*. ## Prefer `parseAsync` over `parse` `parseAsync` can take advantage of NodeJs' libuv thread pool to parse code in parallel threads. This can be faster than the sync version `parse` when handling a lot of code. ```ts import { Lang, parse, parseAsync } from '@ast-grep/napi' // only one thread parsing const ast = parse(Lang.JavaScript, 'console.log("hello world")') const root = ast.root() // better, can use multiple threads const ast2 = await parseAsync(Lang.JavaScript, 'console.log("hello world")') const root2 = ast2.root() ``` This is especially useful when you are using ast-grep in bundlers where the main thread is busy with other CPU intensive tasks. ## Prefer `findAll` over manual traversal One way to find all nodes that match a rule is to traverse the syntax tree manually and check each node against the rule. This is slow because it requires a lot of FFI calls between Rust and JavaScript during the traversal. For example, the following code snippet finds all `member_expression` nodes in the syntax tree. Unfortunately, there are as many FFI calls as the tree node number in the recursion. ```ts const root = sgroot.root() function findMemberExpression(node: SgNode): SgNode[] { let ret: SgNode[] = [] // `node.kind()` is a FFI call if (node.kind() === 'member_expression') { ret.push(node) } // `node.children()` is a FFI call for (let child of node.children()) { // recursion makes more FFI calls ret = ret.concat(findMemberExpression(child)) } return ret } const nodes = findMemberExpression(root) ``` The equivalent code using `findAll` is much faster: ```ts const root = sgroot.root() // only call FFI `findAll` once const nodes = root.findAll({kind: 'member_expression'}) ``` > *One [success](https://x.com/hd_nvim/status/1767971906786128316) [story](https://x.com/sonofmagic95/status/1768433654404104555) on Twitter, as an example.* ## Prefer `findInFiles` when possible If you have a lot of files to parse and want to maximize your programs' performance, ast-grep provides a `findInFiles` function that parses multiple files and searches relevant nodes in parallel Rust threads. APIs we showed above all require parsing code in Rust and pass the `SgRoot` back to JavaScript. This incurs foreign function communication overhead and only utilizes the single main JavaScript thread. By avoiding Rust-JS communication overhead and utilizing multiple core computing, `findInFiles` is much faster than finding files in JavaScript and then passing them to Rust as string. The function signature of `findInFiles` is as follows: ```ts export function findInFiles( /** the language to parse */ lang: Lang, /** specify the file path and matcher */ config: FindConfig, /** callback function for found nodes in a file */ callback: (err: null | Error, result: SgNode[]) => void ): Promise ``` `findInFiles` accepts a `FindConfig` object and a callback function. `FindConfig` specifies both what file path to *parse* and what nodes to *search*. `findInFiles` will parse all files matching paths and will call back the function with nodes matching the `matcher` found in the files as arguments. ### `FindConfig` The `FindConfig` object specifies which paths to search code and what rule to match node against. The `FindConfig` object has the following type: ```ts export interface FindConfig { paths: Array matcher: NapiConfig } ``` The `path` field is an array of strings. You can specify multiple paths to search code. Every path in the array can be a file path or a directory path. For a directory path, ast-grep will recursively find all files matching the language. The `matcher` is the same as `NapiConfig` stated above. ### Callback Function and Termination The `callback` function is called for every file that have nodes that match the rule. The callback function is a standard node-style callback with the first argument as `Error` and second argument as an array of `SgNode` objects that match the rule. The return value of `findInFiles` is a `Promise` object. The promise resolves to the number of files that have nodes that match the rule. :::danger `findInFiles` can return before all file callbacks are called due to NodeJS limitation. See https://github.com/ast-grep/ast-grep/issues/206. ::: If you have a lot of files and `findInFiles` prematurely returns, you can use the total files returned by `findInFiles` as a check point. Maintain a counter outside of `findInFiles` and increment it in callback. If the counter equals the total number, we can conclude all files are processed. The following code is an example, with core logic highlighted. ```ts:line-numbers {11,16-18} type Callback = (t: any, cb: any) => Promise function countedPromise(func: F) { type P = Parameters return async (t: P[0], cb: P[1]) => { let i = 0 let fileCount: number | undefined = undefined // resolve will be called after all files are processed let resolve = () => {} function wrapped(...args: any[]) { let ret = cb(...args) if (++i === fileCount) resolve() return ret } fileCount = await func(t, wrapped as P[1]) // not all files are processed, await `resolve` to be called if (fileCount > i) { await new Promise(r => resolve = r) } return fileCount } } ``` ### Example Example of using `findInFiles` ```ts import { Lang, findInFiles } from '@ast-grep/napi' let fileCount = await findInFiles(Lang.JavaScript, { paths: ['relative/path/to/code'], matcher: { rule: {kind: 'member_expression'} }, }, (err, n) => { if (err) { console.error(err) return } console.log(`Found ${n.length} nodes`) console.log(n[0].text()) }) ``` --- # Source: https://ast-grep.github.io/playground.md # Source: https://ast-grep.github.io/reference/playground.md --- url: /reference/playground.md --- # ast-grep Playground Manual The [ast-grep playground](/playground.html) is an online tool that allows you to try out ast-grep without installing anything on your machine. You can write code patterns and see how they match your code in real time. You can also apply rewrite rules to modify your code based on the patterns. See the video for a quick overview of the playground. The playground is a great way to *learn* ast-grep, *debug* patterns/rules, *report bugs* and *showcase* ast-grep's capabilities. ## Basic Usage Annotated screenshot of the ast-grep playground: ![ast-grep playground](https://user-images.githubusercontent.com/2883231/268551825-2adfe739-c3d1-48c3-94d7-3c0c40fabbbc.png) The ast-grep playground has a simple and intuitive layout that consists of four main areas. ### 1. Source Editor The **source editor** is where you can write or paste the code that you want to search or modify. The source editor supports syntax highlighting and auto-indentation for various languages, such as Python, JavaScript, Java, C#, and more. :::tip How to Change Language? You can choose the language of your code from the drop-down menu at the top right corner. ::: ### 2. Source AST Dump The **source AST dump** is where you can see the AST representation of your source code. The AST dump shows the structure and the [kind and field](/advanced/core-concepts.html#kind-vs-field) of each node in the AST. You can use the AST dump to understand how your code is parsed and how to write patterns that match specific nodes or subtrees. ### 3. Matcher Editor The **matcher editor** is where you can write the code patterns and rewrite rules that you want to apply to your source code. The matcher uses the same language as your source code. The matcher editor has two tabs: **Pattern** and **YAML**. * **Pattern** provides an *approachable* option where you can write the [code pattern](/guide/pattern-syntax.html) that you want to match in your source code. You can also write a rewrite expression that specifies how to modify the matched code in the subeditor below. It roughly emulates the behavior of [`ast-grep run`](/reference/cli/run.html). * **YAML** provides an *advanced* option where you can write a [YAML rule](/reference/yaml.html) that defines the pattern and metadata for your ast-grep scan. You can specify the [rule object](/reference/rule.html), id, message, severity, and other options for your rule. It is a web counterpart of [`ast-grep scan`](/reference/cli/scan.html). ### 4. Matcher Info The **matcher info** is where you can see the information for the matcher section. The matcher info shows different information depending on which tab you are using in the matcher editor: **Pattern** or **YAML**. * If you are using the **Pattern** tab, the matcher info shows the AST dump of your code pattern like the source AST dump. * If you are using the **YAML** tab, the matcher info shows the matched meta-variables and errors if your rule is not valid. You can use the matched meta-variables to see which nodes in the source AST are bound to which variables in your pattern and rewrite expression. You can also use the errors to fix any issues in your rule. *** #### YAML Tab Screenshot ![YAML](https://user-images.githubusercontent.com/2883231/268738518-279f0635-d5af-4b41-87c6-4bd6fa67b135.png) ## Share Results In addition to the four main areas, the playground also has a **share button** at the bottom right corner. You can use this button to generate a unique URL that contains your source code, patterns, rules, and language settings. You can copy this URL and share it with others who want to try out your ast-grep session. ## View Diffs Another feature of the ast-grep playground is the **View Diffs** option. You can use this option to see how your source code is changed by your rewrite expression or the [`fix`](/reference/yaml.html#fix) option in your YAML rule. You can access this option by clicking the **Diff** tab in the source editor area. The Diff tab will show you a unified inline comparison of your original code and your modified code. ![Diff Tab Illustration](https://user-images.githubusercontent.com/2883231/268726696-d5091342-bc07-4859-8c95-abf079221cc2.png) This is a useful way to check and debug your rule/pattern before applying it to your code base. ## Toggle Full AST Display Sometimes you need to match code based on elements that are not encoded in AST. These elements are called [unnamed nodes](/advanced/core-concepts.html#named-vs-unnamed) in ast-grep. ast-grep can represent code using two different types of tree structures: **AST** and **CST**. **AST**, Abstract Syntax Tree, is a simplified representation of the code *excluding* unnamed nodes. **CST**, Concrete Syntax Tree, is a more detailed representation of the code *including* unnamed nodes. We have a standalone [doc page](/advanced/core-concepts.html#ast-vs-cst) for a deep-dive explanation of the two concepts. In case you need to match unnamed nodes, you can toggle between AST and CST in the ast dumper by clicking the **Show Full Tree** option. This option will show you the full CST of your code, which may be useful for debugging or fine-tuning your patterns and rules. |Syntax Tree Format|Screenshot| |---|---| |Named AST|![no full](https://user-images.githubusercontent.com/2883231/268730796-57ffb3be-e2e9-4199-8a71-76f1320cebf7.png)| |Full CST|![full tree](https://user-images.githubusercontent.com/2883231/268730525-ea3b7c71-5389-42e5-abee-fc0d845e4b1b.png)| ## Test Multiple Rules One of the cool features of the ast-grep playground is that you can test multiple rules at once! This can help you simulate how ast-grep would work in your real projects, where you might have several rules to apply to your code base. To test multiple rules, you just need to separate them by `---` in the YAML editor. Each rule will have its own metadata and options, and you can see the results of each rule in the Source tab as well as the Diff tab. Example with [playground link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6ImNvbnNvbGUubG9nKCRNQVRDSCkiLCJyZXdyaXRlIjoibG9nZ2VyLmxvZygkTUFUQ0gpIiwiY29uZmlnIjoiIyBhc3QtZ3JlcCBub3cgc3VwcG9ydHMgbXVsdGlwbGUgcnVsZXMgaW4gcGxheWdyb3VuZCFcbnJ1bGU6XG4gIHBhdHRlcm46IGNvbnNvbGUubG9nKCRBKVxuZml4OlxuICBsb2dnZXIubG9nKCRBKVxuLS0tXG5ydWxlOlxuICBwYXR0ZXJuOiBmdW5jdGlvbiAkQSgpIHsgJCQkQk9EWSB9XG5maXg6ICdjb25zdCAkQSA9ICgpID0+IHsgJCQkQk9EWSB9JyIsInNvdXJjZSI6Ii8vIGNvbnNvbGUubG9nKCkgd2lsbCBiZSBtYXRjaGVkIGJ5IHBhdHRlcm4hXG4vLyBjbGljayBkaWZmIHRhYiB0byBzZWUgcmV3cml0ZS5cblxuZnVuY3Rpb24gdHJ5QXN0R3JlcCgpIHtcbiAgY29uc29sZS5sb2coJ21hdGNoZWQgaW4gbWV0YXZhciEnKVxufVxuXG5jb25zdCBtdWx0aUxpbmVFeHByZXNzaW9uID1cbiAgY29uc29sZVxuICAgLmxvZygnQWxzbyBtYXRjaGVkIScpIn0=): ```yaml rule: pattern: console.log($A) fix: logger.log($A) --- rule: pattern: function $A() { $$$BODY } fix: 'const $A = () => { $$$BODY }' ``` Screenshot: ![multiple rule](https://user-images.githubusercontent.com/2883231/268735920-e6369832-6fa9-4b64-8975-2e813dc14076.png) ## Test Rule Diagnostics Finally, the ast-grep playground also has a powerful feature that lets you see how your YAML rule reports diagnostics in the code editor. This feature is optional, but can be turned on easily. To enable it, you need to specify the following fields in your YAML rule: `id`, `message`, `rule`, and `severity`. The `severity` field should be either `error`, `warning` or `info`, but not `hint`. The playground will then display the diagnostics in the code editor with red or yellow wavy underlines, depending on the severity level. You can also hover over the underlines to see the message and the rule id for each diagnostic. This feature can help you detect and correct code issues more quickly and effectively. [Example Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6ImNvbnNvbGUubG9nKCRNQVRDSCkiLCJyZXdyaXRlIjoibG9nZ2VyLmxvZygkTUFUQ0gpIiwiY29uZmlnIjoiaWQ6IG5vLWNvbnNvbGVcbnJ1bGU6XG4gIHBhdHRlcm46IGNvbnNvbGUuJE1FVEhPRCgkQSlcbm1lc3NhZ2U6IFVuZXhwZWN0ZWQgY29uc29sZVxuc2V2ZXJpdHk6IHdhcm5pbmdcblxuLS0tXG5cbmlkOiBuby1kZWJ1Z2dlclxucnVsZTpcbiAgcGF0dGVybjogZGVidWdnZXJcbm1lc3NhZ2U6IFVuZXhwZWN0ZWQgZGVidWdnZXJcbnNldmVyaXR5OiBlcnJvciIsInNvdXJjZSI6ImZ1bmN0aW9uIHRyeUFzdEdyZXAoKSB7XG4gIGNvbnNvbGUubG9nKCdtYXRjaGVkIGluIG1ldGF2YXIhJylcbn1cblxuY29uc3QgbXVsdGlMaW5lRXhwcmVzc2lvbiA9XG4gIGNvbnNvbGVcbiAgIC5sb2coJ0Fsc28gbWF0Y2hlZCEnKVxuXG5pZiAodHJ1ZSkge1xuICBkZWJ1Z2dlclxufSJ9) ![diagnostics](https://user-images.githubusercontent.com/2883231/268741624-98017dd4-8093-4b11-aa6f-cf7b66e68762.png) --- # Source: https://ast-grep.github.io/catalog/python/prefer-generator-expressions.md --- url: /catalog/python/prefer-generator-expressions.md --- ## Prefer Generator Expressions * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiWyQkJEFdIiwicmV3cml0ZSI6IiRBPy4oKSIsImNvbmZpZyI6InJ1bGU6XG4gIHBhdHRlcm46ICRGVU5DKCRMSVNUKVxuY29uc3RyYWludHM6XG4gIExJU1Q6IHsga2luZDogbGlzdF9jb21wcmVoZW5zaW9uIH1cbiAgRlVOQzpcbiAgICBhbnk6XG4gICAgICAtIHBhdHRlcm46IGFueVxuICAgICAgLSBwYXR0ZXJuOiBhbGxcbiAgICAgIC0gcGF0dGVybjogc3VtXG4gICAgICAjIC4uLlxudHJhbnNmb3JtOlxuICBJTk5FUjpcbiAgICBzdWJzdHJpbmc6IHtzb3VyY2U6ICRMSVNULCBzdGFydENoYXI6IDEsIGVuZENoYXI6IC0xIH1cbmZpeDogJEZVTkMoJElOTkVSKSIsInNvdXJjZSI6ImFsbChbeCBmb3IgeCBpbiB5XSlcblt4IGZvciB4IGluIHldIn0=) ### Description List comprehensions like `[x for x in range(10)]` are a concise way to create lists in Python. However, we can achieve better memory efficiency by using generator expressions like `(x for x in range(10))` instead. List comprehensions create the entire list in memory, while generator expressions generate each element one at a time. We can make the change by replacing the square brackets with parentheses. ### YAML ```yaml id: prefer-generator-expressions language: python rule: pattern: $LIST kind: list_comprehension transform: INNER: substring: {source: $LIST, startChar: 1, endChar: -1 } fix: ($INNER) ``` This rule converts every list comprehension to a generator expression. However, **not every list comprehension can be replaced with a generator expression.** If the list is used multiple times, is modified, is sliced, or is indexed, a generator is not a suitable replacement. Some common functions like `any`, `all`, and `sum` take an `iterable` as an argument. A generator function counts as an `iterable`, so it is safe to change a list comprehension to a generator expression in this context. ```yaml id: prefer-generator-expressions language: python rule: pattern: $FUNC($LIST) constraints: LIST: { kind: list_comprehension } FUNC: any: - pattern: any - pattern: all - pattern: sum # ... transform: INNER: substring: {source: $LIST, startChar: 1, endChar: -1 } fix: $FUNC($INNER) ``` ### Example ```python any([x for x in range(10)]) ``` ### Diff ```python any([x for x in range(10)]) # [!code --] any(x for x in range(10)) # [!code ++] ``` ### Contributed by [Steven Love](https://github.com/StevenLove) --- # Source: https://ast-grep.github.io/catalog/ruby/prefer-symbol-over-proc.md --- url: /catalog/ruby/prefer-symbol-over-proc.md --- ## Prefer Symbol over Proc * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InJ1YnkiLCJxdWVyeSI6IiRMSVNULnNlbGVjdCB7IHwkVnwgJFYuJE1FVEhPRCB9IiwicmV3cml0ZSI6IiRMSVNULnNlbGVjdCgmOiRNRVRIT0QpIiwiY29uZmlnIjoiaWQ6IHByZWZlci1zeW1ib2wtb3Zlci1wcm9jXG5ydWxlOlxuICBwYXR0ZXJuOiAkTElTVC4kSVRFUiB7IHwkVnwgJFYuJE1FVEhPRCB9XG5sYW5ndWFnZTogUnVieVxuY29uc3RyYWludHM6XG4gIElURVI6XG4gICAgcmVnZXg6ICdtYXB8c2VsZWN0fGVhY2gnXG5maXg6ICckTElTVC4kSVRFUigmOiRNRVRIT0QpJ1xuIiwic291cmNlIjoiWzEsIDIsIDNdLnNlbGVjdCB7IHx2fCB2LmV2ZW4/IH1cbigxLi4xMDApLmVhY2ggeyB8aXwgaS50b19zIH1cbm5vdF9saXN0Lm5vX21hdGNoIHsgfHZ8IHYuZXZlbj8gfVxuIn0=) ### Description Ruby has a more concise symbol shorthand `&:` to invoke methods. This rule simplifies `proc` to `symbol`. This example is inspired by this [dev.to article](https://dev.to/baweaver/future-of-ruby-ast-tooling-9i1). ### YAML ```yaml id: prefer-symbol-over-proc language: ruby rule: pattern: $LIST.$ITER { |$V| $V.$METHOD } constraints: ITER: regex: 'map|select|each' fix: '$LIST.$ITER(&:$METHOD)' ``` ### Example ```rb {1,2} [1, 2, 3].select { |v| v.even? } (1..100).each { |i| i.to_s } not_list.no_match { |v| v.even? } ``` ### Diff ```rb [1, 2, 3].select { |v| v.even? } # [!code --] [1, 2, 3].select(&:even?) # [!code ++] (1..100).each { |i| i.to_s } # [!code --] (1..100).each(&:to_s) # [!code ++] not_list.no_match { |v| v.even? } ``` ### Contributed by [Herrington Darkholme](https://twitter.com/hd_nvim) --- # Source: https://ast-grep.github.io/guide/project/project-config.md --- url: /guide/project/project-config.md --- # Project Configuration ## Root Configuration File ast-grep supports using [YAML](https://yaml.org/) to configure its linting rules to scan your code repository. We need a root configuration file `sgconfig.yml` to specify directories where `ast-grep` can find all rules. In your project root, add `sgconfig.yml` with content as below. ```yaml ruleDirs: - rules ``` This instructs ast-grep to use all files *recursively* inside the `rules` folder as rule files. For example, suppose we have the following file structures. ``` my-awesome-project |- rules | |- no-var.yml | |- no-bit-operation.yml | |- my_custom_rules | |- custom-rule.yml | |- fancy-rule.yml |- sgconfig.yml |- not-a-rule.yml ``` All the YAML files under `rules` folder will be treated as rule files by `ast-grep`, while`not-a-rule.yml` is ignored. **Note, the [`ast-grep scan`](/reference/cli.html#scan) command requires you have an `sgconfig.yml` in your project root.** :::tip Pro tip We can also use directories in `node_modules` to reuse preconfigured rules published on npm! More broadly speaking, any git hosted projects can be imported as rule sets by using [`git submodule`](https://www.git-scm.com/book/en/v2/Git-Tools-Submodules). ::: ## Project Discovery ast-grep will try to find the `sgconfig.yml` file in the current working directory. If it is not found, it will traverse up the directory tree until it finds one. You can also specify the path to the configuration file using the `--config` option. ```bash ast-grep scan --config path/to/config.yml ``` :::tip Global Configuration You can put an `sgconfig.yml` in your home directory to set global configurations for `ast-grep`. XDG configuration directory is **NOT** supported yet. ::: Project file discovery and `--config` option are also effective in the `ast-grep run` command. So you can use configurations like [custom languages](/reference/sgconfig.html#customlanguages) and [language globs](/reference/sgconfig.html#languageglobs). Note that `run` command does not require a `sgconfig.yml` file and will stil search code without it, but `scan` command will report an error if project config is not found. ## Project Inspection You can use the [`--inspect summary`](/reference/cli/scan.html#inspect-granularity) flag to see the project directory ast-grep is using. ```bash ast-grep scan --inspect summary ``` It will print the project directory and the configuration file path. ```bash sg: summary|project: isProject=true,projectDir=/path/to/project ``` Output format can be found in the [GitHub issue](https://github.com/ast-grep/ast-grep/issues/1574). --- # Source: https://ast-grep.github.io/advanced/prompting.md --- url: /advanced/prompting.md --- # Using ast-grep with AI Tools This guide outlines several existing methods for leveraging AI with ast-grep. :::warning Disclaimer The field of AI is constantly evolving. The approaches detailed here are for reference, and we encourage you to explore and discover the best ways to utilize ast-grep with emerging AI technologies. ::: ## Using ast-grep with Claude Code Skill This skill teaches Claude how to write and use ast-grep rules to perform advanced code searches. Unlike traditional text-based search (grep, ripgrep), ast-grep understands the structure of your code, allowing you to find patterns like: * "Find all async functions that don't have error handling" * "Locate all React components that use a specific hook" * "Find functions with more than 3 parameters" * "Search for console.log calls inside class methods" Clone or download the [ast-grep skill repository](https://github.com/ast-grep/claude-skill) to your Claude Code skills directory: ```bash # If you have a skills directory configured cp -r ast-grep ~/.claude/skills/ # Or place it wherever your Claude Code skills are located ``` The skill should be automatically detected by Claude Code. You can verify by checking available skills in Claude Code. ## Simple Prompting in `AGENTS.md` For everyday development, you can instruct your AI agent to use ast-grep for code searching and analysis. This method is straightforward but requires a model with up-to-date knowledge of ast-grep to be effective. If the model is not familiar with the tool, it may not utilize it as instructed. You can set a system-level prompt for your AI agent to prioritize ast-grep for syntax-aware searches. Here is an example prompt comes from [this social post](https://x.com/kieranklaassen/status/1938453871086682232). **Example Prompt:** > You are operating in an environment where `ast-grep` is installed. For any code search that requires understanding of syntax or code structure, you should default to using `ast-grep --lang [language] -p ''`. Adjust the `--lang` flag as needed for the specific programming language. Avoid using text-only search tools unless a plain-text search is explicitly requested. This approach is best suited for general code queries and explorations within your projects. ## Providing Comprehensive Context to LLMs Large Language Models (LLMs) with extensive context windows can be made highly effective at using ast-grep by providing them with its complete documentation. The `llms.txt` file for ast-grep is a compilation of the entire documentation, designed to be fed into an LLM's context. This method significantly reduces the likelihood of the model "hallucinating" or generating incorrect ast-grep rules by giving it a thorough and accurate knowledge base to draw from. You can find the full `llms.txt` file here: By loading this text into your session with a capable LLM, you can ask more complex questions and receive more accurate and nuanced answers regarding ast-grep's features and usage. ## Advanced Rule Development with MCP and Sub-agents For more sophisticated and dedicated code analysis tasks, you can use the ast-grep Model Context Protocol (MCP) server. The [ast-grep-mcp](https://github.com/ast-grep/ast-grep-mcp) is an experimental server that connects AI assistants, such as Cursor and Claude Code, with the powerful structural search capabilities of ast-grep. This allows the AI to interact with your codebase in a more structured and intelligent way, moving beyond simple text-based searches. The MCP server provides a set of tools that enable an AI to develop and refine ast-grep rules through a process of trial and error. This is particularly useful for creating complex rules that may require several iterations to perfect. The core of this approach is to have the AI follow a systematic process for rule development: ``` ## Rule Development Process 1. Break down the user's query into smaller parts. 2. Identify sub rules that can be used to match the code. 3. Combine the sub rules into a single rule using relational rules or composite rules. 4. if rule does not match example code, revise the rule by removing some sub rules and debugging unmatching parts. 5. Use ast-grep mcp tool to dump AST or dump pattern query 6. Use ast-grep mcp tool to test the rule against the example code snippet. ``` This iterative process allows the AI to "think" more like a human developer, refining its approach until the rule is correct. You can view a detailed prompt for this agentic rule development process in the `ast-grep-mcp` repository: . --- # Source: https://ast-grep.github.io/guide/api-usage/py-api.md --- url: /guide/api-usage/py-api.md --- # Python API ast-grep's Python API is powered by [PyO3](https://pyo3.rs/). You can write Python to programmatically inspect and change syntax trees. To try out ast-grep's Python API, you can use the [online colab notebook](https://colab.research.google.com/drive/1nVT6rQKRIPv0TsKpCv5uD-Zuw-lUC67A?usp=sharing). ## Installation ast-grep's Python library is distributed on PyPI. You can install it with pip. ```bash pip install ast-grep-py ``` ## Core Concepts The core concepts in ast-grep's Python API are: * `SgRoot`: a class to parse a string into a syntax tree * `SgNode`: a node in the syntax tree :::tip Make AST like a XML/HTML doc! Using ast-grep's API is like [web scraping](https://opensource.com/article/21/9/web-scraping-python-beautiful-soup) using [beautiful soup](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) or [pyquery](https://pyquery.readthedocs.io/en/latest/). You can use `SgNode` to traverse the syntax tree and collect information from the nodes. ::: A common workflow to use ast-grep's Python API is: 1. Parse a string into a syntax tree by using `SgRoot` 2. Get the root node of the syntax tree by calling `root.root()` 3. `find` relevant nodes by using patterns or rules 4. Collect information from the nodes **Example:** ```python{3-6} from ast_grep_py import SgRoot root = SgRoot("print('hello world')", "python") # 1. parse node = root.root() # 2. get root print_stmt = node.find(pattern="print($A)") # 3. find print_stmt.get_match('A').text() # 4. collect information # 'hello world' ``` ### `SgRoot` The `SgRoot` class has the following signature: ```python class SgRoot: def __init__(self, src: str, language: str) -> None: ... def root(self) -> SgNode: ... ``` `__init__` takes two arguments: the first argument is the source code string, and the second argument is the language name. `root` returns the root node of the syntax tree, which is an instance of `SgNode`. **Example:** ```python root = SgRoot("print('hello world')", "python") # 1. parse node = root.root() # 2. get root ``` The code above parses the string `print('hello world')` into a syntax tree, and gets the root node of the syntax tree. The root node can be used to find other nodes in the syntax tree. ### `SgNode` `SgNode` is the most important class in ast-grep's Python API. It provides methods to inspect and traverse the syntax tree. The following sections will introduce several methods in `SgNode`. **Example:** ```python node = root.root() string = node.find(kind="string") assert string # assume we can find a string node in the source print(string.text()) ``` ## Search You can use `find` and `find_all` to search for nodes in the syntax tree. * `find` returns the first node that matches the pattern or rule. * `find_all` returns a list of nodes that match the pattern or rule. ```python # Search class SgNode: @overload def find(self, **kwargs: Unpack[Rule]) -> Optional[SgNode]: ... @overload def find_all(self, **kwargs: Unpack[Rule]) -> List[SgNode]: ... @overload def find(self, config: Config) -> Optional[SgNode]: ... @overload def find_all(self, config: Config) -> List[SgNode]: ... ``` `find` has two overloads: one takes keyword arguments of [`Rule`](/reference/api.html#rule), and the other takes a [`Config`](/reference/api.html#config) object. ### Search with Rule Using keyword arguments rule is the most straightforward way to search for nodes. The argument name is the key of a rule, and the argument value is the rule's value. You can passing multiple keyword arguments to `find` to search for nodes that match **all** the rules. ```python root = SgRoot("print('hello world')", "python") node = root.root() node.find(pattern="print($A)") # will return the print function call node.find(kind="string") # will return the string 'hello world' # below will return print function call because it matches both rules node.find(pattern="print($A)", kind="call") # below will return None because the pattern cannot be a string literal node.find(pattern="print($A)", kind="string") strings = node.find_all(kind="string") # will return [SgNode("hello world")] assert len(strings) == 1 ``` ### Search with Config You can also use a `Config` object to search for nodes. This is similar to directly use YAML in the command line. The main difference between using `Config` and using `Rule` is that `Config` has more options to control the search behavior, like [`constraints`](/guide/rule-config.html#constraints) and [`utils`](/guide/rule-config/utility-rule.html). ```python # will find a string node with text 'hello world' root.root().find({ "rule": { "pattern": "print($A)", }, "constraints": { "A": { "regex": "hello" } } }) # will return None because constraints are not satisfied root.root().find({ "rule": { "pattern": "print($A)", }, "constraints": { "A": { "regex": "no match" } } }) ``` ## Match Once we find a node, we can use the following methods to get meta variables from the search. The `get_match` method returns the single node that matches the [single meta variable](/guide/pattern-syntax.html#meta-variable). And the `get_multiple_matches` returns a list of nodes that match the [multi meta variable](/guide/pattern-syntax.html#multi-meta-variable). ```python class SgNode: def get_match(self, meta_var: str) -> Optional[SgNode]: ... def get_multiple_matches(self, meta_var: str) -> List[SgNode]: ... def __getitem__(self, meta_var: str) -> SgNode: ... ``` **Example:** ```python{7,11,15,16} src = """ print('hello') logger('hello', 'world', '!') """ root = SgRoot(src, "python").root() node = root.find(pattern="print($A)") arg = node.get_match("A") # returns SgNode('hello') assert arg # assert node is found arg.text() # returns 'hello' # returns [] because $A and $$$A are different node.get_multiple_matches("A") logs = root.find(pattern="logger($$$ARGS)") # returns [SgNode('hello'), SgNode(','), SgNode('world'), SgNode(','), SgNode('!')] logs.get_multiple_matches("ARGS") logs.get_match("A") # returns None ``` `SgNode` also supports `__getitem__` to get the match of single meta variable. It is equivalent to `get_match` except that it will either return `SgNode` or raise a `KeyError` if the match is not found. Use `__getitem__` to avoid unnecessary `None` checks when you are using a type checker. ```python node = root.find(pattern="print($A)") # node.get_match("A").text() # error: node.get_match("A") can be None node["A"].text() # Ok ``` ## Inspection The following methods are used to inspect the node. ```python # Node Inspection class SgNode: def range(self) -> Range: ... def is_leaf(self) -> bool: ... def is_named(self) -> bool: ... def is_named_leaf(self) -> bool: ... def kind(self) -> str: ... def text(self) -> str: ... ``` **Example:** ```python root = SgRoot("print('hello world')", "python") node = root.root() node.text() # will return "print('hello world')" ``` Another important method is `range`, which returns two `Pos` object representing the start and end of the node. One `Pos` contains the line, column, and offset of that position. All of them are 0-indexed. You can use the range information to locate the source and modify the source code. ```python rng = node.range() pos = rng.start # or rng.end, both are `Pos` objects pos.line # 0, line starts with 0 pos.column # 0, column starts with 0 rng.end.index # 17, index starts with 0 ``` ## Refinement You can also filter nodes after matching by using the following methods. This is dubbed as "refinement" in the documentation. Note these refinement methods only support using `Rule`. ```python # Search Refinement class SgNode: def matches(self, **rule: Unpack[Rule]) -> bool: ... def inside(self, **rule: Unpack[Rule]) -> bool: ... def has(self, **rule: Unpack[Rule]) -> bool: ... def precedes(self, **rule: Unpack[Rule]) -> bool: ... def follows(self, **rule: Unpack[Rule]) -> bool: ... ``` **Example:** ```python node = root.find(pattern="print($A)") if node["A"].matches(kind="string"): print("A is a string") ``` ## Traversal You can traverse the tree using the following methods, like using pyquery. ```python # Tree Traversal class SgNode: def get_root(self) -> SgRoot: ... def field(self, name: str) -> Optional[SgNode]: ... def parent(self) -> Optional[SgNode]: ... def child(self, nth: int) -> Optional[SgNode]: ... def children(self) -> List[SgNode]: ... def ancestors(self) -> List[SgNode]: ... def next(self) -> Optional[SgNode]: ... def next_all(self) -> List[SgNode]: ... def prev(self) -> Optional[SgNode]: ... def prev_all(self) -> List[SgNode]: ... ``` ## Fix code `SgNode` is immutable so it is impossible to change the code directly. However, `SgNode` has a `replace` method to generate an `Edit` object. You can then use the `commitEdits` method to apply the changes and generate new source string. ```python class Edit: # The start position of the edit start_pos: int # The end position of the edit end_pos: int # The text to be inserted inserted_text: str class SgNode: # Edit def replace(self, new_text: str) -> Edit: ... def commit_edits(self, edits: List[Edit]) -> str: ... ``` **Example** ```python root = SgRoot("print('hello world')", "python").root() node = root.find(pattern="print($A)") edit = node.replace("logger.log('bye world')") new_src = node.commit_edits([edit]) # "logger.log('bye world')" ``` Note, `logger.log($A)` will not generate `logger.log('hello world')` in Python API unlike the CLI. This is because using the host language to generate the replacement string is more flexible. :::warning Metavariable will not be replaced in the `replace` method. You need to create a string using `get_match(var_name)` by using Python. ::: See also [ast-grep#1172](https://github.com/ast-grep/ast-grep/issues/1172) --- # Source: https://ast-grep.github.io/catalog/python.md --- url: /catalog/python.md --- # Python This page curates a list of example ast-grep rules to check and to rewrite Python code. ## Migrate OpenAI SDK * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiZGVmICRGVU5DKCQkJEFSR1MpOiAkJCRCT0RZIiwicmV3cml0ZSI6IiIsImNvbmZpZyI6InJ1bGU6XG4gIHBhdHRlcm46IGltcG9ydCBvcGVuYWlcbmZpeDogZnJvbSBvcGVuYWkgaW1wb3J0IENsaWVudFxuLS0tXG5ydWxlOlxuICBwYXR0ZXJuOiBvcGVuYWkuYXBpX2tleSA9ICRLRVlcbmZpeDogY2xpZW50ID0gQ2xpZW50KCRLRVkpXG4tLS1cbnJ1bGU6XG4gIHBhdHRlcm46IG9wZW5haS5Db21wbGV0aW9uLmNyZWF0ZSgkJCRBUkdTKVxuZml4OiB8LVxuICBjbGllbnQuY29tcGxldGlvbnMuY3JlYXRlKFxuICAgICQkJEFSR1NcbiAgKSIsInNvdXJjZSI6ImltcG9ydCBvc1xuaW1wb3J0IG9wZW5haVxuZnJvbSBmbGFzayBpbXBvcnQgRmxhc2ssIGpzb25pZnlcblxuYXBwID0gRmxhc2soX19uYW1lX18pXG5vcGVuYWkuYXBpX2tleSA9IG9zLmdldGVudihcIk9QRU5BSV9BUElfS0VZXCIpXG5cblxuQGFwcC5yb3V0ZShcIi9jaGF0XCIsIG1ldGhvZHM9KFwiUE9TVFwiKSlcbmRlZiBpbmRleCgpOlxuICAgIGFuaW1hbCA9IHJlcXVlc3QuZm9ybVtcImFuaW1hbFwiXVxuICAgIHJlc3BvbnNlID0gb3BlbmFpLkNvbXBsZXRpb24uY3JlYXRlKFxuICAgICAgICBtb2RlbD1cInRleHQtZGF2aW5jaS0wMDNcIixcbiAgICAgICAgcHJvbXB0PWdlbmVyYXRlX3Byb21wdChhbmltYWwpLFxuICAgICAgICB0ZW1wZXJhdHVyZT0wLjYsXG4gICAgKVxuICAgIHJldHVybiBqc29uaWZ5KHJlc3BvbnNlLmNob2ljZXMpIn0=) ### Description OpenAI has introduced some breaking changes in their API, such as using `Client` to initialize the service and renaming the `Completion` method to `completions` . This example shows how to use ast-grep to automatically update your code to the new API. API migration requires multiple related rules to work together. The example shows how to write [multiple rules](/reference/playground.html#test-multiple-rules) in a [single YAML](/guide/rewrite-code.html#using-fix-in-yaml-rule) file. The rules and patterns in the example are simple and self-explanatory, so we will not explain them further. ### YAML ```yaml id: import-openai language: python rule: pattern: import openai fix: from openai import Client --- id: rewrite-client language: python rule: pattern: openai.api_key = $KEY fix: client = Client($KEY) --- id: rewrite-chat-completion language: python rule: pattern: openai.Completion.create($$$ARGS) fix: |- client.completions.create( $$$ARGS ) ``` ### Example ```python {2,6,11-15} import os import openai from flask import Flask, jsonify app = Flask(__name__) openai.api_key = os.getenv("OPENAI_API_KEY") @app.route("/chat", methods=("POST")) def index(): animal = request.form["animal"] response = openai.Completion.create( model="text-davinci-003", prompt=generate_prompt(animal), temperature=0.6, ) return jsonify(response.choices) ``` ### Diff ```python import os import openai # [!code --] from openai import Client # [!code ++] from flask import Flask, jsonify app = Flask(__name__) openai.api_key = os.getenv("OPENAI_API_KEY") # [!code --] client = Client(os.getenv("OPENAI_API_KEY")) # [!code ++] @app.route("/chat", methods=("POST")) def index(): animal = request.form["animal"] response = openai.Completion.create( # [!code --] response = client.completions.create( # [!code ++] model="text-davinci-003", prompt=generate_prompt(animal), temperature=0.6, ) return jsonify(response.choices) ``` ### Contributed by [Herrington Darkholme](https://twitter.com/hd_nvim), inspired by [Morgante](https://twitter.com/morgantepell/status/1721668781246750952) from [grit.io](https://www.grit.io/) ## Prefer Generator Expressions * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiWyQkJEFdIiwicmV3cml0ZSI6IiRBPy4oKSIsImNvbmZpZyI6InJ1bGU6XG4gIHBhdHRlcm46ICRGVU5DKCRMSVNUKVxuY29uc3RyYWludHM6XG4gIExJU1Q6IHsga2luZDogbGlzdF9jb21wcmVoZW5zaW9uIH1cbiAgRlVOQzpcbiAgICBhbnk6XG4gICAgICAtIHBhdHRlcm46IGFueVxuICAgICAgLSBwYXR0ZXJuOiBhbGxcbiAgICAgIC0gcGF0dGVybjogc3VtXG4gICAgICAjIC4uLlxudHJhbnNmb3JtOlxuICBJTk5FUjpcbiAgICBzdWJzdHJpbmc6IHtzb3VyY2U6ICRMSVNULCBzdGFydENoYXI6IDEsIGVuZENoYXI6IC0xIH1cbmZpeDogJEZVTkMoJElOTkVSKSIsInNvdXJjZSI6ImFsbChbeCBmb3IgeCBpbiB5XSlcblt4IGZvciB4IGluIHldIn0=) ### Description List comprehensions like `[x for x in range(10)]` are a concise way to create lists in Python. However, we can achieve better memory efficiency by using generator expressions like `(x for x in range(10))` instead. List comprehensions create the entire list in memory, while generator expressions generate each element one at a time. We can make the change by replacing the square brackets with parentheses. ### YAML ```yaml id: prefer-generator-expressions language: python rule: pattern: $LIST kind: list_comprehension transform: INNER: substring: {source: $LIST, startChar: 1, endChar: -1 } fix: ($INNER) ``` This rule converts every list comprehension to a generator expression. However, **not every list comprehension can be replaced with a generator expression.** If the list is used multiple times, is modified, is sliced, or is indexed, a generator is not a suitable replacement. Some common functions like `any`, `all`, and `sum` take an `iterable` as an argument. A generator function counts as an `iterable`, so it is safe to change a list comprehension to a generator expression in this context. ```yaml id: prefer-generator-expressions language: python rule: pattern: $FUNC($LIST) constraints: LIST: { kind: list_comprehension } FUNC: any: - pattern: any - pattern: all - pattern: sum # ... transform: INNER: substring: {source: $LIST, startChar: 1, endChar: -1 } fix: $FUNC($INNER) ``` ### Example ```python any([x for x in range(10)]) ``` ### Diff ```python any([x for x in range(10)]) # [!code --] any(x for x in range(10)) # [!code ++] ``` ### Contributed by [Steven Love](https://github.com/StevenLove) ## Use Walrus Operator in `if` statement * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiZm4gbWFpbigpIHsgXG4gICAgJCQkO1xuICAgIGlmKCRBKXskJCRCfSBcbiAgICBpZigkQSl7JCQkQ30gXG4gICAgJCQkRlxufSIsInJld3JpdGUiOiJmbiBtYWluKCkgeyAkJCRFOyBpZigkQSl7JCQkQiAkJCRDfSAkJCRGfSIsImNvbmZpZyI6ImlkOiB1c2Utd2FscnVzLW9wZXJhdG9yXG5ydWxlOlxuICBmb2xsb3dzOlxuICAgIHBhdHRlcm46XG4gICAgICBjb250ZXh0OiAkVkFSID0gJCQkRVhQUlxuICAgICAgc2VsZWN0b3I6IGV4cHJlc3Npb25fc3RhdGVtZW50XG4gIHBhdHRlcm46IFwiaWYgJFZBUjogJCQkQlwiXG5maXg6IHwtXG4gIGlmICRWQVIgOj0gJCQkRVhQUjpcbiAgICAkJCRCXG4tLS1cbmlkOiByZW1vdmUtZGVjbGFyYXRpb25cbnJ1bGU6XG4gIHBhdHRlcm46XG4gICAgY29udGV4dDogJFZBUiA9ICQkJEVYUFJcbiAgICBzZWxlY3RvcjogZXhwcmVzc2lvbl9zdGF0ZW1lbnRcbiAgcHJlY2VkZXM6XG4gICAgcGF0dGVybjogXCJpZiAkVkFSOiAkJCRCXCJcbmZpeDogJyciLCJzb3VyY2UiOiJhID0gZm9vKClcblxuaWYgYTpcbiAgICBkb19iYXIoKSJ9) ### Description The walrus operator (`:=`) introduced in Python 3.8 allows you to assign values to variables as part of an expression. This rule aims to simplify code by using the walrus operator in `if` statements. This first part of the rule identifies cases where a variable is assigned a value and then immediately used in an `if` statement to control flow. ```yaml id: use-walrus-operator language: python rule: pattern: "if $VAR: $$$B" follows: pattern: context: $VAR = $$$EXPR selector: expression_statement fix: |- if $VAR := $$$EXPR: $$$B ``` The `pattern` clause finds an `if` statement that checks the truthiness of `$VAR`. If this pattern `follows` an expression statement where `$VAR` is assigned `$$$EXPR`, the `fix` clause changes the `if` statements to use the walrus operator. The second part of the rule: ```yaml id: remove-declaration rule: pattern: context: $VAR = $$$EXPR selector: expression_statement precedes: pattern: "if $VAR: $$$B" fix: '' ``` This rule removes the standalone variable assignment when it directly precedes an `if` statement that uses the walrus operator. Since the assignment is now part of the `if` statement, the separate declaration is no longer needed. By applying these rules, you can refactor your Python code to be more concise and readable, taking advantage of the walrus operator's ability to combine an assignment with an expression. ### YAML ```yaml id: use-walrus-operator language: python rule: follows: pattern: context: $VAR = $$$EXPR selector: expression_statement pattern: "if $VAR: $$$B" fix: |- if $VAR := $$$EXPR: $$$B --- id: remove-declaration language: python rule: pattern: context: $VAR = $$$EXPR selector: expression_statement precedes: pattern: "if $VAR: $$$B" fix: '' ``` ### Example ```python a = foo() if a: do_bar() ``` ### Diff ```python a = foo() # [!code --] if a: # [!code --] if a := foo(): # [!code ++] do_bar() ``` ### Contributed by Inspired by reddit user [/u/jackerhack](https://www.reddit.com/r/rust/comments/13eg738/comment/kagdklw/?) ## Remove `async` function * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiYXdhaXQgJCQkQ0FMTCIsInJld3JpdGUiOiIkJCRDQUxMICIsImNvbmZpZyI6ImlkOiByZW1vdmUtYXN5bmMtZGVmXG5sYW5ndWFnZTogcHl0aG9uXG5ydWxlOlxuICBwYXR0ZXJuOlxuICAgIGNvbnRleHQ6ICdhc3luYyBkZWYgJEZVTkMoJCQkQVJHUyk6ICQkJEJPRFknXG4gICAgc2VsZWN0b3I6IGZ1bmN0aW9uX2RlZmluaXRpb25cbnRyYW5zZm9ybTpcbiAgUkVNT1ZFRF9CT0RZOlxuICAgIHJld3JpdGU6XG4gICAgICByZXdyaXRlcnM6IFtyZW1vdmUtYXdhaXQtY2FsbF1cbiAgICAgIHNvdXJjZTogJCQkQk9EWVxuZml4OiB8LVxuICBkZWYgJEZVTkMoJCQkQVJHUyk6XG4gICAgJFJFTU9WRURfQk9EWVxucmV3cml0ZXJzOlxuLSBpZDogcmVtb3ZlLWF3YWl0LWNhbGxcbiAgcnVsZTpcbiAgICBwYXR0ZXJuOiAnYXdhaXQgJCQkQ0FMTCdcbiAgZml4OiAkJCRDQUxMXG4iLCJzb3VyY2UiOiJhc3luYyBkZWYgbWFpbjMoKTpcbiAgYXdhaXQgc29tZWNhbGwoMSwgNSkifQ==) ### Description The `async` keyword in Python is used to define asynchronous functions that can be `await`ed. In this example, we want to remove the `async` keyword from a function definition and replace it with a synchronous version of the function. We also need to remove the `await` keyword from the function body. By default, ast-grep will not apply overlapping replacements. This means `await` keywords will not be modified because they are inside the async function body. However, we can use the [`rewriter`](https://ast-grep.github.io/reference/yaml/rewriter.html) to apply changes inside the matched function body. ### YAML ```yaml id: remove-async-def language: python rule: # match async function definition pattern: context: 'async def $FUNC($$$ARGS): $$$BODY' selector: function_definition rewriters: # define a rewriter to remove the await keyword remove-await-call: pattern: 'await $$$CALL' fix: $$$CALL # remove await keyword # apply the rewriter to the function body transform: REMOVED_BODY: rewrite: rewriters: [remove-await-call] source: $$$BODY fix: |- def $FUNC($$$ARGS): $REMOVED_BODY ``` ### Example ```python async def main3(): await somecall(1, 5) ``` ### Diff ```python async def main3(): # [!code --] await somecall(1, 5) # [!code --] def main3(): # [!code ++] somecall(1, 5) # [!code ++] ``` ### Contributed by Inspired by the ast-grep issue [#1185](https://github.com/ast-grep/ast-grep/issues/1185) ## Refactor pytest fixtures * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiZGVmIGZvbygkWCk6XG4gICRTIiwicmV3cml0ZSI6ImxvZ2dlci5sb2coJE1BVENIKSIsImNvbmZpZyI6ImlkOiBweXRlc3QtdHlwZS1oaW50LWZpeHR1cmVcbmxhbmd1YWdlOiBQeXRob25cbnV0aWxzOlxuICBpcy1maXh0dXJlLWZ1bmN0aW9uOlxuICAgIGtpbmQ6IGZ1bmN0aW9uX2RlZmluaXRpb25cbiAgICBmb2xsb3dzOlxuICAgICAga2luZDogZGVjb3JhdG9yXG4gICAgICBoYXM6XG4gICAgICAgIGtpbmQ6IGlkZW50aWZpZXJcbiAgICAgICAgcmVnZXg6IF5maXh0dXJlJFxuICAgICAgICBzdG9wQnk6IGVuZFxuICBpcy10ZXN0LWZ1bmN0aW9uOlxuICAgIGtpbmQ6IGZ1bmN0aW9uX2RlZmluaXRpb25cbiAgICBoYXM6XG4gICAgICBmaWVsZDogbmFtZVxuICAgICAgcmVnZXg6IF50ZXN0X1xuICBpcy1weXRlc3QtY29udGV4dDpcbiAgICAjIFB5dGVzdCBjb250ZXh0IGlzIGEgbm9kZSBpbnNpZGUgYSBweXRlc3RcbiAgICAjIHRlc3QvZml4dHVyZVxuICAgIGluc2lkZTpcbiAgICAgIHN0b3BCeTogZW5kXG4gICAgICBhbnk6XG4gICAgICAgIC0gbWF0Y2hlczogaXMtZml4dHVyZS1mdW5jdGlvblxuICAgICAgICAtIG1hdGNoZXM6IGlzLXRlc3QtZnVuY3Rpb25cbiAgaXMtZml4dHVyZS1hcmc6XG4gICAgIyBGaXh0dXJlIGFyZ3VtZW50cyBhcmUgaWRlbnRpZmllcnMgaW5zaWRlIHRoZSBcbiAgICAjIHBhcmFtZXRlcnMgb2YgYSB0ZXN0L2ZpeHR1cmUgZnVuY3Rpb25cbiAgICBhbGw6XG4gICAgICAtIGtpbmQ6IGlkZW50aWZpZXJcbiAgICAgIC0gbWF0Y2hlczogaXMtcHl0ZXN0LWNvbnRleHRcbiAgICAgIC0gaW5zaWRlOlxuICAgICAgICAgIGtpbmQ6IHBhcmFtZXRlcnNcbnJ1bGU6XG4gIG1hdGNoZXM6IGlzLWZpeHR1cmUtYXJnXG4gIHJlZ2V4OiBeZm9vJFxuZml4OiAnZm9vOiBpbnQnXG4iLCJzb3VyY2UiOiJmcm9tIGNvbGxlY3Rpb25zLmFiYyBpbXBvcnQgSXRlcmFibGVcbmZyb20gdHlwaW5nIGltcG9ydCBBbnlcblxuaW1wb3J0IHB5dGVzdFxuZnJvbSBweXRlc3QgaW1wb3J0IGZpeHR1cmVcblxuQHB5dGVzdC5maXh0dXJlKHNjb3BlPVwic2Vzc2lvblwiKVxuZGVmIGZvbygpIC0+IEl0ZXJhYmxlW2ludF06XG4gICAgeWllbGQgNVxuXG5AZml4dHVyZVxuZGVmIGJhcihmb28pIC0+IHN0cjpcbiAgICByZXR1cm4gc3RyKGZvbylcblxuZGVmIHJlZ3VsYXJfZnVuY3Rpb24oZm9vKSAtPiBOb25lOlxuICAgICMgVGhpcyBmdW5jdGlvbiBkb2Vzbid0IHVzZSB0aGUgJ2ZvbycgZml4dHVyZVxuICAgIHByaW50KGZvbylcblxuZGVmIHRlc3RfMShmb28sIGJhcik6XG4gICAgcHJpbnQoZm9vLCBiYXIpXG5cbmRlZiB0ZXN0XzIoYmFyKTpcbiAgICAuLi4ifQ==) ### Description One of the most commonly used testing framework in Python is [pytest](https://docs.pytest.org/en/8.2.x/). Among other things, it allows the use of [fixtures](https://docs.pytest.org/en/6.2.x/fixture.html). Fixtures are defined as functions that can be required in test code, or in other fixtures, as an argument. This means that all functions arguments with a given name in a pytest context (test function or fixture) are essentially the same entity. However, not every editor's LSP is able to keep track of this, making refactoring challenging. Using ast-grep, we can define some rules to match fixture definition and usage without catching similarly named entities in a non-test context. First, we define utils to select pytest test/fixture functions. ```yaml utils: is-fixture-function: kind: function_definition follows: kind: decorator has: kind: identifier regex: ^fixture$ stopBy: end is-test-function: kind: function_definition has: field: name regex: ^test_ ``` Pytest fixtures are declared with a decorator `@pytest.fixture`. We match the `function_definition` node that directly follows a `decorator` node. That decorator node must have a `fixture` identifier somewhere. This accounts for different location of the `fixture` node depending on the type of imports and whether the decorator is used as is or called with parameters. Pytest functions are fairly straightforward to detect, as they always start with `test_` by convention. The next utils builds onto those two to incrementally: * Find if a node is inside a pytest context (test/fixture) * Find if a node is an argument in such a context ```yaml utils: is-pytest-context: # Pytest context is a node inside a pytest # test/fixture inside: stopBy: end any: - matches: is-fixture-function - matches: is-test-function is-fixture-arg: # Fixture arguments are identifiers inside the # parameters of a test/fixture function all: - kind: identifier - inside: kind: parameters - matches: is-pytest-context ``` Once those utils are declared, you can perform various refactoring on a specific fixture. The following rule adds a type-hint to a fixture. ```yaml rule: matches: is-fixture-arg regex: ^foo$ fix: 'foo: int' ``` This one renames a fixture and all its references. ```yaml rule: kind: identifier matches: is-fixture-context regex: ^foo$ fix: 'five' ``` ### Example #### Renaming Fixtures ```python {2,6,7,12,13} @pytest.fixture def foo() -> int: return 5 @pytest.fixture(scope="function") def some_fixture(foo: int) -> str: return str(foo) def regular_function(foo) -> None: ... def test_code(foo: int) -> None: assert foo == 5 ``` #### Diff ```python {2,6,7,12} @pytest.fixture def foo() -> int: # [!code --] def five() -> int: # [!code ++] return 5 @pytest.fixture(scope="function") def some_fixture(foo: int) -> str: # [!code --] def some_fixture(five: int) -> str: # [!code ++] return str(foo) def regular_function(foo) -> None: ... def test_code(foo: int) -> None: # [!code --] def test_code(five: int) -> None: # [!code ++] assert foo == 5 # [!code --] assert five == 5 # [!code ++] ``` #### Type Hinting Fixtures ```python {6,12} @pytest.fixture def foo() -> int: return 5 @pytest.fixture(scope="function") def some_fixture(foo) -> str: return str(foo) def regular_function(foo) -> None: ... def test_code(foo) -> None: assert foo == 5 ``` #### Diff ```python {2,6,7,12} @pytest.fixture def foo() -> int: return 5 @pytest.fixture(scope="function") def some_fixture(foo) -> str: # [!code --] def some_fixture(foo: int) -> str: # [!code ++] return str(foo) def regular_function(foo) -> None: ... def test_code(foo) -> None: # [!code --] def test_code(foo: int) -> None: # [!code ++] assert foo == 5 ``` ## Rewrite `Optional[Type]` to `Type | None` * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiIiwicmV3cml0ZSI6IiIsInN0cmljdG5lc3MiOiJzaWduYXR1cmUiLCJzZWxlY3RvciI6IiIsImNvbmZpZyI6InJ1bGU6XG4gIHBhdHRlcm46IFxuICAgIGNvbnRleHQ6ICdhOiBPcHRpb25hbFskVF0nXG4gICAgc2VsZWN0b3I6IGdlbmVyaWNfdHlwZVxuZml4OiAkVCB8IE5vbmUiLCJzb3VyY2UiOiJkZWYgYShhcmc6IE9wdGlvbmFsW0ludF0pOiBwYXNzIn0=) ### Description [PEP 604](https://peps.python.org/pep-0604/) recommends that `Type | None` is preferred over `Optional[Type]` for Python 3.10+. This rule performs such rewriting. Note `Optional[$T]` alone is interpreted as subscripting expression instead of generic type, we need to use [pattern object](/guide/rule-config/atomic-rule.html#pattern-object) to disambiguate it with more context code. ### YAML ```yaml id: optional-to-none-union language: python rule: pattern: context: 'a: Optional[$T]' selector: generic_type fix: $T | None ``` ### Example ```py {1} def a(arg: Optional[int]): pass ``` ### Diff ```py def a(arg: Optional[int]): pass # [!code --] def a(arg: int | None): pass # [!code ++] ``` ### Contributed by [Bede Carroll](https://github.com/ast-grep/ast-grep/discussions/1492) ## Recursive Rewrite Type * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiIiwicmV3cml0ZSI6IiIsInN0cmljdG5lc3MiOiJzbWFydCIsInNlbGVjdG9yIjoiIiwiY29uZmlnIjoicmV3cml0ZXJzOlxyXG4tIGlkOiBvcHRpb25hbFxyXG4gIGxhbmd1YWdlOiBQeXRob25cclxuICBydWxlOlxyXG4gICAgYW55OlxyXG4gICAgLSBwYXR0ZXJuOlxyXG4gICAgICAgIGNvbnRleHQ6ICdhcmc6IE9wdGlvbmFsWyRUWVBFXSdcclxuICAgICAgICBzZWxlY3RvcjogZ2VuZXJpY190eXBlXHJcbiAgICAtIHBhdHRlcm46IE9wdGlvbmFsWyRUWVBFXVxyXG4gIHRyYW5zZm9ybTpcclxuICAgIE5UOlxyXG4gICAgICByZXdyaXRlOiBcclxuICAgICAgICByZXdyaXRlcnM6IFtvcHRpb25hbCwgdW5pb25zXVxyXG4gICAgICAgIHNvdXJjZTogJFRZUEVcclxuICBmaXg6ICROVCB8IE5vbmVcclxuLSBpZDogdW5pb25zXHJcbiAgbGFuZ3VhZ2U6IFB5dGhvblxyXG4gIHJ1bGU6XHJcbiAgICBwYXR0ZXJuOlxyXG4gICAgICBjb250ZXh0OiAnYTogVW5pb25bJCQkVFlQRVNdJ1xyXG4gICAgICBzZWxlY3RvcjogZ2VuZXJpY190eXBlXHJcbiAgdHJhbnNmb3JtOlxyXG4gICAgVU5JT05TOlxyXG4gICAgICByZXdyaXRlOlxyXG4gICAgICAgIHJld3JpdGVyczpcclxuICAgICAgICAgIC0gcmV3cml0ZS11bmlvbnNcclxuICAgICAgICBzb3VyY2U6ICQkJFRZUEVTXHJcbiAgICAgICAgam9pbkJ5OiBcIiB8IFwiXHJcbiAgZml4OiAkVU5JT05TXHJcbi0gaWQ6IHJld3JpdGUtdW5pb25zXHJcbiAgcnVsZTpcclxuICAgIHBhdHRlcm46ICRUWVBFXHJcbiAgICBraW5kOiB0eXBlXHJcbiAgdHJhbnNmb3JtOlxyXG4gICAgTlQ6XHJcbiAgICAgIHJld3JpdGU6IFxyXG4gICAgICAgIHJld3JpdGVyczogW29wdGlvbmFsLCB1bmlvbnNdXHJcbiAgICAgICAgc291cmNlOiAkVFlQRVxyXG4gIGZpeDogJE5UXHJcbnJ1bGU6XHJcbiAga2luZDogdHlwZVxyXG4gIHBhdHRlcm46ICRUUEVcclxudHJhbnNmb3JtOlxyXG4gIE5FV19UWVBFOlxyXG4gICAgcmV3cml0ZTogXHJcbiAgICAgIHJld3JpdGVyczogW29wdGlvbmFsLCB1bmlvbnNdXHJcbiAgICAgIHNvdXJjZTogJFRQRVxyXG5maXg6ICRORVdfVFlQRSIsInNvdXJjZSI6InJlc3VsdHM6ICBPcHRpb25hbFtVbmlvbltMaXN0W1VuaW9uW3N0ciwgZGljdF1dLCBzdHJdXVxuIn0=) ### Description Suppose we want to transform Python's `Union[T1, T2]` to `T1 | T2` and `Optional[T]` to `T | None`. By default, ast-grep will only fix the outermost node that matches a pattern and will not rewrite the inner AST nodes inside a match. This avoids unexpected rewriting or infinite rewriting loop. So if you are using non-recursive rewriter like [this](https://github.com/ast-grep/ast-grep/discussions/1566#discussion-7401382), `Optional[Union[int, str]]` will only be converted to `Union[int, str] | None`. Note the inner `Union[int, str]` is not enabled. This is because the rewriter `optional` matches `Optional[$TYPE]` and rewrite it to `$TYPE | None`. The inner `$TYPE` is not processed. However, we can apply `rewriters` to inner types recursively. Take the `optional` rewriter as an example, we need to apply rewriters, `optional` and `unions`, **recursively** to `$TYPE` and get a new variable `$NT`. ### YAML ```yml id: recursive-rewrite-types language: python rewriters: # rewrite Optional[T] to T | None - id: optional rule: any: - pattern: context: 'arg: Optional[$TYPE]' selector: generic_type - pattern: Optional[$TYPE] # recursively apply rewriters to $TYPE transform: NT: rewrite: rewriters: [optional, unions] source: $TYPE # use the new variable $NT fix: $NT | None # similar to Optional, rewrite Union[T1, T2] to T1 | T2 - id: unions language: Python rule: pattern: context: 'a: Union[$$$TYPES]' selector: generic_type transform: UNIONS: # rewrite all types inside $$$TYPES rewrite: rewriters: [ rewrite-unions ] source: $$$TYPES joinBy: " | " fix: $UNIONS - id: rewrite-unions rule: pattern: $TYPE kind: type # recursive part transform: NT: rewrite: rewriters: [optional, unions] source: $TYPE fix: $NT # find all types rule: kind: type pattern: $TPE # apply the recursive rewriters transform: NEW_TYPE: rewrite: rewriters: [optional, unions] source: $TPE # output fix: $NEW_TYPE ``` ### Example ```python results: Optional[Union[List[Union[str, dict]], str]] ``` ### Diff ```python results: Optional[Union[List[Union[str, dict]], str]] # [!code --] results: List[str | dict] | str | None #[!code ++] ``` ### Contributed by Inspired by [steinuil](https://github.com/ast-grep/ast-grep/discussions/1566) ## Rewrite SQLAlchemy with Type Annotations * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiYShudWxsYWJsZT1UcnVlKSIsInJld3JpdGUiOiIxMjMiLCJzdHJpY3RuZXNzIjoic21hcnQiLCJzZWxlY3RvciI6ImtleXdvcmRfYXJndW1lbnQiLCJjb25maWciOiJpZDogcmVtb3ZlLW51bGxhYmxlLWFyZ1xubGFuZ3VhZ2U6IHB5dGhvblxucnVsZTpcbiAgcGF0dGVybjogJFggPSBtYXBwZWRfY29sdW1uKCQkJEFSR1MpXG4gIGFueTpcbiAgICAtIHBhdHRlcm46ICRYID0gbWFwcGVkX2NvbHVtbigkJCRCRUZPUkUsIFN0cmluZywgJCQkTUlELCBudWxsYWJsZT1UcnVlLCAkJCRBRlRFUilcbiAgICAtIHBhdHRlcm46ICRYID0gbWFwcGVkX2NvbHVtbigkJCRCRUZPUkUsIFN0cmluZywgJCQkTUlELCBudWxsYWJsZT1UcnVlKVxucmV3cml0ZXJzOlxuLSBpZDogZmlsdGVyLXN0cmluZy1udWxsYWJsZVxuICBydWxlOlxuICAgIHBhdHRlcm46ICRBUkdcbiAgICBpbnNpZGU6XG4gICAgICBraW5kOiBhcmd1bWVudF9saXN0XG4gICAgYWxsOlxuICAgIC0gbm90OiBcbiAgICAgICAgcGF0dGVybjogU3RyaW5nXG4gICAgLSBub3Q6XG4gICAgICAgIHBhdHRlcm46XG4gICAgICAgICAgY29udGV4dDogYShudWxsYWJsZT1UcnVlKVxuICAgICAgICAgIHNlbGVjdG9yOiBrZXl3b3JkX2FyZ3VtZW50XG4gIGZpeDogJEFSR1xuXG50cmFuc2Zvcm06XG4gIE5FV0FSR1M6XG4gICAgcmV3cml0ZTpcbiAgICAgIHJld3JpdGVyczogW2ZpbHRlci1zdHJpbmctbnVsbGFibGVdXG4gICAgICBzb3VyY2U6ICQkJEFSR1NcbiAgICAgIGpvaW5CeTogJywgJ1xuZml4OiB8LVxuICAkWDogTWFwcGVkW3N0ciB8IE5vbmVdID0gbWFwcGVkX2NvbHVtbigkTkVXQVJHUykiLCJzb3VyY2UiOiJtZXNzYWdlID0gbWFwcGVkX2NvbHVtbihTdHJpbmcsIGRlZmF1bHQ9XCJoZWxsb1wiLCBudWxsYWJsZT1UcnVlKVxuXG5tZXNzYWdlID0gbWFwcGVkX2NvbHVtbihTdHJpbmcsIG51bGxhYmxlPVRydWUpXG5cbl9tZXNzYWdlID0gbWFwcGVkX2NvbHVtbihcIm1lc3NhZ2VcIiwgU3RyaW5nLCBudWxsYWJsZT1UcnVlKVxuXG5tZXNzYWdlID0gbWFwcGVkX2NvbHVtbihTdHJpbmcsIG51bGxhYmxlPVRydWUsIHVuaXF1ZT1UcnVlKVxuXG5tZXNzYWdlID0gbWFwcGVkX2NvbHVtbihcbiAgU3RyaW5nLCBpbmRleD1UcnVlLCBudWxsYWJsZT1UcnVlLCB1bmlxdWU9VHJ1ZSlcblxuIyBTaG91bGQgbm90IGJlIHRyYW5zZm9ybWVkXG5tZXNzYWdlID0gbWFwcGVkX2NvbHVtbihTdHJpbmcsIGRlZmF1bHQ9XCJoZWxsb1wiKVxuXG5tZXNzYWdlID0gbWFwcGVkX2NvbHVtbihTdHJpbmcsIGRlZmF1bHQ9XCJoZWxsb1wiLCBudWxsYWJsZT1GYWxzZSlcblxubWVzc2FnZSA9IG1hcHBlZF9jb2x1bW4oSW50ZWdlciwgZGVmYXVsdD1cImhlbGxvXCIpIn0=) ### Description [SQLAlchemy 2.0](https://docs.sqlalchemy.org/en/20/orm/declarative_tables.html) recommends using type annotations with `Mapped` type for modern declarative mapping. The `mapped_column()` construct can derive its configuration from [PEP 484](https://peps.python.org/pep-0484/) type annotations. This rule helps migrate legacy SQLAlchemy code that explicitly uses `String` type and `nullable=True` to the modern type annotation approach using `Mapped[str | None]`. The key technique demonstrated here is using **rewriters** to selectively filter arguments. The rewriter: 1. Matches each argument inside the `argument_list` 2. Excludes the `String` type argument 3. Excludes the `nullable=True` keyword argument 4. Keeps all other arguments ### YAML ```yaml id: remove-nullable-arg language: python rule: pattern: $X = mapped_column($$$ARGS) any: - pattern: $X = mapped_column($$$BEFORE, String, $$$MID, nullable=True, $$$AFTER) - pattern: $X = mapped_column($$$BEFORE, String, $$$MID, nullable=True) rewriters: - id: filter-string-nullable rule: pattern: $ARG inside: kind: argument_list all: - not: pattern: String - not: pattern: context: a(nullable=True) selector: keyword_argument fix: $ARG transform: NEWARGS: rewrite: rewriters: [filter-string-nullable] source: $$$ARGS joinBy: ', ' fix: |- $X: Mapped[str | None] = mapped_column($NEWARGS) ``` ### Example ```python {1,3,5,7-8} message = mapped_column(String, default="hello", nullable=True) message = mapped_column(String, nullable=True) _message = mapped_column("message", String, nullable=True) message = mapped_column(String, nullable=True, unique=True) message = mapped_column( String, index=True, nullable=True, unique=True) # Should not be transformed message = mapped_column(String, default="hello") message = mapped_column(String, default="hello", nullable=False) message = mapped_column(Integer, default="hello") ``` ### Diff ```python message = mapped_column(String, default="hello", nullable=True) # [!code --] message: Mapped[str | None] = mapped_column(default="hello") # [!code ++] message = mapped_column(String, nullable=True) # [!code --] message: Mapped[str | None] = mapped_column() # [!code ++] _message = mapped_column("message", String, nullable=True) # [!code --] _message: Mapped[str | None] = mapped_column("message") # [!code ++] message = mapped_column(String, nullable=True, unique=True) # [!code --] message: Mapped[str | None] = mapped_column(unique=True) # [!code ++] message = mapped_column( # [!code --] String, index=True, nullable=True, unique=True) # [!code --] message: Mapped[str | None] = mapped_column( # [!code ++] index=True, unique=True) # [!code ++] ``` ### Contributed by Inspired by [discussion #2319](https://github.com/ast-grep/ast-grep/discussions/2319) --- # Source: https://ast-grep.github.io/guide/quick-start.md --- url: /guide/quick-start.md description: >- Learn how to install ast-grep and use it to quickly find and refactor code in your codebase. This powerful tool can help you save time and improve the quality of your code. --- # Quick Start You can unleash `ast-grep`'s power at your fingertips in a few keystrokes on the command line! Let's see it in action by rewriting code in a moderately large codebase: [TypeScript](https://github.com/microsoft/TypeScript/). Our task is to rewrite old defensive code that checks nullable nested method calls to use the shiny new [optional chaining operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) `?.`. ## Installation First, install `ast-grep`. It is distributed by [npm](https://www.npmjs.com/package/@ast-grep/cli), [cargo](https://crates.io/crates/ast-grep), [homebrew](https://formulae.brew.sh/formula/ast-grep) and [macports](https://ports.macports.org/port/ast-grep/). You can also build it [from source](https://github.com/ast-grep/ast-grep#installation). ::: code-group ```shell [homebrew] # install via homebrew brew install ast-grep ``` ```shell [macports] # install via MacPorts sudo port install ast-grep ``` ```shell [nix-shell] # try ast-grep in nix-shell nix-shell -p ast-grep ``` ```shell [cargo] # install via cargo cargo install ast-grep --locked ``` ```shell [npm] # install via npm npm i @ast-grep/cli -g ``` ```shell [pip] # install via pip pip install ast-grep-cli ``` ::: The binary command, `ast-grep` or `sg`, should be available now. Let's try it with `--help`. ```shell ast-grep --help # if you are not on Linux sg --help ``` :::danger Use `sg` on Linux Linux has a default command `sg` for `setgroups`. You can use the full command name `ast-grep` instead of `sg`. You can also use shorter alias if you want by `alias sg=ast-grep`. We will use `ast-grep` in the guide below. ::: Optionally, you can grab TypeScript source code if you want to follow the tutorial. Or you can apply the magic to your own code. ```shell git clone git@github.com:microsoft/TypeScript.git --depth 1 ``` ## Pattern Let's search for instances of calling a method on a nested property. `ast-grep` uses **patterns** to find similar code. Think of patterns like those in our old friend `grep`, but instead of text, they match AST nodes. We can write patterns like we write ordinary code, and it will match all code that has the same syntactical structure. For example, the following pattern code ```javascript obj.val && obj.val() ``` will match all the following code, regardless of white spaces or new lines. ```javascript obj.val && obj.val() // verbatim match, of course obj.val && obj.val() // this matches, too // this matches as well! const result = obj.val && obj.val() ``` Exact AST matching is already powerful, but we can go further with **metavariables** for more flexibility. Use a **metavariable** to match any single AST node. Metavariables begin with `$` and are typically uppercase (e.g., `$PROP`). Think of it like the regex dot `.`, except it matches syntax nodes, not text. We can use the following pattern to find all property checking code. ```javascript $PROP && $PROP() ``` This is a valid `ast-grep` pattern you can run from the command line. The `--pattern` argument specifies the target. Optionally, use `--lang` to specify the target language. :::code-group ```shell [Full Command] ast-grep --pattern '$PROP && $PROP()' --lang ts TypeScript/src ``` ```shell [Short Form] ast-grep -p '$PROP && $PROP()' -l ts TypeScript/src ``` ```shell [Without Lang] # ast-grep will infer languages based on file extensions ast-grep -p '$PROP && $PROP()' TypeScript/src ``` ::: :::tip Pro Tip The pattern must be enclosed in single quotes `'` to prevent the shell from interpreting the `$` sign. `ast-grep -p '$PROP && $PROP()'` is okay. With double quotes, `ast-grep -p "$PROP && $PROP()"` would be interpreted as `ast-grep -p " && ()"` after shell expansion. ::: ## Rewrite Cool? Now we can use this pattern to refactor the TypeScript source! ```shell # pattern and language argument support short form ast-grep -p '$PROP && $PROP()' \ --rewrite '$PROP?.()' \ --interactive \ -l ts \ TypeScript/src ``` `ast-grep` will start an interactive session to let you choose if you want to apply the patch. Press `y` to accept the change! That's it! You have refactored the TypeScript source in minutes. Congratulations! We hope you enjoy the power of AST editing with plain programming-language patterns. Next, learn more about writing patterns. :::tip Pattern does not work? See our FAQ for more [guidance](/advanced/faq.html) on writing patterns. ::: --- # Source: https://ast-grep.github.io/catalog/rust/redundant-unsafe-function.md --- url: /catalog/rust/redundant-unsafe-function.md --- ## Unsafe Function Without Unsafe Block * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InJ1c3QiLCJxdWVyeSI6IntcbiAgZGVzY3JpcHRpb24gPSAkQVxufSIsInJld3JpdGUiOiIiLCJzdHJpY3RuZXNzIjoic21hcnQiLCJzZWxlY3RvciI6ImJpbmRpbmciLCJjb25maWciOiIgIGlkOiByZWR1bmRhbnQtdW5zYWZlLWZ1bmN0aW9uXG4gIGxhbmd1YWdlOiBydXN0XG4gIHNldmVyaXR5OiBlcnJvclxuICBtZXNzYWdlOiBVbnNhZmUgZnVuY3Rpb24gd2l0aG91dCB1bnNhZmUgYmxvY2sgaW5zaWRlXG4gIG5vdGU6IHxcbiAgICBDb25zaWRlciB3aGV0aGVyIHRoaXMgZnVuY3Rpb24gbmVlZHMgdG8gYmUgbWFya2VkIHVuc2FmZSBcbiAgICBvciBpZiB1bnNhZmUgb3BlcmF0aW9ucyBzaG91bGQgYmUgd3JhcHBlZCBpbiBhbiB1bnNhZmUgYmxvY2tcbiAgcnVsZTpcbiAgICBhbGw6XG4gICAgICAtIGtpbmQ6IGZ1bmN0aW9uX2l0ZW1cbiAgICAgIC0gaGFzOlxuICAgICAgICAgIGtpbmQ6IGZ1bmN0aW9uX21vZGlmaWVyc1xuICAgICAgICAgIHJlZ2V4OiBcIl51bnNhZmVcIlxuICAgICAgLSBub3Q6XG4gICAgICAgICAgaGFzOlxuICAgICAgICAgICAga2luZDogdW5zYWZlX2Jsb2NrXG4gICAgICAgICAgICBzdG9wQnk6IGVuZCIsInNvdXJjZSI6IiAgLy8gU2hvdWxkIG1hdGNoIC0gdW5zYWZlIGZ1bmN0aW9uIHdpdGhvdXQgdW5zYWZlIGJsb2NrIChubyByZXR1cm4gdHlwZSlcbiAgdW5zYWZlIGZuIHJlZHVuZGFudF91bnNhZmUoKSB7XG4gICAgICBwcmludGxuIShcIk5vIHVuc2FmZSBvcGVyYXRpb25zIGhlcmVcIik7XG4gIH1cblxuICAvLyBTaG91bGQgbWF0Y2ggLSB1bnNhZmUgZnVuY3Rpb24gd2l0aCByZXR1cm4gdHlwZSwgbm8gdW5zYWZlIGJsb2NrXG4gIHVuc2FmZSBmbiByZWR1bmRhbnRfd2l0aF9yZXR1cm4oKSAtPiBpMzIge1xuICAgICAgbGV0IHggPSA1O1xuICAgICAgeCArIDEwXG4gIH1cblxuICAvLyBTaG91bGQgbWF0Y2ggLSB1bnNhZmUgZnVuY3Rpb24gd2l0aCBjb21wbGV4IHJldHVybiB0eXBlXG4gIHVuc2FmZSBmbiByZWR1bmRhbnRfY29tcGxleF9yZXR1cm4oKSAtPiBSZXN1bHQ8U3RyaW5nLCBzdGQ6OmlvOjpFcnJvcj4ge1xuICAgICAgT2soU3RyaW5nOjpmcm9tKFwic2FmZSBvcGVyYXRpb25cIikpXG4gIH1cblxuICAvLyBTaG91bGQgTk9UIG1hdGNoIC0gdW5zYWZlIGZ1bmN0aW9uIHdpdGggdW5zYWZlIGJsb2NrXG4gIHVuc2FmZSBmbiBwcm9wZXJfdW5zYWZlKCkgLT4gKmNvbnN0IGkzMiB7XG4gICAgICB1bnNhZmUge1xuICAgICAgICAgIGxldCBwdHIgPSAweDEyMzQgYXMgKmNvbnN0IGkzMjtcbiAgICAgICAgICBwdHJcbiAgICAgIH1cbiAgfVxuXG4gIC8vIFNob3VsZCBtYXRjaCAtIHVuc2FmZSBhc3luYyBmdW5jdGlvbiB3aXRob3V0IHVuc2FmZSBibG9ja1xuICB1bnNhZmUgYXN5bmMgZm4gYXN5bmNfcmVkdW5kYW50KCkgLT4gaTMyIHtcbiAgICAgIDQyXG4gIH1cblxuICAvLyBTaG91bGQgbWF0Y2ggLSB1bnNhZmUgY29uc3QgZnVuY3Rpb25cbiAgdW5zYWZlIGNvbnN0IGZuIGNvbnN0X3JlZHVuZGFudCgpIC0+IGkzMiB7XG4gICAgICAxMDBcbiAgfVxuXG4gIC8vIFNob3VsZCBOT1QgbWF0Y2ggLSByZWd1bGFyIGZ1bmN0aW9uXG4gIGZuIHJlZ3VsYXJfZnVuY3Rpb24oKSAtPiBpMzIge1xuICAgICAgNDJcbiAgfSJ9) ### Description This rule detects functions marked with the `unsafe` keyword that do not contain any `unsafe` blocks in their body. When a function is marked `unsafe`, it indicates that the function contains operations that the compiler cannot verify as safe. However, if the function body doesn't contain any `unsafe` blocks, it may be unnecessarily marked as `unsafe`. This could be a sign that: 1. The function should not be marked `unsafe` if it doesn't perform any unsafe operations 2. Or if there are unsafe operations, they should be explicitly wrapped in `unsafe` blocks for clarity This rule helps identify such cases so developers can review whether the `unsafe` marker is truly necessary or if the code needs to be refactored. ### YAML ```yaml id: redundant-unsafe-function language: rust severity: error message: Unsafe function without unsafe block inside note: | Consider whether this function needs to be marked unsafe or if unsafe operations should be wrapped in an unsafe block rule: all: - kind: function_item - has: kind: function_modifiers regex: "^unsafe" - not: has: kind: unsafe_block stopBy: end ``` ### Example ```rs {2,7,12,24,29} // Should match - unsafe function without unsafe block (no return type) unsafe fn redundant_unsafe() { println!("No unsafe operations here"); } // Should match - unsafe function with return type, no unsafe block unsafe fn redundant_with_return() -> i32 { let x = 5; x + 10 } // Should match - unsafe function with complex return type unsafe fn redundant_complex_return() -> Result { Ok(String::from("safe operation")) } // Should NOT match - unsafe function with unsafe block unsafe fn proper_unsafe() -> *const i32 { unsafe { let ptr = 0x1234 as *const i32; ptr } } // Should match - unsafe async function without unsafe block unsafe async fn async_redundant() -> i32 { 42 } // Should match - unsafe const function unsafe const fn const_redundant() -> i32 { 100 } // Should NOT match - regular function fn regular_function() -> i32 { 42 } ``` ### Contributed by Inspired by [@hd\_nvim's Tweet](https://x.com/hd_nvim/status/1992810384072585397?s=20) --- # Source: https://ast-grep.github.io/catalog/tsx/redundant-usestate-type.md --- url: /catalog/tsx/redundant-usestate-type.md --- ## Unnecessary `useState` Type * [Playground Link](/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoidHlwZXNjcmlwdCIsInF1ZXJ5IjoidXNlU3RhdGU8c3RyaW5nPigkQSkiLCJyZXdyaXRlIjoidXNlU3RhdGUoJEEpIiwiY29uZmlnIjoiIyBZQU1MIFJ1bGUgaXMgbW9yZSBwb3dlcmZ1bCFcbiMgaHR0cHM6Ly9hc3QtZ3JlcC5naXRodWIuaW8vZ3VpZGUvcnVsZS1jb25maWcuaHRtbCNydWxlXG5ydWxlOlxuICBhbnk6XG4gICAgLSBwYXR0ZXJuOiBjb25zb2xlLmxvZygkQSlcbiAgICAtIHBhdHRlcm46IGNvbnNvbGUuZGVidWcoJEEpXG5maXg6XG4gIGxvZ2dlci5sb2coJEEpIiwic291cmNlIjoiZnVuY3Rpb24gQ29tcG9uZW50KCkge1xuICBjb25zdCBbbmFtZSwgc2V0TmFtZV0gPSB1c2VTdGF0ZTxzdHJpbmc+KCdSZWFjdCcpXG59In0=) ### Description React's [`useState`](https://react.dev/reference/react/useState) is a Hook that lets you add a state variable to your component. The type annotation of `useState`'s generic type argument, for example `useState(123)`, is unnecessary if TypeScript can infer the type of the state variable from the initial value. We can usually skip annotating if the generic type argument is a single primitive type like `number`, `string` or `boolean`. ### Pattern ::: code-group ```bash [number] ast-grep -p 'useState($A)' -r 'useState($A)' -l tsx ``` ```bash [string] ast-grep -p 'useState($A)' -r 'useState($A)' ``` ```bash [boolean] ast-grep -p 'useState($A)' -r 'useState($A)' ``` ::: ### Example ```ts {2} function Component() { const [name, setName] = useState('React') } ``` ### Diff ```ts function Component() { const [name, setName] = useState('React') // [!code --] const [name, setName] = useState('React') // [!code ++] } ``` ### Contributed by [Herrington Darkholme](https://twitter.com/hd_nvim) --- # Source: https://ast-grep.github.io/catalog/python/refactor-pytest-fixtures.md --- url: /catalog/python/refactor-pytest-fixtures.md --- ## Refactor pytest fixtures * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiZGVmIGZvbygkWCk6XG4gICRTIiwicmV3cml0ZSI6ImxvZ2dlci5sb2coJE1BVENIKSIsImNvbmZpZyI6ImlkOiBweXRlc3QtdHlwZS1oaW50LWZpeHR1cmVcbmxhbmd1YWdlOiBQeXRob25cbnV0aWxzOlxuICBpcy1maXh0dXJlLWZ1bmN0aW9uOlxuICAgIGtpbmQ6IGZ1bmN0aW9uX2RlZmluaXRpb25cbiAgICBmb2xsb3dzOlxuICAgICAga2luZDogZGVjb3JhdG9yXG4gICAgICBoYXM6XG4gICAgICAgIGtpbmQ6IGlkZW50aWZpZXJcbiAgICAgICAgcmVnZXg6IF5maXh0dXJlJFxuICAgICAgICBzdG9wQnk6IGVuZFxuICBpcy10ZXN0LWZ1bmN0aW9uOlxuICAgIGtpbmQ6IGZ1bmN0aW9uX2RlZmluaXRpb25cbiAgICBoYXM6XG4gICAgICBmaWVsZDogbmFtZVxuICAgICAgcmVnZXg6IF50ZXN0X1xuICBpcy1weXRlc3QtY29udGV4dDpcbiAgICAjIFB5dGVzdCBjb250ZXh0IGlzIGEgbm9kZSBpbnNpZGUgYSBweXRlc3RcbiAgICAjIHRlc3QvZml4dHVyZVxuICAgIGluc2lkZTpcbiAgICAgIHN0b3BCeTogZW5kXG4gICAgICBhbnk6XG4gICAgICAgIC0gbWF0Y2hlczogaXMtZml4dHVyZS1mdW5jdGlvblxuICAgICAgICAtIG1hdGNoZXM6IGlzLXRlc3QtZnVuY3Rpb25cbiAgaXMtZml4dHVyZS1hcmc6XG4gICAgIyBGaXh0dXJlIGFyZ3VtZW50cyBhcmUgaWRlbnRpZmllcnMgaW5zaWRlIHRoZSBcbiAgICAjIHBhcmFtZXRlcnMgb2YgYSB0ZXN0L2ZpeHR1cmUgZnVuY3Rpb25cbiAgICBhbGw6XG4gICAgICAtIGtpbmQ6IGlkZW50aWZpZXJcbiAgICAgIC0gbWF0Y2hlczogaXMtcHl0ZXN0LWNvbnRleHRcbiAgICAgIC0gaW5zaWRlOlxuICAgICAgICAgIGtpbmQ6IHBhcmFtZXRlcnNcbnJ1bGU6XG4gIG1hdGNoZXM6IGlzLWZpeHR1cmUtYXJnXG4gIHJlZ2V4OiBeZm9vJFxuZml4OiAnZm9vOiBpbnQnXG4iLCJzb3VyY2UiOiJmcm9tIGNvbGxlY3Rpb25zLmFiYyBpbXBvcnQgSXRlcmFibGVcbmZyb20gdHlwaW5nIGltcG9ydCBBbnlcblxuaW1wb3J0IHB5dGVzdFxuZnJvbSBweXRlc3QgaW1wb3J0IGZpeHR1cmVcblxuQHB5dGVzdC5maXh0dXJlKHNjb3BlPVwic2Vzc2lvblwiKVxuZGVmIGZvbygpIC0+IEl0ZXJhYmxlW2ludF06XG4gICAgeWllbGQgNVxuXG5AZml4dHVyZVxuZGVmIGJhcihmb28pIC0+IHN0cjpcbiAgICByZXR1cm4gc3RyKGZvbylcblxuZGVmIHJlZ3VsYXJfZnVuY3Rpb24oZm9vKSAtPiBOb25lOlxuICAgICMgVGhpcyBmdW5jdGlvbiBkb2Vzbid0IHVzZSB0aGUgJ2ZvbycgZml4dHVyZVxuICAgIHByaW50KGZvbylcblxuZGVmIHRlc3RfMShmb28sIGJhcik6XG4gICAgcHJpbnQoZm9vLCBiYXIpXG5cbmRlZiB0ZXN0XzIoYmFyKTpcbiAgICAuLi4ifQ==) ### Description One of the most commonly used testing framework in Python is [pytest](https://docs.pytest.org/en/8.2.x/). Among other things, it allows the use of [fixtures](https://docs.pytest.org/en/6.2.x/fixture.html). Fixtures are defined as functions that can be required in test code, or in other fixtures, as an argument. This means that all functions arguments with a given name in a pytest context (test function or fixture) are essentially the same entity. However, not every editor's LSP is able to keep track of this, making refactoring challenging. Using ast-grep, we can define some rules to match fixture definition and usage without catching similarly named entities in a non-test context. First, we define utils to select pytest test/fixture functions. ```yaml utils: is-fixture-function: kind: function_definition follows: kind: decorator has: kind: identifier regex: ^fixture$ stopBy: end is-test-function: kind: function_definition has: field: name regex: ^test_ ``` Pytest fixtures are declared with a decorator `@pytest.fixture`. We match the `function_definition` node that directly follows a `decorator` node. That decorator node must have a `fixture` identifier somewhere. This accounts for different location of the `fixture` node depending on the type of imports and whether the decorator is used as is or called with parameters. Pytest functions are fairly straightforward to detect, as they always start with `test_` by convention. The next utils builds onto those two to incrementally: * Find if a node is inside a pytest context (test/fixture) * Find if a node is an argument in such a context ```yaml utils: is-pytest-context: # Pytest context is a node inside a pytest # test/fixture inside: stopBy: end any: - matches: is-fixture-function - matches: is-test-function is-fixture-arg: # Fixture arguments are identifiers inside the # parameters of a test/fixture function all: - kind: identifier - inside: kind: parameters - matches: is-pytest-context ``` Once those utils are declared, you can perform various refactoring on a specific fixture. The following rule adds a type-hint to a fixture. ```yaml rule: matches: is-fixture-arg regex: ^foo$ fix: 'foo: int' ``` This one renames a fixture and all its references. ```yaml rule: kind: identifier matches: is-fixture-context regex: ^foo$ fix: 'five' ``` ### Example #### Renaming Fixtures ```python {2,6,7,12,13} @pytest.fixture def foo() -> int: return 5 @pytest.fixture(scope="function") def some_fixture(foo: int) -> str: return str(foo) def regular_function(foo) -> None: ... def test_code(foo: int) -> None: assert foo == 5 ``` #### Diff ```python {2,6,7,12} @pytest.fixture def foo() -> int: # [!code --] def five() -> int: # [!code ++] return 5 @pytest.fixture(scope="function") def some_fixture(foo: int) -> str: # [!code --] def some_fixture(five: int) -> str: # [!code ++] return str(foo) def regular_function(foo) -> None: ... def test_code(foo: int) -> None: # [!code --] def test_code(five: int) -> None: # [!code ++] assert foo == 5 # [!code --] assert five == 5 # [!code ++] ``` #### Type Hinting Fixtures ```python {6,12} @pytest.fixture def foo() -> int: return 5 @pytest.fixture(scope="function") def some_fixture(foo) -> str: return str(foo) def regular_function(foo) -> None: ... def test_code(foo) -> None: assert foo == 5 ``` #### Diff ```python {2,6,7,12} @pytest.fixture def foo() -> int: return 5 @pytest.fixture(scope="function") def some_fixture(foo) -> str: # [!code --] def some_fixture(foo: int) -> str: # [!code ++] return str(foo) def regular_function(foo) -> None: ... def test_code(foo) -> None: # [!code --] def test_code(foo: int) -> None: # [!code ++] assert foo == 5 ``` --- # Source: https://ast-grep.github.io/guide/rule-config/relational-rule.md --- url: /guide/rule-config/relational-rule.md --- # Relational Rules Atomic rule can only match the target node directly. But sometimes we want to match a node based on its surrounding nodes. For example, we want to find `await` expression inside a `for` loop. Relational rules are powerful operators that can filter the *target* nodes based on their *surrounding* nodes. ast-grep now supports four kinds of relational rules: `inside`, `has`, `follows`, and `precedes`. All four relational rules accept a sub rule object as their value. The sub rule will match the surrounding node while the relational rule itself will match the target node. ## Relational Rule Example Having an `await` expression inside a for loop is usually a bad idea because every iteration will have to wait for the previous promise to resolve. We can use the relational rule `inside` to filter out the `await` expression. ```yaml rule: pattern: await $PROMISE inside: kind: for_in_statement stopBy: end ``` The rule reads as "matches an `await` expression that is `inside` a `for_in_statement`". See [Playground](https://ast-grep.github.io/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InR5cGVzY3JpcHQiLCJxdWVyeSI6IiRDOiAkVCA9IHJlbGF0aW9uc2hpcCgkJCRBLCB1c2VsaXN0PVRydWUsICQkJEIpIiwicmV3cml0ZSI6IiRDOiBMaXN0WyRUXSA9IHJlbGF0aW9uc2hpcCgkJCRBLCB1c2VsaXN0PVRydWUsICQkJEIpIiwiY29uZmlnIjoiaWQ6IG5vLWF3YWl0LWluLWxvb3Bcbmxhbmd1YWdlOiBUeXBlU2NyaXB0XG5ydWxlOlxuICBwYXR0ZXJuOiBhd2FpdCAkUFJPTUlTRVxuICBpbnNpZGU6XG4gICAga2luZDogZm9yX2luX3N0YXRlbWVudFxuICAgIHN0b3BCeTogZW5kIiwic291cmNlIjoiZm9yIChsZXQgaSBvZiBbMSwgMiwzXSkge1xuICAgIGF3YWl0IFByb21pc2UucmVzb2x2ZShpKVxufSJ9). The relational rule `inside` accepts a rule and will match any node that is inside another node that satisfies the inside rule. The `inside` rule itself matches `await` and its sub rule `kind` matches the surrounding loop. ## Relational Rule's Sub Rule Since relational rules accept another ast-grep rule, we can compose more complex examples by using operators recursively. ```yaml rule: pattern: await $PROMISE inside: any: - kind: for_in_statement - kind: for_statement - kind: while_statement - kind: do_statement stopBy: end ``` The above rule will match different kinds of loops, like `for`, `for-in`, `while` and `do-while`. So all the code below matches the rule: ```js while (foo) { await bar() } for (let i = 0; i < 10; i++) { await bar() } for (let key in obj) { await bar() } do { await bar() } while (condition) ``` See in [playground](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InR5cGVzY3JpcHQiLCJxdWVyeSI6IiRDOiAkVCA9IHJlbGF0aW9uc2hpcCgkJCRBLCB1c2VsaXN0PVRydWUsICQkJEIpIiwicmV3cml0ZSI6IiRDOiBMaXN0WyRUXSA9IHJlbGF0aW9uc2hpcCgkJCRBLCB1c2VsaXN0PVRydWUsICQkJEIpIiwiY29uZmlnIjoiaWQ6IG5vLWF3YWl0LWluLWxvb3Bcbmxhbmd1YWdlOiBUeXBlU2NyaXB0XG5ydWxlOlxuICBwYXR0ZXJuOiBhd2FpdCAkUFJPTUlTRVxuICBpbnNpZGU6XG4gICAgYW55OlxuICAgICAgLSBraW5kOiBmb3JfaW5fc3RhdGVtZW50XG4gICAgICAtIGtpbmQ6IGZvcl9zdGF0ZW1lbnRcbiAgICAgIC0ga2luZDogd2hpbGVfc3RhdGVtZW50XG4gICAgICAtIGtpbmQ6IGRvX3N0YXRlbWVudFxuICAgIHN0b3BCeTogZW5kIiwic291cmNlIjoid2hpbGUgKGZvbykge1xuICBhd2FpdCBiYXIoKVxufVxuZm9yIChsZXQgaSA9IDA7IGkgPCAxMDsgaSsrKSB7XG4gIGF3YWl0IGJhcigpXG59XG5mb3IgKGxldCBrZXkgaW4gb2JqKSB7XG4gIGF3YWl0IGJhcigpXG59XG5kbyB7XG4gIGF3YWl0IGJhcigpXG59IHdoaWxlIChjb25kaXRpb24pIn0=). :::tip Pro Tip You can also use `pattern` in relational rule! The metavariable matched in relational rule can also be used in `fix`. This will effectively let you extract a child node from a match. ::: ## Relational Rule Mnemonics The four relational rules can read as: * `inside`: the *target* node must be **inside** a node that matches the sub rule. * `has`: the *target* node must **have** a child node specified by the sub rule. * `follows`: the *target* node must **follow** a node specified by the sub rule. (target after surrounding) * `precedes`: the *target* node must **precede** a node specified by the sub rule. (target before surrounding). It is sometimes confusing to remember whether the rule matches target node or surrounding node. Here is the mnemonics to help you read the rule. First, relational rule is usually used along with another rule. Second, the other rule will match the target node. Finally, the relational rule's sub rule will match the surrounding node. Together, the rule specifies that the target node will `be inside` or `follows` the surrounding node. :::tip All relational rule takes the form of `target` `relates` to `surrounding`. ::: For example, the rule below will match **`hello`(target)** greeting that **follows(relation)** a **`world`(surrounding)** greeting. ```yaml pattern: console.log('hello'); follows: pattern: console.log('world'); ``` Consider the [input source code](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6ImNvbnNvbGUubG9nKCRNQVRDSCkiLCJjb25maWciOiJydWxlOlxuICBhbGw6XG4gICAgLSBwYXR0ZXJuOiBjb25zb2xlLmxvZygnaGVsbG8nKTtcbiAgICAtIGZvbGxvd3M6XG4gICAgICAgIHBhdHRlcm46IGNvbnNvbGUubG9nKCd3b3JsZCcpOyIsInNvdXJjZSI6ImNvbnNvbGUubG9nKCdoZWxsbycpOyAvLyBkb2VzIG5vdCBtYXRjaFxuY29uc29sZS5sb2coJ3dvcmxkJyk7XG5jb25zb2xlLmxvZygnaGVsbG8nKTsgLy8gbWF0Y2hlcyEhIn0=). Only the second `console.log('hello')` will match the rule. ```javascript console.log('hello'); // does not match console.log('world'); console.log('hello'); // matches!! ``` ## Fine Tuning Relational Rule Relational rule has several options to let you find nodes more precisely. ### `stopBy` By default, relational rule will only match nodes one level further. For example, ast-grep will only match the direct children of the target node for the `has` rule. You can change the behavior by using the `stopBy` field. It accepts three kinds of values: string `'end'`, string `'neighbor'` (the default option), and a rule object. `stopBy: end` will make ast-grep search surrounding nodes until it reaches the end. For example, it stops when the rule hits root node, leaf node or the first/last sibling node. ```yaml has: stopBy: end pattern: $MY_PATTERN ``` `stopBy` can also accept a custom rule object, so the searching will only stop when the rule matches the surrounding node. ```yaml # find if a node is inside a function called test. It stops whenever the ancestor node is a function. inside: stopBy: kind: function pattern: function test($$$) { $$$ } ``` Note the `stopBy` rule is inclusive. So when both `stopBy` rule and relational rule hit a node, the node is considered as a match. ### `field` Sometimes it is useful to specify the node by its field. Suppose we want to find a JavaScript object property with the key `prototype`, an outdated practice that we should avoid. ```yaml kind: pair # key-value pair in JS has: field: key # note here regex: 'prototype' ``` This rule will match the following code ```js var a = { prototype: anotherObject } ``` but will not match this code ```js var a = { normalKey: prototype } ``` Though `pair` has a child with text `prototype` in the second example, its relative field is not `key`. That is, `prototype` is not used as `key` but instead used as value. So it does not match the rule. --- # Source: https://ast-grep.github.io/catalog/python/remove-async-await.md --- url: /catalog/python/remove-async-await.md --- ## Remove `async` function * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiYXdhaXQgJCQkQ0FMTCIsInJld3JpdGUiOiIkJCRDQUxMICIsImNvbmZpZyI6ImlkOiByZW1vdmUtYXN5bmMtZGVmXG5sYW5ndWFnZTogcHl0aG9uXG5ydWxlOlxuICBwYXR0ZXJuOlxuICAgIGNvbnRleHQ6ICdhc3luYyBkZWYgJEZVTkMoJCQkQVJHUyk6ICQkJEJPRFknXG4gICAgc2VsZWN0b3I6IGZ1bmN0aW9uX2RlZmluaXRpb25cbnRyYW5zZm9ybTpcbiAgUkVNT1ZFRF9CT0RZOlxuICAgIHJld3JpdGU6XG4gICAgICByZXdyaXRlcnM6IFtyZW1vdmUtYXdhaXQtY2FsbF1cbiAgICAgIHNvdXJjZTogJCQkQk9EWVxuZml4OiB8LVxuICBkZWYgJEZVTkMoJCQkQVJHUyk6XG4gICAgJFJFTU9WRURfQk9EWVxucmV3cml0ZXJzOlxuLSBpZDogcmVtb3ZlLWF3YWl0LWNhbGxcbiAgcnVsZTpcbiAgICBwYXR0ZXJuOiAnYXdhaXQgJCQkQ0FMTCdcbiAgZml4OiAkJCRDQUxMXG4iLCJzb3VyY2UiOiJhc3luYyBkZWYgbWFpbjMoKTpcbiAgYXdhaXQgc29tZWNhbGwoMSwgNSkifQ==) ### Description The `async` keyword in Python is used to define asynchronous functions that can be `await`ed. In this example, we want to remove the `async` keyword from a function definition and replace it with a synchronous version of the function. We also need to remove the `await` keyword from the function body. By default, ast-grep will not apply overlapping replacements. This means `await` keywords will not be modified because they are inside the async function body. However, we can use the [`rewriter`](https://ast-grep.github.io/reference/yaml/rewriter.html) to apply changes inside the matched function body. ### YAML ```yaml id: remove-async-def language: python rule: # match async function definition pattern: context: 'async def $FUNC($$$ARGS): $$$BODY' selector: function_definition rewriters: # define a rewriter to remove the await keyword remove-await-call: pattern: 'await $$$CALL' fix: $$$CALL # remove await keyword # apply the rewriter to the function body transform: REMOVED_BODY: rewrite: rewriters: [remove-await-call] source: $$$BODY fix: |- def $FUNC($$$ARGS): $REMOVED_BODY ``` ### Example ```python async def main3(): await somecall(1, 5) ``` ### Diff ```python async def main3(): # [!code --] await somecall(1, 5) # [!code --] def main3(): # [!code ++] somecall(1, 5) # [!code ++] ``` ### Contributed by Inspired by the ast-grep issue [#1185](https://github.com/ast-grep/ast-grep/issues/1185) --- # Source: https://ast-grep.github.io/catalog/tsx/rename-svg-attribute.md --- url: /catalog/tsx/rename-svg-attribute.md --- ## Rename SVG Attribute * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InRzeCIsInF1ZXJ5IjoiIiwicmV3cml0ZSI6IiIsInN0cmljdG5lc3MiOiJyZWxheGVkIiwic2VsZWN0b3IiOiIiLCJjb25maWciOiJpZDogcmV3cml0ZS1zdmctYXR0cmlidXRlXG5sYW5ndWFnZTogdHN4XG5ydWxlOlxuICBwYXR0ZXJuOiAkUFJPUFxuICByZWdleDogKFthLXpdKyktKFthLXpdKVxuICBraW5kOiBwcm9wZXJ0eV9pZGVudGlmaWVyXG4gIGluc2lkZTpcbiAgICBraW5kOiBqc3hfYXR0cmlidXRlXG50cmFuc2Zvcm06XG4gIE5FV19QUk9QOlxuICAgIGNvbnZlcnQ6XG4gICAgICBzb3VyY2U6ICRQUk9QXG4gICAgICB0b0Nhc2U6IGNhbWVsQ2FzZVxuZml4OiAkTkVXX1BST1AiLCJzb3VyY2UiOiJjb25zdCBlbGVtZW50ID0gKFxuICA8c3ZnIHdpZHRoPVwiMTAwXCIgaGVpZ2h0PVwiMTAwXCIgdmlld0JveD1cIjAgMCAxMDAgMTAwXCI+XG4gICAgPHBhdGggZD1cIk0xMCAyMCBMMzAgNDBcIiBzdHJva2UtbGluZWNhcD1cInJvdW5kXCIgZmlsbC1vcGFjaXR5PVwiMC41XCIgLz5cbiAgPC9zdmc+XG4pIn0=) ### Description [SVG](https://en.wikipedia.org/wiki/SVG)(Scalable Vector Graphics)s' hyphenated names are not compatible with JSX syntax in React. JSX requires [camelCase naming](https://react.dev/learn/writing-markup-with-jsx#3-camelcase-salls-most-of-the-things) for attributes. For example, an SVG attribute like `stroke-linecap` needs to be renamed to `strokeLinecap` to work correctly in React. ### YAML ```yaml id: rewrite-svg-attribute language: tsx rule: pattern: $PROP # capture in metavar regex: ([a-z]+)-([a-z]) # hyphenated name kind: property_identifier inside: kind: jsx_attribute # in JSX attribute transform: NEW_PROP: # new property name convert: # use ast-grep's convert source: $PROP toCase: camelCase # to camelCase naming fix: $NEW_PROP ``` ### Example ```tsx {3} const element = ( ) ``` ### Diff ```ts const element = ( // [!code --] // [!code ++] ) ``` ### Contributed by Inspired by [SVG Renamer](https://admondtamang.medium.com/introducing-svg-renamer-your-solution-for-react-svg-attributes-26503382d5a8) --- # Source: https://ast-grep.github.io/catalog/tsx/reverse-react-compiler.md --- url: /catalog/tsx/reverse-react-compiler.md --- ## Reverse React Compiler™ * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InRzeCIsInF1ZXJ5IjoiIiwicmV3cml0ZSI6IiIsInN0cmljdG5lc3MiOiJyZWxheGVkIiwic2VsZWN0b3IiOiIiLCJjb25maWciOiJpZDogcmV3cml0ZS1jYWNoZSBcbmxhbmd1YWdlOiB0c3hcbnJ1bGU6XG4gIGFueTpcbiAgLSBwYXR0ZXJuOiB1c2VDYWxsYmFjaygkRk4sICQkJClcbiAgLSBwYXR0ZXJuOiBtZW1vKCRGTiwgJCQkKVxuZml4OiAkRk5cblxuLS0tXG5cbmlkOiByZXdyaXRlLXVzZS1tZW1vXG5sYW5ndWFnZTogdHN4XG5ydWxlOiB7IHBhdHRlcm46ICd1c2VNZW1vKCRGTiwgJCQkKScgfVxuZml4OiAoJEZOKSgpIiwic291cmNlIjoiY29uc3QgQ29tcG9uZW50ID0gKCkgPT4ge1xuICBjb25zdCBbY291bnQsIHNldENvdW50XSA9IHVzZVN0YXRlKDApXG4gIGNvbnN0IGluY3JlbWVudCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRDb3VudCgocHJldkNvdW50KSA9PiBwcmV2Q291bnQgKyAxKVxuICB9LCBbXSlcbiAgY29uc3QgZXhwZW5zaXZlQ2FsY3VsYXRpb24gPSB1c2VNZW1vKCgpID0+IHtcbiAgICAvLyBtb2NrIEV4cGVuc2l2ZSBjYWxjdWxhdGlvblxuICAgIHJldHVybiBjb3VudCAqIDJcbiAgfSwgW2NvdW50XSlcblxuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8cD5FeHBlbnNpdmUgUmVzdWx0OiB7ZXhwZW5zaXZlQ2FsY3VsYXRpb259PC9wPlxuICAgICAgPGJ1dHRvbiBvbkNsaWNrPXtpbmNyZW1lbnR9Pntjb3VudH08L2J1dHRvbj5cbiAgICA8Lz5cbiAgKVxufSJ9) ### Description React Compiler is a build-time only tool that automatically optimizes your React app, working with plain JavaScript and understanding the Rules of React without requiring a rewrite. It optimizes apps by automatically memoizing code, similar to `useMemo`, `useCallback`, and `React.memo`, reducing unnecessary recomputation due to incorrect or forgotten memoization. Reverse React Compiler™ is a [parody tweet](https://x.com/aidenybai/status/1881397529369034997) that works in the opposite direction. It takes React code and removes memoization, guaranteed to make your code slower. ([not](https://x.com/kentcdodds/status/1881404373646880997) [necessarily](https://dev.to/prathamisonline/are-you-over-using-usememo-and-usecallback-hooks-in-react-5lp)) It is originally written in Babel and this is an [ast-grep version](https://x.com/hd_nvim/status/1881402678493970620) of it. :::details The Original Babel Implementation For comparison purposes only. Note the original code [does not correctly rewrite](https://x.com/hd_nvim/status/1881404893136896415) `useMemo`. ```js const ReverseReactCompiler = ({ types: t }) => ({ visitor: { CallExpression(path) { const callee = path.node.callee; if ( t.isIdentifier(callee, { name: "useMemo" }) || t.isIdentifier(callee, { name: "useCallback" }) || t.isIdentifier(callee, { name: "memo" }) ) { path.replaceWith(args[0]); } }, }, }); ``` ::: ### YAML ```yaml id: rewrite-cache language: tsx rule: any: - pattern: useCallback($FN, $$$) - pattern: memo($FN, $$$) fix: $FN --- id: rewrite-use-memo language: tsx rule: { pattern: 'useMemo($FN, $$$)' } fix: ($FN)() # need IIFE to wrap memo function ``` ### Example ```tsx {3-5,6-9} const Component = () => { const [count, setCount] = useState(0) const increment = useCallback(() => { setCount((prevCount) => prevCount + 1) }, []) const expensiveCalculation = useMemo(() => { // mock Expensive calculation return count * 2 }, [count]) return ( <>

Expensive Result: {expensiveCalculation}

) } ``` ### Diff ```tsx const Component = () => { const [count, setCount] = useState(0) const increment = useCallback(() => { // [!code --] setCount((prevCount) => prevCount + 1) // [!code --] }, []) // [!code --] const increment = () => { // [!code ++] setCount((prevCount) => prevCount + 1) // [!code ++] } // [!code ++] const expensiveCalculation = useMemo(() => { // [!code --] // mock Expensive calculation // [!code --] return count * 2 // [!code --] }, [count]) // [!code --] const expensiveCalculation = (() => { // [!code ++] // mock Expensive calculation // [!code ++] return count * 2 // [!code ++] })() // [!code ++] return ( <>

Expensive Result: {expensiveCalculation}

) } ``` ### Contributed by Inspired by [Aiden Bai](https://twitter.com/aidenybai) --- # Source: https://ast-grep.github.io/guide/rewrite-code.md --- url: /guide/rewrite-code.md --- # Rewrite Code One of the powers of ast-grep is that it can not only find code patterns, but also transform them into new code. For example, you may want to rename a variable, change a function call, or add a comment. ast-grep provides two ways to do this: using the `--rewrite` flag or using the `fix` key in YAML rules. ## Using `ast-grep run -p 'pat' --rewrite` The simplest way to rewrite code is to use the `--rewrite` flag with the `ast-grep run` command. This flag takes a string argument that specifies the new code to replace the matched pattern. For example, if you want to change all occurrences of identifier `foo` to `bar`, you can run: ```bash ast-grep run --pattern 'foo' --rewrite 'bar' --lang python ``` This will show you a diff of the changes that will be made. If you are using interactive mode by the `--interactive` flag, ast-grep ask you if you want to apply them. :::tip You can also use the `--update-all` or `-U` flag to automatically accept the changes without confirmation. ::: ## Using `fix` in YAML Rule Another way to rewrite code is to use the `fix` option in a YAML rule file. This option allows you to specify more complex and flexible rewrite rules, such as using transformations and regular expressions. Let's look at a simple example of using `fix` in a YAML rule ([playground Link](https://ast-grep.github.io/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiZGVmIGZvbygkWCk6XG4gICRTIiwicmV3cml0ZSI6ImxvZ2dlci5sb2coJE1BVENIKSIsInN0cmljdG5lc3MiOiJzbWFydCIsInNlbGVjdG9yIjoiIiwiY29uZmlnIjoiaWQ6IGNoYW5nZV9uYW1lXG5sYW5ndWFnZTogUHl0aG9uXG5ydWxlOlxuICBwYXR0ZXJuOiB8XG4gICAgZGVmIGZvbygkWCk6XG4gICAgICAkJCRTXG5maXg6IHwtXG4gIGRlZiBiYXooJFgpOlxuICAgICQkJFNcbi0tLVxuaWQ6IGNoYW5nZV9wYXJhbVxucnVsZTpcbiAgcGF0dGVybjogZm9vKCRYKVxuZml4OiBiYXooJFgpIiwic291cmNlIjoiZGVmIGZvbyh4KTpcbiAgICByZXR1cm4geCArIDFcblxueSA9IGZvbygyKVxucHJpbnQoeSkifQ==)). Suppose we have a Python file named `test.py` with the following content: ```python def foo(x): return x + 1 y = foo(2) print(y) ``` We want to only change the name of the function `foo` to `baz`, but not variable/method/class. We can write a YAML rule file named `change_func.yml` with the following content: ```yaml{7-9,16} id: change_def language: Python rule: pattern: | def foo($X): $$$S fix: |- def baz($X): $$$S --- # this is YAML doc separator to have multiple rules in one file id: change_param rule: pattern: foo($X) fix: baz($X) ``` The first rule matches the definition of the function `foo`, and replaces it with `baz`. The second rule matches the calls of the function `foo`, and replaces them with `baz`. Note that we use `$X` and `$$$S` as [meta](/guide/pattern-syntax.html#meta-variable) [variables](/guide/pattern-syntax.html#multi-meta-variable), which can match any expression and any statement, respectively. We can run: ```bash ast-grep scan -r change_func.yml test.py ``` This will show us the following diff: ```python def foo(x): # [!code --] def baz(x): # [!code ++] return x + 1 y = foo(2) # [!code --] y = baz(2) # [!code ++] print(y) ``` We can see that the function name and parameter name are changed as we expected. :::tip Pro Tip You can have multiple rules in one YAML file by using the YAML document separator `---`. This allows you to group related rules together! ::: ## Use Meta Variable in Rewrite As we saw in the previous example, we can use [meta variables](/guide/pattern-syntax.html#meta-variable-capturing) in both the pattern and the fix parts of a YAML rule. They are like regular expression [capture groups](https://regexone.com/lesson/capturing_groups). Meta variables are identifiers that start with `$`, and they can match any syntactic element in the code, such as expressions, statements, types, etc. When we use a meta variable in the fix part of a rule, it will be replaced by whatever it matched in the pattern part. For example, if we have a [rule](https://ast-grep.github.io/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiZGVmIGZvbygkWCk6XG4gICRTIiwicmV3cml0ZSI6ImxvZ2dlci5sb2coJE1BVENIKSIsImNvbmZpZyI6ImlkOiBzd2FwXG5sYW5ndWFnZTogUHl0aG9uXG5ydWxlOlxuICBwYXR0ZXJuOiAkWCA9ICRZXG5maXg6ICRZID0gJFgiLCJzb3VyY2UiOiJhID0gYlxuYyA9IGQgKyBlXG5mID0gZyAqIGgifQ==) like this: ```yaml id: swap language: Python rule: pattern: $X = $Y fix: $Y = $X ``` This rule will swap the left-hand side and right-hand side of any assignment statement. For example, if we have a code like this: ```python a = b c = d + e f = g * h ``` The rule will rewrite it as: ```python b = a d + e = c g * h = f ``` [Playground link](https://ast-grep.github.io/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiZGVmIGZvbygkWCk6XG4gICRTIiwicmV3cml0ZSI6ImxvZ2dlci5sb2coJE1BVENIKSIsImNvbmZpZyI6ImlkOiBzd2FwXG5sYW5ndWFnZTogUHl0aG9uXG5ydWxlOlxuICBwYXR0ZXJuOiAkWCA9ICRZXG5maXg6ICRZID0gJFgiLCJzb3VyY2UiOiJhID0gYlxuYyA9IGQgKyBlXG5mID0gZyAqIGgifQ==) Note that this may **not** be a valid or sensible code transformation, but it illustrates how meta variables work. :::warning Append Uppercase String to Meta Variable It will not work if you want to append a string starting with uppercase letters to a meta variable because it will be parsed as an undefined meta variable. ::: Suppose we want to append `Name` to the meta variable `$VAR`, the fix string `$VARName` will be parsed as `$VARN` + `ame` instead. You can instead use [replace transformation](/guide/rewrite/transform.html#rewrite-with-regex-capture-groups) to create a new variable whose content is `$VAR` plus `Name`. :::danger Non-matched meta-variable non-matched meta-variable will be replaced by empty string in the `fix`. ::: ### Rewrite is Indentation Sensitive ast-grep's rewrite is indentation sensitive. That is, the indentation level of a meta-variable in the fix string is preserved in the rewritten code. For example, if we have a rule like this: ```yaml id: lambda-to-def language: Python rule: pattern: '$B = lambda: $R' fix: |- def $B(): return $R ``` This rule will convert a lambda function to a standard `def` function. For example, if we have a code like this: ```python b = lambda: 123 ``` The rule will rewrite it as: ```python def b(): return 123 ``` Note that the indentation level of `return $R` is preserved as two spaces in the rewritten code, even if the replacement `123` in the original code does not have indentation at all. `fix` string's indentation is preserved relative to their position in the source code. For example, if the `lambda` appears within `if` statement, the diff will be like: ```python if True: c = lambda: 456 # [!code --] def c(): # [!code ++] return 456 # [!code ++] ``` Note that the `return 456` line has an indentation of four spaces. This is because it has two spaces indentation as a part of the fix string, and two additional spaces because the fix string as a whole is inside the `if` statement in the original code. ## Expand the Matching Range **ast-grep rule can only fix one target node at one time by replacing the target node text with a new string.** Using `fix` string alone is not enough to handle complex cases where we need to delete surrounding nodes like a comma, or to change surrounding brackets. We may leave redundant text in the fixed code because we cannot delete the surrounding trivials around the matched node. To accommodate these scenarios, ast-grep's `fix` also accepts an advanced object configuration that specifies how to fix the matched AST node: `FixConfig`. It allows you to expand the matched AST node range via two additional rules. It has one required field `template` and two optional fields `expandStart` and `expandEnd`. `template` is the same as the string fix. Both `expandStart` and `expandEnd` accept a [rule](/guide/rule-config.html) object to specify the expansion. `expandStart` will expand the fixing range's start until the rule is not met, while `expandEnd` will expand the fixing range's end until the rule is not met. ### Example of deleting a key-value pair in a JavaScript object Suppose we have a JavaScript object like this: ```JavaScript const obj = { Remove: 'value1' } const obj2 = { Remove: 'value1', Kept: 'value2', } ``` We want to remove the key-value pair with the key `Remove` completely. Just removing the `pair` AST node is [not enough](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6IiIsInJld3JpdGUiOiIiLCJzdHJpY3RuZXNzIjoic21hcnQiLCJzZWxlY3RvciI6IkVSUk9SIiwiY29uZmlnIjoicnVsZTpcbiAga2luZDogcGFpclxuICBoYXM6XG4gICAgZmllbGQ6IGtleVxuICAgIHJlZ2V4OiBSZW1vdmVcbmZpeDogJyciLCJzb3VyY2UiOiJjb25zdCBvYmogPSB7XG4gIFJlbW92ZTogJ3ZhbHVlMSdcbn1cbmNvbnN0IG9iajIgPSB7XG4gIFJlbW92ZTogJ3ZhbHVlMScsXG4gIEtlcHQ6ICd2YWx1ZTInLFxufVxuIn0=) in `obj2` because we also need to remove the trailing comma. We can write [a rule in playground](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6IiIsInJld3JpdGUiOiIiLCJzdHJpY3RuZXNzIjoic21hcnQiLCJzZWxlY3RvciI6IkVSUk9SIiwiY29uZmlnIjoibGFuZ3VhZ2U6IGphdmFzY3JpcHRcbnJ1bGU6XG4gIGtpbmQ6IHBhaXJcbiAgaGFzOlxuICAgIGZpZWxkOiBrZXlcbiAgICByZWdleDogUmVtb3ZlXG4jIHJlbW92ZSB0aGUga2V5LXZhbHVlIHBhaXIgYW5kIGl0cyBjb21tYVxuZml4OlxuICB0ZW1wbGF0ZTogJydcbiAgZXhwYW5kRW5kOiB7IHJlZ2V4OiAnLCcgfSAjIGV4cGFuZCB0aGUgcmFuZ2UgdG8gdGhlIGNvbW1hXG4iLCJzb3VyY2UiOiJjb25zdCBvYmogPSB7XG4gIFJlbW92ZTogJ3ZhbHVlMSdcbn1cbmNvbnN0IG9iajIgPSB7XG4gIFJlbW92ZTogJ3ZhbHVlMScsXG4gIEtlcHQ6ICd2YWx1ZTInLFxufVxuIn0=) like this: ```yaml language: javascript rule: kind: pair has: field: key regex: Remove # remove the key-value pair and its comma fix: template: '' expandEnd: { regex: ',' } # expand the range to the comma ``` The idea is to remove the `pair` node and expand the fixing range to the comma. The `template` is an empty string, which means we will remove the matched node completely. The `expandEnd` rule will expand the fixing range to the comma. So the eventual matched range will be `Remove: 'value1',`, comma included. ## More Advanced Rewrite The examples above illustrate the basic usage of rewriting code with ast-grep. ast-grep also provides more advanced features for rewriting code, such as using [transformations](/guide/rewrite/transform.html) and [rewriter rules](/guide/rewrite/rewriter.html). These features allow you to change the matched code to desired code, like replace string using regex, slice the string, or convert the case of the string. We will cover these advanced features in more detail in the transform doc page. ## See More in Example Catalog If you want to see more examples of using ast-grep to rewrite code, you can check out our [example catalog](/catalog/). There you can find various use cases and scenarios where ast-grep can help you refactor and improve your code. You can also contribute your own examples and share them with other users. --- # Source: https://ast-grep.github.io/catalog/rust/rewrite-indoc-macro.md --- url: /catalog/rust/rewrite-indoc-macro.md --- ## Rewrite `indoc!` macro * [Playground Link](/playground.html#eyJtb2RlIjoiUGF0Y2giLCJsYW5nIjoicnVzdCIsInF1ZXJ5IjoiaW5kb2MhIHsgciNcIiQkJEFcIiMgfSIsInJld3JpdGUiOiJgJCQkQWAiLCJzdHJpY3RuZXNzIjoicmVsYXhlZCIsInNlbGVjdG9yIjoiIiwiY29uZmlnIjoicnVsZTogXG4gYW55OlxuIC0gcGF0dGVybjogJFYgPT09ICRTRU5TRVRJVkVXT1JEXG4gLSBwYXR0ZXJuOiAkU0VOU0VUSVZFV09SRCA9PT0gJFZcbmNvbnN0cmFpbnRzOlxuICBTRU5TRVRJVkVXT1JEOlxuICAgIHJlZ2V4OiBwYXNzd29yZCIsInNvdXJjZSI6ImZuIG1haW4oKSB7XG4gICAgaW5kb2MhIHtyI1wiXG4gICAgICAgIC5mb28ge1xuICAgICAgICAgICAgb3JkZXI6IDE7XG4gICAgICAgIH1cbiAgICBcIiN9O1xufSJ9) ### Description This example, created from [a Tweet](https://x.com/zack_overflow/status/1885065128590401551), shows a refactoring operation being performed on Rust source code. The changes involve removing `indoc!` macro declarations while preserving the CSS-like content within them. Previously, the same refactor is implemented by a *unreadable monster regex* in vim syntax. :::details Click to see the original regex (neovim, btw) ```vimscript :%s/\v(indoc!|)(| )([|\{)r#"(([^#]+|\n+)+)"#/`\4` ``` I have to confess that I don't understand this regex even if I use neovim, btw. Let Claude break it down piece by piece: * `:%s/` - Vim substitution command for all lines * `\v` - Very magic mode in vim for simpler regex syntax * `(indoc!|)` - First capture group: matches either "indoc!" or nothing * `(| )` - Second capture group: matches either empty string or a space * `([|\{)` - Third capture group: matches either `[` or `{` * `r#"` - Matches literal `r#"` (Rust raw string delimiter) * `(([^#]+|\n+)+)` - Fourth capture group (nested): * `[^#]+` - One or more non-# characters * `|\n+` - OR one or more newlines * Outer `()+` makes this repeat one or more times * `"#` - Matches the closing raw string delimiter * \`\4\` - Replaces with the fourth capture group wrapped in backticks This regex is designed to find Rust raw string literals (possibly wrapped in `indoc!` macro), capture their content, and replace the entire match with just the content wrapped in backticks. It's more precise than my previous explanation and matches the pattern you're showing. ::: ### Pattern ```shell ast-grep --pattern 'indoc! { r#"$$$A"# }' --rewrite '`$$$A`' sgtest.rs ``` ### Example ```rs {2-6} fn main() { indoc! {r#" .foo { order: 1; } "#}; } ``` ### Diff ```rs fn main() { indoc! {r#" // [!code --] `.foo { // [!code ++] order: 1; } "#}; // [!code --] `; // [!code ++] } ``` ### Contributed by [Zack in SF](https://x.com/zack_overflow) --- # Source: https://ast-grep.github.io/catalog/c/rewrite-method-to-function-call.md --- url: /catalog/c/rewrite-method-to-function-call.md --- ## Rewrite Method to Function Call * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImMiLCJxdWVyeSI6IiRDT1VOVCA9ICRcbiIsInJld3JpdGUiOiIiLCJjb25maWciOiJpZDogbWV0aG9kX3JlY2VpdmVyXG5ydWxlOlxuICBwYXR0ZXJuOiAkUi4kTUVUSE9EKCQkJEFSR1MpXG50cmFuc2Zvcm06XG4gIE1BWUJFX0NPTU1BOlxuICAgIHJlcGxhY2U6XG4gICAgICBzb3VyY2U6ICQkJEFSR1NcbiAgICAgIHJlcGxhY2U6ICdeLisnXG4gICAgICBieTogJywgJ1xuZml4OlxuICAkTUVUSE9EKCYkUiRNQVlCRV9DT01NQSQkJEFSR1MpXG4iLCJzb3VyY2UiOiJ2b2lkIHRlc3RfZnVuYygpIHtcbiAgICBzb21lX3N0cnVjdC0+ZmllbGQubWV0aG9kKCk7XG4gICAgc29tZV9zdHJ1Y3QtPmZpZWxkLm90aGVyX21ldGhvZCgxLCAyLCAzKTtcbn0ifQ==) ### Description In C, there is no built-in support for object-oriented programming, but some programmers use structs and function pointers to simulate classes and methods. However, this style can have some drawbacks, such as: * extra memory allocation and deallocation for the struct and the function pointer. * indirection overhead when calling the function pointer. A possible alternative is to use a plain function call with the struct pointer as the first argument. ### YAML ```yaml id: method_receiver language: c rule: pattern: $R.$METHOD($$$ARGS) transform: MAYBE_COMMA: replace: source: $$$ARGS replace: '^.+' by: ', ' fix: $METHOD(&$R$MAYBE_COMMA$$$ARGS) ``` ### Example ```c {2-3} void test_func() { some_struct->field.method(); some_struct->field.other_method(1, 2, 3); } ``` ### Diff ```c void test_func() { some_struct->field.method(); // [!code --] method(&some_struct->field); // [!code ++] some_struct->field.other_method(1, 2, 3); // [!code --] other_method(&some_struct->field, 1, 2, 3); // [!code ++] } ``` ### Contributed by [Surma](https://twitter.com/DasSurma), adapted from the [original tweet](https://twitter.com/DasSurma/status/1706086320051794217) --- # Source: https://ast-grep.github.io/catalog/tsx/rewrite-mobx-component.md --- url: /catalog/tsx/rewrite-mobx-component.md --- ## Rewrite MobX Component Style * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6ImNvbnNvbGUubG9nKCRNQVRDSCkiLCJyZXdyaXRlIjoibG9nZ2VyLmxvZygkTUFUQ0gpIiwiY29uZmlnIjoicnVsZTpcbiAgcGF0dGVybjogZXhwb3J0IGNvbnN0ICRDT01QID0gb2JzZXJ2ZXIoJEZVTkMpXG5maXg6IHwtXG4gIGNvbnN0IEJhc2UkQ09NUCA9ICRGVU5DXG4gIGV4cG9ydCBjb25zdCAkQ09NUCA9IG9ic2VydmVyKEJhc2UkQ09NUCkiLCJzb3VyY2UiOiJleHBvcnQgY29uc3QgRXhhbXBsZSA9IG9ic2VydmVyKCgpID0+IHtcbiAgcmV0dXJuIDxkaXY+SGVsbG8gV29ybGQ8L2Rpdj5cbn0pIn0=) ### Description React and MobX are libraries that help us build user interfaces with JavaScript. [React hooks](https://react.dev/reference/react) allow us to use state and lifecycle methods in functional components. But we need follow some hook rules, or React may break. [MobX](https://mobx.js.org/react-integration.html) has an `observer` function that makes a component update when data changes. When we use the `observer` function like this: ```JavaScript export const Example = observer(() => {…}) ``` ESLint, the tool that checks hooks, thinks that `Example` is not a React component, but just a regular function. So it does not check the hooks inside it, and we may miss some wrong usages. To fix this, we need to change our component style to this: ```JavaScript const BaseExample = () => {…} const Example = observer(BaseExample) ``` Now ESLint can see that `BaseExample` is a React component, and it can check the hooks inside it. ### YAML ```yaml id: rewrite-mobx-component language: typescript rule: pattern: export const $COMP = observer($FUNC) fix: |- const Base$COMP = $FUNC export const $COMP = observer(Base$COMP) ``` ### Example ```js {1-3} export const Example = observer(() => { return
Hello World
}) ``` ### Diff ```js export const Example = observer(() => { // [!code --] return
Hello World
// [!code --] }) // [!code --] const BaseExample = () => { // [!code ++] return
Hello World
// [!code ++] } // [!code ++] export const Example = observer(BaseExample) // [!code ++] ``` ### Contributed by [Bryan Lee](https://twitter.com/meetliby/status/1698601672568901723) --- # Source: https://ast-grep.github.io/catalog/python/rewrite-sqlalchemy-mapped-column.md --- url: /catalog/python/rewrite-sqlalchemy-mapped-column.md --- ## Rewrite SQLAlchemy with Type Annotations * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiYShudWxsYWJsZT1UcnVlKSIsInJld3JpdGUiOiIxMjMiLCJzdHJpY3RuZXNzIjoic21hcnQiLCJzZWxlY3RvciI6ImtleXdvcmRfYXJndW1lbnQiLCJjb25maWciOiJpZDogcmVtb3ZlLW51bGxhYmxlLWFyZ1xubGFuZ3VhZ2U6IHB5dGhvblxucnVsZTpcbiAgcGF0dGVybjogJFggPSBtYXBwZWRfY29sdW1uKCQkJEFSR1MpXG4gIGFueTpcbiAgICAtIHBhdHRlcm46ICRYID0gbWFwcGVkX2NvbHVtbigkJCRCRUZPUkUsIFN0cmluZywgJCQkTUlELCBudWxsYWJsZT1UcnVlLCAkJCRBRlRFUilcbiAgICAtIHBhdHRlcm46ICRYID0gbWFwcGVkX2NvbHVtbigkJCRCRUZPUkUsIFN0cmluZywgJCQkTUlELCBudWxsYWJsZT1UcnVlKVxucmV3cml0ZXJzOlxuLSBpZDogZmlsdGVyLXN0cmluZy1udWxsYWJsZVxuICBydWxlOlxuICAgIHBhdHRlcm46ICRBUkdcbiAgICBpbnNpZGU6XG4gICAgICBraW5kOiBhcmd1bWVudF9saXN0XG4gICAgYWxsOlxuICAgIC0gbm90OiBcbiAgICAgICAgcGF0dGVybjogU3RyaW5nXG4gICAgLSBub3Q6XG4gICAgICAgIHBhdHRlcm46XG4gICAgICAgICAgY29udGV4dDogYShudWxsYWJsZT1UcnVlKVxuICAgICAgICAgIHNlbGVjdG9yOiBrZXl3b3JkX2FyZ3VtZW50XG4gIGZpeDogJEFSR1xuXG50cmFuc2Zvcm06XG4gIE5FV0FSR1M6XG4gICAgcmV3cml0ZTpcbiAgICAgIHJld3JpdGVyczogW2ZpbHRlci1zdHJpbmctbnVsbGFibGVdXG4gICAgICBzb3VyY2U6ICQkJEFSR1NcbiAgICAgIGpvaW5CeTogJywgJ1xuZml4OiB8LVxuICAkWDogTWFwcGVkW3N0ciB8IE5vbmVdID0gbWFwcGVkX2NvbHVtbigkTkVXQVJHUykiLCJzb3VyY2UiOiJtZXNzYWdlID0gbWFwcGVkX2NvbHVtbihTdHJpbmcsIGRlZmF1bHQ9XCJoZWxsb1wiLCBudWxsYWJsZT1UcnVlKVxuXG5tZXNzYWdlID0gbWFwcGVkX2NvbHVtbihTdHJpbmcsIG51bGxhYmxlPVRydWUpXG5cbl9tZXNzYWdlID0gbWFwcGVkX2NvbHVtbihcIm1lc3NhZ2VcIiwgU3RyaW5nLCBudWxsYWJsZT1UcnVlKVxuXG5tZXNzYWdlID0gbWFwcGVkX2NvbHVtbihTdHJpbmcsIG51bGxhYmxlPVRydWUsIHVuaXF1ZT1UcnVlKVxuXG5tZXNzYWdlID0gbWFwcGVkX2NvbHVtbihcbiAgU3RyaW5nLCBpbmRleD1UcnVlLCBudWxsYWJsZT1UcnVlLCB1bmlxdWU9VHJ1ZSlcblxuIyBTaG91bGQgbm90IGJlIHRyYW5zZm9ybWVkXG5tZXNzYWdlID0gbWFwcGVkX2NvbHVtbihTdHJpbmcsIGRlZmF1bHQ9XCJoZWxsb1wiKVxuXG5tZXNzYWdlID0gbWFwcGVkX2NvbHVtbihTdHJpbmcsIGRlZmF1bHQ9XCJoZWxsb1wiLCBudWxsYWJsZT1GYWxzZSlcblxubWVzc2FnZSA9IG1hcHBlZF9jb2x1bW4oSW50ZWdlciwgZGVmYXVsdD1cImhlbGxvXCIpIn0=) ### Description [SQLAlchemy 2.0](https://docs.sqlalchemy.org/en/20/orm/declarative_tables.html) recommends using type annotations with `Mapped` type for modern declarative mapping. The `mapped_column()` construct can derive its configuration from [PEP 484](https://peps.python.org/pep-0484/) type annotations. This rule helps migrate legacy SQLAlchemy code that explicitly uses `String` type and `nullable=True` to the modern type annotation approach using `Mapped[str | None]`. The key technique demonstrated here is using **rewriters** to selectively filter arguments. The rewriter: 1. Matches each argument inside the `argument_list` 2. Excludes the `String` type argument 3. Excludes the `nullable=True` keyword argument 4. Keeps all other arguments ### YAML ```yaml id: remove-nullable-arg language: python rule: pattern: $X = mapped_column($$$ARGS) any: - pattern: $X = mapped_column($$$BEFORE, String, $$$MID, nullable=True, $$$AFTER) - pattern: $X = mapped_column($$$BEFORE, String, $$$MID, nullable=True) rewriters: - id: filter-string-nullable rule: pattern: $ARG inside: kind: argument_list all: - not: pattern: String - not: pattern: context: a(nullable=True) selector: keyword_argument fix: $ARG transform: NEWARGS: rewrite: rewriters: [filter-string-nullable] source: $$$ARGS joinBy: ', ' fix: |- $X: Mapped[str | None] = mapped_column($NEWARGS) ``` ### Example ```python {1,3,5,7-8} message = mapped_column(String, default="hello", nullable=True) message = mapped_column(String, nullable=True) _message = mapped_column("message", String, nullable=True) message = mapped_column(String, nullable=True, unique=True) message = mapped_column( String, index=True, nullable=True, unique=True) # Should not be transformed message = mapped_column(String, default="hello") message = mapped_column(String, default="hello", nullable=False) message = mapped_column(Integer, default="hello") ``` ### Diff ```python message = mapped_column(String, default="hello", nullable=True) # [!code --] message: Mapped[str | None] = mapped_column(default="hello") # [!code ++] message = mapped_column(String, nullable=True) # [!code --] message: Mapped[str | None] = mapped_column() # [!code ++] _message = mapped_column("message", String, nullable=True) # [!code --] _message: Mapped[str | None] = mapped_column("message") # [!code ++] message = mapped_column(String, nullable=True, unique=True) # [!code --] message: Mapped[str | None] = mapped_column(unique=True) # [!code ++] message = mapped_column( # [!code --] String, index=True, nullable=True, unique=True) # [!code --] message: Mapped[str | None] = mapped_column( # [!code ++] index=True, unique=True) # [!code ++] ``` ### Contributed by Inspired by [discussion #2319](https://github.com/ast-grep/ast-grep/discussions/2319) --- # Source: https://ast-grep.github.io/reference/yaml/rewriter.md # Source: https://ast-grep.github.io/guide/rewrite/rewriter.md --- url: /guide/rewrite/rewriter.md --- # Rewriter in Fix `rewriters` allow you to apply rules to specific parts of the matching AST nodes. ast-grep's `fix` will only replace the matched nodes, one node at a time. But it is common to replace multiple nodes with different fixes at once. The `rewriters` field allows you to do this. The basic workflow of `rewriters` is as follows: 1. Find a list of sub-nodes under a meta-variable that match different rewriters. 2. Generate a distinct fix for each sub-node based on the matched rewriter sub-rule. 3. Join the fixes together and store the string in a new metavariable for later use. ## Key Steps to Use Rewriters To use rewriters, you have three steps. **1. Define `rewriters` field in the Yaml rule root.** ```yaml id: rewriter-demo language: Python rewriters: - id: sub-rule rule: # some rule fix: # some fix ``` **2. Apply the defined rewriters to a metavariable via `transform`.** ```yaml transform: NEW_VAR: rewrite: rewriters: [sub-rule] source: $OLD_VAR ``` **3. Use other ast-grep fields to wire them together.** ```yaml rule: { pattern: a = $OLD_VAR } # ... rewriters and transform fix: a = $NEW_VAR ``` ## Rewriter Example Let's see a contrived example: converting `dict` function call to dictionary literal in Python. ### General Idea In Python, you can create a dictionary using the `dict` function or the `{}` literal. ```python # dict function call d = dict(a=1, b=2) # dictionary literal d = {'a': 1, 'b': 2} ``` We will use the `rewriters` field to convert the `dict` function call to a dictionary literal. The recipe is to first find the `dict` function call. Then, extract the keyword arguments like `a=1` and transform them into a dictionary key-value pair `'a': 1`. Finally, we will replace the `dict` function call by combining these transformed pairs and wrapping them in a bracket. The key step is extraction and transformation, which is done by the `rewriters` field. ### Define a Rewriter Our goal is to find keyword arguments in the `dict` function call and transform them into dictionary key-value pairs. So let's first define a rule to match the keyword arguments in the `dict` function call. ```yaml rule: kind: keyword_argument all: - has: field: name pattern: $KEY - has: field: value pattern: $VAL ``` This rule can match the keyword arguments in the `dict` function call and extract key and value in the argument to meta-variables `$KEY` and `$VAL` respectively. [For example](https://ast-grep.github.io/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiIiwicmV3cml0ZSI6IiIsInN0cmljdG5lc3MiOiJzbWFydCIsInNlbGVjdG9yIjoic3RhcnRfdGFnIiwiY29uZmlnIjoicnVsZTpcbiAga2luZDoga2V5d29yZF9hcmd1bWVudFxuICBhbGw6XG4gIC0gaGFzOlxuICAgICAgZmllbGQ6IG5hbWVcbiAgICAgIHBhdHRlcm46ICRLRVlcbiAgLSBoYXM6XG4gICAgICBmaWVsZDogdmFsdWVcbiAgICAgIHBhdHRlcm46ICRWQUwiLCJzb3VyY2UiOiJkID0gZGljdChhPTEsIGI9MikifQ==), `dict(a=1)` will extract `a` to `$KEY` and `1` to `$VAL`. Then, we define the rule as a rewriter and add fix field to transform the keyword argument to a dictionary key-value pair. ```yaml rewriters: - id: dict-rewrite rule: kind: keyword_argument all: - has: field: name pattern: $KEY - has: field: value pattern: $VAL fix: "'$KEY': $VAL" ``` You can see the `rewriters` field accepts a list of regular ast-grep rules. Rewriter rule must have an `id` field to identify the rewriter, a rule to specify the node to match, and a `fix` field to transform the matched node. Applying the rule above alone will transform `a=1` to `'a': 1`. But it is not enough to replace the `dict` function call. We need to combine these pairs and wrap them in a bracket. We need to apply this rewriter to all keyword arguments and join them. ### Apply Rewriter Now, we apply the rewriter to the `dict` function call. This is done by the `transform` field. First, we match the `dict` function call with the pattern `dict($$$ARGS)`. The `$$$ARGS` is a special metavariable that matches all arguments of the function call. Then, we apply the rewriter `dict-rewrite` to the `$$$ARGS` and store the result in a new metavariable `LITERAL`. ```yaml rule: pattern: dict($$$ARGS) # match dict function call, capture $$$ARGS transform: LITERAL: # the transformed code rewrite: rewriters: [dict-rewrite] # specify the rewriter defined above source: $$$ARGS # apply rewriters to $$$ARGS arguments ``` ast-grep will first try match the `dict-rewrite` rule to each sub node inside `$$$ARGS`. If the node has a matching rule, ast-grep will extract the node specified by the meta-variables in the `dict-rewrite` rewriter rule. It will then generate a new string using the `fix`. Finally, the generated strings replace the matched sub-nodes in the `$$$ARGS` and the new code is stored in the `LITERAL` metavariable. For example, `dict(a=1, b=2)` will match the `$$$ARGS` as `a=1, b=2`. The rewriter will transform `a=1` to `'a': 1` and `b=2` to `'b': 2`. The final value of `LITERAL` will be `'a': 1, 'b': 2`. ### Combine and Replace Finally, we combine the transformed keyword arguments and replace the `dict` function call. ```yaml # define rewriters rewriters: - id: dict-rewrite rule: kind: keyword_argument all: - has: field: name pattern: $KEY - has: field: value pattern: $VAL fix: "'$KEY': $VAL" # find the target node rule: pattern: dict($$$ARGS) # apply rewriters to sub node transform: LITERAL: rewrite: rewriters: [dict-rewrite] source: $$$ARGS # combine and replace fix: '{ $LITERAL }' ``` See the final result in [action](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiZGljdCgkJCRBUkdTKSIsInJld3JpdGUiOiIiLCJzdHJpY3RuZXNzIjoic21hcnQiLCJzZWxlY3RvciI6IiIsImNvbmZpZyI6IiMgZGVmaW5lIHJld3JpdGVyc1xucmV3cml0ZXJzOlxuLSBpZDogZGljdC1yZXdyaXRlXG4gIHJ1bGU6XG4gICAga2luZDoga2V5d29yZF9hcmd1bWVudFxuICAgIGFsbDpcbiAgICAtIGhhczpcbiAgICAgICAgZmllbGQ6IG5hbWVcbiAgICAgICAgcGF0dGVybjogJEtFWVxuICAgIC0gaGFzOlxuICAgICAgICBmaWVsZDogdmFsdWVcbiAgICAgICAgcGF0dGVybjogJFZBTFxuICBmaXg6IFwiJyRLRVknOiAkVkFMXCJcbiMgZmluZCB0aGUgdGFyZ2V0IG5vZGVcbnJ1bGU6XG4gIHBhdHRlcm46IGRpY3QoJCQkQVJHUylcbiMgYXBwbHkgcmV3cml0ZXJzIHRvIHN1YiBub2RlXG50cmFuc2Zvcm06XG4gIExJVEVSQUw6XG4gICAgcmV3cml0ZTpcbiAgICAgIHJld3JpdGVyczogW2RpY3QtcmV3cml0ZV1cbiAgICAgIHNvdXJjZTogJCQkQVJHU1xuIyBjb21iaW5lIGFuZCByZXBsYWNlXG5maXg6ICd7ICRMSVRFUkFMIH0nIiwic291cmNlIjoiZCA9IGRpY3QoYT0xLCBiPTIpIn0=). ## `rewriters` is Top Level Every ast-grep rule can have one `rewriters` at top level. The `rewriters` accepts a list of rewriter rules. Every rewriter rule is like a regular ast-grep rule with `fix`. These are required fields for a rewriter rule. * `id`: A unique identifier for the rewriter to be referenced in the `rewrite` transformation field. * `rule`: A rule object to match the sub node. * `fix`: A string to replace the matched sub node. Rewriter rule can also have other fields like `transform` and `constraints`. However, fields like `severity` and `message` are not available in rewriter rules. Generally, only [Finding](/reference/yaml.html#finding) and [Patching](/reference/yaml.html#patching) fields are allowed in rewriter rules. ## Apply Multiple Rewriters Note that the `rewrite` transformation field can accept multiple rewriters. This allows you to apply multiple rewriters to different sub nodes. If the `source` meta variable contains multiple sub nodes, each sub node will be transformed by the corresponding rewriter that matches the sub node. Suppose we have two rewriters to rewrite numbers and strings. ```yaml rewriters: - id: rewrite-int rule: {kind: integer} fix: integer - id: rewrite-str rule: {kind: string} fix: string ``` We can apply both rewriters to the same source meta-variable. ```yaml rule: {pattern: '[$$$LIST]' } transform: NEW_VAR: rewrite: rewriters: [rewrite-num, rewrite-str] source: $$$LIST ``` In this case, the `rewrite-num` rewriter will be applied to the integer nodes in `$$$LIST`, and the `rewrite-str` rewriter will be applied to the string nodes in `$$$LIST`. The produced `NEW_VAR` will contain the transformed nodes from both rewriters. [For example](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiZGljdCgkJCRBUkdTKSIsInJld3JpdGUiOiIiLCJzdHJpY3RuZXNzIjoic21hcnQiLCJzZWxlY3RvciI6IiIsImNvbmZpZyI6InJld3JpdGVyczpcbi0gaWQ6IHJld3JpdGUtaW50XG4gIHJ1bGU6IHtraW5kOiBpbnRlZ2VyfVxuICBmaXg6IGludGVnZXJcbi0gaWQ6IHJld3JpdGUtc3RyXG4gIHJ1bGU6IHtraW5kOiBzdHJpbmd9XG4gIGZpeDogc3RyaW5nXG5ydWxlOiB7cGF0dGVybjogJ1skJCRMSVNUXScgfVxudHJhbnNmb3JtOlxuICBORVdfVkFSOlxuICAgIHJld3JpdGU6XG4gICAgICByZXdyaXRlcnM6IFtyZXdyaXRlLWludCwgcmV3cml0ZS1zdHJdXG4gICAgICBzb3VyY2U6ICQkJExJU1RcbmZpeDogJE5FV19WQVIiLCJzb3VyY2UiOiJbMSwgJ2EnXSJ9), `[1, 'a']` will be transformed to `integer, string`. :::tip Pro Tip Using multiple rewriters can make you dynamically apply different rewriting logic to different sub nodes, based on the matching rules. ::: In case multiple rewriters match the same sub node, only the matching rewriter that appears first in the `rewriters` list will be applied. Therefore, ***the order of rewriters in the `rewriters` list matters.*** ## Use Alternative Joiner By default, ast-grep will generate the new rewritten string by replacing the text in the matched sub nodes. But you can also specify an alternative joiner to join the transformed sub nodes via `joinBy` field. ```yaml transform: NEW_VAR: rewrite: rewriters: [rewrite-num, rewrite-str] source: $$$LIST joinBy: ' + ' ``` This will transform `1, 2, 3` to `integer + integer + integer`. ## Philosophy behind Rewriters You can see a more detailed design philosophy, *Find and Patch*, behind rewriters in [this page](/advanced/find-n-patch.html). --- # Source: https://ast-grep.github.io/links/roadmap.md --- url: /links/roadmap.md --- # TODO: ## Core * \[x] Add replace * \[x] Add find\_all * \[x] Add metavar char customization * \[x] Add per-language customization * \[x] Add support for vec/sequence matcher * \[x] View node in context * \[x] implement iterative DFS mode * \[ ] Investigate perf heuristic (e.g. match fixed-string) * \[x] Group matching rules based on root pattern kind id * \[ ] Remove unwrap usage and implement error handling ## Metavariable Matcher * \[x] Regex * \[x] Pattern * \[x] Kind * \[ ] Use CoW to optimize MetaVarEnv ## Operators/Combinators * \[x] every / all * \[x] either / any * \[x] inside * \[x] has * \[x] follows * \[x] precedes ## CLI * \[x] match against files in directory recursively * \[x] interactive mode * \[x] as dry run mode (listing all rewrite) * \[x] inplace edit mode * \[x] no-color mode * \[x] JSON output * \[ ] execute remote rules ## Config * \[x] support YAML config rule * \[x] Add support for severity * \[x] Add support for error message * \[x] Add support for error labels * \[x] Add support for fix ## Binding * \[ ] NAPI binding * \[x] WASM binding * \[ ] Python binding ## Playground * \[x] build a playground based on WASM binding * \[x] build YAML config for WASM playground * \[x] URL sharing * \[x] add fix/rewrite ## LSP * \[x] Add LSP command * \[ ] implement LSP incremental * \[ ] add code action ## Builtin Ruleset * \[ ] Migrate some ESLint rule (or RSLint rule) --- # Source: https://ast-grep.github.io/catalog/ruby.md --- url: /catalog/ruby.md --- # Ruby This page curates a list of example ast-grep rules to check and to rewrite Ruby applications. ## Migrate action\_filter in Ruby on Rails * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InJ1YnkiLCJxdWVyeSI6ImNvbnNvbGUubG9nKCRNQVRDSCkiLCJyZXdyaXRlIjoibG9nZ2VyLmxvZygkTUFUQ0gpIiwiY29uZmlnIjoiIyBhc3QtZ3JlcCBZQU1MIFJ1bGUgaXMgcG93ZXJmdWwgZm9yIGxpbnRpbmchXG4jIGh0dHBzOi8vYXN0LWdyZXAuZ2l0aHViLmlvL2d1aWRlL3J1bGUtY29uZmlnLmh0bWwjcnVsZVxucnVsZTpcbiAgYW55OlxuICAgIC0gcGF0dGVybjogYmVmb3JlX2ZpbHRlciAkJCRBQ1RJT05cbiAgICAtIHBhdHRlcm46IGFyb3VuZF9maWx0ZXIgJCQkQUNUSU9OXG4gICAgLSBwYXR0ZXJuOiBhZnRlcl9maWx0ZXIgJCQkQUNUSU9OXG4gIGhhczpcbiAgICBwYXR0ZXJuOiAkRklMVEVSXG4gICAgZmllbGQ6IG1ldGhvZFxuZml4OiBcbiAgJE5FV19BQ1RJT04gJCQkQUNUSU9OXG50cmFuc2Zvcm06XG4gIE5FV19BQ1RJT046XG4gICAgcmVwbGFjZTpcbiAgICAgIHNvdXJjZTogJEZJTFRFUlxuICAgICAgcmVwbGFjZTogX2ZpbHRlclxuICAgICAgYnk6IF9hY3Rpb24iLCJzb3VyY2UiOiJjbGFzcyBUb2Rvc0NvbnRyb2xsZXIgPCBBcHBsaWNhdGlvbkNvbnRyb2xsZXJcbiAgYmVmb3JlX2ZpbHRlciA6YXV0aGVudGljYXRlXG4gIGFyb3VuZF9maWx0ZXIgOndyYXBfaW5fdHJhbnNhY3Rpb24sIG9ubHk6IDpzaG93XG4gIGFmdGVyX2ZpbHRlciBkbyB8Y29udHJvbGxlcnxcbiAgICBmbGFzaFs6ZXJyb3JdID0gXCJZb3UgbXVzdCBiZSBsb2dnZWQgaW5cIlxuICBlbmRcblxuICBkZWYgaW5kZXhcbiAgICBAdG9kb3MgPSBUb2RvLmFsbFxuICBlbmRcbmVuZFxuIn0=) ### Description This rule is used to migrate `{before,after,around}_filter` to `{before,after,around}_action` in Ruby on Rails controllers. These are methods that run before, after or around an action is executed, and they can be used to check permissions, set variables, redirect requests, log events, etc. However, these methods are [deprecated](https://stackoverflow.com/questions/16519828/rails-4-before-filter-vs-before-action) in Rails 5.0 and will be removed in Rails 5.1. `{before,after,around}_action` are the new syntax for the same functionality. This rule will replace all occurrences of `{before,after,around}_filter` with `{before,after,around}_action` in the controller code. ### YAML ```yaml id: migration-action-filter language: ruby rule: any: - pattern: before_filter $$$ACTION - pattern: around_filter $$$ACTION - pattern: after_filter $$$ACTION has: pattern: $FILTER field: method fix: $NEW_ACTION $$$ACTION transform: NEW_ACTION: replace: source: $FILTER replace: _filter by: _action ``` ### Example ```rb {2-4} class TodosController < ApplicationController before_filter :authenticate around_filter :wrap_in_transaction, only: :show after_filter do |controller| flash[:error] = "You must be logged in" end def index @todos = Todo.all end end ``` ### Diff ```rb class TodosController < ApplicationController before_action :authenticate # [!code --] before_filter :authenticate # [!code ++] around_action :wrap_in_transaction, only: :show # [!code --] around_filter :wrap_in_transaction, only: :show # [!code ++] after_action do |controller| # [!code --] flash[:error] = "You must be logged in" # [!code --] end # [!code --] after_filter do |controller| # [!code ++] flash[:error] = "You must be logged in" # [!code ++] end # [!code ++] def index @todos = Todo.all end end ``` ### Contributed by [Herrington Darkholme](https://twitter.com/hd_nvim), inspired by [Future of Ruby - AST Tooling](https://dev.to/baweaver/future-of-ruby-ast-tooling-9i1). ## Prefer Symbol over Proc * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InJ1YnkiLCJxdWVyeSI6IiRMSVNULnNlbGVjdCB7IHwkVnwgJFYuJE1FVEhPRCB9IiwicmV3cml0ZSI6IiRMSVNULnNlbGVjdCgmOiRNRVRIT0QpIiwiY29uZmlnIjoiaWQ6IHByZWZlci1zeW1ib2wtb3Zlci1wcm9jXG5ydWxlOlxuICBwYXR0ZXJuOiAkTElTVC4kSVRFUiB7IHwkVnwgJFYuJE1FVEhPRCB9XG5sYW5ndWFnZTogUnVieVxuY29uc3RyYWludHM6XG4gIElURVI6XG4gICAgcmVnZXg6ICdtYXB8c2VsZWN0fGVhY2gnXG5maXg6ICckTElTVC4kSVRFUigmOiRNRVRIT0QpJ1xuIiwic291cmNlIjoiWzEsIDIsIDNdLnNlbGVjdCB7IHx2fCB2LmV2ZW4/IH1cbigxLi4xMDApLmVhY2ggeyB8aXwgaS50b19zIH1cbm5vdF9saXN0Lm5vX21hdGNoIHsgfHZ8IHYuZXZlbj8gfVxuIn0=) ### Description Ruby has a more concise symbol shorthand `&:` to invoke methods. This rule simplifies `proc` to `symbol`. This example is inspired by this [dev.to article](https://dev.to/baweaver/future-of-ruby-ast-tooling-9i1). ### YAML ```yaml id: prefer-symbol-over-proc language: ruby rule: pattern: $LIST.$ITER { |$V| $V.$METHOD } constraints: ITER: regex: 'map|select|each' fix: '$LIST.$ITER(&:$METHOD)' ``` ### Example ```rb {1,2} [1, 2, 3].select { |v| v.even? } (1..100).each { |i| i.to_s } not_list.no_match { |v| v.even? } ``` ### Diff ```rb [1, 2, 3].select { |v| v.even? } # [!code --] [1, 2, 3].select(&:even?) # [!code ++] (1..100).each { |i| i.to_s } # [!code --] (1..100).each(&:to_s) # [!code ++] not_list.no_match { |v| v.even? } ``` ### Contributed by [Herrington Darkholme](https://twitter.com/hd_nvim) ## Detect Path Traversal Vulnerability in Rails * [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InJ1YnkiLCJxdWVyeSI6IiIsInJld3JpdGUiOiIiLCJzdHJpY3RuZXNzIjoiYXN0Iiwic2VsZWN0b3IiOiIiLCJjb25maWciOiJpZDogcGF0aC10cmF2ZXJzYWxcbm1lc3NhZ2U6IFBvdGVudGlhbCBQYXRoIFRyYXZlcnNhbCB2dWxuZXJhYmlsaXR5IGRldGVjdGVkLiBVc2VyIGlucHV0IGlzIGJlaW5nIHVzZWQgdG8gY29uc3RydWN0IGZpbGUgcGF0aHMgd2l0aG91dCB2YWxpZGF0aW9uLlxuc2V2ZXJpdHk6IGhpbnRcbmxhbmd1YWdlOiBSdWJ5XG5ub3RlOiB8XG4gIFBhdGggVHJhdmVyc2FsIChEaXJlY3RvcnkgVHJhdmVyc2FsKSBvY2N1cnMgd2hlbiB1c2VyIGlucHV0IGlzIHVzZWQgdG8gY29uc3RydWN0IGZpbGUgcGF0aHNcbiAgd2l0aG91dCBwcm9wZXIgdmFsaWRhdGlvbi4gVGhpcyBhbGxvd3MgYXR0YWNrZXJzIHRvIGFjY2VzcyBmaWxlcyBvdXRzaWRlIHRoZSBpbnRlbmRlZCBkaXJlY3RvcnkuXG4gIFZhbGlkYXRlIGFuZCBzYW5pdGl6ZSBmaWxlIHBhdGhzLCBhbmQgdXNlIEZpbGUuYmFzZW5hbWUoKSBvciBzaW1pbGFyIGZ1bmN0aW9ucy5cblxucnVsZTpcbiAgYW55OlxuICAgIC0gcGF0dGVybjogUmFpbHMucm9vdC5qb2luKCQkJCwgJFZBUiwgJCQkKVxuICAgIC0gcGF0dGVybjogRmlsZS5qb2luKCQkJCwgJFZBUiwgJCQkKVxuICAgIC0gcGF0dGVybjogc2VuZF9maWxlICRWQVIiLCJzb3VyY2UiOiIjIOaknOWHuuOBleOCjOOCi+OCs+ODvOODieS+i1xuIyDjg5Hjgr/jg7zjg7MxOiBSYWlscy5yb290LmpvaW4gd2l0aCB2YXJpYWJsZVxuUmFpbHMucm9vdC5qb2luKCd1cGxvYWRzJywgcGFyYW1zWzpmaWxlbmFtZV0pXG5SYWlscy5yb290LmpvaW4oJ2RhdGEnLCB1c2VyX2lucHV0LCAnZmlsZS50eHQnKVxuXG4jIOODkeOCv+ODvOODszI6IEZpbGUuam9pbiB3aXRoIHZhcmlhYmxlXG5GaWxlLmpvaW4oJy92YXIvd3d3JywgcGFyYW1zWzpwYXRoXSlcbkZpbGUuam9pbihiYXNlX3BhdGgsIHVzZXJfaWQsIGZpbGVuYW1lKVxuXG4jIOODkeOCv+ODvOODszM6IHNlbmRfZmlsZSB3aXRoIHZhcmlhYmxlXG5zZW5kX2ZpbGUgcGFyYW1zWzpmaWxlXVxuc2VuZF9maWxlIHVzZXIuZG9jdW1lbnRfcGF0aCJ9) ### Description Path Traversal (Directory Traversal) occurs when user input is used to construct file paths without proper validation. This allows attackers to access files outside the intended directory by using special characters like `../` to navigate the filesystem. This rule detects common path traversal patterns in Rails applications where user-controlled variables are used in: * `Rails.root.join()` - Building file paths relative to the Rails application root * `File.join()` - Constructing file paths * `send_file` - Sending files to users To prevent path traversal vulnerabilities, always validate and sanitize file paths, use `File.basename()` to extract only the filename, or use allowlists for permitted files. ### YAML ```yaml id: path-traversal message: Potential Path Traversal vulnerability detected. User input is being used to construct file paths without validation. severity: hint language: Ruby note: | Path Traversal (Directory Traversal) occurs when user input is used to construct file paths without proper validation. This allows attackers to access files outside the intended directory. Validate and sanitize file paths, and use File.basename() or similar functions. rule: any: - pattern: Rails.root.join($$$, $VAR, $$$) - pattern: File.join($$$, $VAR, $$$) - pattern: send_file $VAR ``` ### Example ```rb {2,3,6,7,10,11} # Pattern 1: Rails.root.join with variable Rails.root.join('uploads', params[:filename]) Rails.root.join('data', user_input, 'file.txt') # Pattern 2: File.join with variable File.join('/var/www', params[:path]) File.join(base_path, user_id, filename) # Pattern 3: send_file with variable send_file params[:file] send_file user.document_path ``` ### Contributed by [sora fs0414](https://x.com/_fs0414) from this [blog post](https://fs0414.hatenablog.com/entry/2025/11/02/032114) --- # Source: https://ast-grep.github.io/guide/rule-config.md --- url: /guide/rule-config.md --- # Rule Essentials Now you have learnt the basic of ast-grep's pattern syntax and searching. Pattern is a handy feature for simple search. But it is not expressive enough for more complicated cases. ast-grep provides a more sophisticated way to find your code: Rule. Rules are like [CSS selectors](https://www.w3schools.com/cssref/css_selectors.php) that can compose together to filter AST nodes based on certain criteria. ## A Minimal Example A minimal ast-grep rule looks like this. ```yaml id: no-await-in-promise-all language: TypeScript rule: pattern: Promise.all($A) has: pattern: await $_ stopBy: end ``` The *TypeScript* rule, *no-await-in-promise-all*, will find `Promise.all` that **has** `await` expression in it. It is [suboptimal](https://github.com/hugo-vrijswijk/eslint-plugin-no-await-in-promise/) because `Promise.all` will be called [only after](https://twitter.com/hd_nvim/status/1560108625460355073) the awaited Promise resolves first. Let's walk through the main fields in this configuration. * `id` is a unique short string for the rule. * `language` is the programming language that the rule is intended to check. It specifies what files will be checked against this rule, based on the file extensions. See the list of [supported languages](/reference/languages.html). * `rule` is the most interesting part of ast-grep's configuration. It accepts a [rule object](/reference/rule.html) and defines how the rule behaves and what code will be matched. You can learn how to write rule in the [detailed guide](/guide/rule-config/atomic-rule). ## Run the Rule There are several ways to run the rule. We will illustrate several ast-grep features here. ### `ast-grep scan --rule` The `scan` subcommand of ast-grep CLI can run one rule at a time. To do so, you need to save the rule above in a file on the disk, say `no-await-in-promise-all.yml`. Then you can run the following command to scan your codebase. In the example below, we are scanning a `test.ts` file. ::: code-group ```bash ast-grep scan --rule no-await-in-promise-all.yml test.ts ``` ```typescript await Promise.all([ await foo(), ]) ``` ::: ### `ast-grep scan --inline-rules` You can also run the rule directly from the command line without saving the rule to a file. The `--inline-rules` option is useful for ad-hoc search or calling ast-grep from another program. :::details The full inline-rules command ```bash ast-grep scan --inline-rules ' id: no-await-in-promise-all language: TypeScript rule: pattern: Promise.all($A) has: pattern: await $_ stopBy: end ' test.ts ``` ::: ### Online Playground ast-grep provides an online [playground](https://ast-grep.github.io/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6ImphdmFzY3JpcHQiLCJxdWVyeSI6IlByb21pc2UuYWxsKCRBKSIsInJld3JpdGUiOiIiLCJjb25maWciOiJpZDogbm8tYXdhaXQtaW4tcHJvbWlzZS1hbGxcbmxhbmd1YWdlOiBUeXBlU2NyaXB0XG5ydWxlOlxuICBwYXR0ZXJuOiBQcm9taXNlLmFsbCgkQSlcbiAgaGFzOlxuICAgIHBhdHRlcm46IGF3YWl0ICRfXG4gICAgc3RvcEJ5OiBlbmQiLCJzb3VyY2UiOiJQcm9taXNlLmFsbChbXG4gIGF3YWl0IFByb21pc2UucmVzb2x2ZSgxMjMpXG5dKSJ9) to test your rule. You can paste the rule configuration into the playground and see the matched code. The playground also has a share button that generates a link to share the rule with others. ## Rule Object *Rule object is the core concept of ast-grep's rule system and every other features are built on top of it.* Below is the full list of fields in a rule object. Every rule field is optional and can be omitted but at least one field should be present in a rule. A node will match a rule if and only if it satisfies all fields in the rule object. The equivalent rule object interface in TypeScript is also provided for reference. :::code-group ```yaml [Full Rule Object] rule: # atomic rule pattern: 'search.pattern' kind: 'tree_sitter_node_kind' regex: 'rust|regex' # relational rule inside: { pattern: 'sub.rule' } has: { kind: 'sub_rule' } follows: { regex: 'can|use|any' } precedes: { kind: 'multi_keys', pattern: 'in.sub' } # composite rule all: [ {pattern: 'match.all'}, {kind: 'match_all'} ] any: [ {pattern: 'match.any'}, {kind: 'match_any'} ] not: { pattern: 'not.this' } matches: 'utility-rule' ``` ```typescript [TS Interface] interface RuleObject { // atomic rule pattern?: string | Pattern kind?: string regex?: string // relational rule inside?: RuleObject & Relation has?: RuleObject & Relation follows?: RuleObject & Relation precedes?: RuleObject & Relation // composite rule all?: RuleObject[] any?: RuleObject[] not?: RuleObject matches?: string } // See Atomic rule for explanation interface Pattern { context: string selector: string strictness?: Strictness } // See https://ast-grep.github.io/advanced/match-algorithm.html type Strictness = | 'cst' | 'smart' | 'ast' | 'relaxed' | 'signature' // See Relation rule for explanation interface Relation { stopBy?: 'neighbor' | 'end' | RuleObject field?: string } ``` ::: A node must **satisfies all fields** in the rule object to be considered as a match. So the rule object can be seen as an abbreviated and **unordered** `all` rule. :::warning Rule object is unordered!! Unordered rule object means that certain rules may be applied before others, even if they appear later in the YAML. Whether a node matches or not may depend on the order of rule being applied, especially when using `has`/`inside` rules. If a rule object does not work, you can try using `all` rule to specify the order of rules. See [FAQ](/advanced/faq.html#why-is-rule-matching-order-sensitive) for more details. ::: ## Three Rule Categories To summarize the rule object fields above, we have three categories of rules: * **Atomic Rule**: the most basic rule that checks if AST nodes matches. * **Relational Rule**: rules that check if a node is surrounded by another node. * **Composite Rule**: rules that combine sub-rules together using logical operators. These three categories of rules can be composed together to create more complex rules. The *rule object is inspired by the CSS selectors* but with more composability and expressiveness. Think about how selectors in CSS works can help you understand the rule object! :::tip Don't be daunted! Learn more about how to write a rule in our [detailed guide](/guide/rule-config/atomic-rule). ::: ## Target Node Every rule configuration will have one single root `rule`. The root rule will have *only one* AST node in one match. The matched node is called target node. During scanning and rewriting, ast-grep will produce multiple matches to report all AST nodes that satisfies the `rule` condition as matched instances. Though one rule match only have one AST node as matched, we can have more auxiliary nodes to display context or to perform rewrite. We will cover how rules work in details in the next page. But for a quick primer, a rule can have a pattern and we can extract meta variables from the matched node. For example, the rule below will match the `console.log('Hello World')`. ```yaml rule: pattern: console.log($GREET) ``` And we can get `$GREET` set to `'Hello World'`. ## `language` specifies `rule` interpretation The `language` field in the rule configuration will specify how the rule is interpreted. For example, with `language: TypeScript`, the rule pattern `'hello world'` is parsed as TypeScript string literal. However, the rule will have a parsing error in languages like C/Java/Rust because single quote is used for character literal and double quote should be used for string. --- # Source: https://ast-grep.github.io/catalog/rule-template.md --- url: /catalog/rule-template.md --- ## Your Rule Name * [Playground Link](/playground.html#) ### Description Some Description for your rule! ### Pattern ```shell ast-grep -p pattern -r rewrite -l js # or without fixer ast-grep -p pattern -l js ``` ### YAML ```yaml ``` ### Example ```js {1} var a = 123 ``` ### Diff ```js var a = 123 // [!code --] let a = 123 // [!code ++] ``` ### Contributed by [Author Name](https://your-social.link) --- # Source: https://ast-grep.github.io/reference/rule.md # Source: https://ast-grep.github.io/cheatsheet/rule.md --- url: /cheatsheet/rule.md --- # Rule Cheat Sheet This cheat sheet provides a concise overview of ast-grep's rule object configuration, covering Atomic, Relational, and Composite rules, along with notes on Utility rules. It's designed as a handy reference for common usage. ## Atomic Rules Cheat Sheet These are your precision tools, matching individual AST nodes based on their inherent properties. ```yaml pattern: console.log($ARG) ``` 🧩 Match a node by code structure. e.g. `console.log` call with a single `$ARG` ```yaml pattern: context: '{ key: value }' selector: pair ``` 🧩 To parse ambiguous patterns, use `context` and specify `selector` AST to search. ```yaml kind: if_statement ``` 🏷️ Match an AST node by its `kind` name ```yaml regex: ^regex.+$ ``` 🔍 Matches node text content against a [Rust regular expression](https://docs.rs/regex/latest/regex/) ```yaml nthChild: 1 ``` 🔢 Find a node by its **1-based index** among its *named siblings* ```yaml nthChild: position: 2 reverse: true ofRule: { kind: argument_list } ``` 🔢 Advanced positional control: `position`, `reverse` (count from end), or filter siblings using `ofRule` ```yaml range: start: { line: 0, column: 0 } end: { line: 0, column: 13 } ``` 🎯 Matches a node based on its character span: 0-based, inclusive start, exclusive end ## Relational Rules Cheat Sheet These powerful rules define how nodes relate to each other structurally. Think of them as your AST GPS! ```yaml inside: kind: function_declaration ``` 🏠 Target node must appear **inside** its *parent/ancestor* node matching the sub-rule ```yaml has: kind: method_definition ``` 🌳 Target node must **have** a *child/descendant* node matching the sub-rule ```yaml has: kind: statement_block field: body ``` 🌳 `field` makes `has`/`inside` match nodes by their [semantic role](/advanced/core-concepts.html#kind-vs-field) ```yaml precedes: pattern: function $FUNC() { $$ } ``` ◀️ Target node must appear *before* another node matching the sub-rule ```yaml follows: pattern: let x = 10; ``` ▶️ Target node must appear *after* another node matching the sub-rule. ```yaml inside: kind: function_declaration stopBy: end ``` 🏠 `stopBy` makes relational rules search all the way to the end, not just immediate neighbors. ## Composite Rules Cheat Sheet Combine multiple rules using Boolean logic. Crucially, these operations apply to a single target node! ```yaml all: - pattern: const $VAR = $VALUE - has: { kind: string_literal } ``` ✅ Node must satisfy **ALL** the rules in the list. ```yaml any: - pattern: let $X = $Y - pattern: const $X = $Y ``` 🧡 Node must satisfy **AT LEAST ONE** of the rules in the list. ```yaml not: pattern: console.log($$) ``` 🚫 Node must **NOT** satisfy the specified sub-rule. ```yaml matches: is-function-call ``` 🔄 Matches the node if that utility rule matches it. Your gateway to modularity! ## Utility Rules Cheat Sheet Define reusable rule definitions to cut down on duplication and build complex, maintainable rule sets. ```yaml rules: - id: find-my-pattern rule: matches: my-local-check utils: my-local-check: kind: identifier regex: '^my' ``` 🏡 Defined within the `utils` field of your current config file. Only accessible within that file. ```yaml # In utils/my-global-check.yml id: my-global-check language: javascript rule: kind: variable_declarator has: kind: number_literal ``` 🌍 Defined in separate YAML files in global `utilsDirs` folders, accessible across your entire project. --- # Source: https://ast-grep.github.io/reference/cli/run.md --- url: /reference/cli/run.md --- # `ast-grep run` Run one time search or rewrite in command line. This is the default command when you run the CLI, so `ast-grep -p 'foo()'` is equivalent to `ast-grep run -p 'foo()'`. ## Usage ```shell ast-grep run [OPTIONS] --pattern [PATHS]... ``` ## Arguments `[PATHS]...` The paths to search. You can provide multiple paths separated by spaces \[default: `.`] ## Run Specific Options ### `-p, --pattern ` AST pattern to match ### `-r, --rewrite ` String to replace the matched AST node ### `-l, --lang ` The language of the pattern. For full language list, visit https://ast-grep.github.io/reference/languages.html ### `--debug-query[=]` Print query pattern's tree-sitter AST. Requires lang be set explicitly. Possible values: * **pattern**: Print the query parsed in Pattern format * **ast**: Print the query in tree-sitter AST format, only named nodes are shown * **cst**: Print the query in tree-sitter CST format, both named and unnamed nodes are shown * **sexp**: Print the query in S-expression format ### `--selector ` AST kind to extract sub-part of pattern to match. selector defines the sub-syntax node kind that is the actual matcher of the pattern. See https://ast-grep.github.io/guide/rule-config/atomic-rule.html#pattern-object. ### `--strictness ` The strictness of the pattern. More strict algorithm will match less code. See [match algorithm deep dive](/advanced/match-algorithm.html) for more details. Possible values: * **cst**: Match exact all node * **smart**: Match all node except source trivial nodes * **ast**: Match only ast nodes * **relaxed**: Match ast node except comments * **signature**: Match ast node except comments, without text \[default: smart] ## Input Options ### `--no-ignore ` Do not respect hidden file system or ignore files (.gitignore, .ignore, etc.). You can suppress multiple ignore files by passing `no-ignore` multiple times. Possible values: * **hidden**: Search hidden files and directories. By default, hidden files and directories are skipped * **dot**: Don't respect .ignore files. This does *not* affect whether ast-grep will ignore files and directories whose names begin with a dot. For that, use --no-ignore hidden * **exclude**: Don't respect ignore files that are manually configured for the repository such as git's '.git/info/exclude' * **global**: Don't respect ignore files that come from "global" sources such as git's `core.excludesFile` configuration option (which defaults to `$HOME/.config/git/ignore`) * **parent**: Don't respect ignore files (.gitignore, .ignore, etc.) in parent directories * **vcs**: Don't respect version control ignore files (.gitignore, etc.). This implies --no-ignore parent for VCS files. Note that .ignore files will continue to be respected ### `--stdin` Enable search code from StdIn. Use this if you need to take code stream from standard input. ### `--globs ` Include or exclude file paths. Include or exclude files and directories for searching that match the given glob. This always overrides any other ignore logic. Multiple glob flags may be used. Globbing rules match .gitignore globs. Precede a glob with a `!` to exclude it. If multiple globs match a file or directory, the glob given later in the command line takes precedence. ### `--follow` Follow symbolic links. This flag instructs ast-grep to follow symbolic links while traversing directories. This behavior is disabled by default. Note that ast-grep will check for symbolic link loops and report errors if it finds one. ast-grep will also report errors for broken links. ## Output Options ### `-i, --interactive` Start interactive edit session. You can confirm the code change and apply it to files selectively, or you can open text editor to tweak the matched code. Note that code rewrite only happens inside a session. ### `-j, --threads ` Set the approximate number of threads to use. This flag sets the approximate number of threads to use. A value of 0 (which is the default) causes ast-grep to choose the thread count using heuristics. \[default: 0] ### `-U, --update-all` Apply all rewrite without confirmation if true ### `--json[=