Skip to content
This repository has been archived by the owner on May 9, 2021. It is now read-only.

Commit

Permalink
Use typechecked information to suppress type inference warning, where…
Browse files Browse the repository at this point in the history
… possible.

This does not solve false positives in every situation; notably it will
fail to work with multi-file packages; see #43.

Fixes #7.
  • Loading branch information
dsymonds committed Sep 2, 2014
1 parent c8f2dfe commit a9a6f59
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 10 deletions.
37 changes: 30 additions & 7 deletions lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ import (
"unicode"
"unicode/utf8"

"code.google.com/p/go.tools/go/gcimporter"
"code.google.com/p/go.tools/go/types"

_ "code.google.com/p/go.tools/go/gcimporter" // use gcimporter as types.DefaultImport
)

const styleGuideBase = "http://golang.org/s/comments"
Expand Down Expand Up @@ -155,19 +154,34 @@ argLoop:
f.problems = append(f.problems, problem)
}

var gcImporter = gcimporter.Import

func (f *file) typeCheck() error {
// Do typechecking without errors so we do as much as possible.
config := &types.Config{}
f.typesInfo = &types.Info{
config := &types.Config{
Import: gcImporter,
}
info := &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
}
var err error
f.typesPkg, err = config.Check(f.f.Name.Name, f.fset, []*ast.File{f.f}, f.typesInfo)
pkg, err := config.Check(f.f.Name.Name, f.fset, []*ast.File{f.f}, info)
if err != nil {
return err
}
f.typesPkg = pkg
f.typesInfo = info
return err
}

func (f *file) typeOf(expr ast.Expr) types.Type {
if f.typesInfo == nil {
return nil
}
return f.typesInfo.TypeOf(expr)
}

func (f *file) scanSortable() {
f.sortable = make(map[string]bool)

Expand Down Expand Up @@ -750,11 +764,20 @@ func (f *file) lintVarDecls() {
f.errorf(rhs, 0.9, category("zero-value"), "should drop = %s from declaration of var %s; it is the zero value", f.render(rhs), v.Names[0])
return false
}
lhsTyp := f.typeOf(v.Type)
rhsTyp := f.typeOf(rhs)
if lhsTyp != nil && rhsTyp != nil && !types.Identical(lhsTyp, rhsTyp) {
// Assignment to a different type is not redundant.
return false
}

// The next two conditions are for suppressing the warning in situations
// where we were unable to typecheck.

// If the LHS type is an interface, don't warn, since it is probably a
// concrete type on the RHS. Note that our feeble lexical check here
// will only pick up interface{} and other literal interface types;
// that covers most of the cases we care to exclude right now.
// TODO(dsymonds): Use typechecker to make this heuristic more accurate.
if _, ok := v.Type.(*ast.InterfaceType); ok {
return false
}
Expand Down
13 changes: 10 additions & 3 deletions testdata/var-decl.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@
package foo

import "fmt"
import "net/http"

// Q is a test type.
type Q bool

var mux *http.ServeMux = http.NewServeMux() // MATCH /should.*\*http\.ServeMux.*inferred/
var myInt int = 7 // MATCH /should.*int.*myInt.*inferred/
var myInt int = 7 // MATCH /should.*int.*myInt.*inferred/

var myZeroInt int = 0 // MATCH /should.*= 0.*myZeroInt.*zero value/
var myZeroFlt float32 = 0. // MATCH /should.*= 0\..*myZeroFlt.*zero value/
Expand Down Expand Up @@ -54,3 +52,12 @@ var _ Server = (*serverImpl)(nil)
// Server is a test type.
type Server interface{}
type serverImpl struct{}

// LHS is a different type than the RHS.
var myStringer fmt.Stringer = q(0)

var y string = q(1).String() // MATCH /should.*string/

type q int

func (q) String() string { return "I'm a q" }

0 comments on commit a9a6f59

Please sign in to comment.