| // Copyright 2017 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package shift | |
| // Simplified dead code detector. | |
| // Used for skipping shift checks on unreachable arch-specific code. | |
| import ( | |
| "go/ast" | |
| "go/constant" | |
| "go/types" | |
| ) | |
| // updateDead puts unreachable "if" and "case" nodes into dead. | |
| func updateDead(info *types.Info, dead map[ast.Node]bool, node ast.Node) { | |
| if dead[node] { | |
| // The node is already marked as dead. | |
| return | |
| } | |
| // setDead marks the node and all the children as dead. | |
| setDead := func(n ast.Node) { | |
| ast.Inspect(n, func(node ast.Node) bool { | |
| if node != nil { | |
| dead[node] = true | |
| } | |
| return true | |
| }) | |
| } | |
| switch stmt := node.(type) { | |
| case *ast.IfStmt: | |
| // "if" branch is dead if its condition evaluates | |
| // to constant false. | |
| v := info.Types[stmt.Cond].Value | |
| if v == nil { | |
| return | |
| } | |
| if !constant.BoolVal(v) { | |
| setDead(stmt.Body) | |
| return | |
| } | |
| if stmt.Else != nil { | |
| setDead(stmt.Else) | |
| } | |
| case *ast.SwitchStmt: | |
| // Case clause with empty switch tag is dead if it evaluates | |
| // to constant false. | |
| if stmt.Tag == nil { | |
| BodyLoopBool: | |
| for _, stmt := range stmt.Body.List { | |
| cc := stmt.(*ast.CaseClause) | |
| if cc.List == nil { | |
| // Skip default case. | |
| continue | |
| } | |
| for _, expr := range cc.List { | |
| v := info.Types[expr].Value | |
| if v == nil || v.Kind() != constant.Bool || constant.BoolVal(v) { | |
| continue BodyLoopBool | |
| } | |
| } | |
| setDead(cc) | |
| } | |
| return | |
| } | |
| // Case clause is dead if its constant value doesn't match | |
| // the constant value from the switch tag. | |
| // TODO: This handles integer comparisons only. | |
| v := info.Types[stmt.Tag].Value | |
| if v == nil || v.Kind() != constant.Int { | |
| return | |
| } | |
| tagN, ok := constant.Uint64Val(v) | |
| if !ok { | |
| return | |
| } | |
| BodyLoopInt: | |
| for _, x := range stmt.Body.List { | |
| cc := x.(*ast.CaseClause) | |
| if cc.List == nil { | |
| // Skip default case. | |
| continue | |
| } | |
| for _, expr := range cc.List { | |
| v := info.Types[expr].Value | |
| if v == nil { | |
| continue BodyLoopInt | |
| } | |
| n, ok := constant.Uint64Val(v) | |
| if !ok || tagN == n { | |
| continue BodyLoopInt | |
| } | |
| } | |
| setDead(cc) | |
| } | |
| } | |
| } |