Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / go / analysis / passes / unreachable / unreachable.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/go/analysis/passes/unreachable/unreachable.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/go/analysis/passes/unreachable/unreachable.go
new file mode 100644 (file)
index 0000000..90896dd
--- /dev/null
@@ -0,0 +1,325 @@
+// Copyright 2013 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.
+
+// Package unreachable defines an Analyzer that checks for unreachable code.
+package unreachable
+
+// TODO(adonovan): use the new cfg package, which is more precise.
+
+import (
+       "go/ast"
+       "go/token"
+       "log"
+
+       "golang.org/x/tools/go/analysis"
+       "golang.org/x/tools/go/analysis/passes/inspect"
+       "golang.org/x/tools/go/ast/inspector"
+)
+
+const Doc = `check for unreachable code
+
+The unreachable analyzer finds statements that execution can never reach
+because they are preceded by an return statement, a call to panic, an
+infinite loop, or similar constructs.`
+
+var Analyzer = &analysis.Analyzer{
+       Name:             "unreachable",
+       Doc:              Doc,
+       Requires:         []*analysis.Analyzer{inspect.Analyzer},
+       RunDespiteErrors: true,
+       Run:              run,
+}
+
+func run(pass *analysis.Pass) (interface{}, error) {
+       inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+
+       nodeFilter := []ast.Node{
+               (*ast.FuncDecl)(nil),
+               (*ast.FuncLit)(nil),
+       }
+       inspect.Preorder(nodeFilter, func(n ast.Node) {
+               var body *ast.BlockStmt
+               switch n := n.(type) {
+               case *ast.FuncDecl:
+                       body = n.Body
+               case *ast.FuncLit:
+                       body = n.Body
+               }
+               if body == nil {
+                       return
+               }
+               d := &deadState{
+                       pass:     pass,
+                       hasBreak: make(map[ast.Stmt]bool),
+                       hasGoto:  make(map[string]bool),
+                       labels:   make(map[string]ast.Stmt),
+               }
+               d.findLabels(body)
+               d.reachable = true
+               d.findDead(body)
+       })
+       return nil, nil
+}
+
+type deadState struct {
+       pass        *analysis.Pass
+       hasBreak    map[ast.Stmt]bool
+       hasGoto     map[string]bool
+       labels      map[string]ast.Stmt
+       breakTarget ast.Stmt
+
+       reachable bool
+}
+
+// findLabels gathers information about the labels defined and used by stmt
+// and about which statements break, whether a label is involved or not.
+func (d *deadState) findLabels(stmt ast.Stmt) {
+       switch x := stmt.(type) {
+       default:
+               log.Fatalf("%s: internal error in findLabels: unexpected statement %T", d.pass.Fset.Position(x.Pos()), x)
+
+       case *ast.AssignStmt,
+               *ast.BadStmt,
+               *ast.DeclStmt,
+               *ast.DeferStmt,
+               *ast.EmptyStmt,
+               *ast.ExprStmt,
+               *ast.GoStmt,
+               *ast.IncDecStmt,
+               *ast.ReturnStmt,
+               *ast.SendStmt:
+               // no statements inside
+
+       case *ast.BlockStmt:
+               for _, stmt := range x.List {
+                       d.findLabels(stmt)
+               }
+
+       case *ast.BranchStmt:
+               switch x.Tok {
+               case token.GOTO:
+                       if x.Label != nil {
+                               d.hasGoto[x.Label.Name] = true
+                       }
+
+               case token.BREAK:
+                       stmt := d.breakTarget
+                       if x.Label != nil {
+                               stmt = d.labels[x.Label.Name]
+                       }
+                       if stmt != nil {
+                               d.hasBreak[stmt] = true
+                       }
+               }
+
+       case *ast.IfStmt:
+               d.findLabels(x.Body)
+               if x.Else != nil {
+                       d.findLabels(x.Else)
+               }
+
+       case *ast.LabeledStmt:
+               d.labels[x.Label.Name] = x.Stmt
+               d.findLabels(x.Stmt)
+
+       // These cases are all the same, but the x.Body only works
+       // when the specific type of x is known, so the cases cannot
+       // be merged.
+       case *ast.ForStmt:
+               outer := d.breakTarget
+               d.breakTarget = x
+               d.findLabels(x.Body)
+               d.breakTarget = outer
+
+       case *ast.RangeStmt:
+               outer := d.breakTarget
+               d.breakTarget = x
+               d.findLabels(x.Body)
+               d.breakTarget = outer
+
+       case *ast.SelectStmt:
+               outer := d.breakTarget
+               d.breakTarget = x
+               d.findLabels(x.Body)
+               d.breakTarget = outer
+
+       case *ast.SwitchStmt:
+               outer := d.breakTarget
+               d.breakTarget = x
+               d.findLabels(x.Body)
+               d.breakTarget = outer
+
+       case *ast.TypeSwitchStmt:
+               outer := d.breakTarget
+               d.breakTarget = x
+               d.findLabels(x.Body)
+               d.breakTarget = outer
+
+       case *ast.CommClause:
+               for _, stmt := range x.Body {
+                       d.findLabels(stmt)
+               }
+
+       case *ast.CaseClause:
+               for _, stmt := range x.Body {
+                       d.findLabels(stmt)
+               }
+       }
+}
+
+// findDead walks the statement looking for dead code.
+// If d.reachable is false on entry, stmt itself is dead.
+// When findDead returns, d.reachable tells whether the
+// statement following stmt is reachable.
+func (d *deadState) findDead(stmt ast.Stmt) {
+       // Is this a labeled goto target?
+       // If so, assume it is reachable due to the goto.
+       // This is slightly conservative, in that we don't
+       // check that the goto is reachable, so
+       //      L: goto L
+       // will not provoke a warning.
+       // But it's good enough.
+       if x, isLabel := stmt.(*ast.LabeledStmt); isLabel && d.hasGoto[x.Label.Name] {
+               d.reachable = true
+       }
+
+       if !d.reachable {
+               switch stmt.(type) {
+               case *ast.EmptyStmt:
+                       // do not warn about unreachable empty statements
+               default:
+                       d.pass.Report(analysis.Diagnostic{
+                               Pos:     stmt.Pos(),
+                               End:     stmt.End(),
+                               Message: "unreachable code",
+                               SuggestedFixes: []analysis.SuggestedFix{{
+                                       Message: "Remove",
+                                       TextEdits: []analysis.TextEdit{{
+                                               Pos: stmt.Pos(),
+                                               End: stmt.End(),
+                                       }},
+                               }},
+                       })
+                       d.reachable = true // silence error about next statement
+               }
+       }
+
+       switch x := stmt.(type) {
+       default:
+               log.Fatalf("%s: internal error in findDead: unexpected statement %T", d.pass.Fset.Position(x.Pos()), x)
+
+       case *ast.AssignStmt,
+               *ast.BadStmt,
+               *ast.DeclStmt,
+               *ast.DeferStmt,
+               *ast.EmptyStmt,
+               *ast.GoStmt,
+               *ast.IncDecStmt,
+               *ast.SendStmt:
+               // no control flow
+
+       case *ast.BlockStmt:
+               for _, stmt := range x.List {
+                       d.findDead(stmt)
+               }
+
+       case *ast.BranchStmt:
+               switch x.Tok {
+               case token.BREAK, token.GOTO, token.FALLTHROUGH:
+                       d.reachable = false
+               case token.CONTINUE:
+                       // NOTE: We accept "continue" statements as terminating.
+                       // They are not necessary in the spec definition of terminating,
+                       // because a continue statement cannot be the final statement
+                       // before a return. But for the more general problem of syntactically
+                       // identifying dead code, continue redirects control flow just
+                       // like the other terminating statements.
+                       d.reachable = false
+               }
+
+       case *ast.ExprStmt:
+               // Call to panic?
+               call, ok := x.X.(*ast.CallExpr)
+               if ok {
+                       name, ok := call.Fun.(*ast.Ident)
+                       if ok && name.Name == "panic" && name.Obj == nil {
+                               d.reachable = false
+                       }
+               }
+
+       case *ast.ForStmt:
+               d.findDead(x.Body)
+               d.reachable = x.Cond != nil || d.hasBreak[x]
+
+       case *ast.IfStmt:
+               d.findDead(x.Body)
+               if x.Else != nil {
+                       r := d.reachable
+                       d.reachable = true
+                       d.findDead(x.Else)
+                       d.reachable = d.reachable || r
+               } else {
+                       // might not have executed if statement
+                       d.reachable = true
+               }
+
+       case *ast.LabeledStmt:
+               d.findDead(x.Stmt)
+
+       case *ast.RangeStmt:
+               d.findDead(x.Body)
+               d.reachable = true
+
+       case *ast.ReturnStmt:
+               d.reachable = false
+
+       case *ast.SelectStmt:
+               // NOTE: Unlike switch and type switch below, we don't care
+               // whether a select has a default, because a select without a
+               // default blocks until one of the cases can run. That's different
+               // from a switch without a default, which behaves like it has
+               // a default with an empty body.
+               anyReachable := false
+               for _, comm := range x.Body.List {
+                       d.reachable = true
+                       for _, stmt := range comm.(*ast.CommClause).Body {
+                               d.findDead(stmt)
+                       }
+                       anyReachable = anyReachable || d.reachable
+               }
+               d.reachable = anyReachable || d.hasBreak[x]
+
+       case *ast.SwitchStmt:
+               anyReachable := false
+               hasDefault := false
+               for _, cas := range x.Body.List {
+                       cc := cas.(*ast.CaseClause)
+                       if cc.List == nil {
+                               hasDefault = true
+                       }
+                       d.reachable = true
+                       for _, stmt := range cc.Body {
+                               d.findDead(stmt)
+                       }
+                       anyReachable = anyReachable || d.reachable
+               }
+               d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
+
+       case *ast.TypeSwitchStmt:
+               anyReachable := false
+               hasDefault := false
+               for _, cas := range x.Body.List {
+                       cc := cas.(*ast.CaseClause)
+                       if cc.List == nil {
+                               hasDefault = true
+                       }
+                       d.reachable = true
+                       for _, stmt := range cc.Body {
+                               d.findDead(stmt)
+                       }
+                       anyReachable = anyReachable || d.reachable
+               }
+               d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
+       }
+}