Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions mold_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ func TestNew_ViewParseError(t *testing.T) {
}
}

func TestNew_CyclicReferenceError(t *testing.T) {
testFS := createTestFS(testFile{"parse.html", `{{partial "parse.html"}}`})

if _, err := New(testFS); err == nil {
t.Errorf("New() expected error, got nil %v", err)
}
}

func TestNew_Ext(t *testing.T) {
testFS := createTestFS(
testFile{"layout.mine", "{{render}}"},
Expand Down
26 changes: 15 additions & 11 deletions tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
// processTree traverses the node tree and swaps render and partial declarations with equivalent template calls.
// It returns all referenced templates encountered during the traversal.
func processTree(t *template.Template, raw string) ([]templateName, error) {
ts, err := processNode(nil, 0, t.Tree.Root)
ts, err := processNode(t.Tree, nil, 0, t.Tree.Root)
if err != nil {
if err, ok := err.(posErr); ok {
line, col := pos(raw, err.pos)
Expand All @@ -21,7 +21,7 @@ func processTree(t *template.Template, raw string) ([]templateName, error) {
return ts, nil
}

func processNode(parent *parse.ListNode, index int, node parse.Node) (ts []templateName, err error) {
func processNode(tree *parse.Tree, parent *parse.ListNode, index int, node parse.Node) (ts []templateName, err error) {
// appendResult appends the specified templates to the list of template names when there are no errors
appendResult := func(t []templateName, err1 error) {
if err1 != nil {
Expand All @@ -35,7 +35,7 @@ func processNode(parent *parse.ListNode, index int, node parse.Node) (ts []templ
if a, ok := node.(*parse.ActionNode); ok {
if len(a.Pipe.Cmds) > 0 {
funcName, tname, _ := getActionArgs(a.Pipe.Cmds[0])
if err := processActionNode(parent, index, node, funcName); err != nil {
if err := processActionNode(tree, parent, index, node, funcName); err != nil {
return ts, err
}
if funcName == partialFunc.String() && tname != "" {
Expand All @@ -47,27 +47,27 @@ func processNode(parent *parse.ListNode, index int, node parse.Node) (ts []templ
}

if w, ok := node.(*parse.WithNode); ok && w != nil {
appendResult(processNode(parent, index, w.List))
appendResult(processNode(parent, index, w.ElseList))
appendResult(processNode(tree, parent, index, w.List))
appendResult(processNode(tree, parent, index, w.ElseList))
}
if l, ok := node.(*parse.ListNode); ok && l != nil {
for i, n := range l.Nodes {
appendResult(processNode(l, i, n))
appendResult(processNode(tree, l, i, n))
}
}
if i, ok := node.(*parse.IfNode); ok && i != nil {
appendResult(processNode(parent, index, i.List))
appendResult(processNode(parent, index, i.ElseList))
appendResult(processNode(tree, parent, index, i.List))
appendResult(processNode(tree, parent, index, i.ElseList))
}
if r, ok := node.(*parse.RangeNode); ok && r != nil {
appendResult(processNode(parent, index, r.List))
appendResult(processNode(parent, index, r.ElseList))
appendResult(processNode(tree, parent, index, r.List))
appendResult(processNode(tree, parent, index, r.ElseList))
}

return ts, err
}

func processActionNode(parent *parse.ListNode, index int, node parse.Node, funcName string) error {
func processActionNode(tree *parse.Tree, parent *parse.ListNode, index int, node parse.Node, funcName string) error {
if parent == nil {
// this should never happen
return errors.New("processActionNode error: parent node is nil")
Expand All @@ -77,6 +77,10 @@ func processActionNode(parent *parse.ListNode, index int, node parse.Node, funcN
cmd := actionNode.Pipe.Cmds[0]
_, name, field := getActionArgs(cmd)

if name == tree.ParseName {
return posErr{pos: int(actionNode.Pos), message: fmt.Sprintf(`cyclic reference for '%s'`, name)}
}

var arg parse.Node = &parse.DotNode{}

// only handle if the function name is render or partial
Expand Down
Loading