loader: report all type and syntax errors possible

Previously the loader would only report the first error. With this
change, all syntax and type errors will be reported.
This commit is contained in:
Ayke van Laethem
2024-07-12 15:07:24 +02:00
parent a6602dc708
commit 587ad68bd9
6 changed files with 68 additions and 5 deletions
+1
View File
@@ -17,6 +17,7 @@ import (
func TestErrors(t *testing.T) {
for _, name := range []string{
"cgo",
"multi",
"loader-importcycle",
"loader-invaliddep",
"loader-invalidpackage",
+39 -5
View File
@@ -318,18 +318,34 @@ func (p *Program) MainPkg() *Package {
func (p *Program) Parse() error {
// Parse all packages.
// TODO: do this in parallel.
var errors []error
for _, pkg := range p.sorted {
err := pkg.Parse()
if err != nil {
return err
errors = append(errors, err)
}
}
// Typecheck all packages.
for _, pkg := range p.sorted {
if !pkg.isParsed() || !pkg.allImportsChecked() {
if len(errors) == 0 {
// Sanity check.
// If there are no errors, all packages should have been parsed.
panic("unreachable")
}
continue
}
err := pkg.Check()
if err != nil {
return err
errors = append(errors, err)
}
}
if len(errors) != 0 {
// TODO: use errors.Join in Go 1.20.
return Errors{
Errs: errors,
}
}
@@ -354,12 +370,12 @@ func (p *Package) parseFile(path string, mode parser.Mode) (*ast.File, error) {
return parser.ParseFile(p.program.fset, originalPath, data, mode)
}
// Parse parses and typechecks this package.
// Parse parses this package.
//
// Idempotent.
func (p *Package) Parse() error {
if len(p.Files) != 0 {
return nil // nothing to do (?)
if p.isParsed() {
return nil // nothing to do
}
// Load the AST.
@@ -379,6 +395,24 @@ func (p *Package) Parse() error {
return nil
}
// isParsed returns whether this package has been parsed.
func (p *Package) isParsed() bool {
// Special case: the unsafe package doesn't have files to parse but does
// have p.Pkg set once it is parsed.
return len(p.Files) != 0 || p.Pkg != nil
}
// allImportsChecked returns whether all imports of this package have been
// type-checked.
func (p *Package) allImportsChecked() bool {
for _, dep := range p.Imports {
if p.program.Packages[dep].Pkg == nil {
return false
}
}
return true
}
// Check runs the package through the typechecker. The package must already be
// loaded and all dependencies must have been checked already.
//
+3
View File
@@ -0,0 +1,3 @@
package typesmulti3
var _ int = 3.14
+4
View File
@@ -0,0 +1,4 @@
package syntaxmulti2
func Foo2(type) {
}
+4
View File
@@ -0,0 +1,4 @@
package syntaxmulti1
func Foo1(import) {
}
+17
View File
@@ -0,0 +1,17 @@
package main
import (
_ "github.com/tinygo-org/tinygo/testdata/errors/multi-1"
_ "github.com/tinygo-org/tinygo/testdata/errors/multi-2"
_ "github.com/tinygo-org/tinygo/testdata/errors/multi-3"
)
func main() {
}
// ERROR: # github.com/tinygo-org/tinygo/testdata/errors/multi-2
// ERROR: multi-2/syntax.go:3:11: expected ')', found 'type'
// ERROR: # github.com/tinygo-org/tinygo/testdata/errors/multi-3
// ERROR: multi-3/syntax.go:3:11: expected ')', found 'import'
// ERROR: # github.com/tinygo-org/tinygo/testdata/errors/multi-1
// ERROR: multi-1/types.go:3:13: cannot use 3.14 (untyped float constant) as int value in variable declaration (truncated)