Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / stack / parse.go
1 // Copyright 2020 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package stack
6
7 import (
8         "bufio"
9         "errors"
10         "io"
11         "regexp"
12         "strconv"
13 )
14
15 var (
16         reBlank     = regexp.MustCompile(`^\s*$`)
17         reGoroutine = regexp.MustCompile(`^\s*goroutine (\d+) \[([^\]]*)\]:\s*$`)
18         reCall      = regexp.MustCompile(`^\s*` +
19                 `(created by )?` + //marker
20                 `(([\w/.]+/)?[\w]+)\.` + //package
21                 `(\(([^:.)]*)\)\.)?` + //optional type
22                 `([\w\.]+)` + //function
23                 `(\(.*\))?` + // args
24                 `\s*$`)
25         rePos = regexp.MustCompile(`^\s*(.*):(\d+)( .*)?$`)
26
27         errBreakParse = errors.New("break parse")
28 )
29
30 // Scanner splits an input stream into lines in a way that is consumable by
31 // the parser.
32 type Scanner struct {
33         lines *bufio.Scanner
34         done  bool
35 }
36
37 // NewScanner creates a scanner on top of a reader.
38 func NewScanner(r io.Reader) *Scanner {
39         s := &Scanner{
40                 lines: bufio.NewScanner(r),
41         }
42         s.Skip() // prefill
43         return s
44 }
45
46 // Peek returns the next line without consuming it.
47 func (s *Scanner) Peek() string {
48         if s.done {
49                 return ""
50         }
51         return s.lines.Text()
52 }
53
54 // Skip consumes the next line without looking at it.
55 // Normally used after it has already been looked at using Peek.
56 func (s *Scanner) Skip() {
57         if !s.lines.Scan() {
58                 s.done = true
59         }
60 }
61
62 // Next consumes and returns the next line.
63 func (s *Scanner) Next() string {
64         line := s.Peek()
65         s.Skip()
66         return line
67 }
68
69 // Done returns true if the scanner has reached the end of the underlying
70 // stream.
71 func (s *Scanner) Done() bool {
72         return s.done
73 }
74
75 // Err returns true if the scanner has reached the end of the underlying
76 // stream.
77 func (s *Scanner) Err() error {
78         return s.lines.Err()
79 }
80
81 // Match returns the submatchs of the regular expression against the next line.
82 // If it matched the line is also consumed.
83 func (s *Scanner) Match(re *regexp.Regexp) []string {
84         if s.done {
85                 return nil
86         }
87         match := re.FindStringSubmatch(s.Peek())
88         if match != nil {
89                 s.Skip()
90         }
91         return match
92 }
93
94 // SkipBlank skips any number of pure whitespace lines.
95 func (s *Scanner) SkipBlank() {
96         for !s.done {
97                 line := s.Peek()
98                 if len(line) != 0 && !reBlank.MatchString(line) {
99                         return
100                 }
101                 s.Skip()
102         }
103 }
104
105 // Parse the current contiguous block of goroutine stack traces until the
106 // scanned content no longer matches.
107 func Parse(scanner *Scanner) (Dump, error) {
108         dump := Dump{}
109         for {
110                 gr, ok := parseGoroutine(scanner)
111                 if !ok {
112                         return dump, nil
113                 }
114                 dump = append(dump, gr)
115         }
116 }
117
118 func parseGoroutine(scanner *Scanner) (Goroutine, bool) {
119         match := scanner.Match(reGoroutine)
120         if match == nil {
121                 return Goroutine{}, false
122         }
123         id, _ := strconv.ParseInt(match[1], 0, 32)
124         gr := Goroutine{
125                 ID:    int(id),
126                 State: match[2],
127         }
128         for {
129                 frame, ok := parseFrame(scanner)
130                 if !ok {
131                         scanner.SkipBlank()
132                         return gr, true
133                 }
134                 if frame.Position.Filename != "" {
135                         gr.Stack = append(gr.Stack, frame)
136                 }
137         }
138 }
139
140 func parseFrame(scanner *Scanner) (Frame, bool) {
141         fun, ok := parseFunction(scanner)
142         if !ok {
143                 return Frame{}, false
144         }
145         frame := Frame{
146                 Function: fun,
147         }
148         frame.Position, ok = parsePosition(scanner)
149         // if ok is false, then this is a broken state.
150         // we got the func but not the file that must follow
151         // the consumed line can be recovered from the frame
152         //TODO: push back the fun raw
153         return frame, ok
154 }
155
156 func parseFunction(scanner *Scanner) (Function, bool) {
157         match := scanner.Match(reCall)
158         if match == nil {
159                 return Function{}, false
160         }
161         return Function{
162                 Package: match[2],
163                 Type:    match[5],
164                 Name:    match[6],
165         }, true
166 }
167
168 func parsePosition(scanner *Scanner) (Position, bool) {
169         match := scanner.Match(rePos)
170         if match == nil {
171                 return Position{}, false
172         }
173         line, _ := strconv.ParseInt(match[2], 0, 32)
174         return Position{Filename: match[1], Line: int(line)}, true
175 }