bazelbuild / buildtools

A bazel BUILD file formatter and editor
Apache License 2.0
1.02k stars 416 forks source link

Walk support deleting Node #1070

Open sluongng opened 2 years ago

sluongng commented 2 years ago

I have a use case where in Golang, we often use Gazelle to generate a bunch of go_repository target in a separate starlark file under a macro. Example:

# deps.bzl

def repo_deps():
    go_repository(
        name = "cc_mvdan_interfacer",
        build_external = "external",
        importpath = "mvdan.cc/interfacer",
        sum = "h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=",
        version = "v0.0.0-20180901003855-c20040233aed",
    )
    go_repository(
        name = "cc_mvdan_lint",
        build_external = "external",
        importpath = "mvdan.cc/lint",
        sum = "h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=",
        version = "v0.0.0-20170908181259-adc824a0674b",
    )

or a more advance use case:

# deps.bzl

def repo_deps(enable_feature = True):
    if enable_feature:
        go_repository(
            name = "cc_mvdan_interfacer",
            build_external = "external",
            importpath = "mvdan.cc/interfacer",
            sum = "h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=",
            version = "v0.0.0-20180901003855-c20040233aed",
        )
    go_repository(
        name = "cc_mvdan_lint",
        build_external = "external",
        importpath = "mvdan.cc/lint",
        sum = "h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=",
        version = "v0.0.0-20170908181259-adc824a0674b",
    )

I want to write a program where I would parse up these deps.bzl file and programatically delete rules under a certain condition: i.e. comparing known name or comparing known version etc...

The problem is that the current API https://pkg.go.dev/github.com/bazelbuild/buildtools@v0.0.0-20220531122519-a43aed7014c8/build#File.DelRules does not support deleting rules inside macro, but assumed that all rules are loaded on the top level of the AST.

I tried tinkering with the Walk function families but none of which seems to allow me to delete a node by setting the Expr pointer to nil?

It seems like the only way to delete a node is to update it's parent node to exclude it from the []Expr body/stmt slice. But as shown in the advance case above, the parent node could be almost anything: a DefStmt, an IfStmt, ForStmt etc... So ideally it would be awesome if Walk api would allow you to set pointer of the node to nil to delete it.


Overall, it would be nice if we can have a way to query and edit Starlark AST similar to how tree-sitter does it today. 🙏

sluongng commented 2 years ago

https://github.com/sluongng/nogo-analyzer/blob/f2aa5a39d502b23cc573e98f8ebeba1489c9e029/private/cmd/deps-prune/main.go#L57-L84

Here is my current implementation, which only support the case where go_repository target was defined within defStmt and does not handle any other cases.