github.com/mshitrit/go-mutesting@v0.0.0-20210528084812-ff81dcaedfea/walk.go (about)

     1  package mutesting
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/types"
     7  	"strings"
     8  
     9  	"github.com/zimmski/go-mutesting/mutator"
    10  )
    11  
    12  // CountWalk returns the number of corresponding mutations for a given mutator.
    13  // It traverses the AST of the given node and calls the method Check of the given mutator for every node and sums up the returned counts. After completion of the traversal the final counter is returned.
    14  func CountWalk(pkg *types.Package, info *types.Info, node ast.Node, m mutator.Mutator) int {
    15  	w := &countWalk{
    16  		count:   0,
    17  		mutator: m,
    18  		pkg:     pkg,
    19  		info:    info,
    20  	}
    21  
    22  	ast.Walk(w, node)
    23  
    24  	return w.count
    25  }
    26  
    27  type countWalk struct {
    28  	count   int
    29  	mutator mutator.Mutator
    30  	pkg     *types.Package
    31  	info    *types.Info
    32  }
    33  
    34  // Visit implements the Visit method of the ast.Visitor interface
    35  func (w *countWalk) Visit(node ast.Node) ast.Visitor {
    36  	if node == nil {
    37  		return w
    38  	}
    39  
    40  	w.count += len(w.mutator(w.pkg, w.info, node))
    41  
    42  	return w
    43  }
    44  
    45  // MutateWalk mutates the given node with the given mutator returning a channel to control the mutation steps.
    46  // It traverses the AST of the given node and calls the method Check of the given mutator to verify that a node can be mutated by the mutator. If a node can be mutated the method Mutate of the given mutator is executed with the node and the control channel. After completion of the traversal the control channel is closed.
    47  func MutateWalk(pkg *types.Package, info *types.Info, node ast.Node, m mutator.Mutator) chan bool {
    48  	w := &mutateWalk{
    49  		changed: make(chan bool),
    50  		mutator: m,
    51  		pkg:     pkg,
    52  		info:    info,
    53  	}
    54  
    55  	go func() {
    56  		ast.Walk(w, node)
    57  
    58  		close(w.changed)
    59  	}()
    60  
    61  	return w.changed
    62  }
    63  
    64  type mutateWalk struct {
    65  	changed chan bool
    66  	mutator mutator.Mutator
    67  	pkg     *types.Package
    68  	info    *types.Info
    69  }
    70  
    71  // Visit implements the Visit method of the ast.Visitor interface
    72  func (w *mutateWalk) Visit(node ast.Node) ast.Visitor {
    73  	if node == nil {
    74  		return w
    75  	}
    76  
    77  	for _, m := range w.mutator(w.pkg, w.info, node) {
    78  		m.Change()
    79  		w.changed <- true
    80  		<-w.changed
    81  
    82  		m.Reset()
    83  		w.changed <- true
    84  		<-w.changed
    85  	}
    86  
    87  	return w
    88  }
    89  
    90  // PrintWalk traverses the AST of the given node and prints every node to STDOUT.
    91  func PrintWalk(node ast.Node) {
    92  	w := &printWalk{
    93  		level: 0,
    94  	}
    95  
    96  	ast.Walk(w, node)
    97  }
    98  
    99  type printWalk struct {
   100  	level int
   101  }
   102  
   103  // Visit implements the Visit method of the ast.Visitor interface
   104  func (w *printWalk) Visit(node ast.Node) ast.Visitor {
   105  	if node != nil {
   106  		w.level++
   107  
   108  		fmt.Printf("%s(%p)%#v\n", strings.Repeat("\t", w.level), node, node)
   109  	} else {
   110  		w.level--
   111  	}
   112  
   113  	return w
   114  }