mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-16 18:53:29 +00:00
main: add StartPos and EndPos to -json build output
This is useful for the TinyGo Playground: with this change it can show the error span like in an IDE, instead of just the (single) error position. Errors become a bit more readable as a result.
This commit is contained in:
committed by
Ron Evans
parent
c2765e9eca
commit
de532643b1
+40
-20
@@ -10,6 +10,7 @@ import (
|
|||||||
"go/types"
|
"go/types"
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -23,6 +24,11 @@ import (
|
|||||||
type Diagnostic struct {
|
type Diagnostic struct {
|
||||||
Pos token.Position
|
Pos token.Position
|
||||||
Msg string
|
Msg string
|
||||||
|
|
||||||
|
// Start and end position, if available. For many errors these positions are
|
||||||
|
// not available, but for some they are.
|
||||||
|
StartPos token.Position
|
||||||
|
EndPos token.Position
|
||||||
}
|
}
|
||||||
|
|
||||||
// One or multiple errors of a particular package.
|
// One or multiple errors of a particular package.
|
||||||
@@ -114,12 +120,22 @@ func createPackageDiagnostic(err error) PackageDiagnostic {
|
|||||||
func createDiagnostics(err error) []Diagnostic {
|
func createDiagnostics(err error) []Diagnostic {
|
||||||
switch err := err.(type) {
|
switch err := err.(type) {
|
||||||
case types.Error:
|
case types.Error:
|
||||||
return []Diagnostic{
|
diag := Diagnostic{
|
||||||
{
|
Pos: err.Fset.Position(err.Pos),
|
||||||
Pos: err.Fset.Position(err.Pos),
|
Msg: err.Msg,
|
||||||
Msg: err.Msg,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
// There is a special unexported API since Go 1.16 that provides the
|
||||||
|
// range (start and end position) where the type error exists.
|
||||||
|
// There is no promise of backwards compatibility in future Go versions
|
||||||
|
// so we have to be extra careful here to be resilient.
|
||||||
|
v := reflect.ValueOf(err)
|
||||||
|
start := v.FieldByName("go116start")
|
||||||
|
end := v.FieldByName("go116end")
|
||||||
|
if start.IsValid() && end.IsValid() && start.Int() != end.Int() {
|
||||||
|
diag.StartPos = err.Fset.Position(token.Pos(start.Int()))
|
||||||
|
diag.EndPos = err.Fset.Position(token.Pos(end.Int()))
|
||||||
|
}
|
||||||
|
return []Diagnostic{diag}
|
||||||
case scanner.Error:
|
case scanner.Error:
|
||||||
return []Diagnostic{
|
return []Diagnostic{
|
||||||
{
|
{
|
||||||
@@ -188,25 +204,29 @@ func (diag Diagnostic) WriteTo(w io.Writer, wd string) {
|
|||||||
fmt.Fprintln(w, diag.Msg)
|
fmt.Fprintln(w, diag.Msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pos := diag.Pos // make a copy
|
pos := RelativePosition(diag.Pos, wd)
|
||||||
if !strings.HasPrefix(pos.Filename, filepath.Join(goenv.Get("GOROOT"), "src")) && !strings.HasPrefix(pos.Filename, filepath.Join(goenv.Get("TINYGOROOT"), "src")) {
|
|
||||||
// This file is not from the standard library (either the GOROOT or the
|
|
||||||
// TINYGOROOT). Make the path relative, for easier reading. Ignore any
|
|
||||||
// errors in the process (falling back to the absolute path).
|
|
||||||
pos.Filename = tryToMakePathRelative(pos.Filename, wd)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "%s: %s\n", pos, diag.Msg)
|
fmt.Fprintf(w, "%s: %s\n", pos, diag.Msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to make the path relative to the current working directory. If any error
|
// Convert the position in pos (assumed to have an absolute path) into a
|
||||||
// occurs, this error is ignored and the absolute path is returned instead.
|
// relative path if possible. Paths inside GOROOT/TINYGOROOT will remain
|
||||||
func tryToMakePathRelative(dir, wd string) string {
|
// absolute.
|
||||||
|
func RelativePosition(pos token.Position, wd string) token.Position {
|
||||||
|
// Check whether we even have a working directory.
|
||||||
if wd == "" {
|
if wd == "" {
|
||||||
return dir // working directory not found
|
return pos
|
||||||
}
|
}
|
||||||
relpath, err := filepath.Rel(wd, dir)
|
|
||||||
if err != nil {
|
// Paths inside GOROOT should be printed in full.
|
||||||
return dir
|
if strings.HasPrefix(pos.Filename, filepath.Join(goenv.Get("GOROOT"), "src")) || strings.HasPrefix(pos.Filename, filepath.Join(goenv.Get("TINYGOROOT"), "src")) {
|
||||||
|
return pos
|
||||||
}
|
}
|
||||||
return relpath
|
|
||||||
|
// Make the path relative, for easier reading. Ignore any errors in the
|
||||||
|
// process (falling back to the absolute path).
|
||||||
|
relpath, err := filepath.Rel(wd, pos.Filename)
|
||||||
|
if err == nil {
|
||||||
|
pos.Filename = relpath
|
||||||
|
}
|
||||||
|
return pos
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@ func printBuildOutput(err error, jsonDiagnostics bool) {
|
|||||||
ImportPath string
|
ImportPath string
|
||||||
Action string
|
Action string
|
||||||
Output string `json:",omitempty"`
|
Output string `json:",omitempty"`
|
||||||
|
StartPos string `json:",omitempty"` // non-standard
|
||||||
|
EndPos string `json:",omitempty"` // non-standard
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, diags := range diagnostics.CreateDiagnostics(err) {
|
for _, diags := range diagnostics.CreateDiagnostics(err) {
|
||||||
@@ -1407,19 +1409,25 @@ func printBuildOutput(err error, jsonDiagnostics bool) {
|
|||||||
Action: "build-output",
|
Action: "build-output",
|
||||||
Output: "# " + diags.ImportPath + "\n",
|
Output: "# " + diags.ImportPath + "\n",
|
||||||
})
|
})
|
||||||
os.Stdout.Write(output)
|
os.Stdout.Write(append(output, '\n'))
|
||||||
os.Stdout.Write([]byte{'\n'})
|
|
||||||
}
|
}
|
||||||
for _, diag := range diags.Diagnostics {
|
for _, diag := range diags.Diagnostics {
|
||||||
w := &bytes.Buffer{}
|
w := &bytes.Buffer{}
|
||||||
diag.WriteTo(w, workingDir)
|
diag.WriteTo(w, workingDir)
|
||||||
output, _ := json.Marshal(jsonDiagnosticOutput{
|
data := jsonDiagnosticOutput{
|
||||||
ImportPath: diags.ImportPath,
|
ImportPath: diags.ImportPath,
|
||||||
Action: "build-output",
|
Action: "build-output",
|
||||||
Output: w.String(),
|
Output: w.String(),
|
||||||
})
|
}
|
||||||
os.Stdout.Write(output)
|
if diag.StartPos.IsValid() && diag.EndPos.IsValid() {
|
||||||
os.Stdout.Write([]byte{'\n'})
|
// Include the non-standard StartPos/EndPos values. These
|
||||||
|
// are useful for the TinyGo Playground to show better error
|
||||||
|
// messages.
|
||||||
|
data.StartPos = diagnostics.RelativePosition(diag.StartPos, workingDir).String()
|
||||||
|
data.EndPos = diagnostics.RelativePosition(diag.EndPos, workingDir).String()
|
||||||
|
}
|
||||||
|
output, _ := json.Marshal(data)
|
||||||
|
os.Stdout.Write(append(output, '\n'))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit the "Action":"build-fail" JSON.
|
// Emit the "Action":"build-fail" JSON.
|
||||||
@@ -1427,8 +1435,7 @@ func printBuildOutput(err error, jsonDiagnostics bool) {
|
|||||||
ImportPath: diags.ImportPath,
|
ImportPath: diags.ImportPath,
|
||||||
Action: "build-fail",
|
Action: "build-fail",
|
||||||
})
|
})
|
||||||
os.Stdout.Write(output)
|
os.Stdout.Write(append(output, '\n'))
|
||||||
os.Stdout.Write([]byte{'\n'})
|
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user