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