-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy patherrors.go
155 lines (137 loc) · 4.2 KB
/
errors.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package goose
import (
"bytes"
"fmt"
"go/ast"
"go/printer"
"go/token"
"runtime"
"strings"
)
// errorReporter groups methods for reporting errors, documenting what kind of
// issue was encountered in a uniform way.
type errorReporter struct {
fset *token.FileSet
}
func newErrorReporter(fset *token.FileSet) errorReporter {
return errorReporter{fset}
}
// printField implements custom printing for fields, since printer.Fprint does
// not support fields (the go syntax is somewhat context-sensitive)
func (r errorReporter) printField(f *ast.Field) string {
var what bytes.Buffer
var names []string
for _, n := range f.Names {
names = append(names, n.Name)
}
err := printer.Fprint(&what, r.fset, f.Type)
if err != nil {
panic(err.Error())
}
return fmt.Sprintf("%s %s",
strings.Join(names, ", "),
what.String())
}
func (r errorReporter) printGo(n ast.Node) string {
if f, ok := n.(*ast.Field); ok {
return r.printField(f)
}
if fl, ok := n.(*ast.FieldList); ok {
var fields []string
for _, f := range fl.List {
fields = append(fields, r.printField(f))
}
return strings.Join(fields, "; ")
}
var what bytes.Buffer
err := printer.Fprint(&what, r.fset, n)
if err != nil {
panic(err.Error())
}
return what.String()
}
func getCaller(skip int) string {
_, file, line, ok := runtime.Caller(1 + skip)
if !ok {
return "<no caller>"
}
return fmt.Sprintf("%s:%d", file, line)
}
type gooseError struct{ err *ConversionError }
// A ConversionError is a detailed and structured error encountered while
// converting Go to Coq.
//
// Errors include a category describing the severity of the error.
//
// The category "unsupported" is the only error that should result from normal
// usage, when attempting to use a feature goose intentionally does not support.
//
// "todo" and "future" are markers for code that could be supported but is not
// currently handled.
//
// The categories "impossible(go)" and "impossible(no-examples)" indicate a bug
// in goose (at the very least these cases should be checked and result in an
// unsupported error)
type ConversionError struct {
Category string
// the main description of what went wrong
Message string
// the snippet in the source program responsible for the error
GoCode string
// (for internal debugging) file:lineno for the goose code that threw the
// error
GooseCaller string
// file:lineno for the source program where GoCode appears
GoSrcFile string
// (for systematic tests)
Pos, End token.Pos
}
func (e *ConversionError) Error() string {
lines := []string{
fmt.Sprintf("[%s]: %s", e.Category, e.Message),
e.GoCode,
fmt.Sprintf(" %s", e.GooseCaller),
fmt.Sprintf(" src: %s", e.GoSrcFile),
}
return strings.Join(lines, "\n")
}
func (r errorReporter) prefixed(prefix string, n ast.Node, msg string, args ...interface{}) {
where := r.fset.Position(n.Pos())
what := r.printGo(n)
formatted := fmt.Sprintf(msg, args...)
err := &ConversionError{
Category: prefix,
Message: formatted,
GoCode: what,
GooseCaller: getCaller(2),
GoSrcFile: where.String(),
Pos: n.Pos(),
End: n.End(),
}
panic(gooseError{err: err})
}
// nope reports a situation that I thought was impossible from reading the
// documentation.
func (r errorReporter) nope(n ast.Node, msg string, args ...interface{}) {
r.prefixed("impossible(go)", n, msg, args...)
}
// noExample reports a situation I thought was impossible because I couldn't
// think of how to do it in Go.
func (r errorReporter) noExample(n ast.Node, msg string, args ...interface{}) {
r.prefixed("impossible(no-examples)", n, msg, args...)
}
// futureWork reports something we could theoretically handle but probably
// won't.
func (r errorReporter) futureWork(n ast.Node, msg string, args ...interface{}) {
r.prefixed("future", n, msg, args...)
}
// todo reports a situation that is intended to be handled but we haven't gotten
// around to.
func (r errorReporter) todo(n ast.Node, msg string, args ...interface{}) {
r.prefixed("todo", n, msg, args...)
}
// unsupported reports something intentionally unhandled (the code should not
// use this feature).
func (r errorReporter) unsupported(n ast.Node, msg string, args ...interface{}) {
r.prefixed("unsupported", n, msg, args...)
}