github.com/karrick/go@v0.0.0-20170817181416-d5b0ec858b37/src/cmd/vet/dead.go (about)

     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  //
     8  package main
     9  
    10  import (
    11  	"go/ast"
    12  	"go/constant"
    13  )
    14  
    15  // updateDead puts unreachable "if" and "case" nodes into f.dead.
    16  func (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.
    91  func (f *File) setDead(node ast.Node) {
    92  	dv := deadVisitor{
    93  		f: f,
    94  	}
    95  	ast.Walk(dv, node)
    96  }
    97  
    98  type deadVisitor struct {
    99  	f *File
   100  }
   101  
   102  func (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  }