mirror of
https://github.com/golang/go
synced 2024-11-14 08:50:22 -07:00
109 lines
2.3 KiB
Go
109 lines
2.3 KiB
Go
|
// 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
|
||
|
}
|