mirror of https://github.com/ceph/go-ceph
implements: add initial code for process Go sources
This code makes use of Go's native AST processing packages and allows us to analyze our source code using the go-ceph conventions. Signed-off-by: John Mulligan <jmulligan@redhat.com>
This commit is contained in:
parent
ce7911ea35
commit
18d8083de3
|
@ -0,0 +1,124 @@
|
||||||
|
package implements
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type visitor struct {
|
||||||
|
inFunction *ast.FuncDecl
|
||||||
|
|
||||||
|
callMap map[string]string
|
||||||
|
docMap map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newVisitor() *visitor {
|
||||||
|
return &visitor{
|
||||||
|
callMap: map[string]string{},
|
||||||
|
docMap: map[string]string{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *visitor) checkDocImplements(fdec *ast.FuncDecl) {
|
||||||
|
dtext := fdec.Doc.Text()
|
||||||
|
lines := strings.Split(dtext, "\n")
|
||||||
|
for i := range lines {
|
||||||
|
if lines[i] == "Implements:" {
|
||||||
|
cfunc := cfuncFromComment(lines[i+1])
|
||||||
|
if cfunc == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v.docMap[cfunc] = fdec.Name.Name
|
||||||
|
logger.Printf("updated %s in doc map\n", cfunc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *visitor) checkCalled(s *ast.SelectorExpr) {
|
||||||
|
ident, ok := s.X.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if "C" == ident.String() {
|
||||||
|
v.callMap[s.Sel.String()] = v.inFunction.Name.Name
|
||||||
|
logger.Printf("updated %s in call map\n", s.Sel.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
switch n := node.(type) {
|
||||||
|
case *ast.File:
|
||||||
|
v.inFunction = nil
|
||||||
|
return v
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
logger.Printf("checking function: %v\n", n.Name.Name)
|
||||||
|
v.checkDocImplements(n)
|
||||||
|
v.inFunction = n
|
||||||
|
return v
|
||||||
|
case *ast.CallExpr:
|
||||||
|
if v.inFunction == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if s, ok := n.Fun.(*ast.SelectorExpr); ok {
|
||||||
|
v.checkCalled(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v.inFunction != nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cfuncFromComment(ctext string) string {
|
||||||
|
m := regexp.MustCompile(` ([a-zA-Z0-9_]+)\(`).FindAllSubmatch([]byte(ctext), 1)
|
||||||
|
if len(m) < 1 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(m[0][1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// CephGoFunctions will look for C functions called by the code code and
|
||||||
|
// update the found functions for the package within the inspector.
|
||||||
|
func CephGoFunctions(source, packageName string, ii *Inspector) error {
|
||||||
|
p, err := build.Import("./"+packageName, source, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
toCheck := []string{}
|
||||||
|
toCheck = append(toCheck, p.GoFiles...)
|
||||||
|
toCheck = append(toCheck, p.CgoFiles...)
|
||||||
|
for _, fname := range toCheck {
|
||||||
|
logger.Printf("Reading go file: %v\n", fname)
|
||||||
|
src, err := ioutil.ReadFile(path.Join(p.Dir, fname))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
f, err := parser.ParseFile(
|
||||||
|
fset,
|
||||||
|
fname,
|
||||||
|
src,
|
||||||
|
parser.ParseComments|parser.AllErrors)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ast.Walk(ii.visitor, f)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package implements
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type foundFlags int
|
||||||
|
|
||||||
|
const (
|
||||||
|
isCalled = foundFlags(1)
|
||||||
|
isDocumented = foundFlags(2)
|
||||||
|
isDeprecated = foundFlags(4)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Inspector types collect the high-level results from C and Go
|
||||||
|
// code scans.
|
||||||
|
type Inspector struct {
|
||||||
|
visitor *visitor
|
||||||
|
|
||||||
|
expected CFunctions
|
||||||
|
found map[string]foundFlags
|
||||||
|
deprecatedMissing int
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExpected sets the expected C functions, asuming the supplied prefix.
|
||||||
|
func (ii *Inspector) SetExpected(prefix string, expected CFunctions) error {
|
||||||
|
ii.expected = make([]CFunction, 0, len(expected))
|
||||||
|
for _, cfunc := range expected {
|
||||||
|
if strings.HasPrefix(cfunc.Name, prefix) {
|
||||||
|
logger.Printf("C function \"%s\" has matching prefix", cfunc.Name)
|
||||||
|
ii.expected = append(ii.expected, cfunc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := ii.expected.ensure()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ii *Inspector) update() {
|
||||||
|
ii.found = map[string]foundFlags{}
|
||||||
|
ii.deprecatedMissing = 0
|
||||||
|
for i := range ii.expected {
|
||||||
|
n := ii.expected[i].Name
|
||||||
|
if _, found := ii.visitor.callMap[n]; found {
|
||||||
|
ii.found[n] |= isCalled
|
||||||
|
}
|
||||||
|
if _, found := ii.visitor.docMap[n]; found {
|
||||||
|
ii.found[n] |= isDocumented
|
||||||
|
}
|
||||||
|
if ii.expected[i].isDeprecated() {
|
||||||
|
if _, found := ii.found[n]; found {
|
||||||
|
ii.found[n] |= isDeprecated
|
||||||
|
} else {
|
||||||
|
ii.deprecatedMissing++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInspector returns a newly created code inspector object.
|
||||||
|
func NewInspector() *Inspector {
|
||||||
|
return &Inspector{
|
||||||
|
visitor: newVisitor(),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue