Add support for extracting Go frontend errors.

This commit is contained in:
Max Schaefer
2020-04-09 15:06:59 +01:00
parent 43309b98fd
commit a837d5f4ce
3 changed files with 83 additions and 7 deletions

View File

@@ -4,6 +4,7 @@ import (
"go/ast"
"go/token"
gotypes "go/types"
"golang.org/x/tools/go/packages"
)
var defaultSnippet = AddDefaultSnippet(`
@@ -642,6 +643,20 @@ var ModLParenType = ModExprKind.NewBranch("@modlparen")
// ModRParenType is the type of go.mod line block end AST nodes
var ModRParenType = ModExprKind.NewBranch("@modrparen")
// ErrorType is the type of frontend errors
var ErrorType = NewPrimaryKeyType("@error")
// ErrorKind is a case type for distinguishing different kinds of frontend errors
var ErrorKind = NewCaseType(ErrorType, "kind")
// ErrorTypes is a map from error kinds to the corresponding type
var ErrorTypes = map[packages.ErrorKind]*BranchType{
packages.UnknownError: ErrorKind.NewBranch("@unknownerror"),
packages.ListError: ErrorKind.NewBranch("@listerror"),
packages.ParseError: ErrorKind.NewBranch("@parseerror"),
packages.TypeError: ErrorKind.NewBranch("@typeerror"),
}
// LocationsDefaultTable is the table defining location objects
var LocationsDefaultTable = NewTable("locations_default",
EntityColumn(LocationDefaultType, "id").Key(),
@@ -915,3 +930,16 @@ var ModTokensTable = NewTable("modtokens",
EntityColumn(ModExprType, "parent"),
IntColumn("idx"),
).KeySet("parent", "idx")
// ErrorsTable is the table describing frontend errors
var ErrorsTable = NewTable("errors",
EntityColumn(ErrorType, "id").Key(),
IntColumn("kind"),
StringColumn("msg"),
StringColumn("rawpos"),
StringColumn("file"),
IntColumn("line"),
IntColumn("col"),
EntityColumn(PackageType, "package"),
IntColumn("idx"),
).KeySet("package", "idx")

View File

@@ -57,13 +57,6 @@ func ExtractWithFlags(buildFlags []string, patterns []string) error {
packages.Visit(pkgs, func(pkg *packages.Package) bool {
return true
}, func(pkg *packages.Package) {
if len(pkg.Errors) != 0 {
log.Printf("Warning: encountered errors extracting package `%s`:", pkg.PkgPath)
for _, err := range pkg.Errors {
log.Printf(" %s", err.Error())
}
}
tw, err := trap.NewWriter(pkg.PkgPath, pkg)
if err != nil {
log.Fatal(err)
@@ -74,6 +67,14 @@ func ExtractWithFlags(buildFlags []string, patterns []string) error {
tw.ForEachObject(extractObjectType)
lbl := tw.Labeler.GlobalID(pkg.PkgPath + ";pkg")
dbscheme.PackagesTable.Emit(tw, lbl, pkg.Name, pkg.PkgPath, scope)
if len(pkg.Errors) != 0 {
log.Printf("Warning: encountered errors extracting package `%s`:", pkg.PkgPath)
for i, err := range pkg.Errors {
log.Printf(" %s", err.Error())
extractError(tw, err, lbl, i)
}
}
})
// this sets the number of threads that the Go runtime will spawn; this is separate
@@ -253,6 +254,43 @@ func extractObjectType(tw *trap.Writer, obj types.Object, lbl trap.Label) {
}
}
// extractError extracts the message and location of a frontend error
func extractError(tw *trap.Writer, err packages.Error, pkglbl trap.Label, idx int) {
var (
lbl = tw.Labeler.FreshID()
kind = dbscheme.ErrorTypes[err.Kind].Index()
pos = err.Pos
posComponents = strings.Split(err.Pos, ":")
file = ""
line = 0
col = 0
e error
)
switch len(posComponents) {
case 3:
// "file:line:col"
col, e = strconv.Atoi(posComponents[2])
if e != nil {
log.Printf("Warning: malformed column number `%s`: %v", posComponents[2], e)
}
fallthrough
case 2:
// "file:line"
file = posComponents[0]
line, e = strconv.Atoi(posComponents[1])
if e != nil {
log.Printf("Warning: malformed line number `%s`: %v", posComponents[1], e)
}
default:
// "", "-"
if pos != "" && pos != "-" {
log.Printf("Warning: malformed error position `%s`", pos)
}
}
file = filepath.ToSlash(srcarchive.TransformPath(file))
dbscheme.ErrorsTable.Emit(tw, lbl, kind, err.Msg, pos, file, line, col, pkglbl, idx)
}
// extractPackage extracts AST information for all files in the given package
func extractPackage(pkg *packages.Package, wg *sync.WaitGroup,
goroutineSem *semaphore, fdSem *semaphore) {

View File

@@ -124,6 +124,10 @@ modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent
#keyset[parent, idx]
modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref);
#keyset[package, idx]
errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref,
string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref);
@container = @file | @folder;
@locatable = @node | @localscope;
@@ -418,3 +422,9 @@ case @modexpr.kind of
| 3 = @modlparen
| 4 = @modrparen;
case @error.kind of
0 = @unknownerror
| 1 = @listerror
| 2 = @parseerror
| 3 = @typeerror;