forked from goose-lang/goose
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patherrors.go
163 lines (142 loc) · 4.3 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
156
157
158
159
160
161
162
163
package goose
import (
"bytes"
"fmt"
"go/ast"
"go/printer"
"go/token"
"runtime"
"strings"
)
type locatable interface {
Pos() token.Pos
}
// 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 locatable) 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, "; ")
}
if s, ok := n.(fmt.Stringer); ok {
return s.String()
}
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 token.Pos
}
func (e *ConversionError) Error() string {
lines := []string{
fmt.Sprintf("[%s]: %s", e.Category, e.Message),
fmt.Sprintf("%s", 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 locatable, 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(),
}
panic(gooseError{err: err})
}
// nope reports a situation that believed to be impossible from reading the
// documentation.
func (r errorReporter) nope(n locatable, msg string, args ...interface{}) {
r.prefixed("impossible(go)", n, msg, args...)
}
// noExample reports a situation believed to be impossible because I couldn't
// think of how to do it in Go.
func (r errorReporter) noExample(n locatable, 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 locatable, 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 locatable, 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 locatable, msg string, args ...interface{}) {
r.prefixed("unsupported", n, msg, args...)
}