Repository: cuonglm/gocmt Branch: main Commit: 515d346459b3 Files: 20 Total size: 23.4 KB Directory structure: gitextract_p96zk_1u/ ├── .github/ │ └── workflows/ │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── doc.go ├── go.mod ├── go.sum ├── helper.go ├── helper_test.go ├── main.go ├── parse.go ├── parse_test.go └── testdata/ ├── empty_var_block.go ├── existed.go ├── generic.go ├── invalid_file.go ├── issue7.go ├── main.go ├── parenthesis.go └── vendor/ └── main.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/ci.yml ================================================ name: Test on: pull_request: push: branches: - main jobs: test: name: Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: go-version: 1.19 - name: Cache uses: actions/cache@v2 with: path: | ~/go/bin ~/go/src ~/go/pkg/mod ~/.cache/go-build key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - run: go version - run: go test -race -v ./... lint: name: Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: golangci/golangci-lint-action@v2 ================================================ FILE: .gitignore ================================================ .idea ================================================ FILE: LICENSE ================================================ Author:: LE Manh Cuong Copyright:: Copyright (c) 2016, Cuong Manh Le All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the @organization@ nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LE MANH CUONG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ # gocmt - Add missing comment on exported function, method, type, constant, variable in go file ![Build status](https://github.com/cuonglm/gocmt/actions/workflows/ci.yml/badge.svg?branch=main) [![Go Reference](https://pkg.go.dev/badge/github.com/cuonglm/gocmt.svg)](https://pkg.go.dev/github.com/cuonglm/gocmt) [![Go Report Card](https://goreportcard.com/badge/github.com/cuonglm/gocmt)](https://goreportcard.com/report/github.com/cuonglm/gocmt) # Installation For go1.15 and below: ```sh go get -u github.com/cuonglm/gocmt ``` For go1.16 and above: ```sh go install github.com/cuonglm/gocmt@latest ``` # Why gocmt Some of my projects have many files with exported fields, variables, functions missing comment, so lint tools will complain. I find a way to auto add missing comment to them, just to pass the lint tools but nothing existed. So `gocmt` comes in. # Usage ```sh $ gocmt -h usage: gocmt [flags] [file ...] -d string Directory to process -i Make in-place editing -t string Comment template (default "...") ``` # Example ```sh $ cat testdata/main.go package p var i = 0 var I = 1 var c = "constant un-exported" const C = "constant exported" type t struct{} type T struct{} func main() { } func unexport(s string) { } func Export(s string) { } func ExportWithComment(s string) { } ``` Using `gocmt` give you: ```sh $ gocmt testdata/main.go package p var i = 0 // I ... var I = 1 var c = "constant un-exported" // C ... const C = "constant exported" type t struct{} // T ... type T struct{} func main() { } func unexport(s string) { } // Export ... func Export(s string) { } // ExportWithComment ... func ExportWithComment(s string) { } ``` Default template is `...`, you can change it using `-t` option. # Author Cuong Manh Le # License See [LICENSE](https://github.com/cuonglm/gocmt/blob/main/LICENSE) ================================================ FILE: doc.go ================================================ // gocmt adds missing comments on exported identifiers in Go source files. // // Usage: // // gocmt [-i] [-p] [-t "comment template"] [-d dir] // // This tools exists because I have to work with some existed code base, which // is lack of documentation for many exported identifiers. Iterating over them // is time consuming and maybe not suitable at a stage of the project. So I // wrote this tool to quickly bypassing CI system. Once thing is settle, we can // lookback and fix missing comments. // // You SHOULD always write documentation for all your exported identifiers. package main ================================================ FILE: go.mod ================================================ module github.com/cuonglm/gocmt go 1.19 require github.com/stretchr/testify v1.6.1 require ( github.com/davecgh/go-spew v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) ================================================ FILE: go.sum ================================================ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ================================================ FILE: helper.go ================================================ package main import ( "go/ast" "go/scanner" "os" "strings" ) func isGoFile(f os.FileInfo) bool { name := f.Name() return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") } func printError(err error) { scanner.PrintError(os.Stderr, err) } func walkFunc(path string, fi os.FileInfo, err error) error { if err == nil && isGoFile(fi) { err = processFile(path, *template, *inPlace) } if err != nil { return err } return nil } func isLineComment(comment *ast.CommentGroup) bool { if comment == nil { return false } if len(comment.List) == 0 { return false } head := comment.List[0].Text head = strings.TrimSpace(head) return strings.HasPrefix(head, "//") } func hasCommentPrefix(comment *ast.CommentGroup, prefix string) bool { return strings.HasPrefix(strings.TrimSpace(comment.Text()), prefix) } func appendCommentGroup(list []*ast.CommentGroup, item *ast.CommentGroup) []*ast.CommentGroup { ret := []*ast.CommentGroup{} hasInsert := false for _, group := range list { if group.Pos() < item.Pos() { ret = append(ret, group) continue } if group.Pos() == item.Pos() { ret = append(ret, item) hasInsert = true continue } if group.Pos() > item.Pos() { if !hasInsert { ret = append(ret, item) hasInsert = true } ret = append(ret, group) continue } } if !hasInsert { ret = append(ret, item) } return ret } ================================================ FILE: helper_test.go ================================================ package main import ( "os" "testing" ) func Test_isGoFile(t *testing.T) { isGoFileTest := []struct { path string expected bool }{ {"main.go", true}, {"README.md", false}, } for _, tt := range isGoFileTest { fi, err := os.Stat(tt.path) if err != nil { t.Fatal(err) } if got := isGoFile(fi); got != tt.expected { t.Fatalf("isGoFile(%+v): expected %v, got %v", fi, tt.expected, got) } } } ================================================ FILE: main.go ================================================ package main import ( "bytes" "flag" "fmt" "go/format" "go/token" "os" "path/filepath" "regexp" "strings" ) var ( // ensure that the comment starts on a newline (without the \n, sometimes it starts on the previous } commentBase = "\n// %s " // if it's in an indented block, this makes sure that the indentation is correct commentIndentedBase = "// %s " fset = token.NewFileSet() defaultMode = os.FileMode(0644) tralingWsRegex = regexp.MustCompile(`(?m)[\t ]+$`) newlinesRegex = regexp.MustCompile(`(?m)\n{3,}`) ) var ( inPlace = flag.Bool("i", false, "Make in-place editing") template = flag.String("t", "...", "Comment template") dir = flag.String("d", "", "Directory to process") parenComment = flag.Bool("p", false, "Add comments to all const inside the parens if true") ) func main() { os.Exit(gocmtRun()) } func usage() { fmt.Fprintf(os.Stderr, "usage: gocmt [flags] [file ...]\n") flag.PrintDefaults() } func gocmtRun() int { flag.Parse() if *dir != "" { if err := filepath.Walk(*dir, walkFunc); err != nil { printError(err) return 1 } return 0 } if flag.NArg() == 0 { usage() } for i := 0; i < flag.NArg(); i++ { path := flag.Arg(i) switch fi, err := os.Stat(path); { case err != nil: printError(err) case fi.IsDir(): printError(fmt.Errorf("%s is a directory", path)) default: if err := processFile(path, *template, *inPlace); err != nil { printError(err) return 1 } } } return 0 } func processFile(filename, template string, inPlace bool) error { // skip test files and files in vendor/ if strings.HasSuffix(filename, "_test.go") || strings.Contains(filename, "/vendor/") { return nil } af, modified, err := parseFile(fset, filename, template) if err != nil { return err } var buf bytes.Buffer if err := format.Node(&buf, fset, af); err != nil { panic(err) } newBuf := buf.Bytes() if modified { newBuf = tralingWsRegex.ReplaceAll(newBuf, []byte("")) newBuf = newlinesRegex.ReplaceAll(newBuf, []byte("\n\n")) if inPlace { return os.WriteFile(filename, newBuf, defaultMode) } fmt.Fprintf(os.Stdout, "%s", newBuf) return nil } fmt.Fprintf(os.Stderr, "%s no changes\n", filename) return nil } ================================================ FILE: parse.go ================================================ package main import ( "fmt" "go/ast" "go/parser" "go/token" "strings" ) // parseFile parses and modifies the input file if necessary. Returns AST represents of (new) source, a boolean // to report whether the source file was modified, and any error if occurred. func parseFile(fset *token.FileSet, filePath, template string) (af *ast.File, modified bool, err error) { af, err = parser.ParseFile(fset, filePath, nil, parser.ParseComments|parser.AllErrors) if err != nil { return } // Inject first comment to prevent nil comment map if len(af.Comments) == 0 { af.Comments = []*ast.CommentGroup{{List: []*ast.Comment{{Slash: -1, Text: "// gocmt"}}}} defer func() { // Remove the injected comment af.Comments = af.Comments[1:] }() } commentTemplate := commentBase + template originalCommentSign := "" for _, c := range af.Comments { originalCommentSign += c.Text() } cmap := ast.NewCommentMap(fset, af, af.Comments) skipped := make(map[ast.Node]bool) ast.Inspect(af, func(n ast.Node) bool { switch typ := n.(type) { case *ast.FuncDecl: if skipped[typ] || !typ.Name.IsExported() { return true } addFuncDeclComment(typ, commentTemplate) cmap[typ] = appendCommentGroup(cmap[typ], typ.Doc) case *ast.DeclStmt: skipped[typ.Decl] = true case *ast.GenDecl: switch typ.Tok { case token.CONST, token.VAR: if !(typ.Lparen == token.NoPos && typ.Rparen == token.NoPos) { // if there's a () and parenComment is true, add comment for each sub entry if *parenComment { for _, spec := range typ.Specs { vs := spec.(*ast.ValueSpec) if !vs.Names[0].IsExported() { continue } addParenValueSpecComment(vs, commentTemplate) cmap[vs] = appendCommentGroup(cmap[vs], vs.Doc) } return true } } // empty var block if len(typ.Specs) == 0 { return true } vs := typ.Specs[0].(*ast.ValueSpec) if skipped[typ] || !vs.Names[0].IsExported() { return true } addValueSpecComment(typ, vs, commentTemplate) case token.TYPE: ts := typ.Specs[0].(*ast.TypeSpec) if skipped[typ] || !ts.Name.IsExported() { return true } addTypeSpecComment(typ, ts, commentTemplate) default: return true } cmap[typ] = appendCommentGroup(cmap[typ], typ.Doc) } return true }) // Rebuild comments af.Comments = cmap.Filter(af).Comments() currentCommentSign := "" for _, c := range af.Comments { currentCommentSign += c.Text() } modified = currentCommentSign != originalCommentSign return } func addFuncDeclComment(fd *ast.FuncDecl, commentTemplate string) { if fd.Doc == nil || strings.TrimSpace(fd.Doc.Text()) == fd.Name.Name { text := fmt.Sprintf(commentTemplate, fd.Name) pos := fd.Pos() - token.Pos(1) if fd.Doc != nil { pos = fd.Doc.Pos() } fd.Doc = &ast.CommentGroup{List: []*ast.Comment{{Slash: pos, Text: text}}} return } if fd.Doc != nil && isLineComment(fd.Doc) && !hasCommentPrefix(fd.Doc, fd.Name.Name) { modifyComment(fd.Doc, fd.Name.Name) return } } func addValueSpecComment(gd *ast.GenDecl, vs *ast.ValueSpec, commentTemplate string) { if gd.Doc == nil || strings.TrimSpace(gd.Doc.Text()) == vs.Names[0].Name { text := fmt.Sprintf(commentTemplate, vs.Names[0].Name) pos := gd.Pos() - token.Pos(1) if gd.Doc != nil { pos = gd.Doc.Pos() } gd.Doc = &ast.CommentGroup{List: []*ast.Comment{{Slash: pos, Text: text}}} return } if gd.Doc != nil && isLineComment(gd.Doc) && !hasCommentPrefix(gd.Doc, vs.Names[0].Name) { modifyComment(gd.Doc, vs.Names[0].Name) return } } func addParenValueSpecComment(vs *ast.ValueSpec, commentTemplate string) { if vs.Doc == nil || strings.TrimSpace(vs.Doc.Text()) == vs.Names[0].Name { commentTemplate = strings.Replace(commentTemplate, commentBase, commentIndentedBase, 1) text := fmt.Sprintf(commentTemplate, vs.Names[0].Name) pos := vs.Pos() - token.Pos(1) if vs.Doc != nil { pos = vs.Doc.Pos() } vs.Doc = &ast.CommentGroup{List: []*ast.Comment{{Slash: pos, Text: text}}} return } if vs.Doc != nil && isLineComment(vs.Doc) && !hasCommentPrefix(vs.Doc, vs.Names[0].Name) { modifyComment(vs.Doc, vs.Names[0].Name) return } } func addTypeSpecComment(gd *ast.GenDecl, ts *ast.TypeSpec, commentTemplate string) { if gd.Doc == nil || strings.TrimSpace(gd.Doc.Text()) == ts.Name.Name { text := fmt.Sprintf(commentTemplate, ts.Name.Name) pos := gd.Pos() - token.Pos(1) if gd.Doc != nil { pos = gd.Doc.Pos() } gd.Doc = &ast.CommentGroup{List: []*ast.Comment{{Slash: pos, Text: text}}} return } if gd.Doc != nil && isLineComment(gd.Doc) && !hasCommentPrefix(gd.Doc, ts.Name.Name) { modifyComment(gd.Doc, ts.Name.Name) return } } func modifyComment(comment *ast.CommentGroup, prefix string) { commentTemplate := commentBase + *template first := comment.List[0].Text if strings.HasPrefix(first, "//") && !strings.HasPrefix(first, "// ") { text := fmt.Sprintf(commentTemplate, prefix) comment.List = append([]*ast.Comment{{Text: text, Slash: comment.Pos()}}, comment.List...) return } first = strings.TrimPrefix(first, "// ") first = fmt.Sprintf(commentBase+"%s", prefix, first) comment.List[0].Text = first } ================================================ FILE: parse_test.go ================================================ package main import ( "bytes" "go/format" "go/token" "os" "testing" "github.com/stretchr/testify/assert" ) const baseSrc = `package p var i = 0 // I ... var I = 1 var c = "constant un-exported" // C ... const C = "constant exported" type t struct{} // T ... type T struct{} func main() { } func unexport(s string) { } // Export ... func Export(s string) { } // ExportWithComment ... func ExportWithComment(s string) { } // ExistedComment ... func ExistedComment() {} ` const parenSrc = `package p // Summon ... type Summon string // DarkOmega ... const ( DarkOmega Summon = "celeste" // LightOmega best summon LightOmega Summon = "luminineria" // WindOmega WindOmega Summon = "tiamat" ) // FireUtility ... const FireUtility Summon = "the sun" // Light ... const ( // Light best summon Light Summon = "lucifer" ) // Light2 best summon const ( Light2 Summon = "lucifer" ) ` const parenSrc2 = `package p // Summon ... type Summon string const ( // DarkOmega ... DarkOmega Summon = "celeste" // LightOmega best summon LightOmega Summon = "luminineria" // WindOmega ... WindOmega Summon = "tiamat" ) // FireUtility ... const FireUtility Summon = "the sun" const ( // Light best summon Light Summon = "lucifer" ) // Light2 best summon const ( // Light2 ... Light2 Summon = "lucifer" ) ` const issue7 = `package p import "log" // I ... var I = 1 func a() { // LogAll ... var LogAll map[string]struct{} log.Println(LogAll) }` const existed = `package p // global comments should never be deleted // something import "embed" // global comment 1 // global comment 2 // global comment 3 // ============= function ============= // FuncWithExistedComment1 ... func FuncWithExistedComment1() { } // FuncWithExistedComment2 ... func FuncWithExistedComment2() { // this comments should never be deleted } // FuncWithExistedComment3 something func FuncWithExistedComment3() { } // FuncWithExistedComment4 multi-line comments // something func FuncWithExistedComment4() { } /* something */ func FuncWithExistedComment5() { } // ============= value ============= // ValueWithExistedComment1 existed comment var ValueWithExistedComment1 = 1 // ValueWithExistedComment2 existed comment with spaces var ValueWithExistedComment2 = 1 // ValueWithExistedComment3 multi-line comments // something var ValueWithExistedComment3 = 1 /* should't change C style comment */ var ValueWithExistedComment4 = 1 // ============= paren value ============= // ParenValueWithExistedComment1 existed comment const ( ParenValueWithExistedComment1 = 1 // ParenValueWithExistedComment2 something ParenValueWithExistedComment2 = 1 ) // ParenValueWithExistedComment3 multi-line comments // something const ( ParenValueWithExistedComment3 = 1 // ParenValueWithExistedComment2 something ParenValueWithExistedComment4 = 1 ) // ============= type ============= // TypeWithExistedComment1 existed comment type TypeWithExistedComment1 int // TypeWithExistedComment2 existed comment with spaces type TypeWithExistedComment2 int // TypeWithExistedComment3 multi-line comments // something type TypeWithExistedComment3 int /* should't change C style comment */ type TypeWithExistedComment4 int // ============= marker comment ============= // Embed ... //go:embed dont_modify_this_comment.txt //go:embed image/* var Embed embed.FS // Embed something //go:embed dont_modify_this_comment.txt var Embed embed.FS // Generate ... //go:generate goyacc -o gopher.go -p parser gopher.y func Generate() { } // ============= end ============= ` const generic = `package p // G ... type G[T any] struct{} ` func Test_parseFile(t *testing.T) { parseFileTests := []struct { path string expectedSrc string modified bool wantErr bool }{ {"testdata/main.go", baseSrc, true, false}, {"testdata/parenthesis.go", parenSrc, true, false}, {"testdata/invalid_file.go", "", false, true}, {"testdata/issue7.go", issue7, false, false}, {"testdata/existed.go", existed, true, false}, {"testdata/generic.go", generic, true, false}, } for _, tc := range parseFileTests { tc := tc t.Run(tc.path, func(t *testing.T) { t.Parallel() fset := token.NewFileSet() af, modified, err := parseFile(fset, tc.path, "...") assert.True(t, tc.wantErr == (err != nil)) assert.Equal(t, tc.modified, modified) if tc.modified { buf := new(bytes.Buffer) assert.NoError(t, format.Node(buf, fset, af)) newBuf := buf.Bytes() newBuf = tralingWsRegex.ReplaceAll(newBuf, []byte("")) newBuf = newlinesRegex.ReplaceAll(newBuf, []byte("\n\n")) assert.Equal(t, tc.expectedSrc, string(newBuf)) } }) } } func Test_parseFileWithParenComment(t *testing.T) { *parenComment = true parseFileTests := []struct { path string expectedSrc string modified bool wantErr bool }{ {"testdata/parenthesis.go", parenSrc2, true, false}, } for _, tc := range parseFileTests { tc := tc t.Run(tc.path, func(t *testing.T) { t.Parallel() fset := token.NewFileSet() af, modified, err := parseFile(fset, tc.path, "...") assert.True(t, tc.wantErr == (err != nil)) assert.Equal(t, tc.modified, modified) if tc.modified { buf := new(bytes.Buffer) assert.NoError(t, format.Node(buf, fset, af)) newBuf := buf.Bytes() newBuf = tralingWsRegex.ReplaceAll(newBuf, []byte("")) newBuf = newlinesRegex.ReplaceAll(newBuf, []byte("\n\n")) assert.Equal(t, tc.expectedSrc, string(newBuf)) } }) } } func TestSkipVendor(t *testing.T) { filePath := "testdata/vendor/main.go" origBuf, err := os.ReadFile(filePath) if err != nil { t.Fatal(err) } if err := processFile(filePath, "...", true); err != nil { t.Fatal(err) } buf, err := os.ReadFile(filePath) if err != nil { t.Fatal(err) } if !bytes.Equal(buf, origBuf) { t.Fatal("file in vendor/ directory was edited") } } ================================================ FILE: testdata/empty_var_block.go ================================================ package p var ( // Error ) ================================================ FILE: testdata/existed.go ================================================ package p // global comments should never be deleted // something import "embed" // global comment 1 // global comment 2 // global comment 3 // ============= function ============= // FuncWithExistedComment1 func FuncWithExistedComment1() { } func FuncWithExistedComment2() { // this comments should never be deleted } // something func FuncWithExistedComment3() { } // multi-line comments // something func FuncWithExistedComment4() { } /* something */ func FuncWithExistedComment5() { } // ============= value ============= // existed comment var ValueWithExistedComment1 = 1 // existed comment with spaces var ValueWithExistedComment2 = 1 // multi-line comments // something var ValueWithExistedComment3 = 1 /* should't change C style comment */ var ValueWithExistedComment4 = 1 // ============= paren value ============= // existed comment const ( ParenValueWithExistedComment1 = 1 // ParenValueWithExistedComment2 something ParenValueWithExistedComment2 = 1 ) // multi-line comments // something const ( ParenValueWithExistedComment3 = 1 // ParenValueWithExistedComment2 something ParenValueWithExistedComment4 = 1 ) // ============= type ============= // existed comment type TypeWithExistedComment1 int // existed comment with spaces type TypeWithExistedComment2 int // multi-line comments // something type TypeWithExistedComment3 int /* should't change C style comment */ type TypeWithExistedComment4 int // ============= marker comment ============= //go:embed dont_modify_this_comment.txt //go:embed image/* var Embed embed.FS // something //go:embed dont_modify_this_comment.txt var Embed embed.FS //go:generate goyacc -o gopher.go -p parser gopher.y func Generate() { } // ============= end ============= ================================================ FILE: testdata/generic.go ================================================ package p type G[T any] struct{} ================================================ FILE: testdata/invalid_file.go ================================================ package p var i := 1 ================================================ FILE: testdata/issue7.go ================================================ package p import "log" // I ... var I = 1 func a() { // LogAll ... var LogAll map[string]struct{} log.Println(LogAll) } ================================================ FILE: testdata/main.go ================================================ package p var i = 0 var I = 1 var c = "constant un-exported" const C = "constant exported" type t struct{} type T struct{} func main() { } func unexport(s string) { } func Export(s string) { } func ExportWithComment(s string) { } // ExistedComment func ExistedComment() {} ================================================ FILE: testdata/parenthesis.go ================================================ package p type Summon string const ( DarkOmega Summon = "celeste" // LightOmega best summon LightOmega Summon = "luminineria" // WindOmega WindOmega Summon = "tiamat" ) const FireUtility Summon = "the sun" const ( // Light best summon Light Summon = "lucifer" ) // Light2 best summon const ( Light2 Summon = "lucifer" ) ================================================ FILE: testdata/vendor/main.go ================================================ package p var I = 1