Skip to content

Commit 744370b

Browse files
author
Andrew Carter
committed
Add exclude-private option
Added flag `-exclude-private` to chose to filter any symbols that are not public when building the tag file. This is very useful for building a tag file for the Go standard library or a third party API.
1 parent f9b2760 commit 744370b

File tree

4 files changed

+97
-37
lines changed

4 files changed

+97
-37
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Or using package manager `brew` on OS X
2121

2222
-L="": source file names are read from the specified file. If file is "-", input is read from standard in.
2323
-R=false: recurse into directories in the file list.
24+
-exclude-private=false: exclude private symbols.
2425
-f="": write output to specified file. If file is "-", output is written to standard out.
2526
-silent=false: do not produce any output on error.
2627
-sort=true: sort tags.

main.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@ const (
2222
)
2323

2424
var (
25-
printVersion bool
26-
inputFile string
27-
outputFile string
28-
recurse bool
29-
sortOutput bool
30-
silent bool
31-
relative bool
32-
listLangs bool
33-
fields string
25+
printVersion bool
26+
inputFile string
27+
outputFile string
28+
recurse bool
29+
sortOutput bool
30+
silent bool
31+
relative bool
32+
listLangs bool
33+
fields string
34+
excludePrivate bool
3435
)
3536

3637
// ignore unknown flags
@@ -47,6 +48,7 @@ func init() {
4748
flags.BoolVar(&relative, "tag-relative", false, "file paths should be relative to the directory containing the tag file.")
4849
flags.BoolVar(&listLangs, "list-languages", false, "list supported languages.")
4950
flags.StringVar(&fields, "fields", "", "include selected extension fields (only +l).")
51+
flags.BoolVar(&excludePrivate, "exclude-private", false, "exclude private symbols.")
5052

5153
flags.Usage = func() {
5254
fmt.Fprintf(os.Stderr, "gotags version %s\n\n", Version)
@@ -178,7 +180,7 @@ func main() {
178180

179181
tags := []Tag{}
180182
for _, file := range files {
181-
ts, err := Parse(file, relative, basedir)
183+
ts, err := Parse(file, relative, basedir, excludePrivate)
182184
if err != nil {
183185
if !silent {
184186
fmt.Fprintf(os.Stderr, "parse error: %s\n\n", err)

parser.go

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,25 @@ import (
1212

1313
// tagParser contains the data needed while parsing.
1414
type tagParser struct {
15-
fset *token.FileSet
16-
tags []Tag // list of created tags
17-
types []string // all types we encounter, used to determine the constructors
18-
relative bool // should filenames be relative to basepath
19-
basepath string // output file directory
15+
fset *token.FileSet
16+
tags []Tag // list of created tags
17+
types []string // all types we encounter, used to determine the constructors
18+
relative bool // should filenames be relative to basepath
19+
basepath string // output file directory
20+
excludePrivate bool // exclude private symbols
2021
}
2122

22-
// Parse parses the source in filename and returns a list of tags. If relative
23-
// is true, the filenames in the list of tags are relative to basepath.
24-
func Parse(filename string, relative bool, basepath string) ([]Tag, error) {
23+
// Parse parses the source in filename and returns a list of tags.
24+
// If relative is true, the filenames in the list of tags are relative to basepath.
25+
// If excludePrivate is true, any symbols that are not exported will be excluded.
26+
func Parse(filename string, relative bool, basepath string, excludePrivate bool) ([]Tag, error) {
2527
p := &tagParser{
26-
fset: token.NewFileSet(),
27-
tags: []Tag{},
28-
types: make([]string, 0),
29-
relative: relative,
30-
basepath: basepath,
28+
fset: token.NewFileSet(),
29+
tags: []Tag{},
30+
types: make([]string, 0),
31+
relative: relative,
32+
basepath: basepath,
33+
excludePrivate: excludePrivate,
3134
}
3235

3336
f, err := parser.ParseFile(p.fset, filename, nil, 0)
@@ -90,6 +93,10 @@ func (p *tagParser) parseFunction(f *ast.FuncDecl) {
9093
tag := p.createTag(f.Name.Name, f.Pos(), Function)
9194

9295
tag.Fields[Access] = getAccess(tag.Name)
96+
if p.excludeTag(tag) {
97+
return
98+
}
99+
93100
tag.Fields[Signature] = fmt.Sprintf("(%s)", getTypes(f.Type.Params, true))
94101
tag.Fields[TypeField] = getTypes(f.Type.Results, false)
95102

@@ -113,6 +120,9 @@ func (p *tagParser) parseTypeDeclaration(ts *ast.TypeSpec) {
113120
tag := p.createTag(ts.Name.Name, ts.Pos(), Type)
114121

115122
tag.Fields[Access] = getAccess(tag.Name)
123+
if p.excludeTag(tag) {
124+
return
125+
}
116126

117127
switch s := ts.Type.(type) {
118128
case *ast.StructType:
@@ -140,6 +150,9 @@ func (p *tagParser) parseValueDeclaration(v *ast.ValueSpec) {
140150

141151
tag := p.createTag(d.Name, d.Pos(), Variable)
142152
tag.Fields[Access] = getAccess(tag.Name)
153+
if p.excludeTag(tag) {
154+
continue
155+
}
143156

144157
if v.Type != nil {
145158
tag.Fields[TypeField] = getType(v.Type, true)
@@ -164,6 +177,9 @@ func (p *tagParser) parseStructFields(name string, s *ast.StructType) {
164177
for _, n := range f.Names {
165178
tag = p.createTag(n.Name, n.Pos(), Field)
166179
tag.Fields[Access] = getAccess(tag.Name)
180+
if p.excludeTag(tag) {
181+
continue
182+
}
167183
tag.Fields[ReceiverType] = name
168184
tag.Fields[TypeField] = getType(f.Type, true)
169185
p.tags = append(p.tags, tag)
@@ -172,6 +188,9 @@ func (p *tagParser) parseStructFields(name string, s *ast.StructType) {
172188
// embedded field
173189
tag = p.createTag(getType(f.Type, true), f.Pos(), Embedded)
174190
tag.Fields[Access] = getAccess(tag.Name)
191+
if p.excludeTag(tag) {
192+
continue
193+
}
175194
tag.Fields[ReceiverType] = name
176195
tag.Fields[TypeField] = getType(f.Type, true)
177196
p.tags = append(p.tags, tag)
@@ -192,6 +211,9 @@ func (p *tagParser) parseInterfaceMethods(name string, s *ast.InterfaceType) {
192211
}
193212

194213
tag.Fields[Access] = getAccess(tag.Name)
214+
if p.excludeTag(tag) {
215+
continue
216+
}
195217

196218
if t, ok := f.Type.(*ast.FuncType); ok {
197219
tag.Fields[Signature] = fmt.Sprintf("(%s)", getTypes(t.Params, true))
@@ -249,6 +271,14 @@ func (p *tagParser) belongsToReceiver(types *ast.FieldList) (name string, ok boo
249271
return "", false
250272
}
251273

274+
// excludeTag returns true if tag symbol is not public and excludePrivate is set.
275+
func (p *tagParser) excludeTag(tag Tag) bool {
276+
if p.excludePrivate && tag.Fields[Access] != "public" {
277+
return true
278+
}
279+
return false
280+
}
281+
252282
// getTypes returns a comma separated list of types in fields. If includeNames is
253283
// true each type is preceded by a comma separated list of parameter names.
254284
func getTypes(fields *ast.FieldList, includeNames bool) string {

parser_test.go

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"fmt"
45
"path/filepath"
56
"runtime"
67
"strconv"
@@ -110,23 +111,35 @@ func TestParse(t *testing.T) {
110111
continue
111112
}
112113

113-
tags, err := Parse(testCase.filename, testCase.relative, basepath)
114-
if err != nil {
115-
t.Errorf("[%s] Parse error: %s", testCase.filename, err)
116-
continue
117-
}
114+
// Repeat each test - once with all symbols and once without private symbols
115+
for _, excludePrivate := range []bool{false, true} {
116+
tags, err := Parse(testCase.filename, testCase.relative, basepath, excludePrivate)
117+
if err != nil {
118+
t.Errorf("[%s] Parse error: %s", testCase.filename, err)
119+
continue
120+
}
118121

119-
if len(tags) != len(testCase.tags) {
120-
t.Errorf("[%s] len(tags) == %d, want %d", testCase.filename, len(tags), len(testCase.tags))
121-
continue
122-
}
122+
expected := expectedTags(testCase.tags, excludePrivate)
123123

124-
for i, tag := range testCase.tags {
125-
if len(tag.File) == 0 {
126-
tag.File = testCase.filename
124+
if len(tags) != len(expected) {
125+
msg := fmt.Sprintf("[%s] len(tags) == %d, want %d",
126+
testCase.filename,
127+
len(tags),
128+
len(expected))
129+
if excludePrivate {
130+
msg += " (exclude private)"
131+
}
132+
t.Error(msg)
133+
continue
127134
}
128-
if tags[i].String() != tag.String() {
129-
t.Errorf("[%s] tag(%d)\n is:%s\nwant:%s", testCase.filename, i, tags[i].String(), tag.String())
135+
136+
for i, tag := range expected {
137+
if len(tag.File) == 0 {
138+
tag.File = testCase.filename
139+
}
140+
if tags[i].String() != tag.String() {
141+
t.Errorf("[%s] tag(%d)\n is:%s\nwant:%s", testCase.filename, i, tags[i].String(), tag.String())
142+
}
130143
}
131144
}
132145
}
@@ -145,3 +158,17 @@ func tag(n string, l int, t TagType, fields F) (tag Tag) {
145158

146159
return
147160
}
161+
162+
// expectedTags filters out tags that are private when excludePrivate enabled
163+
func expectedTags(tags []Tag, excludePrivate bool) []Tag {
164+
expected := []Tag{}
165+
for _, tag := range tags {
166+
if tag.Type != Package && tag.Type != Import {
167+
if excludePrivate && tag.Fields[Access] != "public" {
168+
continue
169+
}
170+
}
171+
expected = append(expected, tag)
172+
}
173+
return expected
174+
}

0 commit comments

Comments
 (0)