Skip to content

Commit 72891d2

Browse files
Add BISON-style location tracking
2 parents 6f50ce3 + 06376b2 commit 72891d2

File tree

154 files changed

+6949
-3153
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

154 files changed

+6949
-3153
lines changed

cmd/eg/eg.go

Lines changed: 70 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,18 @@ package main // import "golang.org/x/tools/cmd/eg"
1010
import (
1111
"flag"
1212
"fmt"
13-
"go/build"
13+
"go/ast"
1414
"go/format"
1515
"go/parser"
1616
"go/token"
17-
exec "golang.org/x/sys/execabs"
17+
"go/types"
18+
"io/ioutil"
1819
"os"
20+
"path/filepath"
1921
"strings"
2022

21-
"golang.org/x/tools/go/buildutil"
22-
"golang.org/x/tools/go/loader"
23+
exec "golang.org/x/sys/execabs"
24+
"golang.org/x/tools/go/packages"
2325
"golang.org/x/tools/refactor/eg"
2426
)
2527

@@ -32,13 +34,9 @@ var (
3234
verboseFlag = flag.Bool("v", false, "show verbose matcher diagnostics")
3335
)
3436

35-
func init() {
36-
flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
37-
}
38-
3937
const usage = `eg: an example-based refactoring tool.
4038
41-
Usage: eg -t template.go [-w] [-transitive] <args>...
39+
Usage: eg -t template.go [-w] [-transitive] <packages>
4240
4341
-help show detailed help message
4442
-t template.go specifies the template file (use -help to see explanation)
@@ -47,7 +45,7 @@ Usage: eg -t template.go [-w] [-transitive] <args>...
4745
-v show verbose matcher diagnostics
4846
-beforeedit cmd a command to exec before each file is modified.
4947
"{}" represents the name of the file.
50-
` + loader.FromArgsUsage
48+
`
5149

5250
func main() {
5351
if err := doMain(); err != nil {
@@ -74,51 +72,73 @@ func doMain() error {
7472
return fmt.Errorf("no -t template.go file specified")
7573
}
7674

77-
conf := loader.Config{
78-
Fset: token.NewFileSet(),
79-
ParserMode: parser.ParseComments,
75+
tAbs, err := filepath.Abs(*templateFlag)
76+
if err != nil {
77+
return err
78+
}
79+
template, err := ioutil.ReadFile(tAbs)
80+
if err != nil {
81+
return err
8082
}
8183

82-
// The first Created package is the template.
83-
conf.CreateFromFilenames("template", *templateFlag)
84+
cfg := &packages.Config{
85+
Fset: token.NewFileSet(),
86+
Mode: packages.NeedTypesInfo | packages.NeedName | packages.NeedTypes | packages.NeedSyntax | packages.NeedImports | packages.NeedDeps | packages.NeedCompiledGoFiles,
87+
Tests: true,
88+
}
8489

85-
if _, err := conf.FromArgs(args, true); err != nil {
90+
pkgs, err := packages.Load(cfg, args...)
91+
if err != nil {
8692
return err
8793
}
8894

89-
// Load, parse and type-check the whole program.
90-
iprog, err := conf.Load()
95+
tFile, err := parser.ParseFile(cfg.Fset, tAbs, template, parser.ParseComments)
96+
if err != nil {
97+
return err
98+
}
99+
100+
// Type-check the template.
101+
tInfo := types.Info{
102+
Types: make(map[ast.Expr]types.TypeAndValue),
103+
Defs: make(map[*ast.Ident]types.Object),
104+
Uses: make(map[*ast.Ident]types.Object),
105+
Implicits: make(map[ast.Node]types.Object),
106+
Selections: make(map[*ast.SelectorExpr]*types.Selection),
107+
Scopes: make(map[ast.Node]*types.Scope),
108+
}
109+
conf := types.Config{
110+
Importer: pkgsImporter(pkgs),
111+
}
112+
tPkg, err := conf.Check("egtemplate", cfg.Fset, []*ast.File{tFile}, &tInfo)
91113
if err != nil {
92114
return err
93115
}
94116

95117
// Analyze the template.
96-
template := iprog.Created[0]
97-
xform, err := eg.NewTransformer(iprog.Fset, template.Pkg, template.Files[0], &template.Info, *verboseFlag)
118+
xform, err := eg.NewTransformer(cfg.Fset, tPkg, tFile, &tInfo, *verboseFlag)
98119
if err != nil {
99120
return err
100121
}
101122

102123
// Apply it to the input packages.
103-
var pkgs []*loader.PackageInfo
124+
var all []*packages.Package
104125
if *transitiveFlag {
105-
for _, info := range iprog.AllPackages {
106-
pkgs = append(pkgs, info)
107-
}
126+
packages.Visit(pkgs, nil, func(p *packages.Package) { all = append(all, p) })
108127
} else {
109-
pkgs = iprog.InitialPackages()
128+
all = pkgs
110129
}
111130
var hadErrors bool
112131
for _, pkg := range pkgs {
113-
if pkg == template {
114-
continue
115-
}
116-
for _, file := range pkg.Files {
117-
n := xform.Transform(&pkg.Info, pkg.Pkg, file)
132+
for i, filename := range pkg.CompiledGoFiles {
133+
if filename == tAbs {
134+
// Don't rewrite the template file.
135+
continue
136+
}
137+
file := pkg.Syntax[i]
138+
n := xform.Transform(pkg.TypesInfo, pkg.Types, file)
118139
if n == 0 {
119140
continue
120141
}
121-
filename := iprog.Fset.File(file.Pos()).Name()
122142
fmt.Fprintf(os.Stderr, "=== %s (%d matches)\n", filename, n)
123143
if *writeFlag {
124144
// Run the before-edit command (e.g. "chmod +w", "checkout") if any.
@@ -138,17 +158,34 @@ func doMain() error {
138158
args, err)
139159
}
140160
}
141-
if err := eg.WriteAST(iprog.Fset, filename, file); err != nil {
161+
if err := eg.WriteAST(cfg.Fset, filename, file); err != nil {
142162
fmt.Fprintf(os.Stderr, "eg: %s\n", err)
143163
hadErrors = true
144164
}
145165
} else {
146-
format.Node(os.Stdout, iprog.Fset, file)
166+
format.Node(os.Stdout, cfg.Fset, file)
147167
}
148168
}
149169
}
150170
if hadErrors {
151171
os.Exit(1)
152172
}
173+
153174
return nil
154175
}
176+
177+
type pkgsImporter []*packages.Package
178+
179+
func (p pkgsImporter) Import(path string) (tpkg *types.Package, err error) {
180+
packages.Visit([]*packages.Package(p), func(pkg *packages.Package) bool {
181+
if pkg.PkgPath == path {
182+
tpkg = pkg.Types
183+
return false
184+
}
185+
return true
186+
}, nil)
187+
if tpkg != nil {
188+
return tpkg, nil
189+
}
190+
return nil, fmt.Errorf("package %q not found", path)
191+
}

cmd/godoc/handlers.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@ import (
1414
"golang.org/x/tools/godoc"
1515
"golang.org/x/tools/godoc/redirect"
1616
"golang.org/x/tools/godoc/vfs"
17-
)
1817

19-
// This package registers "/compile" and "/share" handlers
20-
// that redirect to the golang.org playground.
21-
import _ "golang.org/x/tools/playground"
18+
_ "golang.org/x/tools/playground" // register "/compile" playground redirect
19+
)
2220

2321
var (
2422
pres *godoc.Presentation

cmd/godoc/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,10 @@ func main() {
192192
}
193193
if *templateDir != "" {
194194
fs.Bind("/lib/godoc", vfs.OS(*templateDir), "/", vfs.BindBefore)
195+
fs.Bind("/favicon.ico", vfs.OS(*templateDir), "/favicon.ico", vfs.BindReplace)
195196
} else {
196197
fs.Bind("/lib/godoc", mapfs.New(static.Files), "/", vfs.BindReplace)
198+
fs.Bind("/favicon.ico", mapfs.New(static.Files), "/favicon.ico", vfs.BindReplace)
197199
}
198200

199201
// Get the GOMOD value, use it to determine if godoc is being invoked in module mode.

cmd/goyacc/doc.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,25 @@ to be given an argument that conforms to the following interface:
3333
Error(e string)
3434
}
3535
36+
The parser will also define the following
37+
38+
type yyLexerErrLoc interface {
39+
Lex(lval *yySymType) int
40+
Error(e string)
41+
ErrorLoc(e string, loc *yySymLoc)
42+
}
43+
44+
If token location tracking in error messages is desired, the lexer
45+
should also define the following method, in addition to Error:
46+
47+
func (yylex *Lexer) ErrorLoc(e string, errLoc *yySymLoc) {
48+
...
49+
}
50+
51+
If this method is present, the generated parser will call the ErrorLoc
52+
method, passing it the error string, as well as the token location
53+
information for the offending token.
54+
3655
Lex should return the token identifier, and place other token
3756
information in lval (which replaces the usual yylval).
3857
Error is equivalent to yyerror in the original yacc.
@@ -66,5 +85,44 @@ symbols, including types, the parser, and the lexer, generated and
6685
referenced by yacc's generated code. Setting it to distinct values
6786
allows multiple grammars to be placed in a single package.
6887
88+
goyacc will generate a parser compatible with bison's token location
89+
tracking semantics. For more details:
90+
91+
https://www.gnu.org/software/bison/manual/html_node/Tracking-Locations.html
92+
https://www.gnu.org/software/bison/manual/html_node/Token-Locations.html
93+
94+
The generated Go parser will define two types:
95+
96+
type yyPos struct {
97+
line int
98+
column int
99+
}
100+
101+
type yySymLoc struct {
102+
pos yyPos
103+
end yyPos
104+
}
105+
106+
The pos field refers to the beginning of the token in question,
107+
and the end field to the end it. To avoid having to change the
108+
definition of the lexer's Error method, just before the parser calls
109+
the Error method, it will set a global variable, yyErrLoc to the
110+
address of the problematic token's yySymLoc structure. Since the
111+
lexer provides location information to the parser, and in turn is
112+
provided it, if needed, it's up to the lexer to do so consistently.
113+
114+
As in the above-cited BISON web pages, goyacc will support the use
115+
of @N, where N is an integer from 1 to 9, and will be expanded in
116+
the generated parser to the appropriate variable. If an action rule
117+
wants to print a specific error message, the lexer should be written
118+
to provide one to the parser.
119+
120+
If goyacc was invoked with an explicit prefix, via the '-p' switch,
121+
the above types and variables will have the appropriate prefix.
122+
123+
The token tracking structure yySymLoc is stored inside the yySymType
124+
structure. This simplifies the changes, since goyacc already has to
125+
copy the structure in question.
126+
69127
*/
70128
package main

cmd/goyacc/yacc.go

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ func setup() {
386386
fmt.Fprintf(stderr, "yacc: stack size too small\n")
387387
usage()
388388
}
389-
yaccpar = strings.Replace(yaccpartext, "$$", prefix, -1)
389+
yaccpar = strings.ReplaceAll(yaccpartext, "$$", prefix)
390390
openup()
391391

392392
fmt.Fprintf(ftable, "// Code generated by goyacc %s. DO NOT EDIT.\n", strings.Join(os.Args[1:], " "))
@@ -1065,6 +1065,15 @@ func chfind(t int, s string) int {
10651065
// copy the union declaration to the output, and the define file if present
10661066
//
10671067
func cpyunion() {
1068+
fmt.Fprintf(ftable, "\ntype %sPos struct {", prefix)
1069+
fmt.Fprintf(ftable, "\n\tline\tint")
1070+
fmt.Fprintf(ftable, "\n\tcolumn\tint")
1071+
fmt.Fprintf(ftable, "\n}\n")
1072+
1073+
fmt.Fprintf(ftable, "\ntype %sSymLoc struct {", prefix)
1074+
fmt.Fprintf(ftable, "\n\tpos %sPos", prefix)
1075+
fmt.Fprintf(ftable, "\n\tend %sPos", prefix)
1076+
fmt.Fprintf(ftable, "\n}\n")
10681077

10691078
if !lflag {
10701079
fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno)
@@ -1085,7 +1094,8 @@ out:
10851094
lineno++
10861095
case '{':
10871096
if level == 0 {
1088-
fmt.Fprintf(ftable, "\n\tyys int")
1097+
fmt.Fprintf(ftable, "\n\tyys\tint")
1098+
fmt.Fprintf(ftable, "\n\tsymLoc\t%sSymLoc", prefix)
10891099
}
10901100
level++
10911101
case '}':
@@ -1308,6 +1318,29 @@ loop:
13081318
case '{':
13091319
brac++
13101320

1321+
case '@':
1322+
n := 0
1323+
c2 := getrune(finput)
1324+
if isdigit(c2) {
1325+
for isdigit(c2) {
1326+
n = n*10 + int(c2-'0')
1327+
c2 = getrune(finput)
1328+
}
1329+
ungetrune(finput, c2)
1330+
1331+
if n < 1 || n >= max {
1332+
errorf("Illegal use of @%v", n)
1333+
}
1334+
1335+
fmt.Fprintf(fcode, "%sDollar[%d].symLoc", prefix, n)
1336+
continue loop
1337+
} else {
1338+
ungetrune(finput, c2)
1339+
if gettok() != NUMBER {
1340+
errorf("@ must be followed by number")
1341+
}
1342+
}
1343+
13111344
case '$':
13121345
s := 1
13131346
tok := -1
@@ -3287,6 +3320,12 @@ type $$Lexer interface {
32873320
Error(s string)
32883321
}
32893322
3323+
type $$LexerErrLoc interface {
3324+
Lex(lval *$$SymType) int
3325+
Error(s string)
3326+
ErrorLoc(s string, loc *$$SymLoc)
3327+
}
3328+
32903329
type $$Parser interface {
32913330
Parse($$Lexer) int
32923331
Lookahead() int
@@ -3531,7 +3570,13 @@ $$default:
35313570
/* error ... attempt to resume parsing */
35323571
switch Errflag {
35333572
case 0: /* brand new error */
3534-
$$lex.Error($$ErrorMessage($$state, $$token))
3573+
errMsg := $$ErrorMessage($$state, $$token)
3574+
if errLoc, ok := $$lex.($$LexerErrLoc); ok {
3575+
errLoc.ErrorLoc(errMsg, &$$rcvr.lval.symLoc)
3576+
} else {
3577+
$$lex.Error(errMsg)
3578+
}
3579+
35353580
Nerrs++
35363581
if $$Debug >= 1 {
35373582
__yyfmt__.Printf("%s", $$Statname($$state))
@@ -3593,6 +3638,11 @@ $$default:
35933638
}
35943639
$$VAL = $$S[$$p+1]
35953640
3641+
$$VAL.symLoc.pos.line = $$S[$$p+1].symLoc.pos.line
3642+
$$VAL.symLoc.pos.column = $$S[$$p+1].symLoc.pos.column
3643+
$$VAL.symLoc.end.line = $$S[$$pt].symLoc.end.line
3644+
$$VAL.symLoc.end.column = $$S[$$pt].symLoc.end.column
3645+
35963646
/* consult goto table to find next state */
35973647
$$n = $$R1[$$n]
35983648
$$g := $$Pgo[$$n]

0 commit comments

Comments
 (0)