1// Copyright 2017 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package shift
6
7// Simplified dead code detector.
8// Used for skipping shift checks on unreachable arch-specific code.
9
10import (
11	"go/ast"
12	"go/constant"
13	"go/types"
14)
15
16// updateDead puts unreachable "if" and "case" nodes into dead.
17func updateDead(info *types.Info, dead map[ast.Node]bool, node ast.Node) {
18	if dead[node] {
19		// The node is already marked as dead.
20		return
21	}
22
23	// setDead marks the node and all the children as dead.
24	setDead := func(n ast.Node) {
25		ast.Inspect(n, func(node ast.Node) bool {
26			if node != nil {
27				dead[node] = true
28			}
29			return true
30		})
31	}
32
33	switch stmt := node.(type) {
34	case *ast.IfStmt:
35		// "if" branch is dead if its condition evaluates
36		// to constant false.
37		v := info.Types[stmt.Cond].Value
38		if v == nil {
39			return
40		}
41		if !constant.BoolVal(v) {
42			setDead(stmt.Body)
43			return
44		}
45		if stmt.Else != nil {
46			setDead(stmt.Else)
47		}
48	case *ast.SwitchStmt:
49		// Case clause with empty switch tag is dead if it evaluates
50		// to constant false.
51		if stmt.Tag == nil {
52		BodyLoopBool:
53			for _, stmt := range stmt.Body.List {
54				cc := stmt.(*ast.CaseClause)
55				if cc.List == nil {
56					// Skip default case.
57					continue
58				}
59				for _, expr := range cc.List {
60					v := info.Types[expr].Value
61					if v == nil || v.Kind() != constant.Bool || constant.BoolVal(v) {
62						continue BodyLoopBool
63					}
64				}
65				setDead(cc)
66			}
67			return
68		}
69
70		// Case clause is dead if its constant value doesn't match
71		// the constant value from the switch tag.
72		// TODO: This handles integer comparisons only.
73		v := info.Types[stmt.Tag].Value
74		if v == nil || v.Kind() != constant.Int {
75			return
76		}
77		tagN, ok := constant.Uint64Val(v)
78		if !ok {
79			return
80		}
81	BodyLoopInt:
82		for _, x := range stmt.Body.List {
83			cc := x.(*ast.CaseClause)
84			if cc.List == nil {
85				// Skip default case.
86				continue
87			}
88			for _, expr := range cc.List {
89				v := info.Types[expr].Value
90				if v == nil {
91					continue BodyLoopInt
92				}
93				n, ok := constant.Uint64Val(v)
94				if !ok || tagN == n {
95					continue BodyLoopInt
96				}
97			}
98			setDead(cc)
99		}
100	}
101}
102