1// Copyright 2021 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
5package noder
6
7import (
8	"cmd/compile/internal/base"
9	"cmd/compile/internal/syntax"
10	"cmd/internal/src"
11)
12
13// A posMap handles mapping from syntax.Pos to src.XPos.
14type posMap struct {
15	bases map[*syntax.PosBase]*src.PosBase
16	cache struct {
17		last *syntax.PosBase
18		base *src.PosBase
19	}
20}
21
22type poser interface{ Pos() syntax.Pos }
23type ender interface{ End() syntax.Pos }
24
25func (m *posMap) pos(p poser) src.XPos { return m.makeXPos(p.Pos()) }
26func (m *posMap) end(p ender) src.XPos { return m.makeXPos(p.End()) }
27
28func (m *posMap) makeXPos(pos syntax.Pos) src.XPos {
29	// Predeclared objects (e.g., the result parameter for error.Error)
30	// do not have a position.
31	if !pos.IsKnown() {
32		return src.NoXPos
33	}
34
35	posBase := m.makeSrcPosBase(pos.Base())
36	return base.Ctxt.PosTable.XPos(src.MakePos(posBase, pos.Line(), pos.Col()))
37}
38
39// makeSrcPosBase translates from a *syntax.PosBase to a *src.PosBase.
40func (m *posMap) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase {
41	// fast path: most likely PosBase hasn't changed
42	if m.cache.last == b0 {
43		return m.cache.base
44	}
45
46	b1, ok := m.bases[b0]
47	if !ok {
48		fn := b0.Filename()
49		absfn := trimFilename(b0)
50
51		if b0.IsFileBase() {
52			b1 = src.NewFileBase(fn, absfn)
53		} else {
54			// line directive base
55			p0 := b0.Pos()
56			p0b := p0.Base()
57			if p0b == b0 {
58				panic("infinite recursion in makeSrcPosBase")
59			}
60			p1 := src.MakePos(m.makeSrcPosBase(p0b), p0.Line(), p0.Col())
61			b1 = src.NewLinePragmaBase(p1, fn, absfn, b0.Line(), b0.Col())
62		}
63		if m.bases == nil {
64			m.bases = make(map[*syntax.PosBase]*src.PosBase)
65		}
66		m.bases[b0] = b1
67	}
68
69	// update cache
70	m.cache.last = b0
71	m.cache.base = b1
72
73	return b1
74}
75