.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / mod@v0.4.1 / modfile / read.go
1 // Copyright 2018 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 modfile
6
7 import (
8         "bytes"
9         "errors"
10         "fmt"
11         "os"
12         "strconv"
13         "strings"
14         "unicode"
15         "unicode/utf8"
16 )
17
18 // A Position describes an arbitrary source position in a file, including the
19 // file, line, column, and byte offset.
20 type Position struct {
21         Line     int // line in input (starting at 1)
22         LineRune int // rune in line (starting at 1)
23         Byte     int // byte in input (starting at 0)
24 }
25
26 // add returns the position at the end of s, assuming it starts at p.
27 func (p Position) add(s string) Position {
28         p.Byte += len(s)
29         if n := strings.Count(s, "\n"); n > 0 {
30                 p.Line += n
31                 s = s[strings.LastIndex(s, "\n")+1:]
32                 p.LineRune = 1
33         }
34         p.LineRune += utf8.RuneCountInString(s)
35         return p
36 }
37
38 // An Expr represents an input element.
39 type Expr interface {
40         // Span returns the start and end position of the expression,
41         // excluding leading or trailing comments.
42         Span() (start, end Position)
43
44         // Comment returns the comments attached to the expression.
45         // This method would normally be named 'Comments' but that
46         // would interfere with embedding a type of the same name.
47         Comment() *Comments
48 }
49
50 // A Comment represents a single // comment.
51 type Comment struct {
52         Start  Position
53         Token  string // without trailing newline
54         Suffix bool   // an end of line (not whole line) comment
55 }
56
57 // Comments collects the comments associated with an expression.
58 type Comments struct {
59         Before []Comment // whole-line comments before this expression
60         Suffix []Comment // end-of-line comments after this expression
61
62         // For top-level expressions only, After lists whole-line
63         // comments following the expression.
64         After []Comment
65 }
66
67 // Comment returns the receiver. This isn't useful by itself, but
68 // a Comments struct is embedded into all the expression
69 // implementation types, and this gives each of those a Comment
70 // method to satisfy the Expr interface.
71 func (c *Comments) Comment() *Comments {
72         return c
73 }
74
75 // A FileSyntax represents an entire go.mod file.
76 type FileSyntax struct {
77         Name string // file path
78         Comments
79         Stmt []Expr
80 }
81
82 func (x *FileSyntax) Span() (start, end Position) {
83         if len(x.Stmt) == 0 {
84                 return
85         }
86         start, _ = x.Stmt[0].Span()
87         _, end = x.Stmt[len(x.Stmt)-1].Span()
88         return start, end
89 }
90
91 // addLine adds a line containing the given tokens to the file.
92 //
93 // If the first token of the hint matches the first token of the
94 // line, the new line is added at the end of the block containing hint,
95 // extracting hint into a new block if it is not yet in one.
96 //
97 // If the hint is non-nil buts its first token does not match,
98 // the new line is added after the block containing hint
99 // (or hint itself, if not in a block).
100 //
101 // If no hint is provided, addLine appends the line to the end of
102 // the last block with a matching first token,
103 // or to the end of the file if no such block exists.
104 func (x *FileSyntax) addLine(hint Expr, tokens ...string) *Line {
105         if hint == nil {
106                 // If no hint given, add to the last statement of the given type.
107         Loop:
108                 for i := len(x.Stmt) - 1; i >= 0; i-- {
109                         stmt := x.Stmt[i]
110                         switch stmt := stmt.(type) {
111                         case *Line:
112                                 if stmt.Token != nil && stmt.Token[0] == tokens[0] {
113                                         hint = stmt
114                                         break Loop
115                                 }
116                         case *LineBlock:
117                                 if stmt.Token[0] == tokens[0] {
118                                         hint = stmt
119                                         break Loop
120                                 }
121                         }
122                 }
123         }
124
125         newLineAfter := func(i int) *Line {
126                 new := &Line{Token: tokens}
127                 if i == len(x.Stmt) {
128                         x.Stmt = append(x.Stmt, new)
129                 } else {
130                         x.Stmt = append(x.Stmt, nil)
131                         copy(x.Stmt[i+2:], x.Stmt[i+1:])
132                         x.Stmt[i+1] = new
133                 }
134                 return new
135         }
136
137         if hint != nil {
138                 for i, stmt := range x.Stmt {
139                         switch stmt := stmt.(type) {
140                         case *Line:
141                                 if stmt == hint {
142                                         if stmt.Token == nil || stmt.Token[0] != tokens[0] {
143                                                 return newLineAfter(i)
144                                         }
145
146                                         // Convert line to line block.
147                                         stmt.InBlock = true
148                                         block := &LineBlock{Token: stmt.Token[:1], Line: []*Line{stmt}}
149                                         stmt.Token = stmt.Token[1:]
150                                         x.Stmt[i] = block
151                                         new := &Line{Token: tokens[1:], InBlock: true}
152                                         block.Line = append(block.Line, new)
153                                         return new
154                                 }
155
156                         case *LineBlock:
157                                 if stmt == hint {
158                                         if stmt.Token[0] != tokens[0] {
159                                                 return newLineAfter(i)
160                                         }
161
162                                         new := &Line{Token: tokens[1:], InBlock: true}
163                                         stmt.Line = append(stmt.Line, new)
164                                         return new
165                                 }
166
167                                 for j, line := range stmt.Line {
168                                         if line == hint {
169                                                 if stmt.Token[0] != tokens[0] {
170                                                         return newLineAfter(i)
171                                                 }
172
173                                                 // Add new line after hint within the block.
174                                                 stmt.Line = append(stmt.Line, nil)
175                                                 copy(stmt.Line[j+2:], stmt.Line[j+1:])
176                                                 new := &Line{Token: tokens[1:], InBlock: true}
177                                                 stmt.Line[j+1] = new
178                                                 return new
179                                         }
180                                 }
181                         }
182                 }
183         }
184
185         new := &Line{Token: tokens}
186         x.Stmt = append(x.Stmt, new)
187         return new
188 }
189
190 func (x *FileSyntax) updateLine(line *Line, tokens ...string) {
191         if line.InBlock {
192                 tokens = tokens[1:]
193         }
194         line.Token = tokens
195 }
196
197 func (x *FileSyntax) removeLine(line *Line) {
198         line.Token = nil
199 }
200
201 // Cleanup cleans up the file syntax x after any edit operations.
202 // To avoid quadratic behavior, removeLine marks the line as dead
203 // by setting line.Token = nil but does not remove it from the slice
204 // in which it appears. After edits have all been indicated,
205 // calling Cleanup cleans out the dead lines.
206 func (x *FileSyntax) Cleanup() {
207         w := 0
208         for _, stmt := range x.Stmt {
209                 switch stmt := stmt.(type) {
210                 case *Line:
211                         if stmt.Token == nil {
212                                 continue
213                         }
214                 case *LineBlock:
215                         ww := 0
216                         for _, line := range stmt.Line {
217                                 if line.Token != nil {
218                                         stmt.Line[ww] = line
219                                         ww++
220                                 }
221                         }
222                         if ww == 0 {
223                                 continue
224                         }
225                         if ww == 1 {
226                                 // Collapse block into single line.
227                                 line := &Line{
228                                         Comments: Comments{
229                                                 Before: commentsAdd(stmt.Before, stmt.Line[0].Before),
230                                                 Suffix: commentsAdd(stmt.Line[0].Suffix, stmt.Suffix),
231                                                 After:  commentsAdd(stmt.Line[0].After, stmt.After),
232                                         },
233                                         Token: stringsAdd(stmt.Token, stmt.Line[0].Token),
234                                 }
235                                 x.Stmt[w] = line
236                                 w++
237                                 continue
238                         }
239                         stmt.Line = stmt.Line[:ww]
240                 }
241                 x.Stmt[w] = stmt
242                 w++
243         }
244         x.Stmt = x.Stmt[:w]
245 }
246
247 func commentsAdd(x, y []Comment) []Comment {
248         return append(x[:len(x):len(x)], y...)
249 }
250
251 func stringsAdd(x, y []string) []string {
252         return append(x[:len(x):len(x)], y...)
253 }
254
255 // A CommentBlock represents a top-level block of comments separate
256 // from any rule.
257 type CommentBlock struct {
258         Comments
259         Start Position
260 }
261
262 func (x *CommentBlock) Span() (start, end Position) {
263         return x.Start, x.Start
264 }
265
266 // A Line is a single line of tokens.
267 type Line struct {
268         Comments
269         Start   Position
270         Token   []string
271         InBlock bool
272         End     Position
273 }
274
275 func (x *Line) Span() (start, end Position) {
276         return x.Start, x.End
277 }
278
279 // A LineBlock is a factored block of lines, like
280 //
281 //      require (
282 //              "x"
283 //              "y"
284 //      )
285 //
286 type LineBlock struct {
287         Comments
288         Start  Position
289         LParen LParen
290         Token  []string
291         Line   []*Line
292         RParen RParen
293 }
294
295 func (x *LineBlock) Span() (start, end Position) {
296         return x.Start, x.RParen.Pos.add(")")
297 }
298
299 // An LParen represents the beginning of a parenthesized line block.
300 // It is a place to store suffix comments.
301 type LParen struct {
302         Comments
303         Pos Position
304 }
305
306 func (x *LParen) Span() (start, end Position) {
307         return x.Pos, x.Pos.add(")")
308 }
309
310 // An RParen represents the end of a parenthesized line block.
311 // It is a place to store whole-line (before) comments.
312 type RParen struct {
313         Comments
314         Pos Position
315 }
316
317 func (x *RParen) Span() (start, end Position) {
318         return x.Pos, x.Pos.add(")")
319 }
320
321 // An input represents a single input file being parsed.
322 type input struct {
323         // Lexing state.
324         filename   string    // name of input file, for errors
325         complete   []byte    // entire input
326         remaining  []byte    // remaining input
327         tokenStart []byte    // token being scanned to end of input
328         token      token     // next token to be returned by lex, peek
329         pos        Position  // current input position
330         comments   []Comment // accumulated comments
331
332         // Parser state.
333         file        *FileSyntax // returned top-level syntax tree
334         parseErrors ErrorList   // errors encountered during parsing
335
336         // Comment assignment state.
337         pre  []Expr // all expressions, in preorder traversal
338         post []Expr // all expressions, in postorder traversal
339 }
340
341 func newInput(filename string, data []byte) *input {
342         return &input{
343                 filename:  filename,
344                 complete:  data,
345                 remaining: data,
346                 pos:       Position{Line: 1, LineRune: 1, Byte: 0},
347         }
348 }
349
350 // parse parses the input file.
351 func parse(file string, data []byte) (f *FileSyntax, err error) {
352         // The parser panics for both routine errors like syntax errors
353         // and for programmer bugs like array index errors.
354         // Turn both into error returns. Catching bug panics is
355         // especially important when processing many files.
356         in := newInput(file, data)
357         defer func() {
358                 if e := recover(); e != nil && e != &in.parseErrors {
359                         in.parseErrors = append(in.parseErrors, Error{
360                                 Filename: in.filename,
361                                 Pos:      in.pos,
362                                 Err:      fmt.Errorf("internal error: %v", e),
363                         })
364                 }
365                 if err == nil && len(in.parseErrors) > 0 {
366                         err = in.parseErrors
367                 }
368         }()
369
370         // Prime the lexer by reading in the first token. It will be available
371         // in the next peek() or lex() call.
372         in.readToken()
373
374         // Invoke the parser.
375         in.parseFile()
376         if len(in.parseErrors) > 0 {
377                 return nil, in.parseErrors
378         }
379         in.file.Name = in.filename
380
381         // Assign comments to nearby syntax.
382         in.assignComments()
383
384         return in.file, nil
385 }
386
387 // Error is called to report an error.
388 // Error does not return: it panics.
389 func (in *input) Error(s string) {
390         in.parseErrors = append(in.parseErrors, Error{
391                 Filename: in.filename,
392                 Pos:      in.pos,
393                 Err:      errors.New(s),
394         })
395         panic(&in.parseErrors)
396 }
397
398 // eof reports whether the input has reached end of file.
399 func (in *input) eof() bool {
400         return len(in.remaining) == 0
401 }
402
403 // peekRune returns the next rune in the input without consuming it.
404 func (in *input) peekRune() int {
405         if len(in.remaining) == 0 {
406                 return 0
407         }
408         r, _ := utf8.DecodeRune(in.remaining)
409         return int(r)
410 }
411
412 // peekPrefix reports whether the remaining input begins with the given prefix.
413 func (in *input) peekPrefix(prefix string) bool {
414         // This is like bytes.HasPrefix(in.remaining, []byte(prefix))
415         // but without the allocation of the []byte copy of prefix.
416         for i := 0; i < len(prefix); i++ {
417                 if i >= len(in.remaining) || in.remaining[i] != prefix[i] {
418                         return false
419                 }
420         }
421         return true
422 }
423
424 // readRune consumes and returns the next rune in the input.
425 func (in *input) readRune() int {
426         if len(in.remaining) == 0 {
427                 in.Error("internal lexer error: readRune at EOF")
428         }
429         r, size := utf8.DecodeRune(in.remaining)
430         in.remaining = in.remaining[size:]
431         if r == '\n' {
432                 in.pos.Line++
433                 in.pos.LineRune = 1
434         } else {
435                 in.pos.LineRune++
436         }
437         in.pos.Byte += size
438         return int(r)
439 }
440
441 type token struct {
442         kind   tokenKind
443         pos    Position
444         endPos Position
445         text   string
446 }
447
448 type tokenKind int
449
450 const (
451         _EOF tokenKind = -(iota + 1)
452         _EOLCOMMENT
453         _IDENT
454         _STRING
455         _COMMENT
456
457         // newlines and punctuation tokens are allowed as ASCII codes.
458 )
459
460 func (k tokenKind) isComment() bool {
461         return k == _COMMENT || k == _EOLCOMMENT
462 }
463
464 // isEOL returns whether a token terminates a line.
465 func (k tokenKind) isEOL() bool {
466         return k == _EOF || k == _EOLCOMMENT || k == '\n'
467 }
468
469 // startToken marks the beginning of the next input token.
470 // It must be followed by a call to endToken, once the token's text has
471 // been consumed using readRune.
472 func (in *input) startToken() {
473         in.tokenStart = in.remaining
474         in.token.text = ""
475         in.token.pos = in.pos
476 }
477
478 // endToken marks the end of an input token.
479 // It records the actual token string in tok.text.
480 // A single trailing newline (LF or CRLF) will be removed from comment tokens.
481 func (in *input) endToken(kind tokenKind) {
482         in.token.kind = kind
483         text := string(in.tokenStart[:len(in.tokenStart)-len(in.remaining)])
484         if kind.isComment() {
485                 if strings.HasSuffix(text, "\r\n") {
486                         text = text[:len(text)-2]
487                 } else {
488                         text = strings.TrimSuffix(text, "\n")
489                 }
490         }
491         in.token.text = text
492         in.token.endPos = in.pos
493 }
494
495 // peek returns the kind of the the next token returned by lex.
496 func (in *input) peek() tokenKind {
497         return in.token.kind
498 }
499
500 // lex is called from the parser to obtain the next input token.
501 func (in *input) lex() token {
502         tok := in.token
503         in.readToken()
504         return tok
505 }
506
507 // readToken lexes the next token from the text and stores it in in.token.
508 func (in *input) readToken() {
509         // Skip past spaces, stopping at non-space or EOF.
510         for !in.eof() {
511                 c := in.peekRune()
512                 if c == ' ' || c == '\t' || c == '\r' {
513                         in.readRune()
514                         continue
515                 }
516
517                 // Comment runs to end of line.
518                 if in.peekPrefix("//") {
519                         in.startToken()
520
521                         // Is this comment the only thing on its line?
522                         // Find the last \n before this // and see if it's all
523                         // spaces from there to here.
524                         i := bytes.LastIndex(in.complete[:in.pos.Byte], []byte("\n"))
525                         suffix := len(bytes.TrimSpace(in.complete[i+1:in.pos.Byte])) > 0
526                         in.readRune()
527                         in.readRune()
528
529                         // Consume comment.
530                         for len(in.remaining) > 0 && in.readRune() != '\n' {
531                         }
532
533                         // If we are at top level (not in a statement), hand the comment to
534                         // the parser as a _COMMENT token. The grammar is written
535                         // to handle top-level comments itself.
536                         if !suffix {
537                                 in.endToken(_COMMENT)
538                                 return
539                         }
540
541                         // Otherwise, save comment for later attachment to syntax tree.
542                         in.endToken(_EOLCOMMENT)
543                         in.comments = append(in.comments, Comment{in.token.pos, in.token.text, suffix})
544                         return
545                 }
546
547                 if in.peekPrefix("/*") {
548                         in.Error("mod files must use // comments (not /* */ comments)")
549                 }
550
551                 // Found non-space non-comment.
552                 break
553         }
554
555         // Found the beginning of the next token.
556         in.startToken()
557
558         // End of file.
559         if in.eof() {
560                 in.endToken(_EOF)
561                 return
562         }
563
564         // Punctuation tokens.
565         switch c := in.peekRune(); c {
566         case '\n', '(', ')', '[', ']', '{', '}', ',':
567                 in.readRune()
568                 in.endToken(tokenKind(c))
569                 return
570
571         case '"', '`': // quoted string
572                 quote := c
573                 in.readRune()
574                 for {
575                         if in.eof() {
576                                 in.pos = in.token.pos
577                                 in.Error("unexpected EOF in string")
578                         }
579                         if in.peekRune() == '\n' {
580                                 in.Error("unexpected newline in string")
581                         }
582                         c := in.readRune()
583                         if c == quote {
584                                 break
585                         }
586                         if c == '\\' && quote != '`' {
587                                 if in.eof() {
588                                         in.pos = in.token.pos
589                                         in.Error("unexpected EOF in string")
590                                 }
591                                 in.readRune()
592                         }
593                 }
594                 in.endToken(_STRING)
595                 return
596         }
597
598         // Checked all punctuation. Must be identifier token.
599         if c := in.peekRune(); !isIdent(c) {
600                 in.Error(fmt.Sprintf("unexpected input character %#q", c))
601         }
602
603         // Scan over identifier.
604         for isIdent(in.peekRune()) {
605                 if in.peekPrefix("//") {
606                         break
607                 }
608                 if in.peekPrefix("/*") {
609                         in.Error("mod files must use // comments (not /* */ comments)")
610                 }
611                 in.readRune()
612         }
613         in.endToken(_IDENT)
614 }
615
616 // isIdent reports whether c is an identifier rune.
617 // We treat most printable runes as identifier runes, except for a handful of
618 // ASCII punctuation characters.
619 func isIdent(c int) bool {
620         switch r := rune(c); r {
621         case ' ', '(', ')', '[', ']', '{', '}', ',':
622                 return false
623         default:
624                 return !unicode.IsSpace(r) && unicode.IsPrint(r)
625         }
626 }
627
628 // Comment assignment.
629 // We build two lists of all subexpressions, preorder and postorder.
630 // The preorder list is ordered by start location, with outer expressions first.
631 // The postorder list is ordered by end location, with outer expressions last.
632 // We use the preorder list to assign each whole-line comment to the syntax
633 // immediately following it, and we use the postorder list to assign each
634 // end-of-line comment to the syntax immediately preceding it.
635
636 // order walks the expression adding it and its subexpressions to the
637 // preorder and postorder lists.
638 func (in *input) order(x Expr) {
639         if x != nil {
640                 in.pre = append(in.pre, x)
641         }
642         switch x := x.(type) {
643         default:
644                 panic(fmt.Errorf("order: unexpected type %T", x))
645         case nil:
646                 // nothing
647         case *LParen, *RParen:
648                 // nothing
649         case *CommentBlock:
650                 // nothing
651         case *Line:
652                 // nothing
653         case *FileSyntax:
654                 for _, stmt := range x.Stmt {
655                         in.order(stmt)
656                 }
657         case *LineBlock:
658                 in.order(&x.LParen)
659                 for _, l := range x.Line {
660                         in.order(l)
661                 }
662                 in.order(&x.RParen)
663         }
664         if x != nil {
665                 in.post = append(in.post, x)
666         }
667 }
668
669 // assignComments attaches comments to nearby syntax.
670 func (in *input) assignComments() {
671         const debug = false
672
673         // Generate preorder and postorder lists.
674         in.order(in.file)
675
676         // Split into whole-line comments and suffix comments.
677         var line, suffix []Comment
678         for _, com := range in.comments {
679                 if com.Suffix {
680                         suffix = append(suffix, com)
681                 } else {
682                         line = append(line, com)
683                 }
684         }
685
686         if debug {
687                 for _, c := range line {
688                         fmt.Fprintf(os.Stderr, "LINE %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte)
689                 }
690         }
691
692         // Assign line comments to syntax immediately following.
693         for _, x := range in.pre {
694                 start, _ := x.Span()
695                 if debug {
696                         fmt.Fprintf(os.Stderr, "pre %T :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte)
697                 }
698                 xcom := x.Comment()
699                 for len(line) > 0 && start.Byte >= line[0].Start.Byte {
700                         if debug {
701                                 fmt.Fprintf(os.Stderr, "ASSIGN LINE %q #%d\n", line[0].Token, line[0].Start.Byte)
702                         }
703                         xcom.Before = append(xcom.Before, line[0])
704                         line = line[1:]
705                 }
706         }
707
708         // Remaining line comments go at end of file.
709         in.file.After = append(in.file.After, line...)
710
711         if debug {
712                 for _, c := range suffix {
713                         fmt.Fprintf(os.Stderr, "SUFFIX %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte)
714                 }
715         }
716
717         // Assign suffix comments to syntax immediately before.
718         for i := len(in.post) - 1; i >= 0; i-- {
719                 x := in.post[i]
720
721                 start, end := x.Span()
722                 if debug {
723                         fmt.Fprintf(os.Stderr, "post %T :%d:%d #%d :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte, end.Line, end.LineRune, end.Byte)
724                 }
725
726                 // Do not assign suffix comments to end of line block or whole file.
727                 // Instead assign them to the last element inside.
728                 switch x.(type) {
729                 case *FileSyntax:
730                         continue
731                 }
732
733                 // Do not assign suffix comments to something that starts
734                 // on an earlier line, so that in
735                 //
736                 //      x ( y
737                 //              z ) // comment
738                 //
739                 // we assign the comment to z and not to x ( ... ).
740                 if start.Line != end.Line {
741                         continue
742                 }
743                 xcom := x.Comment()
744                 for len(suffix) > 0 && end.Byte <= suffix[len(suffix)-1].Start.Byte {
745                         if debug {
746                                 fmt.Fprintf(os.Stderr, "ASSIGN SUFFIX %q #%d\n", suffix[len(suffix)-1].Token, suffix[len(suffix)-1].Start.Byte)
747                         }
748                         xcom.Suffix = append(xcom.Suffix, suffix[len(suffix)-1])
749                         suffix = suffix[:len(suffix)-1]
750                 }
751         }
752
753         // We assigned suffix comments in reverse.
754         // If multiple suffix comments were appended to the same
755         // expression node, they are now in reverse. Fix that.
756         for _, x := range in.post {
757                 reverseComments(x.Comment().Suffix)
758         }
759
760         // Remaining suffix comments go at beginning of file.
761         in.file.Before = append(in.file.Before, suffix...)
762 }
763
764 // reverseComments reverses the []Comment list.
765 func reverseComments(list []Comment) {
766         for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
767                 list[i], list[j] = list[j], list[i]
768         }
769 }
770
771 func (in *input) parseFile() {
772         in.file = new(FileSyntax)
773         var cb *CommentBlock
774         for {
775                 switch in.peek() {
776                 case '\n':
777                         in.lex()
778                         if cb != nil {
779                                 in.file.Stmt = append(in.file.Stmt, cb)
780                                 cb = nil
781                         }
782                 case _COMMENT:
783                         tok := in.lex()
784                         if cb == nil {
785                                 cb = &CommentBlock{Start: tok.pos}
786                         }
787                         com := cb.Comment()
788                         com.Before = append(com.Before, Comment{Start: tok.pos, Token: tok.text})
789                 case _EOF:
790                         if cb != nil {
791                                 in.file.Stmt = append(in.file.Stmt, cb)
792                         }
793                         return
794                 default:
795                         in.parseStmt()
796                         if cb != nil {
797                                 in.file.Stmt[len(in.file.Stmt)-1].Comment().Before = cb.Before
798                                 cb = nil
799                         }
800                 }
801         }
802 }
803
804 func (in *input) parseStmt() {
805         tok := in.lex()
806         start := tok.pos
807         end := tok.endPos
808         tokens := []string{tok.text}
809         for {
810                 tok := in.lex()
811                 switch {
812                 case tok.kind.isEOL():
813                         in.file.Stmt = append(in.file.Stmt, &Line{
814                                 Start: start,
815                                 Token: tokens,
816                                 End:   end,
817                         })
818                         return
819
820                 case tok.kind == '(':
821                         if next := in.peek(); next.isEOL() {
822                                 // Start of block: no more tokens on this line.
823                                 in.file.Stmt = append(in.file.Stmt, in.parseLineBlock(start, tokens, tok))
824                                 return
825                         } else if next == ')' {
826                                 rparen := in.lex()
827                                 if in.peek().isEOL() {
828                                         // Empty block.
829                                         in.lex()
830                                         in.file.Stmt = append(in.file.Stmt, &LineBlock{
831                                                 Start:  start,
832                                                 Token:  tokens,
833                                                 LParen: LParen{Pos: tok.pos},
834                                                 RParen: RParen{Pos: rparen.pos},
835                                         })
836                                         return
837                                 }
838                                 // '( )' in the middle of the line, not a block.
839                                 tokens = append(tokens, tok.text, rparen.text)
840                         } else {
841                                 // '(' in the middle of the line, not a block.
842                                 tokens = append(tokens, tok.text)
843                         }
844
845                 default:
846                         tokens = append(tokens, tok.text)
847                         end = tok.endPos
848                 }
849         }
850 }
851
852 func (in *input) parseLineBlock(start Position, token []string, lparen token) *LineBlock {
853         x := &LineBlock{
854                 Start:  start,
855                 Token:  token,
856                 LParen: LParen{Pos: lparen.pos},
857         }
858         var comments []Comment
859         for {
860                 switch in.peek() {
861                 case _EOLCOMMENT:
862                         // Suffix comment, will be attached later by assignComments.
863                         in.lex()
864                 case '\n':
865                         // Blank line. Add an empty comment to preserve it.
866                         in.lex()
867                         if len(comments) == 0 && len(x.Line) > 0 || len(comments) > 0 && comments[len(comments)-1].Token != "" {
868                                 comments = append(comments, Comment{})
869                         }
870                 case _COMMENT:
871                         tok := in.lex()
872                         comments = append(comments, Comment{Start: tok.pos, Token: tok.text})
873                 case _EOF:
874                         in.Error(fmt.Sprintf("syntax error (unterminated block started at %s:%d:%d)", in.filename, x.Start.Line, x.Start.LineRune))
875                 case ')':
876                         rparen := in.lex()
877                         x.RParen.Before = comments
878                         x.RParen.Pos = rparen.pos
879                         if !in.peek().isEOL() {
880                                 in.Error("syntax error (expected newline after closing paren)")
881                         }
882                         in.lex()
883                         return x
884                 default:
885                         l := in.parseLine()
886                         x.Line = append(x.Line, l)
887                         l.Comment().Before = comments
888                         comments = nil
889                 }
890         }
891 }
892
893 func (in *input) parseLine() *Line {
894         tok := in.lex()
895         if tok.kind.isEOL() {
896                 in.Error("internal parse error: parseLine at end of line")
897         }
898         start := tok.pos
899         end := tok.endPos
900         tokens := []string{tok.text}
901         for {
902                 tok := in.lex()
903                 if tok.kind.isEOL() {
904                         return &Line{
905                                 Start:   start,
906                                 Token:   tokens,
907                                 End:     end,
908                                 InBlock: true,
909                         }
910                 }
911                 tokens = append(tokens, tok.text)
912                 end = tok.endPos
913         }
914 }
915
916 var (
917         slashSlash = []byte("//")
918         moduleStr  = []byte("module")
919 )
920
921 // ModulePath returns the module path from the gomod file text.
922 // If it cannot find a module path, it returns an empty string.
923 // It is tolerant of unrelated problems in the go.mod file.
924 func ModulePath(mod []byte) string {
925         for len(mod) > 0 {
926                 line := mod
927                 mod = nil
928                 if i := bytes.IndexByte(line, '\n'); i >= 0 {
929                         line, mod = line[:i], line[i+1:]
930                 }
931                 if i := bytes.Index(line, slashSlash); i >= 0 {
932                         line = line[:i]
933                 }
934                 line = bytes.TrimSpace(line)
935                 if !bytes.HasPrefix(line, moduleStr) {
936                         continue
937                 }
938                 line = line[len(moduleStr):]
939                 n := len(line)
940                 line = bytes.TrimSpace(line)
941                 if len(line) == n || len(line) == 0 {
942                         continue
943                 }
944
945                 if line[0] == '"' || line[0] == '`' {
946                         p, err := strconv.Unquote(string(line))
947                         if err != nil {
948                                 return "" // malformed quoted string or multiline module path
949                         }
950                         return p
951                 }
952
953                 return string(line)
954         }
955         return "" // missing module path
956 }