github.com/AndrienkoAleksandr/go@v0.0.19/src/go/types/commentMap_test.go (about) 1 // Copyright 2022 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 types_test 6 7 import ( 8 "fmt" 9 "go/scanner" 10 "go/token" 11 "regexp" 12 "strings" 13 "testing" 14 ) 15 16 type comment struct { 17 line, col int // comment position 18 text string // comment text, excluding "//", "/*", or "*/" 19 } 20 21 // commentMap collects all comments in the given src with comment text 22 // that matches the supplied regular expression rx and returns them as 23 // []comment lists in a map indexed by line number. The comment text is 24 // the comment with any comment markers ("//", "/*", or "*/") stripped. 25 // The position for each comment is the position of the token immediately 26 // preceding the comment, with all comments that are on the same line 27 // collected in a slice, in source order. If there is no preceding token 28 // (the matching comment appears at the beginning of the file), then the 29 // recorded position is unknown (line, col = 0, 0). 30 // If there are no matching comments, the result is nil. 31 func commentMap(src []byte, rx *regexp.Regexp) (res map[int][]comment) { 32 fset := token.NewFileSet() 33 file := fset.AddFile("", -1, len(src)) 34 35 var s scanner.Scanner 36 s.Init(file, src, nil, scanner.ScanComments) 37 var prev token.Pos // position of last non-comment, non-semicolon token 38 39 for { 40 pos, tok, lit := s.Scan() 41 switch tok { 42 case token.EOF: 43 return 44 case token.COMMENT: 45 if lit[1] == '*' { 46 lit = lit[:len(lit)-2] // strip trailing */ 47 } 48 lit = lit[2:] // strip leading // or /* 49 if rx.MatchString(lit) { 50 p := fset.Position(prev) 51 err := comment{p.Line, p.Column, lit} 52 if res == nil { 53 res = make(map[int][]comment) 54 } 55 res[p.Line] = append(res[p.Line], err) 56 } 57 case token.SEMICOLON: 58 // ignore automatically inserted semicolon 59 if lit == "\n" { 60 continue 61 } 62 fallthrough 63 default: 64 prev = pos 65 } 66 } 67 } 68 69 func TestCommentMap(t *testing.T) { 70 const src = `/* ERROR "0:0" */ /* ERROR "0:0" */ // ERROR "0:0" 71 // ERROR "0:0" 72 x /* ERROR "3:1" */ // ignore automatically inserted semicolon here 73 /* ERROR "3:1" */ // position of x on previous line 74 x /* ERROR "5:4" */ ; // do not ignore this semicolon 75 /* ERROR "5:24" */ // position of ; on previous line 76 package /* ERROR "7:2" */ // indented with tab 77 import /* ERROR "8:9" */ // indented with blanks 78 ` 79 m := commentMap([]byte(src), regexp.MustCompile("^ ERROR ")) 80 found := 0 // number of errors found 81 for line, errlist := range m { 82 for _, err := range errlist { 83 if err.line != line { 84 t.Errorf("%v: got map line %d; want %d", err, err.line, line) 85 continue 86 } 87 // err.line == line 88 89 got := strings.TrimSpace(err.text[len(" ERROR "):]) 90 want := fmt.Sprintf(`"%d:%d"`, line, err.col) 91 if got != want { 92 t.Errorf("%v: got msg %q; want %q", err, got, want) 93 continue 94 } 95 found++ 96 } 97 } 98 99 want := strings.Count(src, " ERROR ") 100 if found != want { 101 t.Errorf("commentMap got %d errors; want %d", found, want) 102 } 103 }