-
-
Notifications
You must be signed in to change notification settings - Fork 523
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ExtError() to display "extended errors" with context: toml: error: expected a digit but got '+' on line 1; last key parsed was "double-sign-plus" 1 | double-sign-plus = ++99 ^ Still WIP; the position is usually but not always always quite correct; for example: toml: error: Invalid float value: "0.1.2" on line 1; last key parsed was "double-point-2" 1 | double-point-2 = 0.1.2 ^ This is mostly because the Parser doesn't always set it correctly. Would also be nice to expand it a bit so it would show: 1 | double-sign-plus = ++99 ^~ 1 | double-point-2 = 0.1.2 ^~~~~ Also: - The "Line" in ParseError isn't always accurate, but Pos is. The "Line" should always be accurate. - Maybe record as Col instead? I just used Pos because it was easier for now. - It's a (hard-coded) option, since this attached the input to the error and is slower. Need to be able to pass that option, and maybe also just have Error() print out the "extended error" if it's enabled instead of an ExtError() method. - Quite a few of the error messages could do with improving.
- Loading branch information
Showing
5 changed files
with
202 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package toml | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
) | ||
|
||
// ParseError is used when there is an error decoding TOML data. | ||
// | ||
// For example invalid TOML syntax, duplicate keys, etc. | ||
type ParseError struct { | ||
Message string | ||
Line int | ||
Pos int // Byte offset | ||
LastKey string // Last parsed key, may be blank. | ||
Input string | ||
} | ||
|
||
func (pe ParseError) Error() string { | ||
if pe.LastKey == "" { | ||
return fmt.Sprintf("toml: line %d: %s", pe.Line, pe.Message) | ||
} | ||
return fmt.Sprintf("toml: line %d (last key parsed '%s'): %s", | ||
pe.Line, pe.LastKey, pe.Message) | ||
} | ||
|
||
// Clang error: | ||
// | ||
// a.c:2:9: warning: incompatible pointer to integer conversion returning 'char [4]' from a function with result type 'int' [-Wint-conversion] | ||
// return "zxc"; | ||
// ^~~~~ | ||
// 1 warning generated. | ||
// | ||
// Rust: | ||
// | ||
// error[E0425]: cannot find value `err` in this scope | ||
// --> a.rs:3:5 | ||
// | | ||
// 3 | err | ||
// | ^^^ help: a tuple variant with a similar name exists: `Err` | ||
// | ||
// error: aborting due to previous error | ||
// | ||
// For more information about this error, try `rustc --explain E0425`. | ||
|
||
func (pe ParseError) ExtError() string { | ||
if pe.Input == "" { | ||
return pe.Error() | ||
} | ||
|
||
lines := strings.Split(pe.Input, "\n") | ||
var line, pos, col int | ||
for i := range lines { | ||
ll := len(lines[i]) + 1 // +1 for the removed newline | ||
if pos+ll >= pe.Pos { | ||
line = i | ||
col = pe.Pos - pos - 1 | ||
if col < 0 { // Should never happen, but just in case. | ||
col = 0 | ||
} | ||
break | ||
} | ||
pos += ll | ||
} | ||
|
||
b := new(strings.Builder) | ||
//fmt.Fprintf(b, "toml: error on line %d: %s\n", line, pe.Message) | ||
fmt.Fprintf(b, "toml: error: %s\n", pe.Message) | ||
fmt.Fprintf(b, " on line %d", line+1) | ||
if pe.LastKey != "" { | ||
fmt.Fprintf(b, "; last key parsed was %q", pe.LastKey) | ||
} | ||
b.WriteString("\n\n") | ||
|
||
if line > 1 { | ||
fmt.Fprintf(b, "% 6d | %s\n", line-1, lines[line-2]) | ||
} | ||
if line > 0 { | ||
fmt.Fprintf(b, "% 6d | %s\n", line, lines[line-1]) | ||
} | ||
|
||
fmt.Fprintf(b, "% 6d | %s\n", line+1, lines[line]) | ||
fmt.Fprintf(b, "% 9s%s^\n", "", strings.Repeat(" ", col)) | ||
|
||
// if len(lines)-1 > line && lines[line+1] != "" { | ||
// fmt.Fprintf(b, "% 6d | %s\n", line+1, lines[line+1]) | ||
// } | ||
|
||
return b.String() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package toml_test | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io/fs" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/BurntSushi/toml" | ||
tomltest "github.com/BurntSushi/toml-test" | ||
) | ||
|
||
/* | ||
func TestDecodeError(t *testing.T) { | ||
file := | ||
`a = "a" | ||
b = "b" | ||
c = 001 # invalid | ||
` | ||
var s struct { | ||
A, B string | ||
C int | ||
} | ||
_, err := Decode(file, &s) | ||
if err == nil { | ||
t.Fatal("err is nil") | ||
} | ||
var dErr DecodeError | ||
if !errors.As(err, &dErr) { | ||
t.Fatalf("err is not a DecodeError: %T %[1]v", err) | ||
} | ||
want := DecodeError{ | ||
Line: 3, | ||
Pos: 17, | ||
LastKey: "c", | ||
Message: `Invalid integer "001": cannot have leading zeroes`, | ||
} | ||
if !reflect.DeepEqual(dErr, want) { | ||
t.Errorf("unexpected data\nhave: %#v\nwant: %#v", dErr, want) | ||
} | ||
} | ||
*/ | ||
|
||
func TestParseError(t *testing.T) { | ||
fsys := tomltest.EmbeddedTests() | ||
ls, err := fs.ReadDir(fsys, "invalid") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
for _, f := range ls { | ||
if !strings.HasSuffix(f.Name(), ".toml") { | ||
continue | ||
} | ||
|
||
if f.Name() == "string-multiline-escape-space.toml" { | ||
continue | ||
} | ||
|
||
input, err := fs.ReadFile(fsys, "invalid/"+f.Name()) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
var x interface{} | ||
_, err = toml.Decode(string(input), &x) | ||
if err == nil { | ||
continue | ||
} | ||
|
||
var dErr toml.ParseError | ||
if !errors.As(err, &dErr) { | ||
t.Errorf("err is not a ParseError: %T %[1]v", err) | ||
continue | ||
} | ||
|
||
fmt.Println() | ||
fmt.Println("–––", f.Name(), strings.Repeat("–", 65-len(f.Name()))) | ||
fmt.Print(dErr.ExtError()) | ||
fmt.Println(strings.Repeat("–", 70)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters