From 526b8956c23dee7be386720247e143f87e5ec2d2 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 8 Dec 2022 08:29:14 -0800 Subject: [PATCH] go/types: add commentMap and test This adds the (adjusted) syntax.CommentMap function and corresponding test to the types_test package so that we can use it for collecting ERROR comments in the next CL. For #51006. Change-Id: I63ce96e7394c28c02d5a292250586cc49c1f6e19 Reviewed-on: https://go-review.googlesource.com/c/go/+/456125 Reviewed-by: Robert Findley TryBot-Result: Gopher Robot Reviewed-by: Robert Griesemer Auto-Submit: Robert Griesemer Run-TryBot: Robert Griesemer --- src/go/types/commentMap_test.go | 103 ++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/go/types/commentMap_test.go diff --git a/src/go/types/commentMap_test.go b/src/go/types/commentMap_test.go new file mode 100644 index 0000000000..ef6017a0aa --- /dev/null +++ b/src/go/types/commentMap_test.go @@ -0,0 +1,103 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types_test + +import ( + "fmt" + "go/scanner" + "go/token" + "regexp" + "strings" + "testing" +) + +type comment struct { + line, col int // comment position + text string // comment text, excluding "//", "/*", or "*/" +} + +// commentMap collects all comments in the given src with comment text +// that matches the supplied regular expression rx and returns them as +// []comment lists in a map indexed by line number. The comment text is +// the comment with any comment markers ("//", "/*", or "*/") stripped. +// The position for each comment is the position of the token immediately +// preceding the comment, with all comments that are on the same line +// collected in a slice, in source order. If there is no preceding token +// (the matching comment appears at the beginning of the file), then the +// recorded position is unknown (line, col = 0, 0). +// If there are no matching comments, the result is nil. +func commentMap(src []byte, rx *regexp.Regexp) (res map[int][]comment) { + fset := token.NewFileSet() + file := fset.AddFile("", -1, len(src)) + + var s scanner.Scanner + s.Init(file, src, nil, scanner.ScanComments) + var prev token.Pos // position of last non-comment, non-semicolon token + + for { + pos, tok, lit := s.Scan() + switch tok { + case token.EOF: + return + case token.COMMENT: + if lit[1] == '*' { + lit = lit[:len(lit)-2] // strip trailing */ + } + lit = lit[2:] // strip leading // or /* + if rx.MatchString(lit) { + p := fset.Position(prev) + err := comment{p.Line, p.Column, lit} + if res == nil { + res = make(map[int][]comment) + } + res[p.Line] = append(res[p.Line], err) + } + case token.SEMICOLON: + // ignore automatically inserted semicolon + if lit == "\n" { + continue + } + fallthrough + default: + prev = pos + } + } +} + +func TestCommentMap(t *testing.T) { + const src = `/* ERROR "0:0" */ /* ERROR "0:0" */ // ERROR "0:0" +// ERROR "0:0" +x /* ERROR "3:1" */ // ignore automatically inserted semicolon here +/* ERROR "3:1" */ // position of x on previous line + x /* ERROR "5:4" */ ; // do not ignore this semicolon +/* ERROR "5:24" */ // position of ; on previous line + package /* ERROR "7:2" */ // indented with tab + import /* ERROR "8:9" */ // indented with blanks +` + m := commentMap([]byte(src), regexp.MustCompile("^ ERROR ")) + found := 0 // number of errors found + for line, errlist := range m { + for _, err := range errlist { + if err.line != line { + t.Errorf("%v: got map line %d; want %d", err, err.line, line) + continue + } + // err.line == line + + got := strings.TrimSpace(err.text[len(" ERROR "):]) + want := fmt.Sprintf(`"%d:%d"`, line, err.col) + if got != want { + t.Errorf("%v: got msg %q; want %q", err, got, want) + continue + } + found++ + } + } + + want := strings.Count(src, " ERROR ") + if found != want { + t.Errorf("commentMap got %d errors; want %d", found, want) + } +} -- 2.50.0