Skip to content

Generate accessors for structs with pointer fields #543

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 0 commits into from

Conversation

gmlewis
Copy link
Collaborator

@gmlewis gmlewis commented Feb 3, 2017

Fixes #45.

Change-Id: Ib2b6cc5d713e2eb833ee3c7fcfbd804bfe8fa313

@dmitshur
Copy link
Member

dmitshur commented Feb 3, 2017

Thanks for working on this @gmlewis. Before I look at the details of the implementation, I'd like to discuss this change at a higher level. I have some concerns about the general direction and I'd like to see where others stand. Let's maybe do that in #45 itself, and leave this PR to discuss implementation details. I'll ping you there.

@dmitshur
Copy link
Member

As I posted in #45 (comment), my potential concerns are resolved and I have no objections to doing this. I'll leave some review comments on the implementation now.

@dmitshur
Copy link
Member

dmitshur commented Feb 14, 2017

First, some high level questions/thoughts for @gmlewis, looking only at the final API after this PR.

  1. The "Get" prefix is slightly unfortunate, given Go style suggests to avoid "Get" prefix from getters (source: https://golang.org/doc/effective_go.html#Getters).

    However, I understand this is unavoidable because otherwise field name and method name would have same name, right? Also, this is the same solution that protobuf uses and is therefore consistent, right?

    Just mentioning this, I don't think we need to change it. Also, one way to justify the "Get" prefix is that it's not a simple getter, it has logic for returning zero value if the pointer is nil.

  2. The documentation text for methods seems to prioritize mentioning the nil case:

    // GetMessage returns the zero value if nil, otherwise the actual value.
    

    Is that the way to go? I'd expect the nil case to be the less common path, and the actual value being the primary path. So would it better to reflect that in the docs by swapping the order? Something that mentions the actual value first, nil case second, like:

    // GetMessage returns the Message field if it's non-nil, zero value otherwise.
    

    (That phrasing doesn't sound better, but perhaps it can be tweaked. Just want to get your thoughts.)

  3. This is actually somewhat implementation-related. Currently, this PR generates 56 "-accessors.go" files. Given they're all generated and most users/developers won't need to look at/modify the detailed contents of those files, maybe it'd be better to consolidate them all in a single generated .go file?

    That way, when working on this package, there are less files get in your way that you don't want to modify. Also smaller risk of accidentally modifying a generated file, because there are fewer of them.

    Compare the file list before and after this PR.

    Please don't change this yet, I just want to get your thoughts on this first. We should consider the trade-offs of both approaches.

@gmlewis
Copy link
Collaborator Author

gmlewis commented Feb 14, 2017

@shurcooL - addressing your questions/thoughts:

  1. Yes, "Get" is consistent with the proto2 implementation.
  2. I went through a few iterations on the comment, and am fine with changing it. Both versions seem clear to me, but am happy to emphasize as suggested.
  3. Sure, I'm happy to consolidate to a single file... I was originally thinking it would be nice to easily jump to just the accessors for a particular file, but with all the editor tooling we have these days, maybe a single file is better after all.

@willnorris
Copy link
Collaborator

willnorris commented Feb 14, 2017 via email

github/github.go Outdated
// go-github authors should run the following commands before
// sending PRs to GitHub:
//
// cd github.com/go-github
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cd github.com/go-github? That doesn't sound right.

github/github.go Outdated
// cd github.com/go-github
// go generate ./...
// go test ./...
// go vet ./...
Copy link
Member

@dmitshur dmitshur Feb 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about deleting the cd step and making these commands use absolute import path patterns rather than relative? E.g.:

go generate github.com/google/go-github/...

Then the same command can be run from any directory, and it'll do the right thing.

// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// github-accessors.go generated by gen-accessors; DO NOT EDIT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The standardized generated file disclaimer header format not official yet, but it seems to be on its way to become so. Let's start following that standard.

// Code generated by gen-accessors; DO NOT EDIT.

//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

Copy link
Member

@dmitshur dmitshur Feb 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should consider adding:

// +build ignore

It won't affect go generate functionality since the .go file is being go run directly.

But it'll prevent a poorly named and unneccessary tools binary from being placed in users $GOPATH/bin directory if they install all packages in go-github repository. Thoughts?

In fact, is there an advantage in placing this file in a separate directory, instead of just inside github folder, the same folder as the github package where it's being used?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want gen-accessors.go in the same github directory because it is not in the github package and may cause confusion. It seems to be common practice to move command-line tools to a subdirectory called tools or cmd or tools/cmd or cmd/tools... I'm flexible with the directory name, but I think we want to keep it in a separate directory.

// {{.Filename}} generated by gen-accessors; DO NOT EDIT

package {{.Package}}
{{if .Imports}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible to simplify the template in this instance by using {{with .Imports}} instead of {{if .Imports}}.

if err := sourceTmpl.Execute(&buf, t); err != nil {
return err
}
clean, err := format.Source(buf.Bytes())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does gofmting the output from template have an effect, i.e., does it change something, or is it being done "just in case"?

If it changes something, what is it? Asking because I'm curious.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it correctly converts spaces to tabs. I worked on this project:
https://godoc.org/google.golang.org/api
and calling gofmt is standard operating procedure when generating Go code.

@gmlewis
Copy link
Collaborator Author

gmlewis commented Feb 19, 2017

I believe I've addressed the review comments from @shurcooL.
PTAL.

github/github.go Outdated
@@ -3,6 +3,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// go-github authors should run the following commands before
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these instructions should be added to CONTRIBUTING.md. Either in addition to, or probably instead of, here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

@dmitshur dmitshur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gmlewis, let me reply to one of your inline comments here, because it's really hard to track conversations in inline comments in large PRs. You said:

We don't want gen-accessors.go in the same github directory because it is not in the github package and may cause confusion. It seems to be common practice to move command-line tools to a subdirectory called tools or cmd or tools/cmd or cmd/tools... I'm flexible with the directory name, but I think we want to keep it in a separate directory.

Given your latest changes (to add // +build ignore and rename directory to gen-accessors, although the directory name doesn't matter as long as there's a // +build ignore), I'm okay with the current version an don't have objections to it being merged as is.

However, I don't fully agree with what you said, so I wanted to share my perspective for your consideration. You should consider my counter-points and then proceed with what you think this the best approach.

In my experience and from what I've seen done more commonly in the Go project, its subrepos, and 3rd party projects, the general pattern that's used is roughly as follows.

However, gen-accessors.go is a small, single-file generator. It generates code very specific to the github package, and I think it therefore belongs in the github package (directory). I think having it here would not be more confusing. That way, the github package is more self-contained: every single file needed to build the package, and every file needed to generate a part of the package, are contained in the package's directory.

"ErrorResponse.GetResponse": true,
"RateLimitError.GetResponse": true,
"AbuseRateLimitError.GetResponse": true,
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a question. What's the pattern of what's included in the blacklist?

Are all entries here simply methods that already exist, that happen to begin with "Get"? And the blacklist is needed to prevent the generated code from creating a duplicate method name, is that so?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pattern is <receiver type> + "." + <method name>.

Yes, the blacklist prevents these methods from being created... either because they already exist or because they would otherwise create problems if they were to be created.

}

// Sort getters by ReceiverType.FieldName
sort.Sort(byName(t.Getters))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, you could use sort.Slice now to make this a little shorter (i.e., byName type and its 3 methods can go away), if you wanted to, since #554 is merged.

(Completely optional, just mentioning it.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tried this with Go 1.7 and it failed.
It looks like sort.Slice is Go 1.8 only:
https://beta.golang.org/doc/go1.8#sort_slice
and since we still support Go 1.7, I will not yet switch this to sort.Slice.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes, my mistake. I was off by one. :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although, this is code that runs during go generate, only by developers of this package, not users. So in theory we could require/expect developers to be running 1.8. But, this is absolutely not worth it to save a few lines. Just a fun observation.

Copy link
Member

@dmitshur dmitshur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@gmlewis
Copy link
Collaborator Author

gmlewis commented Feb 21, 2017

Thank you for the detailed discussion regarding generators, @shurcooL!
I was not aware of this common usage, and it makes sense, so I've moved gen-accessors.go into the github directory.

@gmlewis
Copy link
Collaborator Author

gmlewis commented Feb 24, 2017

Merging.

@gmlewis gmlewis closed this Feb 24, 2017
@gmlewis gmlewis deleted the gen-accessors branch February 24, 2017 19:57
@gmlewis
Copy link
Collaborator Author

gmlewis commented Feb 24, 2017

Integrated in cd756c0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants