Skip to content

Commit 335eecb

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 be986a3 commit 335eecb

File tree

4 files changed

+105
-36
lines changed

4 files changed

+105
-36
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)
@@ -180,7 +182,7 @@ func main() {
180182

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

parser.go

Lines changed: 31 additions & 19 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)
@@ -104,7 +107,7 @@ func (p *tagParser) parseFunction(f *ast.FuncDecl) {
104107
tag.Type = Function
105108
}
106109

107-
p.tags = append(p.tags, tag)
110+
p.addTag(tag)
108111
}
109112

110113
// parseTypeDeclaration creates a tag for type declaration ts and for each
@@ -127,7 +130,7 @@ func (p *tagParser) parseTypeDeclaration(ts *ast.TypeSpec) {
127130
tag.Fields[TypeField] = getType(ts.Type, true)
128131
}
129132

130-
p.tags = append(p.tags, tag)
133+
p.addTag(tag)
131134
}
132135

133136
// parseValueDeclaration creates a tag for each variable or constant declaration,
@@ -151,7 +154,8 @@ func (p *tagParser) parseValueDeclaration(v *ast.ValueSpec) {
151154
case ast.Con:
152155
tag.Type = Constant
153156
}
154-
p.tags = append(p.tags, tag)
157+
158+
p.addTag(tag)
155159
}
156160
}
157161

@@ -166,15 +170,15 @@ func (p *tagParser) parseStructFields(name string, s *ast.StructType) {
166170
tag.Fields[Access] = getAccess(tag.Name)
167171
tag.Fields[ReceiverType] = name
168172
tag.Fields[TypeField] = getType(f.Type, true)
169-
p.tags = append(p.tags, tag)
173+
p.addTag(tag)
170174
}
171175
} else {
172176
// embedded field
173177
tag = p.createTag(getType(f.Type, true), f.Pos(), Embedded)
174178
tag.Fields[Access] = getAccess(tag.Name)
175179
tag.Fields[ReceiverType] = name
176180
tag.Fields[TypeField] = getType(f.Type, true)
177-
p.tags = append(p.tags, tag)
181+
p.addTag(tag)
178182
}
179183
}
180184
}
@@ -200,7 +204,7 @@ func (p *tagParser) parseInterfaceMethods(name string, s *ast.InterfaceType) {
200204

201205
tag.Fields[InterfaceType] = name
202206

203-
p.tags = append(p.tags, tag)
207+
p.addTag(tag)
204208
}
205209
}
206210

@@ -249,6 +253,14 @@ func (p *tagParser) belongsToReceiver(types *ast.FieldList) (name string, ok boo
249253
return "", false
250254
}
251255

256+
// addTag adds tag to the list or is skipped if exclude private and tag is private
257+
func (p *tagParser) addTag(tag Tag) {
258+
if p.excludePrivate && tag.Fields[Access] != "public" {
259+
return
260+
}
261+
p.tags = append(p.tags, tag)
262+
}
263+
252264
// getTypes returns a comma separated list of types in fields. If includeNames is
253265
// true each type is preceded by a comma separated list of parameter names.
254266
func getTypes(fields *ast.FieldList, includeNames bool) string {

parser_test.go

Lines changed: 61 additions & 7 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"
@@ -10,11 +11,12 @@ import (
1011
type F map[TagField]string
1112

1213
var testCases = []struct {
13-
filename string
14-
relative bool
15-
basepath string
16-
minversion string
17-
tags []Tag
14+
filename string
15+
relative bool
16+
basepath string
17+
minversion string
18+
tags []Tag
19+
excludePrivate bool
1820
}{
1921
{filename: "tests/const.go-src", tags: []Tag{
2022
tag("Test", 1, "p", F{}),
@@ -95,6 +97,51 @@ var testCases = []struct {
9597
tag("fmt", 3, "i", F{}),
9698
tag("main", 5, "f", F{"access": "private", "signature": "()"}),
9799
}},
100+
// excludePrivate tests
101+
{filename: "tests/func.go-src", excludePrivate: true, tags: []Tag{
102+
tag("Test", 1, "p", F{}),
103+
tag("Function1", 3, "f", F{"access": "public", "signature": "()", "type": "string"}),
104+
}},
105+
{filename: "tests/interface.go-src", excludePrivate: true, tags: []Tag{
106+
tag("Test", 1, "p", F{}),
107+
tag("InterfaceMethod", 4, "m", F{"access": "public", "signature": "(int)", "ntype": "Interface", "type": "string"}),
108+
tag("OtherMethod", 5, "m", F{"access": "public", "signature": "()", "ntype": "Interface"}),
109+
tag("io.Reader", 6, "e", F{"access": "public", "ntype": "Interface"}),
110+
tag("Interface", 3, "n", F{"access": "public", "type": "interface"}),
111+
}},
112+
{filename: "tests/struct.go-src", excludePrivate: true, tags: []Tag{
113+
tag("Test", 1, "p", F{}),
114+
tag("Field1", 4, "w", F{"access": "public", "ctype": "Struct", "type": "int"}),
115+
tag("Field2", 4, "w", F{"access": "public", "ctype": "Struct", "type": "int"}),
116+
tag("Struct", 3, "t", F{"access": "public", "type": "struct"}),
117+
tag("Struct", 20, "e", F{"access": "public", "ctype": "TestEmbed", "type": "Struct"}),
118+
tag("*io.Writer", 21, "e", F{"access": "public", "ctype": "TestEmbed", "type": "*io.Writer"}),
119+
tag("TestEmbed", 19, "t", F{"access": "public", "type": "struct"}),
120+
tag("Struct2", 27, "t", F{"access": "public", "type": "struct"}),
121+
tag("Connection", 36, "t", F{"access": "public", "type": "struct"}),
122+
tag("NewStruct", 9, "f", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "*Struct"}),
123+
tag("F1", 13, "m", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "[]bool, [2]*string"}),
124+
tag("F2", 16, "m", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "bool"}),
125+
tag("NewTestEmbed", 24, "f", F{"access": "public", "ctype": "TestEmbed", "signature": "()", "type": "TestEmbed"}),
126+
tag("NewStruct2", 30, "f", F{"access": "public", "ctype": "Struct2", "signature": "()", "type": "*Struct2, error"}),
127+
tag("Dial", 33, "f", F{"access": "public", "ctype": "Connection", "signature": "()", "type": "*Connection, error"}),
128+
tag("Dial2", 39, "f", F{"access": "public", "ctype": "Connection", "signature": "()", "type": "*Connection, *Struct2"}),
129+
tag("Dial3", 42, "f", F{"access": "public", "signature": "()", "type": "*Connection, *Connection"}),
130+
}},
131+
{filename: "tests/type.go-src", excludePrivate: true, tags: []Tag{
132+
tag("Test", 1, "p", F{}),
133+
}},
134+
{filename: "tests/var.go-src", excludePrivate: true, tags: []Tag{
135+
tag("Test", 1, "p", F{}),
136+
tag("A", 7, "v", F{"access": "public"}),
137+
tag("B", 8, "v", F{"access": "public"}),
138+
tag("C", 8, "v", F{"access": "public"}),
139+
tag("D", 9, "v", F{"access": "public"}),
140+
}},
141+
{filename: "tests/range.go-src", excludePrivate: true, minversion: "go1.4", tags: []Tag{
142+
tag("main", 1, "p", F{}),
143+
tag("fmt", 3, "i", F{}),
144+
}},
98145
}
99146

100147
func TestParse(t *testing.T) {
@@ -110,14 +157,21 @@ func TestParse(t *testing.T) {
110157
continue
111158
}
112159

113-
tags, err := Parse(testCase.filename, testCase.relative, basepath)
160+
tags, err := Parse(testCase.filename, testCase.relative, basepath, testCase.excludePrivate)
114161
if err != nil {
115162
t.Errorf("[%s] Parse error: %s", testCase.filename, err)
116163
continue
117164
}
118165

119166
if len(tags) != len(testCase.tags) {
120-
t.Errorf("[%s] len(tags) == %d, want %d", testCase.filename, len(tags), len(testCase.tags))
167+
msg := fmt.Sprintf("[%s] len(tags) == %d, want %d",
168+
testCase.filename,
169+
len(tags),
170+
len(testCase.tags))
171+
if testCase.excludePrivate {
172+
msg += " (exclude private)"
173+
}
174+
t.Error(msg)
121175
continue
122176
}
123177

0 commit comments

Comments
 (0)