libgo: update to Go 1.11
[official-gcc.git] / libgo / go / cmd / vet / method.go
blob5783278d2c29b635e40bc3740980abe9b6a4a12f
1 // Copyright 2010 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.
5 // This file contains the code to check canonical methods.
7 package main
9 import (
10 "fmt"
11 "go/ast"
12 "go/printer"
13 "strings"
16 func init() {
17 register("methods",
18 "check that canonically named methods are canonically defined",
19 checkCanonicalMethod,
20 funcDecl, interfaceType)
23 type MethodSig struct {
24 args []string
25 results []string
28 // canonicalMethods lists the input and output types for Go methods
29 // that are checked using dynamic interface checks. Because the
30 // checks are dynamic, such methods would not cause a compile error
31 // if they have the wrong signature: instead the dynamic check would
32 // fail, sometimes mysteriously. If a method is found with a name listed
33 // here but not the input/output types listed here, vet complains.
35 // A few of the canonical methods have very common names.
36 // For example, a type might implement a Scan method that
37 // has nothing to do with fmt.Scanner, but we still want to check
38 // the methods that are intended to implement fmt.Scanner.
39 // To do that, the arguments that have a = prefix are treated as
40 // signals that the canonical meaning is intended: if a Scan
41 // method doesn't have a fmt.ScanState as its first argument,
42 // we let it go. But if it does have a fmt.ScanState, then the
43 // rest has to match.
44 var canonicalMethods = map[string]MethodSig{
45 // "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict
46 "Format": {[]string{"=fmt.State", "rune"}, []string{}}, // fmt.Formatter
47 "GobDecode": {[]string{"[]byte"}, []string{"error"}}, // gob.GobDecoder
48 "GobEncode": {[]string{}, []string{"[]byte", "error"}}, // gob.GobEncoder
49 "MarshalJSON": {[]string{}, []string{"[]byte", "error"}}, // json.Marshaler
50 "MarshalXML": {[]string{"*xml.Encoder", "xml.StartElement"}, []string{"error"}}, // xml.Marshaler
51 "ReadByte": {[]string{}, []string{"byte", "error"}}, // io.ByteReader
52 "ReadFrom": {[]string{"=io.Reader"}, []string{"int64", "error"}}, // io.ReaderFrom
53 "ReadRune": {[]string{}, []string{"rune", "int", "error"}}, // io.RuneReader
54 "Scan": {[]string{"=fmt.ScanState", "rune"}, []string{"error"}}, // fmt.Scanner
55 "Seek": {[]string{"=int64", "int"}, []string{"int64", "error"}}, // io.Seeker
56 "UnmarshalJSON": {[]string{"[]byte"}, []string{"error"}}, // json.Unmarshaler
57 "UnmarshalXML": {[]string{"*xml.Decoder", "xml.StartElement"}, []string{"error"}}, // xml.Unmarshaler
58 "UnreadByte": {[]string{}, []string{"error"}},
59 "UnreadRune": {[]string{}, []string{"error"}},
60 "WriteByte": {[]string{"byte"}, []string{"error"}}, // jpeg.writer (matching bufio.Writer)
61 "WriteTo": {[]string{"=io.Writer"}, []string{"int64", "error"}}, // io.WriterTo
64 func checkCanonicalMethod(f *File, node ast.Node) {
65 switch n := node.(type) {
66 case *ast.FuncDecl:
67 if n.Recv != nil {
68 canonicalMethod(f, n.Name, n.Type)
70 case *ast.InterfaceType:
71 for _, field := range n.Methods.List {
72 for _, id := range field.Names {
73 canonicalMethod(f, id, field.Type.(*ast.FuncType))
79 func canonicalMethod(f *File, id *ast.Ident, t *ast.FuncType) {
80 // Expected input/output.
81 expect, ok := canonicalMethods[id.Name]
82 if !ok {
83 return
86 // Actual input/output
87 args := typeFlatten(t.Params.List)
88 var results []ast.Expr
89 if t.Results != nil {
90 results = typeFlatten(t.Results.List)
93 // Do the =s (if any) all match?
94 if !f.matchParams(expect.args, args, "=") || !f.matchParams(expect.results, results, "=") {
95 return
98 // Everything must match.
99 if !f.matchParams(expect.args, args, "") || !f.matchParams(expect.results, results, "") {
100 expectFmt := id.Name + "(" + argjoin(expect.args) + ")"
101 if len(expect.results) == 1 {
102 expectFmt += " " + argjoin(expect.results)
103 } else if len(expect.results) > 1 {
104 expectFmt += " (" + argjoin(expect.results) + ")"
107 f.b.Reset()
108 if err := printer.Fprint(&f.b, f.fset, t); err != nil {
109 fmt.Fprintf(&f.b, "<%s>", err)
111 actual := f.b.String()
112 actual = strings.TrimPrefix(actual, "func")
113 actual = id.Name + actual
115 f.Badf(id.Pos(), "method %s should have signature %s", actual, expectFmt)
119 func argjoin(x []string) string {
120 y := make([]string, len(x))
121 for i, s := range x {
122 if s[0] == '=' {
123 s = s[1:]
125 y[i] = s
127 return strings.Join(y, ", ")
130 // Turn parameter list into slice of types
131 // (in the ast, types are Exprs).
132 // Have to handle f(int, bool) and f(x, y, z int)
133 // so not a simple 1-to-1 conversion.
134 func typeFlatten(l []*ast.Field) []ast.Expr {
135 var t []ast.Expr
136 for _, f := range l {
137 if len(f.Names) == 0 {
138 t = append(t, f.Type)
139 continue
141 for range f.Names {
142 t = append(t, f.Type)
145 return t
148 // Does each type in expect with the given prefix match the corresponding type in actual?
149 func (f *File) matchParams(expect []string, actual []ast.Expr, prefix string) bool {
150 for i, x := range expect {
151 if !strings.HasPrefix(x, prefix) {
152 continue
154 if i >= len(actual) {
155 return false
157 if !f.matchParamType(x, actual[i]) {
158 return false
161 if prefix == "" && len(actual) > len(expect) {
162 return false
164 return true
167 // Does this one type match?
168 func (f *File) matchParamType(expect string, actual ast.Expr) bool {
169 expect = strings.TrimPrefix(expect, "=")
170 // Strip package name if we're in that package.
171 if n := len(f.file.Name.Name); len(expect) > n && expect[:n] == f.file.Name.Name && expect[n] == '.' {
172 expect = expect[n+1:]
175 // Overkill but easy.
176 f.b.Reset()
177 printer.Fprint(&f.b, f.fset, actual)
178 return f.b.String() == expect