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//
5// Simplified dead code detector. Used for skipping certain checks
6// on unreachable code (for instance, shift checks on arch-specific code).
7
8package main
9
10import (
11	"go/ast"
12	"go/constant"
13)
14
15// updateDead puts unreachable "if" and "case" nodes into f.dead.
16func (f *File) updateDead(node ast.Node) {
17	if f.dead[node] {
18		// The node is already marked as dead.
19		return
20	}
21
22	switch stmt := node.(type) {
23	case *ast.IfStmt:
24		// "if" branch is dead if its condition evaluates
25		// to constant false.
26		v := f.pkg.types[stmt.Cond].Value
27		if v == nil {
28			return
29		}
30		if !constant.BoolVal(v) {
31			f.setDead(stmt.Body)
32			return
33		}
34		f.setDead(stmt.Else)
35	case *ast.SwitchStmt:
36		// Case clause with empty switch tag is dead if it evaluates
37		// to constant false.
38		if stmt.Tag == nil {
39		BodyLoopBool:
40			for _, stmt := range stmt.Body.List {
41				cc := stmt.(*ast.CaseClause)
42				if cc.List == nil {
43					// Skip default case.
44					continue
45				}
46				for _, expr := range cc.List {
47					v := f.pkg.types[expr].Value
48					if v == nil || constant.BoolVal(v) {
49						continue BodyLoopBool
50					}
51				}
52				f.setDead(cc)
53			}
54			return
55		}
56
57		// Case clause is dead if its constant value doesn't match
58		// the constant value from the switch tag.
59		// TODO: This handles integer comparisons only.
60		v := f.pkg.types[stmt.Tag].Value
61		if v == nil || v.Kind() != constant.Int {
62			return
63		}
64		tagN, ok := constant.Uint64Val(v)
65		if !ok {
66			return
67		}
68	BodyLoopInt:
69		for _, x := range stmt.Body.List {
70			cc := x.(*ast.CaseClause)
71			if cc.List == nil {
72				// Skip default case.
73				continue
74			}
75			for _, expr := range cc.List {
76				v := f.pkg.types[expr].Value
77				if v == nil {
78					continue BodyLoopInt
79				}
80				n, ok := constant.Uint64Val(v)
81				if !ok || tagN == n {
82					continue BodyLoopInt
83				}
84			}
85			f.setDead(cc)
86		}
87	}
88}
89
90// setDead marks the node and all the children as dead.
91func (f *File) setDead(node ast.Node) {
92	dv := deadVisitor{
93		f: f,
94	}
95	ast.Walk(dv, node)
96}
97
98type deadVisitor struct {
99	f *File
100}
101
102func (dv deadVisitor) Visit(node ast.Node) ast.Visitor {
103	if node == nil {
104		return nil
105	}
106	dv.f.dead[node] = true
107	return dv
108}
109