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 / internal / lsp / analysis / simplifyrange / simplifyrange.go
1 // Copyright 2020 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 // Package simplifyrange defines an Analyzer that simplifies range statements.
6 // https://golang.org/cmd/gofmt/#hdr-The_simplify_command
7 // https://github.com/golang/go/blob/master/src/cmd/gofmt/simplify.go
8 package simplifyrange
9
10 import (
11         "bytes"
12         "go/ast"
13         "go/printer"
14         "go/token"
15
16         "golang.org/x/tools/go/analysis"
17         "golang.org/x/tools/go/analysis/passes/inspect"
18         "golang.org/x/tools/go/ast/inspector"
19 )
20
21 const Doc = `check for range statement simplifications
22
23 A range of the form:
24         for x, _ = range v {...}
25 will be simplified to:
26         for x = range v {...}
27
28 A range of the form:
29         for _ = range v {...}
30 will be simplified to:
31         for range v {...}
32
33 This is one of the simplifications that "gofmt -s" applies.`
34
35 var Analyzer = &analysis.Analyzer{
36         Name:     "simplifyrange",
37         Doc:      Doc,
38         Requires: []*analysis.Analyzer{inspect.Analyzer},
39         Run:      run,
40 }
41
42 func run(pass *analysis.Pass) (interface{}, error) {
43         inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
44         nodeFilter := []ast.Node{
45                 (*ast.RangeStmt)(nil),
46         }
47         inspect.Preorder(nodeFilter, func(n ast.Node) {
48                 var copy *ast.RangeStmt
49                 if stmt, ok := n.(*ast.RangeStmt); ok {
50                         x := *stmt
51                         copy = &x
52                 }
53                 if copy == nil {
54                         return
55                 }
56                 end := newlineIndex(pass.Fset, copy)
57
58                 // Range statements of the form: for i, _ := range x {}
59                 var old ast.Expr
60                 if isBlank(copy.Value) {
61                         old = copy.Value
62                         copy.Value = nil
63                 }
64                 // Range statements of the form: for _ := range x {}
65                 if isBlank(copy.Key) && copy.Value == nil {
66                         old = copy.Key
67                         copy.Key = nil
68                 }
69                 // Return early if neither if condition is met.
70                 if old == nil {
71                         return
72                 }
73                 pass.Report(analysis.Diagnostic{
74                         Pos:            old.Pos(),
75                         End:            old.End(),
76                         Message:        "simplify range expression",
77                         SuggestedFixes: suggestedFixes(pass.Fset, copy, end),
78                 })
79         })
80         return nil, nil
81 }
82
83 func suggestedFixes(fset *token.FileSet, rng *ast.RangeStmt, end token.Pos) []analysis.SuggestedFix {
84         var b bytes.Buffer
85         printer.Fprint(&b, fset, rng)
86         stmt := b.Bytes()
87         index := bytes.Index(stmt, []byte("\n"))
88         // If there is a new line character, then don't replace the body.
89         if index != -1 {
90                 stmt = stmt[:index]
91         }
92         return []analysis.SuggestedFix{{
93                 Message: "Remove empty value",
94                 TextEdits: []analysis.TextEdit{{
95                         Pos:     rng.Pos(),
96                         End:     end,
97                         NewText: stmt[:index],
98                 }},
99         }}
100 }
101
102 func newlineIndex(fset *token.FileSet, rng *ast.RangeStmt) token.Pos {
103         var b bytes.Buffer
104         printer.Fprint(&b, fset, rng)
105         contents := b.Bytes()
106         index := bytes.Index(contents, []byte("\n"))
107         if index == -1 {
108                 return rng.End()
109         }
110         return rng.Pos() + token.Pos(index)
111 }
112
113 func isBlank(x ast.Expr) bool {
114         ident, ok := x.(*ast.Ident)
115         return ok && ident.Name == "_"
116 }