GoPLS Viewer

Home|gopls/go/analysis/passes/sortslice/analyzer.go
1// Copyright 2019 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 sortslice defines an Analyzer that checks for calls
6// to sort.Slice that do not use a slice type as first argument.
7package sortslice
8
9import (
10    "bytes"
11    "fmt"
12    "go/ast"
13    "go/format"
14    "go/types"
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    "golang.org/x/tools/go/types/typeutil"
20)
21
22const Doc = `check the argument type of sort.Slice
23
24sort.Slice requires an argument of a slice type. Check that
25the interface{} value passed to sort.Slice is actually a slice.`
26
27var Analyzer = &analysis.Analyzer{
28    Name:     "sortslice",
29    Doc:      Doc,
30    Requires: []*analysis.Analyzer{inspect.Analyzer},
31    Run:      run,
32}
33
34func run(pass *analysis.Pass) (interface{}, error) {
35    inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
36
37    nodeFilter := []ast.Node{
38        (*ast.CallExpr)(nil),
39    }
40
41    inspect.Preorder(nodeFilter, func(n ast.Node) {
42        call := n.(*ast.CallExpr)
43        fn_ := typeutil.Callee(pass.TypesInfocall).(*types.Func)
44        if fn == nil {
45            return
46        }
47
48        fnName := fn.FullName()
49        if fnName != "sort.Slice" && fnName != "sort.SliceStable" && fnName != "sort.SliceIsSorted" {
50            return
51        }
52
53        arg := call.Args[0]
54        typ := pass.TypesInfo.Types[arg].Type
55
56        if tupleok := typ.(*types.Tuple); ok {
57            typ = tuple.At(0).Type() // special case for Slice(f(...))
58        }
59
60        switch typ.Underlying().(type) {
61        case *types.Slice, *types.Interface:
62            return
63        }
64
65        // Restore typ to the original type, we may unwrap the tuple above,
66        // typ might not be the type of arg.
67        typ = pass.TypesInfo.Types[arg].Type
68
69        var fixes []analysis.SuggestedFix
70        switch v := typ.Underlying().(type) {
71        case *types.Array:
72            var buf bytes.Buffer
73            format.Node(&bufpass.Fset, &ast.SliceExpr{
74                X:      arg,
75                Slice3false,
76                Lbrackarg.End() + 1,
77                Rbrackarg.End() + 3,
78            })
79            fixes = append(fixesanalysis.SuggestedFix{
80                Message"Get a slice of the full array",
81                TextEdits: []analysis.TextEdit{{
82                    Pos:     arg.Pos(),
83                    End:     arg.End(),
84                    NewTextbuf.Bytes(),
85                }},
86            })
87        case *types.Pointer:
88            _ok := v.Elem().Underlying().(*types.Slice)
89            if !ok {
90                break
91            }
92            var buf bytes.Buffer
93            format.Node(&bufpass.Fset, &ast.StarExpr{
94                Xarg,
95            })
96            fixes = append(fixesanalysis.SuggestedFix{
97                Message"Dereference the pointer to the slice",
98                TextEdits: []analysis.TextEdit{{
99                    Pos:     arg.Pos(),
100                    End:     arg.End(),
101                    NewTextbuf.Bytes(),
102                }},
103            })
104        case *types.Signature:
105            if v.Params().Len() != 0 || v.Results().Len() != 1 {
106                break
107            }
108            if _ok := v.Results().At(0).Type().Underlying().(*types.Slice); !ok {
109                break
110            }
111            var buf bytes.Buffer
112            format.Node(&bufpass.Fset, &ast.CallExpr{
113                Funarg,
114            })
115            fixes = append(fixesanalysis.SuggestedFix{
116                Message"Call the function",
117                TextEdits: []analysis.TextEdit{{
118                    Pos:     arg.Pos(),
119                    End:     arg.End(),
120                    NewTextbuf.Bytes(),
121                }},
122            })
123        }
124
125        pass.Report(analysis.Diagnostic{
126            Pos:            call.Pos(),
127            End:            call.End(),
128            Message:        fmt.Sprintf("%s's argument must be a slice; is called with %s"fnNametyp.String()),
129            SuggestedFixesfixes,
130        })
131    })
132    return nilnil
133}
134
MembersX
run.nodeFilter
run.BlockStmt.typ
Doc
run
format
analysis
typeutil
inspector
bytes
types
inspect
run.BlockStmt.fnName
run.BlockStmt.fixes
run.BlockStmt.BlockStmt.buf
fmt
ast
run.pass
Members
X