golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
-golang.org/x/tools v0.1.1-0.20210505014545-7cab0ef2e9a5 h1:ImcI7RFHWLu2QWpFDXaReu0j+sQAHIy65vUFZImXiqY=
-golang.org/x/tools v0.1.1-0.20210505014545-7cab0ef2e9a5/go.mod h1:sH/Eidr0EddymY8HZSakBo32zU3fG5ovDq874hJLjVg=
+golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9 h1:2XlR/j4I4xz5GQZI7zBjqTfezYyRIE2jD5IMousB2rg=
+golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
if chanDecl == nil || len(chanDecl.Args) != 1 {
return
}
- chanDecl.Args = append(chanDecl.Args, &ast.BasicLit{
+
+ // Make a copy of the channel's declaration to avoid
+ // mutating the AST. See https://golang.org/issue/46129.
+ chanDeclCopy := &ast.CallExpr{}
+ *chanDeclCopy = *chanDecl
+ chanDeclCopy.Args = append([]ast.Expr(nil), chanDecl.Args...)
+ chanDeclCopy.Args = append(chanDeclCopy.Args, &ast.BasicLit{
Kind: token.INT,
Value: "1",
})
+
var buf bytes.Buffer
- if err := format.Node(&buf, token.NewFileSet(), chanDecl); err != nil {
+ if err := format.Node(&buf, token.NewFileSet(), chanDeclCopy); err != nil {
return
}
pass.Report(analysis.Diagnostic{
// we let it go. But if it does have a fmt.ScanState, then the
// rest has to match.
var canonicalMethods = map[string]struct{ args, results []string }{
+ "As": {[]string{"interface{}"}, []string{"bool"}}, // errors.As
// "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict
"Format": {[]string{"=fmt.State", "rune"}, []string{}}, // fmt.Formatter
"GobDecode": {[]string{"[]byte"}, []string{"error"}}, // gob.GobDecoder
"GobEncode": {[]string{}, []string{"[]byte", "error"}}, // gob.GobEncoder
+ "Is": {[]string{"error"}, []string{"bool"}}, // errors.Is
"MarshalJSON": {[]string{}, []string{"[]byte", "error"}}, // json.Marshaler
"MarshalXML": {[]string{"*xml.Encoder", "xml.StartElement"}, []string{"error"}}, // xml.Marshaler
"ReadByte": {[]string{}, []string{"byte", "error"}}, // io.ByteReader
"UnmarshalXML": {[]string{"*xml.Decoder", "xml.StartElement"}, []string{"error"}}, // xml.Unmarshaler
"UnreadByte": {[]string{}, []string{"error"}},
"UnreadRune": {[]string{}, []string{"error"}},
+ "Unwrap": {[]string{}, []string{"error"}}, // errors.Unwrap
"WriteByte": {[]string{"byte"}, []string{"error"}}, // jpeg.writer (matching bufio.Writer)
"WriteTo": {[]string{"=io.Writer"}, []string{"int64", "error"}}, // io.WriterTo
}
return
}
+ // Special case: Is, As and Unwrap only apply when type
+ // implements error.
+ if id.Name == "Is" || id.Name == "As" || id.Name == "Unwrap" {
+ if recv := sign.Recv(); recv == nil || !implementsError(recv.Type()) {
+ return
+ }
+ }
+
// Do the =s (if any) all match?
if !matchParams(pass, expect.args, args, "=") || !matchParams(pass, expect.results, results, "=") {
return
// Overkill but easy.
return typeString(actual) == expect
}
+
+var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
+
+func implementsError(actual types.Type) bool {
+ return types.Implements(actual, errorType)
+}