Skip to content

Commit

Permalink
Flag uses of deprecated objects
Browse files Browse the repository at this point in the history
Might interest the folks at golang/lint#238
  • Loading branch information
dominikh committed Dec 3, 2016
1 parent 0ae935b commit ea162f0
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 0 deletions.
1 change: 1 addition & 0 deletions staticcheck/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ The following things are currently checked by staticcheck:
| SA1016 | Trapping a signal that cannot be trapped |
| SA1017 | Channels used with signal.Notify should be buffered |
| SA1018 | `strings.Replace` called with n == 0, which does nothing |
| SA1019 | Using a deprecated function, variable, constant or field |
| | |
| **SA2???** | **Concurrency issues** |
| SA2000 | `sync.WaitGroup.Add` called inside the goroutine, leading to a race condition |
Expand Down
81 changes: 81 additions & 0 deletions staticcheck/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"regexp"
"strconv"
"strings"
"sync"
texttemplate "text/template"
"time"
"unicode/utf8"
Expand Down Expand Up @@ -88,6 +89,9 @@ func (d FunctionDescriptions) Merge(fn *ssa.Function, desc Function) {

type Checker struct {
funcDescs FunctionDescriptions

depmu sync.Mutex
deprecatedObjs map[types.Object]string
}

func NewChecker() *Checker {
Expand Down Expand Up @@ -115,6 +119,7 @@ func (c *Checker) Funcs() map[string]lint.Func {
"SA1016": c.CheckUntrappableSignal,
"SA1017": c.CheckUnbufferedSignalChan,
"SA1018": c.CheckStringsReplaceZero,
"SA1019": c.CheckDeprecated,

"SA2000": c.CheckWaitgroupAdd,
"SA2001": c.CheckEmptyCriticalSection,
Expand Down Expand Up @@ -182,6 +187,7 @@ func terminates(fn *ssa.Function) (ret bool) {

func (c *Checker) Init(prog *lint.Program) {
c.funcDescs = FunctionDescriptions{}
c.deprecatedObjs = map[types.Object]string{}

for fn, desc := range stdlibDescs {
c.funcDescs[fn] = desc
Expand Down Expand Up @@ -2646,3 +2652,78 @@ func (c *Checker) CheckPureFunctions(f *lint.File) {
}
f.Walk(fn)
}

func (c *Checker) CheckDeprecated(f *lint.File) {
c.depmu.Lock()
defer c.depmu.Unlock()

fn := func(node ast.Node) bool {
sel, ok := node.(*ast.SelectorExpr)
if !ok {
return true
}
obj := f.Pkg.TypesInfo.ObjectOf(sel.Sel)
if obj.Pkg() == nil {
return true
}
if f.Pkg.PkgInfo.Pkg == obj.Pkg() || obj.Pkg().Path()+"_test" == f.Pkg.PkgInfo.Pkg.Path() {
// Don't flag stuff in our own package
return true
}
if alt, ok := c.deprecatedObjs[obj]; ok {
if alt == "" {
return true
}
f.Errorf(sel, "%s is deprecated: %s", f.Render(sel), alt)
return true
}
_, path, _ := f.Program.PathEnclosingInterval(obj.Pos(), obj.Pos())
if len(path) <= 2 {
c.deprecatedObjs[obj] = ""
return true
}
var docs []*ast.CommentGroup
switch n := path[1].(type) {
case *ast.FuncDecl:
docs = append(docs, n.Doc)
case *ast.Field:
docs = append(docs, n.Doc)
case *ast.ValueSpec:
docs = append(docs, n.Doc)
if len(path) >= 3 {
if n, ok := path[2].(*ast.GenDecl); ok {
docs = append(docs, n.Doc)
}
}
case *ast.TypeSpec:
docs = append(docs, n.Doc)
if len(path) >= 3 {
if n, ok := path[2].(*ast.GenDecl); ok {
docs = append(docs, n.Doc)
}
}
default:
c.deprecatedObjs[obj] = ""
return true
}

for _, doc := range docs {
if doc == nil {
continue
}
parts := strings.Split(doc.Text(), "\n\n")
last := parts[len(parts)-1]
if !strings.HasPrefix(last, "Deprecated: ") {
continue
}
alt := last[len("Deprecated: "):]
alt = strings.Replace(alt, "\n", " ", -1)
f.Errorf(sel, "%s is deprecated: %s", f.Render(sel), alt)
c.deprecatedObjs[obj] = alt
return true
}
c.deprecatedObjs[obj] = ""
return true
}
f.Walk(fn)
}
18 changes: 18 additions & 0 deletions staticcheck/testdata/CheckDeprecated.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package pkg

import (
"compress/flate"
"net/http"
"os"
"syscall"
)

func fn(err error) {
var r *http.Request
_ = r.Cancel // MATCH /Use the Context and WithContext methods/
_ = syscall.StringByteSlice("") // MATCH /Use ByteSliceFromString instead/
_ = os.SEEK_SET // MATCH /Use io.SeekStart, io.SeekCurrent, and io.SeekEnd/
if err == http.ErrWriteAfterFlush { // MATCH /ErrWriteAfterFlush is no longer used/
}
var _ flate.ReadError // MATCH /No longer returned/
}

0 comments on commit ea162f0

Please sign in to comment.