Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.0.1-2020.1.5 / internal / cmd / ast-to-pattern / parse.go
1 package main
2
3 import (
4         "bytes"
5         "fmt"
6         "go/ast"
7         "go/parser"
8         "go/scanner"
9         "go/token"
10         "text/template"
11 )
12
13 var tmplDecl = template.Must(template.New("").Parse(`` +
14         `package p; {{ . }}`))
15
16 var tmplExprs = template.Must(template.New("").Parse(`` +
17         `package p; var _ = []interface{}{ {{ . }}, }`))
18
19 var tmplStmts = template.Must(template.New("").Parse(`` +
20         `package p; func _() { {{ . }} }`))
21
22 var tmplType = template.Must(template.New("").Parse(`` +
23         `package p; var _ {{ . }}`))
24
25 var tmplValSpec = template.Must(template.New("").Parse(`` +
26         `package p; var {{ . }}`))
27
28 func execTmpl(tmpl *template.Template, src string) string {
29         var buf bytes.Buffer
30         if err := tmpl.Execute(&buf, src); err != nil {
31                 panic(err)
32         }
33         return buf.String()
34 }
35
36 func noBadNodes(node ast.Node) bool {
37         any := false
38         ast.Inspect(node, func(n ast.Node) bool {
39                 if any {
40                         return false
41                 }
42                 switch n.(type) {
43                 case *ast.BadExpr, *ast.BadDecl:
44                         any = true
45                 }
46                 return true
47         })
48         return !any
49 }
50
51 func parseType(fset *token.FileSet, src string) (ast.Expr, *ast.File, error) {
52         asType := execTmpl(tmplType, src)
53         f, err := parser.ParseFile(fset, "", asType, 0)
54         if err != nil {
55                 return nil, nil, err
56         }
57         vs := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
58         return vs.Type, f, nil
59 }
60
61 // parseDetectingNode tries its best to parse the ast.Node contained in src, as
62 // one of: *ast.File, ast.Decl, ast.Expr, ast.Stmt, *ast.ValueSpec.
63 // It also returns the *ast.File used for the parsing, so that the returned node
64 // can be easily type-checked.
65 func parseDetectingNode(fset *token.FileSet, src string) (interface{}, error) {
66         file := fset.AddFile("", fset.Base(), len(src))
67         scan := scanner.Scanner{}
68         scan.Init(file, []byte(src), nil, 0)
69         if _, tok, _ := scan.Scan(); tok == token.EOF {
70                 return nil, fmt.Errorf("empty source code")
71         }
72         var mainErr error
73
74         // first try as a whole file
75         if f, err := parser.ParseFile(fset, "", src, 0); err == nil && noBadNodes(f) {
76                 return f, nil
77         }
78
79         // then as a single declaration, or many
80         asDecl := execTmpl(tmplDecl, src)
81         if f, err := parser.ParseFile(fset, "", asDecl, 0); err == nil && noBadNodes(f) {
82                 if len(f.Decls) == 1 {
83                         return f.Decls[0], nil
84                 }
85                 return f, nil
86         }
87
88         // then as value expressions
89         asExprs := execTmpl(tmplExprs, src)
90         if f, err := parser.ParseFile(fset, "", asExprs, 0); err == nil && noBadNodes(f) {
91                 vs := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
92                 cl := vs.Values[0].(*ast.CompositeLit)
93                 if len(cl.Elts) == 1 {
94                         return cl.Elts[0], nil
95                 }
96                 return cl.Elts, nil
97         }
98
99         // then try as statements
100         asStmts := execTmpl(tmplStmts, src)
101         if f, err := parser.ParseFile(fset, "", asStmts, 0); err == nil && noBadNodes(f) {
102                 bl := f.Decls[0].(*ast.FuncDecl).Body
103                 if len(bl.List) == 1 {
104                         return bl.List[0], nil
105                 }
106                 return bl.List, nil
107         } else {
108                 // Statements is what covers most cases, so it will give
109                 // the best overall error message. Show positions
110                 // relative to where the user's code is put in the
111                 // template.
112                 mainErr = err
113         }
114
115         // type expressions not yet picked up, for e.g. chans and interfaces
116         if typ, f, err := parseType(fset, src); err == nil && noBadNodes(f) {
117                 return typ, nil
118         }
119
120         // value specs
121         asValSpec := execTmpl(tmplValSpec, src)
122         if f, err := parser.ParseFile(fset, "", asValSpec, 0); err == nil && noBadNodes(f) {
123                 vs := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
124                 return vs, nil
125         }
126         return nil, mainErr
127 }