1
0
mirror of https://github.com/golang/go synced 2024-10-01 12:28:37 -06:00
go/cmd/guru/pos.go
Alan Donovan e08a7ae6bc cmd/guru: add support for loading modified files
The -modified flag causes guru to read a simple archive file from stdin.
This archive specifies alternative contents for one or more file names.
The build.Context checks this table before delegating to the usual
behavior.

This will not work for files that import "C" since cgo accesses the
file system directly.

Added end-to-end test via Emacs.

Simplify findQueryPos (now: fileOffsetToPos)

Credit: Daniel Morsing, for the prototype of this feature.

Change-Id: I5ae818ed5e8bb81001781893dded2d085e9cf8d6
Reviewed-on: https://go-review.googlesource.com/19498
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-15 15:04:13 +00:00

133 lines
3.3 KiB
Go

// Copyright 2013 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.
package main
// This file defines utilities for working with file positions.
import (
"fmt"
"go/parser"
"go/token"
"os"
"path/filepath"
"strconv"
"strings"
"golang.org/x/tools/go/ast/astutil"
)
// parseOctothorpDecimal returns the numeric value if s matches "#%d",
// otherwise -1.
func parseOctothorpDecimal(s string) int {
if s != "" && s[0] == '#' {
if s, err := strconv.ParseInt(s[1:], 10, 32); err == nil {
return int(s)
}
}
return -1
}
// parsePos parses a string of the form "file:pos" or
// file:start,end" where pos, start, end match #%d and represent byte
// offsets, and returns its components.
//
// (Numbers without a '#' prefix are reserved for future use,
// e.g. to indicate line/column positions.)
//
func parsePos(pos string) (filename string, startOffset, endOffset int, err error) {
if pos == "" {
err = fmt.Errorf("no source position specified")
return
}
colon := strings.LastIndex(pos, ":")
if colon < 0 {
err = fmt.Errorf("bad position syntax %q", pos)
return
}
filename, offset := pos[:colon], pos[colon+1:]
startOffset = -1
endOffset = -1
if hyphen := strings.Index(offset, ","); hyphen < 0 {
// e.g. "foo.go:#123"
startOffset = parseOctothorpDecimal(offset)
endOffset = startOffset
} else {
// e.g. "foo.go:#123,#456"
startOffset = parseOctothorpDecimal(offset[:hyphen])
endOffset = parseOctothorpDecimal(offset[hyphen+1:])
}
if startOffset < 0 || endOffset < 0 {
err = fmt.Errorf("invalid offset %q in query position", offset)
return
}
return
}
// fileOffsetToPos translates the specified file-relative byte offsets
// into token.Pos form. It returns an error if the file was not found
// or the offsets were out of bounds.
//
func fileOffsetToPos(file *token.File, startOffset, endOffset int) (start, end token.Pos, err error) {
// Range check [start..end], inclusive of both end-points.
if 0 <= startOffset && startOffset <= file.Size() {
start = file.Pos(int(startOffset))
} else {
err = fmt.Errorf("start position is beyond end of file")
return
}
if 0 <= endOffset && endOffset <= file.Size() {
end = file.Pos(int(endOffset))
} else {
err = fmt.Errorf("end position is beyond end of file")
return
}
return
}
// sameFile returns true if x and y have the same basename and denote
// the same file.
//
func sameFile(x, y string) bool {
if filepath.Base(x) == filepath.Base(y) { // (optimisation)
if xi, err := os.Stat(x); err == nil {
if yi, err := os.Stat(y); err == nil {
return os.SameFile(xi, yi)
}
}
}
return false
}
// fastQueryPos parses the position string and returns a queryPos.
// It parses only a single file and does not run the type checker.
func fastQueryPos(pos string) (*queryPos, error) {
filename, startOffset, endOffset, err := parsePos(pos)
if err != nil {
return nil, err
}
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, filename, nil, 0)
if err != nil {
return nil, err
}
start, end, err := fileOffsetToPos(fset.File(f.Pos()), startOffset, endOffset)
if err != nil {
return nil, err
}
path, exact := astutil.PathEnclosingInterval(f, start, end)
if path == nil {
return nil, fmt.Errorf("no syntax here")
}
return &queryPos{fset, start, end, path, exact, nil}, nil
}