| // 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. |
| // |
| // Simplified dead code detector. Used for skipping certain checks |
| // on unreachable code (for instance, shift checks on arch-specific code). |
| // |
| package main |
| |
| import ( |
| "go/ast" |
| "go/constant" |
| ) |
| |
| // updateDead puts unreachable "if" and "case" nodes into f.dead. |
| func (f *File) updateDead(node ast.Node) { |
| if f.dead[node] { |
| // The node is already marked as dead. |
| return |
| } |
| |
| switch stmt := node.(type) { |
| case *ast.IfStmt: |
| // "if" branch is dead if its condition evaluates |
| // to constant false. |
| v := f.pkg.types[stmt.Cond].Value |
| if v == nil { |
| return |
| } |
| if !constant.BoolVal(v) { |
| f.setDead(stmt.Body) |
| return |
| } |
| f.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 := f.pkg.types[expr].Value |
| if v == nil || constant.BoolVal(v) { |
| continue BodyLoopBool |
| } |
| } |
| f.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 := f.pkg.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 := f.pkg.types[expr].Value |
| if v == nil { |
| continue BodyLoopInt |
| } |
| n, ok := constant.Uint64Val(v) |
| if !ok || tagN == n { |
| continue BodyLoopInt |
| } |
| } |
| f.setDead(cc) |
| } |
| } |
| } |
| |
| // setDead marks the node and all the children as dead. |
| func (f *File) setDead(node ast.Node) { |
| dv := deadVisitor{ |
| f: f, |
| } |
| ast.Walk(dv, node) |
| } |
| |
| type deadVisitor struct { |
| f *File |
| } |
| |
| func (dv deadVisitor) Visit(node ast.Node) ast.Visitor { |
| if node == nil { |
| return nil |
| } |
| dv.f.dead[node] = true |
| return dv |
| } |