~mna/snow unlisted

snow/pkg/semantic/analysis_pass.go -rw-r--r-- 2.5 KiB
424066c5Martin Angers doc: v0.0.5 1 year, 6 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package semantic

import (
	"git.sr.ht/~mna/snow/pkg/token"
)

// The analysis pass validates some semantic requirements that can be statically
// verified with the semantic graph. Currently, it:
//
// - checks that a guard statement's else clause does not fall through in the
//   parent block
// - records the uses of values (right-hand-side uses) of identifiers, allowing
//   to identify unused ones (which may be useful to generate warnings or for
//   the codegen phase)
func analysis(unit *Unit, errh func(token.Pos, string)) {
	a := &analysisVisitor{
		errh: errh,
		uses: make(map[*Ident]Decl),
	}
	Walk(a, unit)
	unit.ValueUses = a.uses
}

type analysisVisitor struct {
	errh func(token.Pos, string)
	uses map[*Ident]Decl
	// set to true when visiting on the right-hand-side (or argument call)
	// of an expression
	rhs bool
}

func (a *analysisVisitor) cloneRHS() *analysisVisitor {
	return &analysisVisitor{
		errh: a.errh,
		uses: a.uses,
		rhs:  true,
	}
}

func (a *analysisVisitor) Visit(n Node) Visitor {
	switch n := n.(type) {

	// ************** DECLARATIONS *****************

	case *Fn:
		// if the function has a body and does not return void, make sure
		// every branch of the function has a return or does not fallthrough
		// without returning.
		if st := AsSignatureType(n.Type()); n.Body != nil && st != nil && !IsBasicOfKind(st.Return, Void) {
			if FallsThrough(n.Body) {
				a.errh(n.Pos(), "not all branches of the function body return a value")
			}
		}

	case *Var:
		if n.Value != nil {
			aa := a.cloneRHS()
			Walk(aa, n.Value)
		}
		return nil

		// ************** STATEMENTS *****************

	case *Return:
		if n.Value != nil {
			aa := a.cloneRHS()
			Walk(aa, n.Value)
		}
		return nil

	case *Assign:
		if n.Left != nil {
			Walk(a, n.Left)
		}
		if n.Right != nil {
			aa := a.cloneRHS()
			Walk(aa, n.Right)
		}
		return nil

	case *ExprStmt:
		aa := a.cloneRHS()
		Walk(aa, n.Value)
		return nil

	case *If:
		for _, cond := range n.Conds {
			aa := a.cloneRHS()
			Walk(aa, cond)
		}
		if n.Body != nil {
			Walk(a, n.Body)
		}
		if n.Else != nil {
			Walk(a, n.Else)
		}
		return nil

	case *Guard:
		for _, cond := range n.Conds {
			aa := a.cloneRHS()
			Walk(aa, cond)
		}
		if n.Else != nil {
			Walk(a, n.Else)
			if FallsThrough(n.Else) {
				a.errh(n.Pos(), "guard else block must not fall through")
			}
		}
		return nil

		// ************** EXPRESSIONS *****************

	case *Ident:
		if a.rhs && n.Ref != nil {
			a.uses[n] = n.Ref
		}
	}
	return a
}