You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

360 regels
8.6 KiB

  1. // Copyright 2013 The Go Authors. All rights reserved.
  2. //
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file or at
  5. // https://developers.google.com/open-source/licenses/bsd.
  6. package doc
  7. import (
  8. "bytes"
  9. "fmt"
  10. "go/ast"
  11. "go/doc"
  12. "go/printer"
  13. "go/scanner"
  14. "go/token"
  15. "math"
  16. "strconv"
  17. )
  18. const (
  19. notPredeclared = iota
  20. predeclaredType
  21. predeclaredConstant
  22. predeclaredFunction
  23. )
  24. // predeclared represents the set of all predeclared identifiers.
  25. var predeclared = map[string]int{
  26. "bool": predeclaredType,
  27. "byte": predeclaredType,
  28. "complex128": predeclaredType,
  29. "complex64": predeclaredType,
  30. "error": predeclaredType,
  31. "float32": predeclaredType,
  32. "float64": predeclaredType,
  33. "int16": predeclaredType,
  34. "int32": predeclaredType,
  35. "int64": predeclaredType,
  36. "int8": predeclaredType,
  37. "int": predeclaredType,
  38. "rune": predeclaredType,
  39. "string": predeclaredType,
  40. "uint16": predeclaredType,
  41. "uint32": predeclaredType,
  42. "uint64": predeclaredType,
  43. "uint8": predeclaredType,
  44. "uint": predeclaredType,
  45. "uintptr": predeclaredType,
  46. "true": predeclaredConstant,
  47. "false": predeclaredConstant,
  48. "iota": predeclaredConstant,
  49. "nil": predeclaredConstant,
  50. "append": predeclaredFunction,
  51. "cap": predeclaredFunction,
  52. "close": predeclaredFunction,
  53. "complex": predeclaredFunction,
  54. "copy": predeclaredFunction,
  55. "delete": predeclaredFunction,
  56. "imag": predeclaredFunction,
  57. "len": predeclaredFunction,
  58. "make": predeclaredFunction,
  59. "new": predeclaredFunction,
  60. "panic": predeclaredFunction,
  61. "print": predeclaredFunction,
  62. "println": predeclaredFunction,
  63. "real": predeclaredFunction,
  64. "recover": predeclaredFunction,
  65. }
  66. type AnnotationKind int16
  67. const (
  68. // Link to export in package specified by Paths[PathIndex] with fragment
  69. // Text[strings.LastIndex(Text[Pos:End], ".")+1:End].
  70. LinkAnnotation AnnotationKind = iota
  71. // Anchor with name specified by Text[Pos:End] or typeName + "." +
  72. // Text[Pos:End] for type declarations.
  73. AnchorAnnotation
  74. // Comment.
  75. CommentAnnotation
  76. // Link to package specified by Paths[PathIndex].
  77. PackageLinkAnnotation
  78. // Link to builtin entity with name Text[Pos:End].
  79. BuiltinAnnotation
  80. )
  81. type Annotation struct {
  82. Pos, End int32
  83. Kind AnnotationKind
  84. PathIndex int16
  85. }
  86. type Code struct {
  87. Text string
  88. Annotations []Annotation
  89. Paths []string
  90. }
  91. // declVisitor modifies a declaration AST for printing and collects annotations.
  92. type declVisitor struct {
  93. annotations []Annotation
  94. paths []string
  95. pathIndex map[string]int
  96. comments []*ast.CommentGroup
  97. }
  98. func (v *declVisitor) add(kind AnnotationKind, importPath string) {
  99. pathIndex := -1
  100. if importPath != "" {
  101. var ok bool
  102. pathIndex, ok = v.pathIndex[importPath]
  103. if !ok {
  104. pathIndex = len(v.paths)
  105. v.paths = append(v.paths, importPath)
  106. v.pathIndex[importPath] = pathIndex
  107. }
  108. }
  109. v.annotations = append(v.annotations, Annotation{Kind: kind, PathIndex: int16(pathIndex)})
  110. }
  111. func (v *declVisitor) ignoreName() {
  112. v.add(-1, "")
  113. }
  114. func (v *declVisitor) Visit(n ast.Node) ast.Visitor {
  115. switch n := n.(type) {
  116. case *ast.TypeSpec:
  117. v.ignoreName()
  118. switch n := n.Type.(type) {
  119. case *ast.InterfaceType:
  120. for _, f := range n.Methods.List {
  121. for _ = range f.Names {
  122. v.add(AnchorAnnotation, "")
  123. }
  124. ast.Walk(v, f.Type)
  125. }
  126. case *ast.StructType:
  127. for _, f := range n.Fields.List {
  128. for _ = range f.Names {
  129. v.add(AnchorAnnotation, "")
  130. }
  131. ast.Walk(v, f.Type)
  132. }
  133. default:
  134. ast.Walk(v, n)
  135. }
  136. case *ast.FuncDecl:
  137. if n.Recv != nil {
  138. ast.Walk(v, n.Recv)
  139. }
  140. v.ignoreName()
  141. ast.Walk(v, n.Type)
  142. case *ast.Field:
  143. for _ = range n.Names {
  144. v.ignoreName()
  145. }
  146. ast.Walk(v, n.Type)
  147. case *ast.ValueSpec:
  148. for _ = range n.Names {
  149. v.add(AnchorAnnotation, "")
  150. }
  151. if n.Type != nil {
  152. ast.Walk(v, n.Type)
  153. }
  154. for _, x := range n.Values {
  155. ast.Walk(v, x)
  156. }
  157. case *ast.Ident:
  158. switch {
  159. case n.Obj == nil && predeclared[n.Name] != notPredeclared:
  160. v.add(BuiltinAnnotation, "")
  161. case n.Obj != nil && ast.IsExported(n.Name):
  162. v.add(LinkAnnotation, "")
  163. default:
  164. v.ignoreName()
  165. }
  166. case *ast.SelectorExpr:
  167. if x, _ := n.X.(*ast.Ident); x != nil {
  168. if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg {
  169. if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil {
  170. if path, err := strconv.Unquote(spec.Path.Value); err == nil {
  171. v.add(PackageLinkAnnotation, path)
  172. if path == "C" {
  173. v.ignoreName()
  174. } else {
  175. v.add(LinkAnnotation, path)
  176. }
  177. return nil
  178. }
  179. }
  180. }
  181. }
  182. ast.Walk(v, n.X)
  183. v.ignoreName()
  184. case *ast.BasicLit:
  185. if n.Kind == token.STRING && len(n.Value) > 128 {
  186. v.comments = append(v.comments,
  187. &ast.CommentGroup{List: []*ast.Comment{{
  188. Slash: n.Pos(),
  189. Text: fmt.Sprintf("/* %d byte string literal not displayed */", len(n.Value)),
  190. }}})
  191. n.Value = `""`
  192. } else {
  193. return v
  194. }
  195. case *ast.CompositeLit:
  196. if len(n.Elts) > 100 {
  197. if n.Type != nil {
  198. ast.Walk(v, n.Type)
  199. }
  200. v.comments = append(v.comments,
  201. &ast.CommentGroup{List: []*ast.Comment{{
  202. Slash: n.Lbrace,
  203. Text: fmt.Sprintf("/* %d elements not displayed */", len(n.Elts)),
  204. }}})
  205. n.Elts = n.Elts[:0]
  206. } else {
  207. return v
  208. }
  209. default:
  210. return v
  211. }
  212. return nil
  213. }
  214. func (b *builder) printDecl(decl ast.Decl) (d Code) {
  215. v := &declVisitor{pathIndex: make(map[string]int)}
  216. ast.Walk(v, decl)
  217. b.buf = b.buf[:0]
  218. err := (&printer.Config{Mode: printer.UseSpaces, Tabwidth: 4}).Fprint(
  219. sliceWriter{&b.buf},
  220. b.fset,
  221. &printer.CommentedNode{Node: decl, Comments: v.comments})
  222. if err != nil {
  223. return Code{Text: err.Error()}
  224. }
  225. var annotations []Annotation
  226. var s scanner.Scanner
  227. fset := token.NewFileSet()
  228. file := fset.AddFile("", fset.Base(), len(b.buf))
  229. s.Init(file, b.buf, nil, scanner.ScanComments)
  230. prevTok := token.ILLEGAL
  231. loop:
  232. for {
  233. pos, tok, lit := s.Scan()
  234. switch tok {
  235. case token.EOF:
  236. break loop
  237. case token.COMMENT:
  238. p := file.Offset(pos)
  239. e := p + len(lit)
  240. if prevTok == token.COMMENT {
  241. annotations[len(annotations)-1].End = int32(e)
  242. } else {
  243. annotations = append(annotations, Annotation{Kind: CommentAnnotation, Pos: int32(p), End: int32(e)})
  244. }
  245. case token.IDENT:
  246. if len(v.annotations) == 0 {
  247. // Oops!
  248. break loop
  249. }
  250. annotation := v.annotations[0]
  251. v.annotations = v.annotations[1:]
  252. if annotation.Kind == -1 {
  253. continue
  254. }
  255. p := file.Offset(pos)
  256. e := p + len(lit)
  257. annotation.Pos = int32(p)
  258. annotation.End = int32(e)
  259. annotations = append(annotations, annotation)
  260. }
  261. prevTok = tok
  262. }
  263. return Code{Text: string(b.buf), Annotations: annotations, Paths: v.paths}
  264. }
  265. func (b *builder) position(n ast.Node) Pos {
  266. var position Pos
  267. pos := b.fset.Position(n.Pos())
  268. src := b.srcs[pos.Filename]
  269. if src != nil {
  270. position.File = int16(src.index)
  271. position.Line = int32(pos.Line)
  272. end := b.fset.Position(n.End())
  273. if src == b.srcs[end.Filename] {
  274. n := end.Line - pos.Line
  275. if n >= 0 && n <= math.MaxUint16 {
  276. position.N = uint16(n)
  277. }
  278. }
  279. }
  280. return position
  281. }
  282. func (b *builder) printExample(e *doc.Example) (code Code, output string) {
  283. output = e.Output
  284. b.buf = b.buf[:0]
  285. var n interface{}
  286. if _, ok := e.Code.(*ast.File); ok {
  287. n = e.Play
  288. } else {
  289. n = &printer.CommentedNode{Node: e.Code, Comments: e.Comments}
  290. }
  291. err := (&printer.Config{Mode: printer.UseSpaces, Tabwidth: 4}).Fprint(sliceWriter{&b.buf}, b.fset, n)
  292. if err != nil {
  293. return Code{Text: err.Error()}, output
  294. }
  295. // additional formatting if this is a function body
  296. if i := len(b.buf); i >= 2 && b.buf[0] == '{' && b.buf[i-1] == '}' {
  297. // remove surrounding braces
  298. b.buf = b.buf[1 : i-1]
  299. // unindent
  300. b.buf = bytes.Replace(b.buf, []byte("\n "), []byte("\n"), -1)
  301. // remove output comment
  302. if j := exampleOutputRx.FindIndex(b.buf); j != nil {
  303. b.buf = bytes.TrimSpace(b.buf[:j[0]])
  304. }
  305. } else {
  306. // drop output, as the output comment will appear in the code
  307. output = ""
  308. }
  309. var annotations []Annotation
  310. var s scanner.Scanner
  311. fset := token.NewFileSet()
  312. file := fset.AddFile("", fset.Base(), len(b.buf))
  313. s.Init(file, b.buf, nil, scanner.ScanComments)
  314. prevTok := token.ILLEGAL
  315. scanLoop:
  316. for {
  317. pos, tok, lit := s.Scan()
  318. switch tok {
  319. case token.EOF:
  320. break scanLoop
  321. case token.COMMENT:
  322. p := file.Offset(pos)
  323. e := p + len(lit)
  324. if prevTok == token.COMMENT {
  325. annotations[len(annotations)-1].End = int32(e)
  326. } else {
  327. annotations = append(annotations, Annotation{Kind: CommentAnnotation, Pos: int32(p), End: int32(e)})
  328. }
  329. }
  330. prevTok = tok
  331. }
  332. return Code{Text: string(b.buf), Annotations: annotations}, output
  333. }