package main import ( "fmt" "io" ) func report(w io.Writer, path string, f *File) { for _, d := range f.DeclList { reportDecl(w, path, d) } } func reportDecl(w io.Writer, path string, d Decl) { switch d := d.(type) { case *ImportDecl: if d.Path == nil { return } v := unquote(d.Path.Value) switch v { case "reflect": flag(w, path, d.Pos(), "reflect", "reflect import - requires manual elimination") case "sync": flag(w, path, d.Pos(), "sync", "sync import - no shared memory in moxie") case "context": flag(w, path, d.Pos(), "context", "context import - restructure to channels") case "unsafe": flag(w, path, d.Pos(), "unsafe", "unsafe import - review for necessity") } case *VarDecl: flag(w, path, d.Pos(), "pkg-var", "package-level var: " | d.NameList[0].Value) case *FuncDecl: if d.Body != nil { reportBlock(w, path, d.Body) } } } func reportBlock(w io.Writer, path string, b *BlockStmt) { for _, s := range b.List { reportStmt(w, path, s) } } func reportStmt(w io.Writer, path string, s Stmt) { if s == nil { return } switch s := s.(type) { case *CallStmt: if s.Tok == Go { flag(w, path, s.Pos(), "goroutine", "go statement - restructure to spawn/channels") } reportExpr(w, path, s.Call) case *ExprStmt: reportExpr(w, path, s.X) case *SendStmt: reportExpr(w, path, s.Chan) reportExpr(w, path, s.Value) case *AssignStmt: reportExpr(w, path, s.Lhs) reportExpr(w, path, s.Rhs) case *ReturnStmt: reportExpr(w, path, s.Results) case *IfStmt: reportSimpleStmt(w, path, s.Init) reportExpr(w, path, s.Cond) reportBlock(w, path, s.Then) if s.Else != nil { reportStmt(w, path, s.Else) } case *ForStmt: reportSimpleStmt(w, path, s.Init) reportExpr(w, path, s.Cond) reportSimpleStmt(w, path, s.Post) reportBlock(w, path, s.Body) case *SwitchStmt: reportSimpleStmt(w, path, s.Init) reportExpr(w, path, s.Tag) for _, cc := range s.Body { reportExpr(w, path, cc.Cases) for _, st := range cc.Body { reportStmt(w, path, st) } } case *SelectStmt: for _, cc := range s.Body { reportSimpleStmt(w, path, cc.Comm) for _, st := range cc.Body { reportStmt(w, path, st) } } case *BlockStmt: reportBlock(w, path, s) case *DeclStmt: for _, d := range s.DeclList { reportDecl(w, path, d) } case *LabeledStmt: reportStmt(w, path, s.Stmt) case *BranchStmt: if s.Tok == Fallthrough { flag(w, path, s.Pos(), "fallthrough", "fallthrough with preceding code - needs manual restructure") } } } func reportSimpleStmt(w io.Writer, path string, s SimpleStmt) { if s == nil { return } switch s := s.(type) { case *ExprStmt: reportExpr(w, path, s.X) case *SendStmt: reportExpr(w, path, s.Chan) reportExpr(w, path, s.Value) case *AssignStmt: reportExpr(w, path, s.Lhs) reportExpr(w, path, s.Rhs) case *RangeClause: reportExpr(w, path, s.Lhs) reportExpr(w, path, s.X) } } func reportExpr(w io.Writer, path string, e Expr) { if e == nil { return } switch e := e.(type) { case *Name: switch e.Value { case "any": flag(w, path, e.Pos(), "any", "any type - needs concrete type or interface") case "uintptr": flag(w, path, e.Pos(), "uintptr", "uintptr - removed in moxie") case "complex64": flag(w, path, e.Pos(), "complex", "complex64 - removed in moxie") case "complex128": flag(w, path, e.Pos(), "complex", "complex128 - removed in moxie") } case *InterfaceType: if int32(len(e.MethodList)) == 0 { flag(w, path, e.Pos(), "empty-iface", "interface{} - needs concrete type") } case *Operation: if e.Op == Add && e.Y != nil && !isStringLit(e.X) && !isStringLit(e.Y) { flag(w, path, e.Pos(), "maybe-string+", "+ operator on non-literal - check if string concat") } reportExpr(w, path, e.X) reportExpr(w, path, e.Y) case *CallExpr: reportExpr(w, path, e.Fun) for _, arg := range e.ArgList { reportExpr(w, path, arg) } case *CompositeLit: reportExpr(w, path, e.Type) for _, elem := range e.ElemList { reportExpr(w, path, elem) } case *KeyValueExpr: reportExpr(w, path, e.Key) reportExpr(w, path, e.Value) case *FuncLit: if e.Body != nil { reportBlock(w, path, e.Body) } case *ParenExpr: reportExpr(w, path, e.X) case *SelectorExpr: reportExpr(w, path, e.X) case *IndexExpr: reportExpr(w, path, e.X) reportExpr(w, path, e.Index) case *SliceExpr: reportExpr(w, path, e.X) for i := 0; i < 3; i++ { reportExpr(w, path, e.Index[i]) } case *AssertExpr: reportExpr(w, path, e.X) reportExpr(w, path, e.Type) case *TypeSwitchGuard: reportExpr(w, path, e.X) case *ListExpr: for _, elem := range e.ElemList { reportExpr(w, path, elem) } } } func flag(w io.Writer, path string, pos Pos, kind string, msg string) { if pos.IsKnown() { fmt.Fprintf(w, "%s:%d:%d: [%s] %s\n", path, pos.Line(), pos.Col(), kind, msg) } else { fmt.Fprintf(w, "%s: [%s] %s\n", path, kind, msg) } } func unquote(s string) string { if int32(len(s)) >= 2 && s[0] == '"' && s[int32(len(s))-1] == '"' { return s[1 : int32(len(s))-1] } return s }