func (ctxt *Context) visitExpr(f *ast.File, e ast.Expr, local bool, visitf func(*Info) bool) bool { var info Info info.Expr = e switch e := e.(type) { case *ast.Ident: if e.Name == "_" { return true } info.Pos = e.Pos() info.Ident = e case *ast.SelectorExpr: info.Pos = e.Sel.Pos() info.Ident = e.Sel } obj, t := types.ExprType(e, ctxt.importer) if obj == nil { ctxt.logf(e.Pos(), "no object for %s", pretty(e)) return true } info.ExprType = t info.ReferObj = obj if parser.Universe.Lookup(obj.Name) != obj { info.ReferPos = types.DeclPos(obj) if info.ReferPos == token.NoPos { name := pretty(e) if name != "init" { ctxt.logf(e.Pos(), "no declaration for %s", pretty(e)) } return true } } else { info.Universe = true } info.Local = local oldName := info.Ident.Name more := visitf(&info) if info.Ident.Name != oldName { ctxt.ChangedFiles[ctxt.filename(f)] = f } return more }
// Sets multiLine to true if the expression spans multiple lines. func (p *printer) expr1(expr ast.Expr, prec1, depth int, multiLine *bool) { p.print(expr.Pos()) switch x := expr.(type) { case *ast.BadExpr: p.print("BadExpr") case *ast.Ident: p.print(x) case *ast.BinaryExpr: if depth < 1 { p.internalError("depth < 1:", depth) depth = 1 } p.binaryExpr(x, prec1, cutoff(x, depth), depth, multiLine) case *ast.KeyValueExpr: p.expr(x.Key, multiLine) p.print(x.Colon, token.COLON, blank) p.expr(x.Value, multiLine) case *ast.StarExpr: const prec = token.UnaryPrec if prec < prec1 { // parenthesis needed p.print(token.LPAREN) p.print(token.MUL) p.expr(x.X, multiLine) p.print(token.RPAREN) } else { // no parenthesis needed p.print(token.MUL) p.expr(x.X, multiLine) } case *ast.UnaryExpr: const prec = token.UnaryPrec if prec < prec1 { // parenthesis needed p.print(token.LPAREN) p.expr(x, multiLine) p.print(token.RPAREN) } else { // no parenthesis needed p.print(x.Op) if x.Op == token.RANGE { // TODO(gri) Remove this code if it cannot be reached. p.print(blank) } p.expr1(x.X, prec, depth, multiLine) } case *ast.BasicLit: p.print(x) case *ast.FuncLit: p.expr(x.Type, multiLine) p.funcBody(x.Body, p.distance(x.Type.Pos(), p.pos), true, multiLine) case *ast.ParenExpr: if _, hasParens := x.X.(*ast.ParenExpr); hasParens { // don't print parentheses around an already parenthesized expression // TODO(gri) consider making this more general and incorporate precedence levels p.expr0(x.X, reduceDepth(depth), multiLine) // parentheses undo one level of depth } else { p.print(token.LPAREN) p.expr0(x.X, reduceDepth(depth), multiLine) // parentheses undo one level of depth p.print(x.Rparen, token.RPAREN) } case *ast.SelectorExpr: parts := selectorExprList(expr) p.exprList(token.NoPos, parts, depth, periodSep, multiLine, token.NoPos) case *ast.TypeAssertExpr: p.expr1(x.X, token.HighestPrec, depth, multiLine) p.print(token.PERIOD, token.LPAREN) if x.Type != nil { p.expr(x.Type, multiLine) } else { p.print(token.TYPE) } p.print(token.RPAREN) case *ast.IndexExpr: // TODO(gri): should treat[] like parentheses and undo one level of depth p.expr1(x.X, token.HighestPrec, 1, multiLine) p.print(x.Lbrack, token.LBRACK) p.expr0(x.Index, depth+1, multiLine) p.print(x.Rbrack, token.RBRACK) case *ast.SliceExpr: // TODO(gri): should treat[] like parentheses and undo one level of depth p.expr1(x.X, token.HighestPrec, 1, multiLine) p.print(x.Lbrack, token.LBRACK) if x.Low != nil { p.expr0(x.Low, depth+1, multiLine) } // blanks around ":" if both sides exist and either side is a binary expression if depth <= 1 && x.Low != nil && x.High != nil && (isBinary(x.Low) || isBinary(x.High)) { p.print(blank, token.COLON, blank) } else { p.print(token.COLON) } if x.High != nil { p.expr0(x.High, depth+1, multiLine) } p.print(x.Rbrack, token.RBRACK) case *ast.CallExpr: if len(x.Args) > 1 { depth++ } p.expr1(x.Fun, token.HighestPrec, depth, multiLine) p.print(x.Lparen, token.LPAREN) p.exprList(x.Lparen, x.Args, depth, commaSep|commaTerm, multiLine, x.Rparen) if x.Ellipsis.IsValid() { p.print(x.Ellipsis, token.ELLIPSIS) } p.print(x.Rparen, token.RPAREN) case *ast.CompositeLit: // composite literal elements that are composite literals themselves may have the type omitted if x.Type != nil { p.expr1(x.Type, token.HighestPrec, depth, multiLine) } p.print(x.Lbrace, token.LBRACE) p.exprList(x.Lbrace, x.Elts, 1, commaSep|commaTerm, multiLine, x.Rbrace) // do not insert extra line breaks because of comments before // the closing '}' as it might break the code if there is no // trailing ',' p.print(noExtraLinebreak, x.Rbrace, token.RBRACE, noExtraLinebreak) case *ast.Ellipsis: p.print(token.ELLIPSIS) if x.Elt != nil { p.expr(x.Elt, multiLine) } case *ast.ArrayType: p.print(token.LBRACK) if x.Len != nil { p.expr(x.Len, multiLine) } p.print(token.RBRACK) p.expr(x.Elt, multiLine) case *ast.StructType: p.print(token.STRUCT) p.fieldList(x.Fields, true, x.Incomplete) case *ast.FuncType: p.print(token.FUNC) p.signature(x.Params, x.Results, multiLine) case *ast.InterfaceType: p.print(token.INTERFACE) p.fieldList(x.Methods, false, x.Incomplete) case *ast.MapType: p.print(token.MAP, token.LBRACK) p.expr(x.Key, multiLine) p.print(token.RBRACK) p.expr(x.Value, multiLine) case *ast.ChanType: switch x.Dir { case ast.SEND | ast.RECV: p.print(token.CHAN) case ast.RECV: p.print(token.ARROW, token.CHAN) case ast.SEND: p.print(token.CHAN, token.ARROW) } p.print(blank) p.expr(x.Value, multiLine) default: panic("unreachable") } return }
func checkExprs(t *testing.T, pkg *ast.File, importer Importer) { var visit astVisitor stopped := false visit = func(n ast.Node) bool { if stopped { return false } mustResolve := false var e ast.Expr switch n := n.(type) { case *ast.ImportSpec: // If the file imports a package to ".", abort // because we don't support that (yet). if n.Name != nil && n.Name.Name == "." { stopped = true return false } return true case *ast.FuncDecl: // add object for init functions if n.Recv == nil && n.Name.Name == "init" { n.Name.Obj = ast.NewObj(ast.Fun, "init") } return true case *ast.Ident: if n.Name == "_" { return false } e = n mustResolve = true case *ast.KeyValueExpr: // don't try to resolve the key part of a key-value // because it might be a map key which doesn't // need resolving, and we can't tell without being // complicated with types. ast.Walk(visit, n.Value) return false case *ast.SelectorExpr: ast.Walk(visit, n.X) e = n mustResolve = true case *ast.File: for _, d := range n.Decls { ast.Walk(visit, d) } return false case ast.Expr: e = n default: return true } defer func() { if err := recover(); err != nil { t.Fatalf("panic (%v) on %T", err, e) //t.Fatalf("panic (%v) on %v at %v\n", err, e, FileSet.Position(e.Pos())) } }() obj, _ := ExprType(e, importer) if obj == nil && mustResolve { t.Errorf("no object for %v(%p, %T) at %v\n", e, e, e, FileSet.Position(e.Pos())) } return false } ast.Walk(visit, pkg) }