Permalink
Browse files

Detect composite literals that should be conversions

  • Loading branch information...
1 parent aa6f9e7 commit f6eddb4a78cd18862190fcd24a298f3ae2f4671e @dominikh committed Sep 17, 2016
Showing with 140 additions and 0 deletions.
  1. +2 −0 README.md
  2. +98 −0 lint.go
  3. +7 −0 lint17.go
  4. +7 −0 lint18.go
  5. +26 −0 testdata/convert.go
View
@@ -77,6 +77,8 @@ constructs:
instead
- Don't use `_ = <-ch`, use `<-ch` instead
- Use `strconv.Itoa` instead of `strconv.FormatInt` when it's simpler.
+- Don't use a struct composite literal when a simple type conversion
+ is enough
## gofmt -r
View
@@ -30,6 +30,7 @@ var Funcs = []lint.Func{
LintSimplerReturn,
LintReceiveIntoBlank,
LintFormatInt,
+ LintSimplerStructConversion,
}
func LintSingleCaseSelect(f *lint.File) {
@@ -960,3 +961,100 @@ func LintFormatInt(f *lint.File) {
}
f.Walk(fn)
}
+
+func LintSimplerStructConversion(f *lint.File) {
+ fn := func(node ast.Node) bool {
+ lit, ok := node.(*ast.CompositeLit)
+ if !ok {
+ return true
+ }
+ typ1 := f.Pkg.TypesInfo.TypeOf(lit.Type)
+ if typ1 == nil {
+ return true
+ }
+ // FIXME support pointer to struct
+ s1, ok := typ1.Underlying().(*types.Struct)
+ if !ok {
+ return true
+ }
+
+ n := s1.NumFields()
+ var typ2 types.Type
+ var ident *ast.Ident
+ getSelType := func(expr ast.Expr) (types.Type, *ast.Ident, bool) {
+ sel, ok := expr.(*ast.SelectorExpr)
+ if !ok {
+ return nil, nil, false
+ }
+ ident, ok := sel.X.(*ast.Ident)
+ if !ok {
+ return nil, nil, false
+ }
+ typ := f.Pkg.TypesInfo.TypeOf(sel.X)
+ return typ, ident, typ != nil
+ }
+ if len(lit.Elts) == 0 {
+ return true
+ }
+ for i, elt := range lit.Elts {
+ n--
+ var t types.Type
+ var id *ast.Ident
+ var ok bool
+ switch elt := elt.(type) {
+ case *ast.SelectorExpr:
+ t, id, ok = getSelType(elt)
+ if !ok {
+ return true
+ }
+ if i >= s1.NumFields() || s1.Field(i).Name() != elt.Sel.Name {
+ return true
+ }
+ case *ast.KeyValueExpr:
+ var sel *ast.SelectorExpr
+ sel, ok = elt.Value.(*ast.SelectorExpr)
+ if !ok {
+ return true
+ }
+
+ if elt.Key.(*ast.Ident).Name != sel.Sel.Name {
+ return true
+ }
+ t, id, ok = getSelType(elt.Value)
+ }
+ if !ok {
+ return true
+ }
+ if typ2 != nil && typ2 != t {
+ return true
+ }
+ if ident != nil && ident.Obj != id.Obj {
+ return true
+ }
+ typ2 = t
+ ident = id
+ }
+
+ if n != 0 {
+ return true
+ }
+
+ if typ2 == nil {
+ return true
+ }
+
+ s2, ok := typ2.Underlying().(*types.Struct)
+ if !ok {
+ return true
+ }
+ if typ1 == typ2 {
+ return true
+ }
+ if !structsIdentical(s1, s2) {
+ return true
+ }
+ f.Errorf(node, 1, "should use type conversion instead of struct literal")
+ return true
+ }
+ f.Walk(fn)
+}
View
@@ -0,0 +1,7 @@
+// +build !go1.8
+
+package simple
+
+import "go/types"
+
+var structsIdentical = types.Identical
View
@@ -0,0 +1,7 @@
+// +build go1.8
+
+package simple
+
+import "go/types"
+
+var structsIdentical = types.IdenticalIgnoreTags
View
@@ -0,0 +1,26 @@
+package pkg
+
+type t1 struct {
+ a int
+ b int
+}
+
+type t2 struct {
+ a int
+ b int
+}
+
+type t3 t1
+
+func fn() {
+ v1 := t1{1, 2}
+ _ = t2{v1.a, v1.b} // MATCH /should use type conversion/
+ _ = t2{a: v1.a, b: v1.b} // MATCH /should use type conversion/
+ _ = t2{b: v1.b, a: v1.a} // MATCH /should use type conversion/
+ _ = t3{v1.a, v1.b} // MATCH /should use type conversion/
+
+ _ = t2{v1.b, v1.a}
+ _ = t2{a: v1.b, b: v1.a}
+ _ = t2{a: v1.a}
+ _ = t1{v1.a, v1.b}
+}

0 comments on commit f6eddb4

Please sign in to comment.