Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / go / analysis / passes / stringintconv / string.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 stringintconv defines an Analyzer that flags type conversions
6 // from integers to strings.
7 package stringintconv
8
9 import (
10         "fmt"
11         "go/ast"
12         "go/types"
13
14         "golang.org/x/tools/go/analysis"
15         "golang.org/x/tools/go/analysis/passes/inspect"
16         "golang.org/x/tools/go/ast/inspector"
17 )
18
19 const Doc = `check for string(int) conversions
20
21 This checker flags conversions of the form string(x) where x is an integer
22 (but not byte or rune) type. Such conversions are discouraged because they
23 return the UTF-8 representation of the Unicode code point x, and not a decimal
24 string representation of x as one might expect. Furthermore, if x denotes an
25 invalid code point, the conversion cannot be statically rejected.
26
27 For conversions that intend on using the code point, consider replacing them
28 with string(rune(x)). Otherwise, strconv.Itoa and its equivalents return the
29 string representation of the value in the desired base.
30 `
31
32 var Analyzer = &analysis.Analyzer{
33         Name:     "stringintconv",
34         Doc:      Doc,
35         Requires: []*analysis.Analyzer{inspect.Analyzer},
36         Run:      run,
37 }
38
39 func typeName(typ types.Type) string {
40         if v, _ := typ.(interface{ Name() string }); v != nil {
41                 return v.Name()
42         }
43         if v, _ := typ.(interface{ Obj() *types.TypeName }); v != nil {
44                 return v.Obj().Name()
45         }
46         return ""
47 }
48
49 func run(pass *analysis.Pass) (interface{}, error) {
50         inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
51         nodeFilter := []ast.Node{
52                 (*ast.CallExpr)(nil),
53         }
54         inspect.Preorder(nodeFilter, func(n ast.Node) {
55                 call := n.(*ast.CallExpr)
56
57                 // Retrieve target type name.
58                 var tname *types.TypeName
59                 switch fun := call.Fun.(type) {
60                 case *ast.Ident:
61                         tname, _ = pass.TypesInfo.Uses[fun].(*types.TypeName)
62                 case *ast.SelectorExpr:
63                         tname, _ = pass.TypesInfo.Uses[fun.Sel].(*types.TypeName)
64                 }
65                 if tname == nil {
66                         return
67                 }
68                 target := tname.Name()
69
70                 // Check that target type T in T(v) has an underlying type of string.
71                 T, _ := tname.Type().Underlying().(*types.Basic)
72                 if T == nil || T.Kind() != types.String {
73                         return
74                 }
75                 if s := T.Name(); target != s {
76                         target += " (" + s + ")"
77                 }
78
79                 // Check that type V of v has an underlying integral type that is not byte or rune.
80                 if len(call.Args) != 1 {
81                         return
82                 }
83                 v := call.Args[0]
84                 vtyp := pass.TypesInfo.TypeOf(v)
85                 V, _ := vtyp.Underlying().(*types.Basic)
86                 if V == nil || V.Info()&types.IsInteger == 0 {
87                         return
88                 }
89                 switch V.Kind() {
90                 case types.Byte, types.Rune, types.UntypedRune:
91                         return
92                 }
93
94                 // Retrieve source type name.
95                 source := typeName(vtyp)
96                 if source == "" {
97                         return
98                 }
99                 if s := V.Name(); source != s {
100                         source += " (" + s + ")"
101                 }
102                 diag := analysis.Diagnostic{
103                         Pos:     n.Pos(),
104                         Message: fmt.Sprintf("conversion from %s to %s yields a string of one rune, not a string of digits (did you mean fmt.Sprint(x)?)", source, target),
105                         SuggestedFixes: []analysis.SuggestedFix{
106                                 {
107                                         Message: "Did you mean to convert a rune to a string?",
108                                         TextEdits: []analysis.TextEdit{
109                                                 {
110                                                         Pos:     v.Pos(),
111                                                         End:     v.Pos(),
112                                                         NewText: []byte("rune("),
113                                                 },
114                                                 {
115                                                         Pos:     v.End(),
116                                                         End:     v.End(),
117                                                         NewText: []byte(")"),
118                                                 },
119                                         },
120                                 },
121                         },
122                 }
123                 pass.Report(diag)
124         })
125         return nil, nil
126 }