-
Notifications
You must be signed in to change notification settings - Fork 1
/
bson.go
103 lines (97 loc) · 2.45 KB
/
bson.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package gotaglint
import (
"go/ast"
"go/types"
"golang.org/x/tools/go/analysis"
"strings"
)
type BsonTag struct {
Name string
Skip bool
OmitEmpty bool
MinSize bool
Inline bool
InvalidOptions []string
}
func ParseBsonTag(tag string) BsonTag {
if tag == "-" {
return BsonTag{Skip: true}
}
fields := strings.Split(tag, ",")
var bsonTag BsonTag
bsonTag.Name = fields[0]
if len(fields) > 1 {
for _, field := range fields[1:] {
switch field {
case "omitempty":
bsonTag.OmitEmpty = true
case "minsize":
bsonTag.MinSize = true
case "inline":
bsonTag.Inline = true
default:
bsonTag.InvalidOptions = append(bsonTag.InvalidOptions, field)
}
}
}
return bsonTag
}
func isBsonInlineable(t types.Type) bool {
switch b := t.(type) {
case *types.Map:
key, ok := b.Key().(*types.Basic)
if !ok {
return false
}
return key.Kind() == types.String
case *types.Pointer:
_, ok := b.Elem().(*types.Struct)
return ok
case *types.Struct:
return true
case *types.Named:
return isBsonInlineable(b.Underlying())
default:
return false
}
}
func isBsonMinSizeable(t types.Type) bool {
switch b := t.(type) {
case *types.Pointer:
return isBsonMinSizeable(b.Elem())
case *types.Named:
return isBsonMinSizeable(b.Underlying())
case *types.Basic:
switch b.Kind() {
case types.Uint64, types.Uintptr, types.Int64:
return true
default:
return false
}
default:
return false
}
}
var BsonAnalyzer = analysis.Analyzer{
Name: "bsontaglint",
Doc: "report bson tag problem",
Run: runTagChecker("bson", func(pass *analysis.Pass, field *ast.Field, tag string) {
bsonTag := ParseBsonTag(tag)
if bsonTag.Skip {
return
}
if len(bsonTag.InvalidOptions) > 0 {
pass.Reportf(field.Tag.Pos(), "invalid bson options:%q", render(pass.Fset, field.Tag))
}
if len(field.Names) > 0 && bsonTag.Name == strings.ToLower(field.Names[0].Name) {
pass.Reportf(field.Tag.Pos(), "same bson tag name:%q", render(pass.Fset, field.Tag))
}
if bsonTag.Inline && !isBsonInlineable(pass.TypesInfo.TypeOf(field.Type)) {
pass.Reportf(field.Tag.Pos(), "inline must be struct, pointer to struct, map[string]*:%q", render(pass.Fset, field.Tag))
}
if bsonTag.MinSize && !isBsonMinSizeable(pass.TypesInfo.TypeOf(field.Type)) {
pass.Reportf(field.Tag.Pos(), "minsize must be int64, uint64, uintptr:%q", render(pass.Fset, field.Tag))
}
//TODO check duplicate key and more than one map
}),
}