// CheckStatement checks if a statement is type safe func (scope *Scope) CheckStatement(node ast.Node) (errs []error) { for _, file := range scope.files { name := "." + scope.Render(file.Name) + ".gopry" if name == filepath.Base(scope.path) { ast.Walk(walker(func(n ast.Node) bool { switch s := n.(type) { case *ast.BlockStmt: for i, stmt := range s.List { pos := scope.fset.Position(stmt.Pos()) if pos.Line == scope.line { r := scope.Render(stmt) if strings.HasPrefix(r, "pry.Apply") { var iStmt []ast.Stmt switch s2 := node.(type) { case *ast.BlockStmt: iStmt = append(iStmt, s2.List...) case ast.Stmt: iStmt = append(iStmt, s2) case ast.Expr: iStmt = append(iStmt, ast.Stmt(&ast.ExprStmt{X: s2})) default: errs = append(errs, errors.New("not a statement")) return false } oldList := make([]ast.Stmt, len(s.List)) copy(oldList, s.List) s.List = append(s.List, make([]ast.Stmt, len(iStmt))...) copy(s.List[i+len(iStmt):], s.List[i:]) copy(s.List[i:], iStmt) _, errs = scope.TypeCheck() if len(errs) > 0 { s.List = oldList return false } return false } } } } return true }), file) } } return }
func (r *reader) block(b *block, s []ast.Stmt) { for _, s := range s { switch s := s.(type) { case *ast.AssignStmt: if s.Tok == token.DEFINE { switch x := s.Rhs[0].(type) { case *ast.BinaryExpr: n := newOperatorNode(types.NewFunc(0, nil, x.Op.String(), nil)) b.addNode(n) r.in(x.X, n.ins[0]) r.in(x.Y, n.ins[1]) r.out(s.Lhs[0], n.outs[0]) case *ast.CallExpr: if p, ok := x.Fun.(*ast.ParenExpr); ok { // writer puts conversions in parens for easy recognition n := newConvertNode(r.pkg) b.addNode(n) n.setType(r.typ(p.X)) r.in(x.Args[0], n.ins[0]) r.out(s.Lhs[0], n.outs[0]) } else { n := r.call(b, x, "", s) for i, res := range s.Lhs { if i >= len(outs(n)) { out := n.(*callNode).newOutput(nil) out.bad = true } r.out(res, outs(n)[i]) } } case *ast.CompositeLit: r.compositeLit(b, x, false, s) case *ast.FuncLit: n := newFuncNode(nil, b.childArranged) b.addNode(n) n.output.setType(r.typ(x.Type)) r.out(s.Lhs[0], n.output) r.fun(n, x.Type, x.Body) case *ast.Ident, *ast.SelectorExpr, *ast.StarExpr: r.value(b, x, s.Lhs[0], false, s) case *ast.IndexExpr: r.index(b, x, s.Lhs[0], false, s) case *ast.SliceExpr: n := newSliceNode() b.addNode(n) r.in(x.X, n.x) r.in(x.Low, n.low) if x.High == nil { n.removePortBase(n.high) n.high = nil } else { r.in(x.High, n.high) } if x.Max != nil { n.max = n.newInput(newVar("max", types.Typ[types.Int])) r.in(x.Max, n.max) } r.out(s.Lhs[0], n.y) case *ast.TypeAssertExpr: n := newTypeAssertNode(r.pkg) b.addNode(n) n.setType(r.typ(x.Type)) r.in(x.X, n.ins[0]) r.out(s.Lhs[0], n.outs[0]) r.out(s.Lhs[1], n.outs[1]) case *ast.UnaryExpr: switch x.Op { case token.AND: switch y := x.X.(type) { case *ast.CompositeLit: r.compositeLit(b, y, true, s) case *ast.IndexExpr: r.index(b, y, s.Lhs[0], false, s) default: r.value(b, x, s.Lhs[0], false, s) } case token.NOT: n := newOperatorNode(types.NewFunc(0, nil, x.Op.String(), nil)) b.addNode(n) r.in(x.X, n.ins[0]) r.out(s.Lhs[0], n.outs[0]) case token.ARROW: n := r.sendrecv(b, x.X, nil, s) r.out(s.Lhs[0], n.elem) r.out(s.Lhs[1], n.ok) } } } else { lh := s.Lhs[0] rh := s.Rhs[0] if x, ok := lh.(*ast.IndexExpr); ok { r.index(b, x, rh, true, s) } else if id, ok := lh.(*ast.Ident); !ok || r.conns[id.Name] == nil { r.value(b, lh, rh, true, s) } else { c := newConnection() lh := name(lh) rh := name(rh) c.setSrc(r.ports[rh]) if cmt, ok := r.cmap[s]; ok { c.src.conntxt.SetText(cmt[0].List[0].Text[2:]) c.toggleHidden() } if p, ok := r.ports[lh]; ok { c.feedback = true c.setDst(p) } else { r.conns[lh] = append(r.conns[lh], c) } } } case *ast.BranchStmt: n := newBranchNode(s.Tok.String()) b.addNode(n) r.seq(n, s) case *ast.DeclStmt: decl := s.Decl.(*ast.GenDecl) v := decl.Specs[0].(*ast.ValueSpec) switch decl.Tok { case token.VAR: name := v.Names[0].Name if v.Type != nil { r.scope.Insert(newVar(name, r.typ(v.Type))) // local var has nil Pkg r.conns[name] = []*connection{} } else { r.out(v.Names[0], b.node.(*loopNode).inputsNode.outs[1]) } case token.CONST: switch x := v.Values[0].(type) { case *ast.BasicLit: n := newBasicLiteralNode(x.Kind) b.addNode(n) switch x.Kind { case token.INT, token.FLOAT: n.text.SetText(x.Value) case token.IMAG: // TODO case token.STRING, token.CHAR: text, _ := strconv.Unquote(x.Value) n.text.SetText(text) } r.out(v.Names[0], n.outs[0]) r.seq(n, s) case *ast.Ident, *ast.SelectorExpr: r.value(b, x, v.Names[0], false, s) } } case *ast.DeferStmt: r.call(b, s.Call, "defer ", s) case *ast.ExprStmt: switch x := s.X.(type) { case *ast.CallExpr: r.call(b, x, "", s) case *ast.UnaryExpr: r.sendrecv(b, x.X, nil, s) } case *ast.ForStmt: n := newLoopNode(b.childArranged) b.addNode(n) if s.Cond != nil { r.in(s.Cond.(*ast.BinaryExpr).Y, n.input) } if s.Init != nil { r.out(s.Init.(*ast.AssignStmt).Lhs[0], n.inputsNode.outs[0]) } r.block(n.loopblk, s.Body.List) r.seq(n, s) case *ast.GoStmt: r.call(b, s.Call, "go ", s) case *ast.IfStmt: n := newIfNode(b.childArranged) b.addNode(n) for s := ast.Stmt(s); s != nil; { b, cond := n.newBlock() switch s2 := s.(type) { case *ast.IfStmt: r.in(s2.Cond, cond) r.block(b, s2.Body.List) s = s2.Else case *ast.BlockStmt: r.block(b, s2.List) s = nil } } r.seq(n, s) case *ast.RangeStmt: n := newLoopNode(b.childArranged) b.addNode(n) r.in(s.X, n.input) r.out(s.Key, n.inputsNode.outs[0]) if s.Value != nil { r.out(s.Value, n.inputsNode.outs[1]) } r.block(n.loopblk, s.Body.List) r.seq(n, s) case *ast.ReturnStmt: n := newBranchNode("return") b.addNode(n) r.seq(n, s) case *ast.SelectStmt: n := newSelectNode(b.childArranged) b.addNode(n) for _, s := range s.Body.List { s := s.(*ast.CommClause) c := n.newCase() switch s := s.Comm.(type) { case *ast.AssignStmt: c.send = false r.in(s.Rhs[0].(*ast.UnaryExpr).X, c.ch) r.out(s.Lhs[0], c.elemOk.outs[0]) r.out(s.Lhs[1], c.elemOk.outs[1]) case *ast.ExprStmt: c.send = false r.in(s.X.(*ast.UnaryExpr).X, c.ch) case *ast.SendStmt: r.in(s.Chan, c.ch) r.in(s.Value, c.elem) case nil: c.setDefault() } r.block(c.blk, s.Body) } r.seq(n, s) case *ast.SendStmt: r.sendrecv(b, s.Chan, s.Value, s) } } }
// SuggestionsGoCode is a suggestion engine that uses gocode for autocomplete. func (scope *Scope) SuggestionsGoCode(line string, index int) ([]string, error) { var suggestions []string var code string for name, file := range scope.Files { name = filepath.Dir(name) + "/." + filepath.Base(name) + "pry" if name == scope.path { ast.Walk(walker(func(n ast.Node) bool { switch s := n.(type) { case *ast.BlockStmt: for i, stmt := range s.List { pos := scope.fset.Position(stmt.Pos()) if pos.Line == scope.line { r := scope.Render(stmt) if strings.HasPrefix(r, "pry.Apply") { var iStmt []ast.Stmt iStmt = append(iStmt, ast.Stmt(&ast.ExprStmt{X: ast.NewIdent(placeholder)})) oldList := make([]ast.Stmt, len(s.List)) copy(oldList, s.List) s.List = append(s.List, make([]ast.Stmt, len(iStmt))...) copy(s.List[i+len(iStmt):], s.List[i:]) copy(s.List[i:], iStmt) code = scope.Render(file) s.List = oldList return false } } } } return true }), file) i := strings.Index(code, placeholder) + index code = strings.Replace(code, placeholder, line, 1) subProcess := exec.Command("gocode", "autocomplete", filepath.Dir(name), strconv.Itoa(i)) stdin, err := subProcess.StdinPipe() if err != nil { return nil, err } stdout, err := subProcess.StdoutPipe() if err != nil { return nil, err } defer stdout.Close() subProcess.Stderr = os.Stderr if err = subProcess.Start(); err != nil { return nil, err } io.WriteString(stdin, code) stdin.Close() output, err := ioutil.ReadAll(bufio.NewReader(stdout)) if err != nil { return nil, err } rawSuggestions := strings.Split(string(output), "\n")[1:] for _, suggestion := range rawSuggestions { trimmed := strings.TrimSpace(suggestion) if len(trimmed) > 0 { suggestions = append(suggestions, trimmed) } } subProcess.Wait() break } } return suggestions, nil }