Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@LevOspennikov
Copy link
Contributor

Add checker, that requires to declare the definition of type before its methods (#488)

switch decl := declaration.(type) {
case *ast.FuncDecl:
if decl.Recv != nil {
receiver := decl.Recv.List[0]
Copy link
Contributor Author

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?

Copy link
Member

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 :)

Copy link
Contributor Author

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

}
}

func NodeToString(set *token.FileSet, node ast.Node) string {
Copy link
Contributor Author

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

Copy link
Member

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 👀

Copy link
Contributor Author

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?

Copy link
Member

@quasilyte quasilyte Aug 16, 2020

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.

Copy link
Contributor Author

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

Copy link
Member

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.

cristaloleg
cristaloleg previously approved these changes Aug 16, 2020
Copy link
Member

@cristaloleg cristaloleg left a 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"
Copy link
Member

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]
Copy link
Member

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 :)

package checkers

import (
"github.com/go-toolsmith/astfmt"
Copy link
Member

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

}
}

func NodeToString(set *token.FileSet, node ast.Node) string {
Copy link
Member

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 👀

@quasilyte
Copy link
Member

I can give a review when Oleg comments are addressed. :)

case *ast.GenDecl:
if decl.Tok == token.TYPE {
for _, spec := range decl.Specs {
if spec, ok := spec.(*ast.TypeSpec); ok {
Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Member

@quasilyte quasilyte Aug 16, 2020

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).

@quasilyte
Copy link
Member

quasilyte commented Aug 16, 2020

The algorithm looks good.

NodeToString function can be either replaced with the proposed type switch. Another way is to use astfmt.Sprint(node) function, but then you'll need to handle * as a text. I think working with AST is better than trimming some text out of the printed representation.

@LevOspennikov
Copy link
Contributor Author

LevOspennikov commented Aug 16, 2020

Thanks for the detailed review, has updated request, using type switch now.

@quasilyte quasilyte merged commit 794687e into go-critic:master Aug 16, 2020
@quasilyte
Copy link
Member

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.

quasilyte added a commit that referenced this pull request Aug 16, 2020
Follow-up for the #960

Signed-off-by: Iskander Sharipov <[email protected]>
quasilyte added a commit that referenced this pull request Aug 17, 2020
Follow-up for the #960

Signed-off-by: Iskander Sharipov <[email protected]>
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