 6a979fb40d
			
		
	
	
		6a979fb40d
		
			
		
	
	
	
	
		
			
			* update staticcheck Don't fork staticcheck: use the upstream version. Remove unneeded SSA loading. * Cache go/analysis facts Don't load unneeded packages for go/analysis. Repeated run of go/analysis linters now 10x faster (2s vs 20s on this repo) than before.
		
			
				
	
	
		
			145 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			145 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package facts
 | |
| 
 | |
| import (
 | |
| 	"go/ast"
 | |
| 	"go/token"
 | |
| 	"go/types"
 | |
| 	"reflect"
 | |
| 	"strings"
 | |
| 
 | |
| 	"golang.org/x/tools/go/analysis"
 | |
| )
 | |
| 
 | |
| type IsDeprecated struct{ Msg string }
 | |
| 
 | |
| func (*IsDeprecated) AFact()           {}
 | |
| func (d *IsDeprecated) String() string { return "Deprecated: " + d.Msg }
 | |
| 
 | |
| type DeprecatedResult struct {
 | |
| 	Objects  map[types.Object]*IsDeprecated
 | |
| 	Packages map[*types.Package]*IsDeprecated
 | |
| }
 | |
| 
 | |
| var Deprecated = &analysis.Analyzer{
 | |
| 	Name:       "fact_deprecated",
 | |
| 	Doc:        "Mark deprecated objects",
 | |
| 	Run:        deprecated,
 | |
| 	FactTypes:  []analysis.Fact{(*IsDeprecated)(nil)},
 | |
| 	ResultType: reflect.TypeOf(DeprecatedResult{}),
 | |
| }
 | |
| 
 | |
| func deprecated(pass *analysis.Pass) (interface{}, error) {
 | |
| 	var names []*ast.Ident
 | |
| 
 | |
| 	extractDeprecatedMessage := func(docs []*ast.CommentGroup) string {
 | |
| 		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)
 | |
| 			return alt
 | |
| 		}
 | |
| 		return ""
 | |
| 	}
 | |
| 	doDocs := func(names []*ast.Ident, docs []*ast.CommentGroup) {
 | |
| 		alt := extractDeprecatedMessage(docs)
 | |
| 		if alt == "" {
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		for _, name := range names {
 | |
| 			obj := pass.TypesInfo.ObjectOf(name)
 | |
| 			pass.ExportObjectFact(obj, &IsDeprecated{alt})
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	var docs []*ast.CommentGroup
 | |
| 	for _, f := range pass.Files {
 | |
| 		docs = append(docs, f.Doc)
 | |
| 	}
 | |
| 	if alt := extractDeprecatedMessage(docs); alt != "" {
 | |
| 		// Don't mark package syscall as deprecated, even though
 | |
| 		// it is. A lot of people still use it for simple
 | |
| 		// constants like SIGKILL, and I am not comfortable
 | |
| 		// telling them to use x/sys for that.
 | |
| 		if pass.Pkg.Path() != "syscall" {
 | |
| 			pass.ExportPackageFact(&IsDeprecated{alt})
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	docs = docs[:0]
 | |
| 	for _, f := range pass.Files {
 | |
| 		fn := func(node ast.Node) bool {
 | |
| 			if node == nil {
 | |
| 				return true
 | |
| 			}
 | |
| 			var ret bool
 | |
| 			switch node := node.(type) {
 | |
| 			case *ast.GenDecl:
 | |
| 				switch node.Tok {
 | |
| 				case token.TYPE, token.CONST, token.VAR:
 | |
| 					docs = append(docs, node.Doc)
 | |
| 					return true
 | |
| 				default:
 | |
| 					return false
 | |
| 				}
 | |
| 			case *ast.FuncDecl:
 | |
| 				docs = append(docs, node.Doc)
 | |
| 				names = []*ast.Ident{node.Name}
 | |
| 				ret = false
 | |
| 			case *ast.TypeSpec:
 | |
| 				docs = append(docs, node.Doc)
 | |
| 				names = []*ast.Ident{node.Name}
 | |
| 				ret = true
 | |
| 			case *ast.ValueSpec:
 | |
| 				docs = append(docs, node.Doc)
 | |
| 				names = node.Names
 | |
| 				ret = false
 | |
| 			case *ast.File:
 | |
| 				return true
 | |
| 			case *ast.StructType:
 | |
| 				for _, field := range node.Fields.List {
 | |
| 					doDocs(field.Names, []*ast.CommentGroup{field.Doc})
 | |
| 				}
 | |
| 				return false
 | |
| 			case *ast.InterfaceType:
 | |
| 				for _, field := range node.Methods.List {
 | |
| 					doDocs(field.Names, []*ast.CommentGroup{field.Doc})
 | |
| 				}
 | |
| 				return false
 | |
| 			default:
 | |
| 				return false
 | |
| 			}
 | |
| 			if len(names) == 0 || len(docs) == 0 {
 | |
| 				return ret
 | |
| 			}
 | |
| 			doDocs(names, docs)
 | |
| 
 | |
| 			docs = docs[:0]
 | |
| 			names = nil
 | |
| 			return ret
 | |
| 		}
 | |
| 		ast.Inspect(f, fn)
 | |
| 	}
 | |
| 
 | |
| 	out := DeprecatedResult{
 | |
| 		Objects:  map[types.Object]*IsDeprecated{},
 | |
| 		Packages: map[*types.Package]*IsDeprecated{},
 | |
| 	}
 | |
| 
 | |
| 	for _, fact := range pass.AllObjectFacts() {
 | |
| 		out.Objects[fact.Object] = fact.Fact.(*IsDeprecated)
 | |
| 	}
 | |
| 	for _, fact := range pass.AllPackageFacts() {
 | |
| 		out.Packages[fact.Package] = fact.Fact.(*IsDeprecated)
 | |
| 	}
 | |
| 
 | |
| 	return out, nil
 | |
| }
 |