Преглед изворни кода

Whitelist good panics instead of bad ones.

Gustavo Niemeyer пре 11 година
родитељ
комит
a6dc653f93
5 измењених фајлова са 42 додато и 49 уклоњено
  1. 11 13
      decode.go
  2. 2 2
      encode.go
  3. 16 12
      encode_test.go
  4. 1 1
      resolve.go
  5. 12 21
      yaml.go

+ 11 - 13
decode.go

@@ -63,7 +63,7 @@ func (p *parser) destroy() {
 func (p *parser) skip() {
 	if p.event.typ != yaml_NO_EVENT {
 		if p.event.typ == yaml_STREAM_END_EVENT {
-			panic("Attempted to go past the end of stream. Corrupted value?")
+			fail("Attempted to go past the end of stream. Corrupted value?")
 		}
 		yaml_event_delete(&p.event)
 	}
@@ -89,7 +89,7 @@ func (p *parser) fail() {
 	} else {
 		msg = "Unknown problem parsing YAML content"
 	}
-	panic(where + msg)
+	fail(where + msg)
 }
 
 func (p *parser) anchor(n *node, anchor []byte) {
@@ -114,10 +114,9 @@ func (p *parser) parse() *node {
 		// Happens when attempting to decode an empty buffer.
 		return nil
 	default:
-		panic("Attempted to parse unknown event: " +
-			strconv.Itoa(int(p.event.typ)))
+		panic("Attempted to parse unknown event: " + strconv.Itoa(int(p.event.typ)))
 	}
-	panic("Unreachable")
+	panic("unreachable")
 }
 
 func (p *parser) node(kind int) *node {
@@ -135,8 +134,7 @@ func (p *parser) document() *node {
 	p.skip()
 	n.children = append(n.children, p.parse())
 	if p.event.typ != yaml_DOCUMENT_END_EVENT {
-		panic("Expected end of document event but got " +
-			strconv.Itoa(int(p.event.typ)))
+		panic("Expected end of document event but got " + strconv.Itoa(int(p.event.typ)))
 	}
 	p.skip()
 	return n
@@ -279,10 +277,10 @@ func (d *decoder) document(n *node, out reflect.Value) (good bool) {
 func (d *decoder) alias(n *node, out reflect.Value) (good bool) {
 	an, ok := d.doc.anchors[n.value]
 	if !ok {
-		panic("Unknown anchor '" + n.value + "' referenced")
+		fail("Unknown anchor '" + n.value + "' referenced")
 	}
 	if d.aliases[n.value] {
-		panic("Anchor '" + n.value + "' value contains itself")
+		fail("Anchor '" + n.value + "' value contains itself")
 	}
 	d.aliases[n.value] = true
 	good = d.unmarshal(an, out)
@@ -511,7 +509,7 @@ func (d *decoder) merge(n *node, out reflect.Value) {
 	case aliasNode:
 		an, ok := d.doc.anchors[n.value]
 		if ok && an.kind != mappingNode {
-			panic(wantMap)
+			fail(wantMap)
 		}
 		d.unmarshal(n, out)
 	case sequenceNode:
@@ -521,15 +519,15 @@ func (d *decoder) merge(n *node, out reflect.Value) {
 			if ni.kind == aliasNode {
 				an, ok := d.doc.anchors[ni.value]
 				if ok && an.kind != mappingNode {
-					panic(wantMap)
+					fail(wantMap)
 				}
 			} else if ni.kind != mappingNode {
-				panic(wantMap)
+				fail(wantMap)
 			}
 			d.unmarshal(ni, out)
 		}
 	default:
-		panic(wantMap)
+		fail(wantMap)
 	}
 }
 

+ 2 - 2
encode.go

@@ -52,7 +52,7 @@ func (e *encoder) must(ok bool) {
 		if msg == "" {
 			msg = "Unknown problem generating YAML content"
 		}
-		panic(msg)
+		fail(msg)
 	}
 }
 
@@ -100,7 +100,7 @@ func (e *encoder) marshal(tag string, in reflect.Value) {
 	case reflect.Bool:
 		e.boolv(tag, in)
 	default:
-		panic("Can't marshal type yet: " + in.Type().String())
+		panic("Can't marshal type: " + in.Type().String())
 	}
 }
 

+ 16 - 12
encode_test.go

@@ -2,8 +2,8 @@ package yaml_test
 
 import (
 	"fmt"
-	"gopkg.in/yaml.v1"
 	. "gopkg.in/check.v1"
+	"gopkg.in/yaml.v1"
 	"math"
 	"strconv"
 	"strings"
@@ -245,20 +245,24 @@ func (s *S) TestMarshal(c *C) {
 var marshalErrorTests = []struct {
 	value interface{}
 	error string
-}{
-	{
-		&struct {
-			B       int
-			inlineB ",inline"
-		}{1, inlineB{2, inlineC{3}}},
-		`Duplicated key 'b' in struct struct \{ B int; .*`,
-	},
-}
+	panic string
+}{{
+	&struct {
+		B       int
+		inlineB ",inline"
+	}{1, inlineB{2, inlineC{3}}},
+	"",
+	`Duplicated key 'b' in struct struct \{ B int; .*`,
+}}
 
 func (s *S) TestMarshalErrors(c *C) {
 	for _, item := range marshalErrorTests {
-		_, err := yaml.Marshal(item.value)
-		c.Assert(err, ErrorMatches, item.error)
+		if item.panic != "" {
+			c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic)
+		} else {
+			_, err := yaml.Marshal(item.value)
+			c.Assert(err, ErrorMatches, item.error)
+		}
 	}
 }
 

+ 1 - 1
resolve.go

@@ -80,7 +80,7 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
 
 	defer func() {
 		if tag != "" && tag != rtag {
-			panic("Can't decode " + rtag + " '" + in + "' as a " + tag)
+			fail("Can't decode " + rtag + " '" + in + "' as a " + tag)
 		}
 	}()
 

+ 12 - 21
yaml.go

@@ -10,23 +10,20 @@ import (
 	"errors"
 	"fmt"
 	"reflect"
-	"runtime"
 	"strings"
 	"sync"
 )
 
+type yamlError string
+
+func fail(msg string) {
+	panic(yamlError(msg))
+}
+
 func handleErr(err *error) {
 	if r := recover(); r != nil {
-		if _, ok := r.(runtime.Error); ok {
-			panic(r)
-		} else if _, ok := r.(*reflect.ValueError); ok {
-			panic(r)
-		} else if _, ok := r.(externalPanic); ok {
-			panic(r)
-		} else if s, ok := r.(string); ok {
-			*err = errors.New("YAML error: " + s)
-		} else if e, ok := r.(error); ok {
-			*err = e
+		if e, ok := r.(yamlError); ok {
+			*err = errors.New("YAML error: " + string(e))
 		} else {
 			panic(r)
 		}
@@ -174,12 +171,6 @@ type fieldInfo struct {
 var structMap = make(map[reflect.Type]*structInfo)
 var fieldMapMutex sync.RWMutex
 
-type externalPanic string
-
-func (e externalPanic) String() string {
-	return string(e)
-}
-
 func getStructInfo(st reflect.Type) (*structInfo, error) {
 	fieldMapMutex.RLock()
 	sinfo, found := structMap[st]
@@ -220,8 +211,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
 				case "inline":
 					inline = true
 				default:
-					msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)
-					panic(externalPanic(msg))
+					return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st))
 				}
 			}
 			tag = fields[0]
@@ -229,6 +219,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
 
 		if inline {
 			switch field.Type.Kind() {
+			// TODO: Implement support for inline maps.
 			//case reflect.Map:
 			//	if inlineMap >= 0 {
 			//		return nil, errors.New("Multiple ,inline maps in struct " + st.String())
@@ -256,8 +247,8 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
 					fieldsList = append(fieldsList, finfo)
 				}
 			default:
-				//panic("Option ,inline needs a struct value or map field")
-				panic("Option ,inline needs a struct value field")
+				//return nil, errors.New("Option ,inline needs a struct value or map field")
+				return nil, errors.New("Option ,inline needs a struct value field")
 			}
 			continue
 		}