[POC] Prune Unused Methods in Default Mode #65
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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. I apologize for the wall-o-text, there's a 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 (c32665f) 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 formx.Y
wherex
is any expression (often an identifier, but not necessarily), andY
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)()
notx(.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).GetSelectorsOn
returns a[]string
of all the unique identifiers that were selected on expressions of the same type as the passedtypes.Type
. It works by walking the AST while maintaining the currenttypes.Scope
so the expression part of everyast.SelectorExpr
can be evaluated to a specific type. A mapping fromast.Node
totypes.Scope
is provided by passing atypes.Info
struct to thetypes.Config.Check
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 ofast.FuncDecl.Body
statements is actually underast.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:
// +gen
and use the not-yet-generated plural type and methods as needed.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.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.gen
a couple more times to fully prune the project. I'm not sure why this is necessary. (I told you, rough edges 馃槃)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 with instructions for how it would work. The current procedure is less than ideal and could be greatly improved.