forked from golang/go
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpos.go
More file actions
268 lines (229 loc) · 8.42 KB
/
Copy pathpos.go
File metadata and controls
268 lines (229 loc) · 8.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements the encoding of source positions.
package src
import "strconv"
// A Pos encodes a source position consisting of a (line, column) number pair
// and a position base. A zero Pos is a ready to use "unknown" position (nil
// position base and zero line number).
//
// The (line, column) values refer to a position in a file independent of any
// position base ("absolute" file position).
//
// The position base is used to determine the "relative" position, that is the
// filename and line number relative to the position base. If the base refers
// to the current file, there is no difference between absolute and relative
// positions. If it refers to a //line pragma, a relative position is relative
// to that pragma. A position base in turn contains the position at which it
// was introduced in the current file.
type Pos struct {
base *PosBase
lico
}
// NoPos is a valid unknown position.
var NoPos Pos
// MakePos creates a new Pos value with the given base, and (file-absolute)
// line and column.
func MakePos(base *PosBase, line, col uint) Pos {
return Pos{base, makeLico(line, col)}
}
// IsKnown reports whether the position p is known.
// A position is known if it either has a non-nil
// position base, or a non-zero line number.
func (p Pos) IsKnown() bool {
return p.base != nil || p.Line() != 0
}
// Before reports whether the position p comes before q in the source.
// For positions in different files, ordering is by filename.
func (p Pos) Before(q Pos) bool {
n, m := p.Filename(), q.Filename()
return n < m || n == m && p.lico < q.lico
}
// After reports whether the position p comes after q in the source.
// For positions in different files, ordering is by filename.
func (p Pos) After(q Pos) bool {
n, m := p.Filename(), q.Filename()
return n > m || n == m && p.lico > q.lico
}
// Filename returns the name of the actual file containing this position.
func (p Pos) Filename() string { return p.base.Pos().RelFilename() }
// Base returns the position base.
func (p Pos) Base() *PosBase { return p.base }
// SetBase sets the position base.
func (p *Pos) SetBase(base *PosBase) { p.base = base }
// RelFilename returns the filename recorded with the position's base.
func (p Pos) RelFilename() string { return p.base.Filename() }
// RelLine returns the line number relative to the positions's base.
func (p Pos) RelLine() uint { b := p.base; return b.Line() + p.Line() - b.Pos().Line() }
// AbsFilename() returns the absolute filename recorded with the position's base.
func (p Pos) AbsFilename() string { return p.base.AbsFilename() }
// SymFilename() returns the absolute filename recorded with the position's base,
// prefixed by FileSymPrefix to make it appropriate for use as a linker symbol.
func (p Pos) SymFilename() string { return p.base.SymFilename() }
func (p Pos) String() string {
return p.Format(true)
}
// Format formats a position as "filename:line" or "filename:line:column",
// controlled by the showCol flag.
// If the position is relative to a line directive, the original position
// is appended in square brackets without column (since the column doesn't
// change).
func (p Pos) Format(showCol bool) string {
if !p.IsKnown() {
return "<unknown line number>"
}
if b := p.base; b == b.Pos().base {
// base is file base (incl. nil)
return format(p.Filename(), p.Line(), p.Col(), showCol)
}
// base is relative
// Print the column only for the original position since the
// relative position's column information may be bogus (it's
// typically generated code and we can't say much about the
// original source at that point but for the file:line info
// that's provided via a line directive).
// TODO(gri) This may not be true if we have an inlining base.
// We may want to differentiate at some point.
return format(p.RelFilename(), p.RelLine(), 0, false) +
"[" + format(p.Filename(), p.Line(), p.Col(), showCol) + "]"
}
// format formats a (filename, line, col) tuple as "filename:line" (showCol
// is false) or "filename:line:column" (showCol is true).
func format(filename string, line, col uint, showCol bool) string {
s := filename + ":" + strconv.FormatUint(uint64(line), 10)
// col == colMax is interpreted as unknown column value
if showCol && col < colMax {
s += ":" + strconv.FormatUint(uint64(col), 10)
}
return s
}
// ----------------------------------------------------------------------------
// PosBase
// A PosBase encodes a filename and base line number.
// Typically, each file and line pragma introduce a PosBase.
// A nil *PosBase is a ready to use file PosBase for an unnamed
// file with line numbers starting at 1.
type PosBase struct {
pos Pos
filename string // file name used to open source file, for error messages
absFilename string // absolute file name, for PC-Line tables
symFilename string // cached symbol file name, to avoid repeated string concatenation
line uint // relative line number at pos
inl int // inlining index (see cmd/internal/obj/inl.go)
}
// NewFileBase returns a new *PosBase for a file with the given (relative and
// absolute) filenames.
func NewFileBase(filename, absFilename string) *PosBase {
if filename != "" {
base := &PosBase{
filename: filename,
absFilename: absFilename,
symFilename: FileSymPrefix + absFilename,
inl: -1,
}
base.pos = MakePos(base, 0, 0)
return base
}
return nil
}
// NewLinePragmaBase returns a new *PosBase for a line pragma of the form
// //line filename:line
// at position pos.
func NewLinePragmaBase(pos Pos, filename string, line uint) *PosBase {
return &PosBase{pos, filename, filename, FileSymPrefix + filename, line - 1, -1}
}
// NewInliningBase returns a copy of the old PosBase with the given inlining
// index. If old == nil, the resulting PosBase has no filename.
func NewInliningBase(old *PosBase, inlTreeIndex int) *PosBase {
if old == nil {
base := &PosBase{inl: inlTreeIndex}
base.pos = MakePos(base, 0, 0)
return base
}
copy := *old
base := ©
base.inl = inlTreeIndex
if old == old.pos.base {
base.pos.base = base
}
return base
}
var noPos Pos
// Pos returns the position at which base is located.
// If b == nil, the result is the zero position.
func (b *PosBase) Pos() *Pos {
if b != nil {
return &b.pos
}
return &noPos
}
// Filename returns the filename recorded with the base.
// If b == nil, the result is the empty string.
func (b *PosBase) Filename() string {
if b != nil {
return b.filename
}
return ""
}
// AbsFilename returns the absolute filename recorded with the base.
// If b == nil, the result is the empty string.
func (b *PosBase) AbsFilename() string {
if b != nil {
return b.absFilename
}
return ""
}
const FileSymPrefix = "gofile.."
// SymFilename returns the absolute filename recorded with the base,
// prefixed by FileSymPrefix to make it appropriate for use as a linker symbol.
// If b is nil, SymFilename returns FileSymPrefix + "??".
func (b *PosBase) SymFilename() string {
if b != nil {
return b.symFilename
}
return FileSymPrefix + "??"
}
// Line returns the line number recorded with the base.
// If b == nil, the result is 0.
func (b *PosBase) Line() uint {
if b != nil {
return b.line
}
return 0
}
// InliningIndex returns the index into the global inlining
// tree recorded with the base. If b == nil or the base has
// not been inlined, the result is < 0.
func (b *PosBase) InliningIndex() int {
if b != nil {
return b.inl
}
return -1
}
// ----------------------------------------------------------------------------
// lico
// A lico is a compact encoding of a LIne and COlumn number.
type lico uint32
// Layout constants: 24 bits for line, 8 bits for column.
// (If this is too tight, we can either make lico 64b wide,
// or we can introduce a tiered encoding where we remove column
// information as line numbers grow bigger; similar to what gcc
// does.)
const (
lineBits, lineMax = 24, 1<<lineBits - 1
colBits, colMax = 32 - lineBits, 1<<colBits - 1
)
func makeLico(line, col uint) lico {
if line > lineMax {
// cannot represent line, use max. line so we have some information
line = lineMax
}
if col > colMax {
// cannot represent column, use max. column so we have some information
col = colMax
}
return lico(line<<colBits | col)
}
func (x lico) Line() uint { return uint(x) >> colBits }
func (x lico) Col() uint { return uint(x) & colMax }