|
package main |
|
|
|
import ( |
|
"fmt" |
|
"go/ast" |
|
"go/parser" |
|
"go/token" |
|
"strconv" |
|
"strings" |
|
) |
|
|
|
var ( |
|
fieldsWithGodocMatchingIdent int |
|
fieldsWithGodocMatchingJson int |
|
fieldsWithWhackadoodleGodoc int |
|
) |
|
|
|
func inspect(node ast.Node) bool { |
|
t, ok := node.(*ast.TypeSpec) |
|
if !ok { |
|
return true |
|
} |
|
|
|
v, ok := t.Type.(*ast.StructType) |
|
if !ok { |
|
return true |
|
} |
|
|
|
for _, f := range v.Fields.List { |
|
// Skip the field if it has no godoc. |
|
if f.Doc == nil || len(f.Doc.List) == 0 { |
|
continue |
|
} |
|
commentWords := strings.Split(f.Doc.List[0].Text, " ") |
|
if len(commentWords) < 2 { |
|
continue |
|
} |
|
// commentWords[0] is "//". |
|
godocName := strings.TrimSuffix(commentWords[1], ",") |
|
|
|
// Skip the field if it doesn't have a name. |
|
// TODO Handle embedded fields. |
|
if len(f.Names) == 0 { |
|
continue |
|
} |
|
goIdent := f.Names[0].Name |
|
|
|
// Skip the field if it doesn't have a tag. |
|
if f.Tag == nil { |
|
continue |
|
} |
|
tag, err := strconv.Unquote(f.Tag.Value) |
|
if err != nil { |
|
fmt.Println("failed to unquote tag:", err) |
|
continue |
|
} |
|
tagWords := strings.Split(tag, " ") |
|
var ( |
|
jsonTag string |
|
foundJsonTag bool |
|
) |
|
for _, w := range tagWords { |
|
kv := strings.Split(w, ":") |
|
if len(kv) < 2 { |
|
continue |
|
} |
|
key, quotedValue := kv[0], kv[1] |
|
if key != "json" { |
|
continue |
|
} |
|
value, err := strconv.Unquote(quotedValue) |
|
if err != nil { |
|
fmt.Println("failed to unquote tag value:", err) |
|
break |
|
} |
|
jsonTag = strings.Split(value, ",")[0] |
|
if jsonTag == "" { |
|
break |
|
} |
|
foundJsonTag = true |
|
} |
|
if !foundJsonTag { |
|
continue |
|
} |
|
var prefix string |
|
if godocName == goIdent { |
|
prefix = "✗" |
|
fieldsWithGodocMatchingIdent++ |
|
} else if godocName == jsonTag { |
|
prefix = "✓" |
|
fieldsWithGodocMatchingJson++ |
|
} else { |
|
prefix = "?" |
|
fieldsWithWhackadoodleGodoc++ |
|
} |
|
fmt.Printf("%s %s.%s has godoc name %q.\n", prefix, t.Name.Name, goIdent, godocName) |
|
} |
|
return true |
|
} |
|
|
|
func main() { |
|
fset := token.NewFileSet() |
|
packages, err := parser.ParseDir(fset, "./", nil, parser.ParseComments) |
|
if err != nil { |
|
panic(err) |
|
} |
|
for _, p := range packages { |
|
for _, f := range p.Files { |
|
ast.Inspect(f, inspect) |
|
} |
|
} |
|
fmt.Println(fieldsWithGodocMatchingJson, "fields have godoc matching their json tags.") |
|
fmt.Println(fieldsWithGodocMatchingIdent, "fields have godoc matching their Go idents.") |
|
fmt.Println(fieldsWithWhackadoodleGodoc, "fields have godoc matching neither the ident nor the json tag.") |
|
} |