Skip to content

Commit d7773d3

Browse files
committed
loader: handle go list errors inside TinyGo
Instead of exiting with an error, handle these errors internally. This will enable a few improvements in the future.
1 parent b04b690 commit d7773d3

File tree

9 files changed

+95
-9
lines changed

9 files changed

+95
-9
lines changed

errors_test.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77
"path/filepath"
8+
"runtime"
89
"strings"
910
"testing"
1011
"time"
@@ -16,6 +17,10 @@ import (
1617
func TestErrors(t *testing.T) {
1718
for _, name := range []string{
1819
"cgo",
20+
"loader-importcycle",
21+
"loader-invaliddep",
22+
"loader-invalidpackage",
23+
"loader-nopackage",
1924
"syntax",
2025
"types",
2126
} {
@@ -57,11 +62,22 @@ func testErrorMessages(t *testing.T, filename string) {
5762
actual := strings.TrimRight(buf.String(), "\n")
5863

5964
// Check whether the error is as expected.
60-
if actual != expected {
65+
if canonicalizeErrors(actual) != canonicalizeErrors(expected) {
6166
t.Errorf("expected error:\n%s\ngot:\n%s", indentText(expected, "> "), indentText(actual, "> "))
6267
}
6368
}
6469

70+
func canonicalizeErrors(text string) string {
71+
// Fix for Windows: replace all backslashes with forward slashes so that
72+
// paths will be the same as on POSIX systems.
73+
// (It may also change some other backslashes, but since this is only for
74+
// comparing text it should be fine).
75+
if runtime.GOOS == "windows" {
76+
text = strings.ReplaceAll(text, "\\", "/")
77+
}
78+
return text
79+
}
80+
6581
// Indent the given text with a given indentation string.
6682
func indentText(text, indent string) string {
6783
return indent + strings.ReplaceAll(text, "\n", "\n"+indent)

loader/loader.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ func Load(config *compileopts.Config, inputPkg string, typeChecker types.Config)
128128
}
129129

130130
// List the dependencies of this package, in raw JSON format.
131-
extraArgs := []string{"-json", "-deps"}
131+
extraArgs := []string{"-json", "-deps", "-e"}
132132
if config.TestConfig.CompileTestBinary {
133133
extraArgs = append(extraArgs, "-test")
134134
}
@@ -149,6 +149,7 @@ func Load(config *compileopts.Config, inputPkg string, typeChecker types.Config)
149149

150150
// Parse the returned json from `go list`.
151151
decoder := json.NewDecoder(buf)
152+
var pkgErrors []error
152153
for {
153154
pkg := &Package{
154155
program: p,
@@ -188,17 +189,24 @@ func Load(config *compileopts.Config, inputPkg string, typeChecker types.Config)
188189
pos.Filename = strings.Join(fields[:len(fields)-1], ":")
189190
pos.Line, _ = strconv.Atoi(fields[len(fields)-1])
190191
}
192+
if abs, err := filepath.Abs(pos.Filename); err == nil {
193+
// Make the path absolute, so that error messages will be
194+
// prettier (it will be turned back into a relative path
195+
// when printing the error).
196+
pos.Filename = abs
197+
}
191198
pos.Filename = p.getOriginalPath(pos.Filename)
192199
}
193200
err := scanner.Error{
194201
Pos: pos,
195202
Msg: pkg.Error.Err,
196203
}
197204
if len(pkg.Error.ImportStack) != 0 {
198-
return nil, Error{
205+
pkgErrors = append(pkgErrors, Error{
199206
ImportStack: pkg.Error.ImportStack,
200207
Err: err,
201-
}
208+
})
209+
continue
202210
}
203211
return nil, err
204212
}
@@ -241,6 +249,13 @@ func Load(config *compileopts.Config, inputPkg string, typeChecker types.Config)
241249
p.Packages[pkg.ImportPath] = pkg
242250
}
243251

252+
if len(pkgErrors) != 0 {
253+
// TODO: use errors.Join in Go 1.20.
254+
return nil, Errors{
255+
Errs: pkgErrors,
256+
}
257+
}
258+
244259
if config.TestConfig.CompileTestBinary && !strings.HasSuffix(p.sorted[len(p.sorted)-1].ImportPath, ".test") {
245260
// Trying to compile a test binary but there are no test files in this
246261
// package.

main.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,15 +1340,31 @@ func printCompilerError(err error, logln func(...interface{}), wd string) {
13401340
}
13411341
}
13421342
case loader.Errors:
1343-
logln("#", err.Pkg.ImportPath)
1343+
// Parser errors, typechecking errors, or `go list` errors.
1344+
// err.Pkg is nil for `go list` errors.
1345+
if err.Pkg != nil {
1346+
logln("#", err.Pkg.ImportPath)
1347+
}
13441348
for _, err := range err.Errs {
13451349
printCompilerError(err, logln, wd)
13461350
}
13471351
case loader.Error:
1348-
logln(err.Err.Error())
1349-
logln("package", err.ImportStack[0])
1350-
for _, pkgPath := range err.ImportStack[1:] {
1351-
logln("\timports", pkgPath)
1352+
if err.Err.Pos.Filename != "" {
1353+
// Probably a syntax error in a dependency.
1354+
printCompilerError(err.Err, logln, wd)
1355+
} else {
1356+
// Probably an "import cycle not allowed" error.
1357+
logln("package", err.ImportStack[0])
1358+
for i := 1; i < len(err.ImportStack); i++ {
1359+
pkgPath := err.ImportStack[i]
1360+
if i == len(err.ImportStack)-1 {
1361+
// last package
1362+
logln("\timports", pkgPath+": "+err.Err.Error())
1363+
} else {
1364+
// not the last pacakge
1365+
logln("\timports", pkgPath)
1366+
}
1367+
}
13521368
}
13531369
case *builder.MultiError:
13541370
for _, err := range err.Errs {

testdata/errors/importcycle/cycle.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package importcycle
2+
3+
import _ "github.com/tinygo-org/tinygo/testdata/errors/importcycle"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ppackage // syntax error

testdata/errors/loader-importcycle.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package main
2+
3+
import _ "github.com/tinygo-org/tinygo/testdata/errors/importcycle"
4+
5+
func main() {
6+
}
7+
8+
// ERROR: package command-line-arguments
9+
// ERROR: imports github.com/tinygo-org/tinygo/testdata/errors/importcycle
10+
// ERROR: imports github.com/tinygo-org/tinygo/testdata/errors/importcycle: import cycle not allowed

testdata/errors/loader-invaliddep.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package main
2+
3+
import _ "github.com/tinygo-org/tinygo/testdata/errors/invaliddep"
4+
5+
func main() {
6+
}
7+
8+
// ERROR: invaliddep/invaliddep.go:1:1: expected 'package', found ppackage
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ppackage // syntax error
2+
3+
// ERROR: loader-invalidpackage.go:1:1: expected 'package', found ppackage

testdata/errors/loader-nopackage.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package main
2+
3+
import (
4+
_ "github.com/tinygo-org/tinygo/testdata/errors/non-existing-package"
5+
_ "github.com/tinygo-org/tinygo/testdata/errors/non-existing-package-2"
6+
)
7+
8+
func main() {
9+
}
10+
11+
// ERROR: loader-nopackage.go:4:2: no required module provides package github.com/tinygo-org/tinygo/testdata/errors/non-existing-package; to add it:
12+
// ERROR: go get github.com/tinygo-org/tinygo/testdata/errors/non-existing-package
13+
// ERROR: loader-nopackage.go:5:2: no required module provides package github.com/tinygo-org/tinygo/testdata/errors/non-existing-package-2; to add it:
14+
// ERROR: go get github.com/tinygo-org/tinygo/testdata/errors/non-existing-package-2

0 commit comments

Comments
 (0)