+++ /dev/null
-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
-}