Skip to content

Commit

Permalink
Split stack.Location into a private helper
Browse files Browse the repository at this point in the history
  • Loading branch information
davecheney committed May 23, 2016
1 parent 80cce0e commit 567ccaa
Showing 1 changed file with 50 additions and 46 deletions.
96 changes: 50 additions & 46 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,52 +59,7 @@ import (
type stack []uintptr

func (s stack) Location() (string, int) {
pc := s[0] - 1
fn := runtime.FuncForPC(pc)
if fn == nil {
return "unknown", 0
}

file, line := fn.FileLine(pc)

// Here we want to get the source file path relative to the compile time
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
// GOPATH at runtime, but we can infer the number of path segments in the
// GOPATH. We note that fn.Name() returns the function name qualified by
// the import path, which does not include the GOPATH. Thus we can trim
// segments from the beginning of the file path until the number of path
// separators remaining is one more than the number of path separators in
// the function name. For example, given:
//
// GOPATH /home/user
// file /home/user/src/pkg/sub/file.go
// fn.Name() pkg/sub.Type.Method
//
// We want to produce:
//
// pkg/sub/file.go
//
// From this we can easily see that fn.Name() has one less path separator
// than our desired output. We count separators from the end of the file
// path until it finds two more than in the function name and then move
// one character forward to preserve the initial path segment without a
// leading separator.
const sep = "/"
goal := strings.Count(fn.Name(), sep) + 2
i := len(file)
for n := 0; n < goal; n++ {
i = strings.LastIndex(file[:i], sep)
if i == -1 {
// not enough separators found, set i so that the slice expression
// below leaves file unmodified
i = -len(sep)
break
}
}
// get back to 0 or trim the leading separator
file = file[i+len(sep):]

return file, line
return location(s[0] - 1)
}

// New returns an error that formats as the given text.
Expand Down Expand Up @@ -241,3 +196,52 @@ func callers() stack {
n := runtime.Callers(3, pcs[:])
return pcs[0:n]
}

// location returns the source file and line matching pc.
func location(pc uintptr) (string, int) {
fn := runtime.FuncForPC(pc)
if fn == nil {
return "unknown", 0
}

file, line := fn.FileLine(pc)

// Here we want to get the source file path relative to the compile time
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
// GOPATH at runtime, but we can infer the number of path segments in the
// GOPATH. We note that fn.Name() returns the function name qualified by
// the import path, which does not include the GOPATH. Thus we can trim
// segments from the beginning of the file path until the number of path
// separators remaining is one more than the number of path separators in
// the function name. For example, given:
//
// GOPATH /home/user
// file /home/user/src/pkg/sub/file.go
// fn.Name() pkg/sub.Type.Method
//
// We want to produce:
//
// pkg/sub/file.go
//
// From this we can easily see that fn.Name() has one less path separator
// than our desired output. We count separators from the end of the file
// path until it finds two more than in the function name and then move
// one character forward to preserve the initial path segment without a
// leading separator.
const sep = "/"
goal := strings.Count(fn.Name(), sep) + 2
i := len(file)
for n := 0; n < goal; n++ {
i = strings.LastIndex(file[:i], sep)
if i == -1 {
// not enough separators found, set i so that the slice expression
// below leaves file unmodified
i = -len(sep)
break
}
}
// get back to 0 or trim the leading separator
file = file[i+len(sep):]

return file, line
}

0 comments on commit 567ccaa

Please sign in to comment.