Black Lives Matter. Support the Equal Justice Initiative.

Source file src/go/types/return.go

Documentation: go/types

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

View as plain text