github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/go/types/return.go (about)

     1  // Copyright 2013 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  // This file implements isTerminating.
     6  
     7  package types
     8  
     9  import (
    10  	"go/ast"
    11  	"go/token"
    12  )
    13  
    14  // isTerminating reports if s is a terminating statement.
    15  // If s is labeled, label is the label name; otherwise s
    16  // is "".
    17  func (check *Checker) isTerminating(s ast.Stmt, label string) bool {
    18  	switch s := s.(type) {
    19  	default:
    20  		unreachable()
    21  
    22  	case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.SendStmt,
    23  		*ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt, *ast.DeferStmt,
    24  		*ast.RangeStmt:
    25  		// no chance
    26  
    27  	case *ast.LabeledStmt:
    28  		return check.isTerminating(s.Stmt, s.Label.Name)
    29  
    30  	case *ast.ExprStmt:
    31  		// the predeclared (possibly parenthesized) panic() function is terminating
    32  		if call, _ := unparen(s.X).(*ast.CallExpr); call != nil {
    33  			if id, _ := call.Fun.(*ast.Ident); id != nil {
    34  				if _, obj := check.scope.LookupParent(id.Name, token.NoPos); obj != nil {
    35  					if b, _ := obj.(*Builtin); b != nil && b.id == _Panic {
    36  						return true
    37  					}
    38  				}
    39  			}
    40  		}
    41  
    42  	case *ast.ReturnStmt:
    43  		return true
    44  
    45  	case *ast.BranchStmt:
    46  		if s.Tok == token.GOTO || s.Tok == token.FALLTHROUGH {
    47  			return true
    48  		}
    49  
    50  	case *ast.BlockStmt:
    51  		return check.isTerminatingList(s.List, "")
    52  
    53  	case *ast.IfStmt:
    54  		if s.Else != nil &&
    55  			check.isTerminating(s.Body, "") &&
    56  			check.isTerminating(s.Else, "") {
    57  			return true
    58  		}
    59  
    60  	case *ast.SwitchStmt:
    61  		return check.isTerminatingSwitch(s.Body, label)
    62  
    63  	case *ast.TypeSwitchStmt:
    64  		return check.isTerminatingSwitch(s.Body, label)
    65  
    66  	case *ast.SelectStmt:
    67  		for _, s := range s.Body.List {
    68  			cc := s.(*ast.CommClause)
    69  			if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) {
    70  				return false
    71  			}
    72  
    73  		}
    74  		return true
    75  
    76  	case *ast.ForStmt:
    77  		if s.Cond == nil && !hasBreak(s.Body, label, true) {
    78  			return true
    79  		}
    80  	}
    81  
    82  	return false
    83  }
    84  
    85  func (check *Checker) isTerminatingList(list []ast.Stmt, label string) bool {
    86  	n := len(list)
    87  	return n > 0 && check.isTerminating(list[n-1], label)
    88  }
    89  
    90  func (check *Checker) isTerminatingSwitch(body *ast.BlockStmt, label string) bool {
    91  	hasDefault := false
    92  	for _, s := range body.List {
    93  		cc := s.(*ast.CaseClause)
    94  		if cc.List == nil {
    95  			hasDefault = true
    96  		}
    97  		if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) {
    98  			return false
    99  		}
   100  	}
   101  	return hasDefault
   102  }
   103  
   104  // TODO(gri) For nested breakable statements, the current implementation of hasBreak
   105  //	     will traverse the same subtree repeatedly, once for each label. Replace
   106  //           with a single-pass label/break matching phase.
   107  
   108  // hasBreak reports if s is or contains a break statement
   109  // referring to the label-ed statement or implicit-ly the
   110  // closest outer breakable statement.
   111  func hasBreak(s ast.Stmt, label string, implicit bool) bool {
   112  	switch s := s.(type) {
   113  	default:
   114  		unreachable()
   115  
   116  	case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.ExprStmt,
   117  		*ast.SendStmt, *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt,
   118  		*ast.DeferStmt, *ast.ReturnStmt:
   119  		// no chance
   120  
   121  	case *ast.LabeledStmt:
   122  		return hasBreak(s.Stmt, label, implicit)
   123  
   124  	case *ast.BranchStmt:
   125  		if s.Tok == token.BREAK {
   126  			if s.Label == nil {
   127  				return implicit
   128  			}
   129  			if s.Label.Name == label {
   130  				return true
   131  			}
   132  		}
   133  
   134  	case *ast.BlockStmt:
   135  		return hasBreakList(s.List, label, implicit)
   136  
   137  	case *ast.IfStmt:
   138  		if hasBreak(s.Body, label, implicit) ||
   139  			s.Else != nil && hasBreak(s.Else, label, implicit) {
   140  			return true
   141  		}
   142  
   143  	case *ast.CaseClause:
   144  		return hasBreakList(s.Body, label, implicit)
   145  
   146  	case *ast.SwitchStmt:
   147  		if label != "" && hasBreak(s.Body, label, false) {
   148  			return true
   149  		}
   150  
   151  	case *ast.TypeSwitchStmt:
   152  		if label != "" && hasBreak(s.Body, label, false) {
   153  			return true
   154  		}
   155  
   156  	case *ast.CommClause:
   157  		return hasBreakList(s.Body, label, implicit)
   158  
   159  	case *ast.SelectStmt:
   160  		if label != "" && hasBreak(s.Body, label, false) {
   161  			return true
   162  		}
   163  
   164  	case *ast.ForStmt:
   165  		if label != "" && hasBreak(s.Body, label, false) {
   166  			return true
   167  		}
   168  
   169  	case *ast.RangeStmt:
   170  		if label != "" && hasBreak(s.Body, label, false) {
   171  			return true
   172  		}
   173  	}
   174  
   175  	return false
   176  }
   177  
   178  func hasBreakList(list []ast.Stmt, label string, implicit bool) bool {
   179  	for _, s := range list {
   180  		if hasBreak(s, label, implicit) {
   181  			return true
   182  		}
   183  	}
   184  	return false
   185  }