Skip to content

Commit ce8e982

Browse files
committed
Merge branch 'fix-bad-walkerror'
* fix-bad-walkerror: Add an additional test around Map and nil nodes Only use Map() node result if no error is returned Add test for #12 - nil WalkError.Node panics
2 parents a220d8b + dd252e3 commit ce8e982

File tree

2 files changed

+61
-4
lines changed

2 files changed

+61
-4
lines changed

walk.go

+16-4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ type WalkExiter interface {
4242
//
4343
// Walk will return a *WalkError if any error occurs during a walk. The WalkError will contain both
4444
// the parent and child node that the error occurred for.
45+
//
46+
// If the walker is a WalkMapper, any error in attempting to map a node will return a WalkError with
47+
// the original node, not any resulting node. If the mapping is to a nil node without error, the
48+
// node is deleted from the parent.
49+
//
50+
// Nil child nodes are skipped.
4551
func Walk(parent ParentNode, walker Walker) (err error) {
4652
return walkInContext(parent, parent, walker)
4753
}
@@ -53,24 +59,30 @@ func walkInContext(context, parent ParentNode, walker Walker) (err error) {
5359

5460
for i := 0; i < len(children); i++ {
5561
child := children[i]
62+
if child == nil {
63+
// Skip over nil nodes because they're mostly harmless -- you just can't act
64+
// on them at all.
65+
continue
66+
}
5667

5768
// Remap the child node if the walker implemented WalkMapper
5869
if mapper != nil {
59-
child, err = mapper.Map(child)
70+
var newChild Node
71+
newChild, err = mapper.Map(child)
6072
if err != nil {
6173
return walkErr(parent, context, child, err)
6274
}
6375

64-
// If the new child is nil, remove it from the slice of children
65-
if child == nil {
76+
// If the new child is nil, remove the original child from the slice of children
77+
if newChild == nil {
6678
copy(children[i:], children[i+1:])
6779
children[len(children)-1] = nil
6880
children = children[:len(children)-1]
6981
// Now that the next child is in this child's place, walk i back one cell
7082
i--
7183
continue
7284
}
73-
children[i] = child
85+
children[i] = newChild
7486
}
7587

7688
switch child := child.(type) {

walk_test.go

+45
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
package codf
22

33
import (
4+
"errors"
45
"fmt"
56
"reflect"
67
"testing"
78
)
89

10+
type mapFunc func(Node) (Node, error)
11+
12+
var _ WalkMapper = mapFunc(nil)
13+
14+
func (f mapFunc) Map(n Node) (Node, error) {
15+
return f(n)
16+
}
17+
18+
func (f mapFunc) Statement(*Statement) error { return nil }
19+
func (f mapFunc) EnterSection(*Section) (Walker, error) { return f, nil }
20+
921
type docWalker struct {
1022
name string
1123
statements map[string]struct{}
@@ -313,3 +325,36 @@ func TestWalkMapper(t *testing.T) {
313325
t.Errorf("statements = %q; want %q", walker.statements, wantStatements)
314326
}
315327
}
328+
329+
func TestMapDelete(t *testing.T) {
330+
const DocSource = `stmt 1; section arg {}`
331+
332+
defer setlogf(t)()
333+
334+
doc := mustParseNamed(t, "root.conf", DocSource)
335+
err := Walk(doc, mapFunc(func(Node) (Node, error) { return nil, nil }))
336+
if err != nil {
337+
t.Fatalf("err = %v; want nil", err)
338+
}
339+
340+
if got := doc.Children; len(got) != 0 {
341+
t.Fatalf("children = %#v; want an empty slice", got)
342+
}
343+
}
344+
345+
func TestMapError(t *testing.T) {
346+
const wantErrMessage = `[1:1:0] root.conf: stmt in main: expected error`
347+
const DocSource = `stmt 1; section arg {}`
348+
349+
defer setlogf(t)()
350+
351+
doc := mustParseNamed(t, "root.conf", DocSource)
352+
doc.Children = append([]Node{nil}, doc.Children...)
353+
354+
wantErr := errors.New("expected error")
355+
err := Walk(doc, mapFunc(func(Node) (Node, error) { return nil, wantErr }))
356+
357+
if got := err.Error(); got != wantErrMessage {
358+
t.Fatalf("err = %q; want %q", got, wantErrMessage)
359+
}
360+
}

0 commit comments

Comments
 (0)