Skip to content

Commit 37e0463

Browse files
committed
format: ignore //line directives when computing positions
Otherwise, if the line directives give us line numbers which don't make any sense given the real file's line numbers, our use of MergeLines can cause a panic or just bad formatting. Fixes #288.
1 parent 9e77a5f commit 37e0463

File tree

2 files changed

+192
-7
lines changed

2 files changed

+192
-7
lines changed

format/format.go

+19-7
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func File(fset *token.FileSet, file *ast.File, opts Options) {
106106
}
107107
opts.LangVersion = "v" + m[2] + "." + m[3]
108108
f := &fumpter{
109-
File: fset.File(file.Pos()),
109+
file: fset.File(file.Pos()),
110110
fset: fset,
111111
astFile: file,
112112
Options: opts,
@@ -179,7 +179,7 @@ var rxOctalInteger = regexp.MustCompile(`\A0[0-7_]+\z`)
179179
type fumpter struct {
180180
Options
181181

182-
*token.File
182+
file *token.File
183183
fset *token.FileSet
184184

185185
astFile *ast.File
@@ -227,7 +227,7 @@ func (f *fumpter) addNewline(at token.Pos) {
227227
offset := f.Offset(at)
228228

229229
// TODO: replace with the new Lines method once we require Go 1.21 or later
230-
field := reflect.ValueOf(f.File).Elem().FieldByName("lines")
230+
field := reflect.ValueOf(f.file).Elem().FieldByName("lines")
231231
n := field.Len()
232232
lines := make([]int, 0, n+1)
233233
for i := 0; i < n; i++ {
@@ -246,7 +246,7 @@ func (f *fumpter) addNewline(at token.Pos) {
246246
if offset >= 0 {
247247
lines = append(lines, offset)
248248
}
249-
if !f.SetLines(lines) {
249+
if !f.file.SetLines(lines) {
250250
panic(fmt.Sprintf("could not set lines to %v", lines))
251251
}
252252
}
@@ -255,7 +255,7 @@ func (f *fumpter) addNewline(at token.Pos) {
255255
// up on the same line.
256256
func (f *fumpter) removeLines(fromLine, toLine int) {
257257
for fromLine < toLine {
258-
f.MergeLine(fromLine)
258+
f.file.MergeLine(fromLine)
259259
toLine--
260260
}
261261
}
@@ -266,6 +266,18 @@ func (f *fumpter) removeLinesBetween(from, to token.Pos) {
266266
f.removeLines(f.Line(from)+1, f.Line(to))
267267
}
268268

269+
func (f *fumpter) Position(p token.Pos) token.Position {
270+
return f.file.PositionFor(p, false)
271+
}
272+
273+
func (f *fumpter) Line(p token.Pos) int {
274+
return f.Position(p).Line
275+
}
276+
277+
func (f *fumpter) Offset(p token.Pos) int {
278+
return f.file.Offset(p)
279+
}
280+
269281
type byteCounter int
270282

271283
func (b *byteCounter) Write(p []byte) (n int, err error) {
@@ -295,14 +307,14 @@ func (f *fumpter) lineEnd(line int) token.Pos {
295307
if line < 1 {
296308
panic("illegal line number")
297309
}
298-
total := f.LineCount()
310+
total := f.file.LineCount()
299311
if line > total {
300312
panic("illegal line number")
301313
}
302314
if line == total {
303315
return f.astFile.End()
304316
}
305-
return f.LineStart(line+1) - 1
317+
return f.file.LineStart(line+1) - 1
306318
}
307319

308320
// rxCommentDirective covers all common Go comment directives:

testdata/script/linedirectives.txtar

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# Line directives can throw off our use of MergeLines.
2+
# We should ignore them entirely when calculating line numbers.
3+
# The file below is borrowed from Go's test/dwarf/linedirectives.go.
4+
5+
exec gofumpt -w foo.go
6+
cmp foo.go foo.go.golden
7+
8+
-- foo.go --
9+
// Copyright 2011 The Go Authors. All rights reserved.
10+
// Use of this source code is governed by a BSD-style
11+
// license that can be found in the LICENSE file.
12+
13+
//line foo/bar.y:4
14+
package main
15+
//line foo/bar.y:60
16+
func main() {
17+
//line foo/bar.y:297
18+
f, l := 0, 0
19+
//line yacctab:1
20+
f, l = 1, 1
21+
//line yaccpar:1
22+
f, l = 2, 1
23+
//line foo/bar.y:82
24+
f, l = 3, 82
25+
//line foo/bar.y:90
26+
f, l = 3, 90
27+
//line foo/bar.y:92
28+
f, l = 3, 92
29+
//line foo/bar.y:100
30+
f, l = 3, 100
31+
//line foo/bar.y:104
32+
l = 104
33+
//line foo/bar.y:112
34+
l = 112
35+
//line foo/bar.y:117
36+
l = 117
37+
//line foo/bar.y:121
38+
l = 121
39+
//line foo/bar.y:125
40+
l = 125
41+
//line foo/bar.y:133
42+
l = 133
43+
//line foo/bar.y:146
44+
l = 146
45+
//line foo/bar.y:148
46+
//line foo/bar.y:153
47+
//line foo/bar.y:155
48+
l = 155
49+
//line foo/bar.y:160
50+
51+
//line foo/bar.y:164
52+
//line foo/bar.y:173
53+
54+
//line foo/bar.y:178
55+
//line foo/bar.y:180
56+
//line foo/bar.y:185
57+
//line foo/bar.y:195
58+
//line foo/bar.y:197
59+
//line foo/bar.y:202
60+
//line foo/bar.y:204
61+
//line foo/bar.y:208
62+
//line foo/bar.y:211
63+
//line foo/bar.y:213
64+
//line foo/bar.y:215
65+
//line foo/bar.y:217
66+
//line foo/bar.y:221
67+
//line foo/bar.y:229
68+
//line foo/bar.y:236
69+
//line foo/bar.y:238
70+
//line foo/bar.y:240
71+
//line foo/bar.y:244
72+
//line foo/bar.y:249
73+
//line foo/bar.y:253
74+
//line foo/bar.y:257
75+
//line foo/bar.y:262
76+
//line foo/bar.y:267
77+
//line foo/bar.y:272
78+
if l == f {
79+
//line foo/bar.y:277
80+
panic("aie!")
81+
//line foo/bar.y:281
82+
}
83+
//line foo/bar.y:285
84+
return
85+
//line foo/bar.y:288
86+
//line foo/bar.y:290
87+
}
88+
//line foo/bar.y:293
89+
//line foo/bar.y:295
90+
-- foo.go.golden --
91+
// Copyright 2011 The Go Authors. All rights reserved.
92+
// Use of this source code is governed by a BSD-style
93+
// license that can be found in the LICENSE file.
94+
95+
//line foo/bar.y:4
96+
package main
97+
98+
//line foo/bar.y:60
99+
func main() {
100+
//line foo/bar.y:297
101+
f, l := 0, 0
102+
//line yacctab:1
103+
f, l = 1, 1
104+
//line yaccpar:1
105+
f, l = 2, 1
106+
//line foo/bar.y:82
107+
f, l = 3, 82
108+
//line foo/bar.y:90
109+
f, l = 3, 90
110+
//line foo/bar.y:92
111+
f, l = 3, 92
112+
//line foo/bar.y:100
113+
f, l = 3, 100
114+
//line foo/bar.y:104
115+
l = 104
116+
//line foo/bar.y:112
117+
l = 112
118+
//line foo/bar.y:117
119+
l = 117
120+
//line foo/bar.y:121
121+
l = 121
122+
//line foo/bar.y:125
123+
l = 125
124+
//line foo/bar.y:133
125+
l = 133
126+
//line foo/bar.y:146
127+
l = 146
128+
//line foo/bar.y:148
129+
//line foo/bar.y:153
130+
//line foo/bar.y:155
131+
l = 155
132+
//line foo/bar.y:160
133+
134+
//line foo/bar.y:164
135+
//line foo/bar.y:173
136+
137+
//line foo/bar.y:178
138+
//line foo/bar.y:180
139+
//line foo/bar.y:185
140+
//line foo/bar.y:195
141+
//line foo/bar.y:197
142+
//line foo/bar.y:202
143+
//line foo/bar.y:204
144+
//line foo/bar.y:208
145+
//line foo/bar.y:211
146+
//line foo/bar.y:213
147+
//line foo/bar.y:215
148+
//line foo/bar.y:217
149+
//line foo/bar.y:221
150+
//line foo/bar.y:229
151+
//line foo/bar.y:236
152+
//line foo/bar.y:238
153+
//line foo/bar.y:240
154+
//line foo/bar.y:244
155+
//line foo/bar.y:249
156+
//line foo/bar.y:253
157+
//line foo/bar.y:257
158+
//line foo/bar.y:262
159+
//line foo/bar.y:267
160+
//line foo/bar.y:272
161+
if l == f {
162+
//line foo/bar.y:277
163+
panic("aie!")
164+
//line foo/bar.y:281
165+
}
166+
//line foo/bar.y:285
167+
return
168+
//line foo/bar.y:288
169+
//line foo/bar.y:290
170+
}
171+
172+
//line foo/bar.y:293
173+
//line foo/bar.y:295

0 commit comments

Comments
 (0)