implements: gather more per-api-function stats into a struct

Do more than collect names, because we're going to need more information
to feed to the eventual check tool.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
This commit is contained in:
John Mulligan 2021-09-13 15:05:05 -04:00 committed by mergify[bot]
parent 12e7178bc0
commit 18a134b535
4 changed files with 89 additions and 43 deletions

View File

@ -1,6 +1,7 @@
package implements
import (
"fmt"
"go/ast"
"go/build"
"go/parser"
@ -11,34 +12,63 @@ import (
"strings"
)
type visitor struct {
inFunction *ast.FuncDecl
type goFunction struct {
shortName string
fullName string
comment string
implementsCFunc string
callsCFunc string
isDeprecated bool
isPreview bool
callMap map[string]string
docMap map[string]string
deprecated map[string]string
preview map[string]string
endPos token.Pos
}
type visitor struct {
currentFunc *goFunction
callMap map[string]*goFunction
docMap map[string]*goFunction
deprecated []*goFunction
preview []*goFunction
}
func newVisitor() *visitor {
return &visitor{
callMap: map[string]string{},
docMap: map[string]string{},
deprecated: map[string]string{},
preview: map[string]string{},
callMap: map[string]*goFunction{},
docMap: map[string]*goFunction{},
deprecated: []*goFunction{},
preview: []*goFunction{},
}
}
func (v *visitor) checkDocComment(fdec *ast.FuncDecl) {
dtext := fdec.Doc.Text()
lines := strings.Split(dtext, "\n")
func funcDeclFullName(fdec *ast.FuncDecl) string {
if fdec.Recv == nil {
return fdec.Name.Name
}
if len(fdec.Recv.List) != 1 {
return fdec.Name.Name
}
typeName := "UNKNOWN!"
switch t := fdec.Recv.List[0].Type.(type) {
case *ast.StarExpr:
typeName = t.X.(*ast.Ident).Name
case *ast.Ident:
typeName = t.Name
}
return fmt.Sprintf("%s.%s", typeName, fdec.Name.Name)
}
func readDocComment(fdec *ast.FuncDecl, gfunc *goFunction) {
gfunc.comment = fdec.Doc.Text()
lines := strings.Split(gfunc.comment, "\n")
for i := range lines {
if strings.Contains(lines[i], "DEPRECATED") {
v.deprecated[fdec.Name.Name] = dtext
gfunc.isDeprecated = true
logger.Printf("marked deprecated: %s\n", fdec.Name.Name)
}
if strings.Contains(lines[i], "PREVIEW") {
v.preview[fdec.Name.Name] = dtext
gfunc.isPreview = true
logger.Printf("marked preview: %s\n", fdec.Name.Name)
}
@ -47,8 +77,8 @@ func (v *visitor) checkDocComment(fdec *ast.FuncDecl) {
if cfunc == "" {
return
}
v.docMap[cfunc] = fdec.Name.Name
logger.Printf("updated %s in doc map\n", cfunc)
gfunc.implementsCFunc = cfunc
logger.Printf("implements c function %s: %s\n", fdec.Name.Name, cfunc)
}
}
}
@ -59,7 +89,7 @@ func (v *visitor) checkCalled(s *ast.SelectorExpr) {
return
}
if "C" == ident.String() {
v.callMap[s.Sel.String()] = v.inFunction.Name.Name
v.callMap[s.Sel.String()] = v.currentFunc
logger.Printf("updated %s in call map\n", s.Sel.String())
}
}
@ -68,30 +98,43 @@ func (v *visitor) Visit(node ast.Node) ast.Visitor {
switch {
case node == nil:
return nil
case v.inFunction == nil:
case node.Pos() > v.inFunction.End():
logger.Printf("left function %v\n", v.inFunction.Name.Name)
v.inFunction = nil
case v.currentFunc == nil:
case node.Pos() > v.currentFunc.endPos:
logger.Printf("left function %v\n", v.currentFunc.shortName)
v.currentFunc = nil
}
switch n := node.(type) {
case *ast.File:
v.inFunction = nil
v.currentFunc = nil
return v
case *ast.FuncDecl:
logger.Printf("checking function: %v\n", n.Name.Name)
v.checkDocComment(n)
v.inFunction = n
gfunc := &goFunction{
shortName: n.Name.Name,
fullName: funcDeclFullName(n),
endPos: n.End(),
}
readDocComment(n, gfunc)
v.currentFunc = gfunc
if gfunc.isDeprecated {
v.deprecated = append(v.deprecated, gfunc)
logger.Printf("rem1 %v\n", v.deprecated)
}
if gfunc.isPreview {
v.preview = append(v.preview, gfunc)
logger.Printf("rem2 %v\n", v.preview)
}
return v
case *ast.CallExpr:
if v.inFunction == nil {
if v.currentFunc == nil {
return nil
}
if s, ok := n.Fun.(*ast.SelectorExpr); ok {
v.checkCalled(s)
}
}
if v.inFunction != nil {
if v.currentFunc != nil {
return v
}
return nil

View File

@ -21,8 +21,8 @@ type Inspector struct {
found map[string]foundFlags
deprecatedMissing int
deprecated map[string]string
preview map[string]string
deprecated []*goFunction
preview []*goFunction
}
// SetExpected sets the expected C functions, asuming the supplied prefix.
@ -41,6 +41,9 @@ func (ii *Inspector) SetExpected(prefix string, expected CFunctions) error {
func (ii *Inspector) update() {
ii.found = map[string]foundFlags{}
ii.deprecatedMissing = 0
ii.deprecated = []*goFunction{}
ii.preview = []*goFunction{}
for i := range ii.expected {
n := ii.expected[i].Name
if _, found := ii.visitor.callMap[n]; found {
@ -57,8 +60,8 @@ func (ii *Inspector) update() {
}
}
}
ii.deprecated = ii.visitor.deprecated
ii.preview = ii.visitor.preview
ii.deprecated = append(ii.deprecated, ii.visitor.deprecated...)
ii.preview = append(ii.preview, ii.visitor.preview...)
}
// NewInspector returns a newly created code inspector object.

View File

@ -91,11 +91,11 @@ func collectFuncs(jp *jrPackage, ii *Inspector) {
for _, cf := range ii.expected {
if flags, ok := ii.found[cf.Name]; ok {
refm := map[string]bool{}
if n := ii.visitor.callMap[cf.Name]; n != "" {
refm[n] = true
if gf := ii.visitor.callMap[cf.Name]; gf != nil {
refm[gf.shortName] = true
}
if n := ii.visitor.docMap[cf.Name]; n != "" {
refm[n] = true
if gf := ii.visitor.docMap[cf.Name]; gf != nil {
refm[gf.shortName] = true
}
jp.Found = append(jp.Found,
jrFunction{cf.Name, jrFlags(flags), mkeys(refm)})
@ -113,13 +113,13 @@ func collectFuncs(jp *jrPackage, ii *Inspector) {
jrFunction{cf.Name, flags, []string{}})
}
for d, v := range ii.deprecated {
for _, gf := range ii.deprecated {
jp.Deprecated = append(jp.Deprecated,
gFunc{Name: d, Comment: v})
gFunc{Name: gf.fullName, Comment: gf.comment})
}
for p, v := range ii.preview {
for _, gf := range ii.preview {
jp.Preview = append(jp.Preview,
gFunc{Name: p, Comment: v})
gFunc{Name: gf.fullName, Comment: gf.comment})
}
}

View File

@ -102,12 +102,12 @@ func (r *TextReport) Report(name string, ii *Inspector) error {
r.printf(" Missing: %s%s\n", cf.Name, d)
}
r.printf("Deprecated by go-ceph:\n")
for d := range ii.deprecated {
r.printf(" %s\n", d)
for _, gf := range ii.deprecated {
r.printf(" %s\n", gf.fullName)
}
r.printf("Preview in go-ceph:\n")
for p := range ii.preview {
r.printf(" %s\n", p)
for _, gf := range ii.preview {
r.printf(" %s\n", gf.fullName)
}
return nil
}