deprecated.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package facts
  2. import (
  3. "go/ast"
  4. "go/token"
  5. "go/types"
  6. "reflect"
  7. "strings"
  8. "golang.org/x/tools/go/analysis"
  9. )
  10. type IsDeprecated struct{ Msg string }
  11. func (*IsDeprecated) AFact() {}
  12. func (d *IsDeprecated) String() string { return "Deprecated: " + d.Msg }
  13. type DeprecatedResult struct {
  14. Objects map[types.Object]*IsDeprecated
  15. Packages map[*types.Package]*IsDeprecated
  16. }
  17. var Deprecated = &analysis.Analyzer{
  18. Name: "fact_deprecated",
  19. Doc: "Mark deprecated objects",
  20. Run: deprecated,
  21. FactTypes: []analysis.Fact{(*IsDeprecated)(nil)},
  22. ResultType: reflect.TypeOf(DeprecatedResult{}),
  23. }
  24. func deprecated(pass *analysis.Pass) (interface{}, error) {
  25. var names []*ast.Ident
  26. extractDeprecatedMessage := func(docs []*ast.CommentGroup) string {
  27. for _, doc := range docs {
  28. if doc == nil {
  29. continue
  30. }
  31. parts := strings.Split(doc.Text(), "\n\n")
  32. last := parts[len(parts)-1]
  33. if !strings.HasPrefix(last, "Deprecated: ") {
  34. continue
  35. }
  36. alt := last[len("Deprecated: "):]
  37. alt = strings.Replace(alt, "\n", " ", -1)
  38. return alt
  39. }
  40. return ""
  41. }
  42. doDocs := func(names []*ast.Ident, docs []*ast.CommentGroup) {
  43. alt := extractDeprecatedMessage(docs)
  44. if alt == "" {
  45. return
  46. }
  47. for _, name := range names {
  48. obj := pass.TypesInfo.ObjectOf(name)
  49. pass.ExportObjectFact(obj, &IsDeprecated{alt})
  50. }
  51. }
  52. var docs []*ast.CommentGroup
  53. for _, f := range pass.Files {
  54. docs = append(docs, f.Doc)
  55. }
  56. if alt := extractDeprecatedMessage(docs); alt != "" {
  57. // Don't mark package syscall as deprecated, even though
  58. // it is. A lot of people still use it for simple
  59. // constants like SIGKILL, and I am not comfortable
  60. // telling them to use x/sys for that.
  61. if pass.Pkg.Path() != "syscall" {
  62. pass.ExportPackageFact(&IsDeprecated{alt})
  63. }
  64. }
  65. docs = docs[:0]
  66. for _, f := range pass.Files {
  67. fn := func(node ast.Node) bool {
  68. if node == nil {
  69. return true
  70. }
  71. var ret bool
  72. switch node := node.(type) {
  73. case *ast.GenDecl:
  74. switch node.Tok {
  75. case token.TYPE, token.CONST, token.VAR:
  76. docs = append(docs, node.Doc)
  77. return true
  78. default:
  79. return false
  80. }
  81. case *ast.FuncDecl:
  82. docs = append(docs, node.Doc)
  83. names = []*ast.Ident{node.Name}
  84. ret = false
  85. case *ast.TypeSpec:
  86. docs = append(docs, node.Doc)
  87. names = []*ast.Ident{node.Name}
  88. ret = true
  89. case *ast.ValueSpec:
  90. docs = append(docs, node.Doc)
  91. names = node.Names
  92. ret = false
  93. case *ast.File:
  94. return true
  95. case *ast.StructType:
  96. for _, field := range node.Fields.List {
  97. doDocs(field.Names, []*ast.CommentGroup{field.Doc})
  98. }
  99. return false
  100. case *ast.InterfaceType:
  101. for _, field := range node.Methods.List {
  102. doDocs(field.Names, []*ast.CommentGroup{field.Doc})
  103. }
  104. return false
  105. default:
  106. return false
  107. }
  108. if len(names) == 0 || len(docs) == 0 {
  109. return ret
  110. }
  111. doDocs(names, docs)
  112. docs = docs[:0]
  113. names = nil
  114. return ret
  115. }
  116. ast.Inspect(f, fn)
  117. }
  118. out := DeprecatedResult{
  119. Objects: map[types.Object]*IsDeprecated{},
  120. Packages: map[*types.Package]*IsDeprecated{},
  121. }
  122. for _, fact := range pass.AllObjectFacts() {
  123. out.Objects[fact.Object] = fact.Fact.(*IsDeprecated)
  124. }
  125. for _, fact := range pass.AllPackageFacts() {
  126. out.Packages[fact.Package] = fact.Fact.(*IsDeprecated)
  127. }
  128. return out, nil
  129. }