From a837d5f4ceddee27006de97bcbaa498756032c81 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Thu, 9 Apr 2020 15:06:59 +0100 Subject: [PATCH] Add support for extracting Go frontend errors. --- extractor/dbscheme/tables.go | 28 +++++++++++++++++++ extractor/extractor.go | 52 +++++++++++++++++++++++++++++++----- ql/src/go.dbscheme | 10 +++++++ 3 files changed, 83 insertions(+), 7 deletions(-) diff --git a/extractor/dbscheme/tables.go b/extractor/dbscheme/tables.go index c82fa0459b1..df146c8e4e5 100644 --- a/extractor/dbscheme/tables.go +++ b/extractor/dbscheme/tables.go @@ -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") diff --git a/extractor/extractor.go b/extractor/extractor.go index f59ee9c228c..8fe96147808 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -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) { diff --git a/ql/src/go.dbscheme b/ql/src/go.dbscheme index f7fb4ff6229..ee5c327face 100644 --- a/ql/src/go.dbscheme +++ b/ql/src/go.dbscheme @@ -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; +