clipperhouse/gen — pull request #65 by infogulch (2014-07-11 21:03:37+00:00)
Title: [POC] Prune Unused Methods in Default Mode

In default mode, (where the user just annotates the type and doesn't specify exactly which methods should be generated by using tags) only the methods that are used should be generated, and `gen` should automatically detect these instances.

Here, I demonstrate a proof-of-concept for how this could work. TL;DR at the end.

Note: This PR is a very rough Proof-Of-Concept, and I don't expect it to be merged. Specifically, the last commit (7d5c120) is quite incomplete. This is based on commits from PR #64 because it has functionality that I think is necessary for gen to work on a changing codebase with this feature.

In commit f508d49 I add the method `typewriter.Package.GetSelectorsOn(types.Type)`. In the AST, selector nodes are all the dot-expressions. They have the form `x.Y` where `x` is any expression (often an identifier, but not necessarily), and `Y` is an identifier. Note that there doesn't exist a 'method-call node', instead, a method call is just a function call where the function to call is determined by the result of the selector node instead of a raw identifier. (Think `(x.Method)()` not `x(.Method())`.) It turns out we don't *want* to consider function calls at all, just because we don't want to miss cases where the user saves the result of a selector and calls it later, like: `fn := x.Method; fn();` (which is [perfectly valid][1]).

`GetSelectorsOn` returns a `[]string` of all the unique identifiers that were selected on expressions of the same type as the passed `types.Type`. It works by walking the AST while maintaining the current `types.Scope` so the expression part of every `ast.SelectorExpr` can be evaluated to a specific type. A mapping from `ast.Node` to `types.Scope` is provided by passing a [`types.Info`][2] struct to the [`types.Config.Check`][3] method. Unfortunately, this mapping contains only the scope nodes themselves so a scope hierarchy must be maintained while walking the tree. Worse, the scope node of a given node is not always an ancestor. For example, the scope of `ast.FuncDecl.Body` statements is actually under `ast.FuncDecl.FuncType`. This isn't impossible to work around, it just adds a bit of special case handling, and I haven't tested all possible scope node types for these special cases.

Also, I changed `genwriter.evaluateTags` in commit c32665f to use the new GetSelectorsOn method to prune the generated methods to only the ones that are used.

The current, *not ideal*, workflow for new projects looks like this:
1. Start writing your project files, annotate a type with `// +gen` and use the not-yet-generated plural type and methods as needed.
2. Run `gen`. Because the plural type doesn't yet exist, type checking cannot determine which selectors are called on it yet, so it generates *all methods*.
3. Run `gen` again immediately afterwards. Now that the plural exists, more detailed typechecking can occur. Now it gets a list of exactly what methods were used throughout the project, and it generates those methods.
4. You may need to run `gen` a couple more times to fully prune the project. I'm not sure why this is necessary. (I told you, rough edges :smile:)

Ideally, steps 2-4 would be collapsed down to one step where the user runs `gen` only once (which might execute itself in the background multiple times). The workflow for existing projects starts at step 2 above.

*TL;DR*: [Here's a test file][4] with instructions for how it would work.

[1]: http://play.golang.org/p/9VsK7ohp3e
[2]: https://godoc.org/code.google.com/p/go.tools/go/types#Info
[3]: https://godoc.org/code.google.com/p/go.tools/go/types#Config.Check
[4]: https://gist.github.com/infogulch/cb67ad61a2b06741768f