Skip to content

Commit a162815

Browse files
catalog: add recursive rewriter example
fix #681
1 parent dfcd0e5 commit a162815

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed

website/catalog/python/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ This page curates a list of example ast-grep rules to check and to rewrite Pytho
77
<!--@include: ./use-walrus-operator-in-if.md-->
88
<!--@include: ./remove-async-await.md-->
99
<!--@include: ./refactor-pytest-fixtures.md-->
10-
<!--@include: ./optional-to-none-union.md-->
10+
<!--@include: ./optional-to-none-union.md-->
11+
<!--@include: ./recursive-rewrite-type.md-->
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
## Recursive Rewrite Type <Badge type="tip" text="Has Fix" />
2+
3+
4+
* [Playground Link](/playground.html#eyJtb2RlIjoiQ29uZmlnIiwibGFuZyI6InB5dGhvbiIsInF1ZXJ5IjoiIiwicmV3cml0ZSI6IiIsInN0cmljdG5lc3MiOiJzbWFydCIsInNlbGVjdG9yIjoiIiwiY29uZmlnIjoicmV3cml0ZXJzOlxyXG4tIGlkOiBvcHRpb25hbFxyXG4gIGxhbmd1YWdlOiBQeXRob25cclxuICBydWxlOlxyXG4gICAgYW55OlxyXG4gICAgLSBwYXR0ZXJuOlxyXG4gICAgICAgIGNvbnRleHQ6ICdhcmc6IE9wdGlvbmFsWyRUWVBFXSdcclxuICAgICAgICBzZWxlY3RvcjogZ2VuZXJpY190eXBlXHJcbiAgICAtIHBhdHRlcm46IE9wdGlvbmFsWyRUWVBFXVxyXG4gIHRyYW5zZm9ybTpcclxuICAgIE5UOlxyXG4gICAgICByZXdyaXRlOiBcclxuICAgICAgICByZXdyaXRlcnM6IFtvcHRpb25hbCwgdW5pb25zXVxyXG4gICAgICAgIHNvdXJjZTogJFRZUEVcclxuICBmaXg6ICROVCB8IE5vbmVcclxuLSBpZDogdW5pb25zXHJcbiAgbGFuZ3VhZ2U6IFB5dGhvblxyXG4gIHJ1bGU6XHJcbiAgICBwYXR0ZXJuOlxyXG4gICAgICBjb250ZXh0OiAnYTogVW5pb25bJCQkVFlQRVNdJ1xyXG4gICAgICBzZWxlY3RvcjogZ2VuZXJpY190eXBlXHJcbiAgdHJhbnNmb3JtOlxyXG4gICAgVU5JT05TOlxyXG4gICAgICByZXdyaXRlOlxyXG4gICAgICAgIHJld3JpdGVyczpcclxuICAgICAgICAgIC0gcmV3cml0ZS11bmlvbnNcclxuICAgICAgICBzb3VyY2U6ICQkJFRZUEVTXHJcbiAgICAgICAgam9pbkJ5OiBcIiB8IFwiXHJcbiAgZml4OiAkVU5JT05TXHJcbi0gaWQ6IHJld3JpdGUtdW5pb25zXHJcbiAgcnVsZTpcclxuICAgIHBhdHRlcm46ICRUWVBFXHJcbiAgICBraW5kOiB0eXBlXHJcbiAgdHJhbnNmb3JtOlxyXG4gICAgTlQ6XHJcbiAgICAgIHJld3JpdGU6IFxyXG4gICAgICAgIHJld3JpdGVyczogW29wdGlvbmFsLCB1bmlvbnNdXHJcbiAgICAgICAgc291cmNlOiAkVFlQRVxyXG4gIGZpeDogJE5UXHJcbnJ1bGU6XHJcbiAga2luZDogdHlwZVxyXG4gIHBhdHRlcm46ICRUUEVcclxudHJhbnNmb3JtOlxyXG4gIE5FV19UWVBFOlxyXG4gICAgcmV3cml0ZTogXHJcbiAgICAgIHJld3JpdGVyczogW29wdGlvbmFsLCB1bmlvbnNdXHJcbiAgICAgIHNvdXJjZTogJFRQRVxyXG5maXg6ICRORVdfVFlQRSIsInNvdXJjZSI6InJlc3VsdHM6ICBPcHRpb25hbFtVbmlvbltMaXN0W1VuaW9uW3N0ciwgZGljdF1dLCBzdHJdXVxuIn0=)
5+
6+
### Description
7+
8+
Suppose we want to transform Python's `Union[T1, T2]` to `T1 | T2` and `Optional[T]` to `T | None`.
9+
10+
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.
11+
12+
So if you are using non-recurisve 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.
13+
14+
However, we can apply `rewriters` to inner types recursively. Take the `optional` rewriter as an example, we need to apply rewriters, `optional` and `unioins`, **recursively** to `$TYPE` and get a new variable `$NT`.
15+
16+
### YAML
17+
```yml
18+
id: recursive-rewrite-types
19+
language: python
20+
rewriters:
21+
# rewrite Optional[T] to T | None
22+
- id: optional
23+
rule:
24+
any:
25+
- pattern:
26+
context: 'arg: Optional[$TYPE]'
27+
selector: generic_type
28+
- pattern: Optional[$TYPE]
29+
# recursively apply rewriters to $TYPE
30+
transform:
31+
NT:
32+
rewrite:
33+
rewriters: [optional, unions]
34+
source: $TYPE
35+
# use the new variable $NT
36+
fix: $NT | None
37+
38+
# similar to Optional, rewrite Union[T1, T2] to T1 | T2
39+
- id: unions
40+
language: Python
41+
rule:
42+
pattern:
43+
context: 'a: Union[$$$TYPES]'
44+
selector: generic_type
45+
transform:
46+
UNIONS:
47+
# rewrite all types inside $$$TYPES
48+
rewrite:
49+
rewriters: [ rewrite-unions ]
50+
source: $$$TYPES
51+
joinBy: " | "
52+
fix: $UNIONS
53+
- id: rewrite-unions
54+
rule:
55+
pattern: $TYPE
56+
kind: type
57+
# recursive part
58+
transform:
59+
NT:
60+
rewrite:
61+
rewriters: [optional, unions]
62+
source: $TYPE
63+
fix: $NT
64+
65+
# find all types
66+
rule:
67+
kind: type
68+
pattern: $TPE
69+
# apply the recursive rewriters
70+
transform:
71+
NEW_TYPE:
72+
rewrite:
73+
rewriters: [optional, unions]
74+
source: $TPE
75+
# output
76+
fix: $NEW_TYPE
77+
```
78+
79+
80+
### Example
81+
82+
<!-- highlight matched code in curly-brace {lineNum} -->
83+
```python
84+
results: Optional[Union[List[Union[str, dict]], str]]
85+
```
86+
87+
### Diff
88+
<!-- use # [!code --] and # [!code ++] to annotate diff -->
89+
```python
90+
results: Optional[Union[List[Union[str, dict]], str]] # [!code --]
91+
results: List[str | dict] | str | None #[!code ++]
92+
```
93+
94+
### Contributed by
95+
Inspired by [steinuil](https://github.com/ast-grep/ast-grep/discussions/1566)

0 commit comments

Comments
 (0)