-
Notifications
You must be signed in to change notification settings - Fork 132
#488 type def first checker #960
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
#488 type def first checker #960
Conversation
| switch decl := declaration.(type) { | ||
| case *ast.FuncDecl: | ||
| if decl.Recv != nil { | ||
| receiver := decl.Recv.List[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should I check the array for not nilness? as far as I understood, in this case (where its receiver) there are always should be some fields, but the safer the better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can add a simple func in golden files if it fails - better to add :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not fail, because it goes after decl.Recv != nil where Recv is recivier
checkers/utils.go
Outdated
| } | ||
| } | ||
|
|
||
| func NodeToString(set *token.FileSet, node ast.Node) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the project already has similar function plz, point my nose
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not 100% sure that we need this func yet 👀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so, what should we do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK, the receiver name node is either *ast.Ident or *ast.StarExpr. So instead of doing an AST->string->trimAsterisk you can do:
func (c *typeDefFirstChecker) receiverType(e ast.Expr) string {
switch e := e.(type) {
case *ast.Pointer:
return c.receiverType(e.X)
case *ast.Ident:
return e.Name
default:
panic("unreachable")
}
}The trimAsterisk function, if we even wanted it, should be a method of the checker type. All helpers that are not generally useful should be encapsulated as a checker detail. As a general rule: no top-level functions should be defined inside the checker file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, I am not familiar with all type relations, maybe some map or guide exists? That makes trimAsterisk
unnecessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the closest thing is Go spec.
Although it's not always the most precise source as it defines valid receiver in its grammar as a normal param, but you can't have a slice type for a receiver, for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sgtm
| var info linter.CheckerInfo | ||
| info.Name = "typeDefFirst" | ||
| info.Tags = []string{"style", "experimental"} | ||
| info.Summary = "File-scoped checker, that requires type definition before its method definitions" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
File-scoped checker I'm curious - should be use this phrase in other checkers? maybe another tag or subtype? @quasilyte :)
| switch decl := declaration.(type) { | ||
| case *ast.FuncDecl: | ||
| if decl.Recv != nil { | ||
| receiver := decl.Recv.List[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can add a simple func in golden files if it fails - better to add :)
checkers/utils.go
Outdated
| package checkers | ||
|
|
||
| import ( | ||
| "github.com/go-toolsmith/astfmt" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move it after "github.com/go-critic/go-critic/framework/linter" please
checkers/utils.go
Outdated
| } | ||
| } | ||
|
|
||
| func NodeToString(set *token.FileSet, node ast.Node) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not 100% sure that we need this func yet 👀
|
I can give a review when Oleg comments are addressed. :) |
checkers/typeDefFirst_checker.go
Outdated
| case *ast.GenDecl: | ||
| if decl.Tok == token.TYPE { | ||
| for _, spec := range decl.Specs { | ||
| if spec, ok := spec.(*ast.TypeSpec); ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can decrease the nesting even further:
for _, spec := range decl.Specs {
spec, ok := spec.(*ast.TypeSpec)
if !ok {
continue
}This idiom is quite common.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know, but continue in nested for it is a little harder to understand, that is why I asked
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You know the solution to that: don't nest a lot of fors. :)
It's always possible to define another method and call that, so you don't have too much control flow on the same level.
But I'm OK with the current level of nesting. AST requires a lot of things like this, unfortunately (GenDecl is the most horrible one, the next one is assignment-related stuff).
|
The algorithm looks good. NodeToString function can be either replaced with the proposed type switch. Another way is to use |
|
Thanks for the detailed review, has updated request, using type switch now. |
|
I'm almost positive that we'll need to figure out a better name for this checker. But it's OK for now as it's marked as experimental. |
Follow-up for the #960 Signed-off-by: Iskander Sharipov <[email protected]>
Follow-up for the #960 Signed-off-by: Iskander Sharipov <[email protected]>
Add checker, that requires to declare the definition of type before its methods (#488)