Commit 42f20102 by Ian Lance Taylor

libgo: update to go1.8rc3

    
    Reviewed-on: https://go-review.googlesource.com/35844

From-SVN: r244981
parent 3f54004b
59f181b6fda68ece22882945853ca2df9dbf1c88 2a5f65a98ca483aad2dd74dc2636a7baecc59cf2
The first line of this file holds the git revision number of the The first line of this file holds the git revision number of the
last merge done from the master library sources. last merge done from the master library sources.
...@@ -3787,3 +3787,26 @@ GLOBL ·constants<>(SB),8,$8 ...@@ -3787,3 +3787,26 @@ GLOBL ·constants<>(SB),8,$8
tg.setenv("GOPATH", tg.path("go")) tg.setenv("GOPATH", tg.path("go"))
tg.run("build", "p") tg.run("build", "p")
} }
// Issue 18778.
func TestDotDotDotOutsideGOPATH(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.tempFile("pkgs/a.go", `package x`)
tg.tempFile("pkgs/a_test.go", `package x_test
import "testing"
func TestX(t *testing.T) {}`)
tg.tempFile("pkgs/a/a.go", `package a`)
tg.tempFile("pkgs/a/a_test.go", `package a_test
import "testing"
func TestA(t *testing.T) {}`)
tg.cd(tg.path("pkgs"))
tg.run("build", "./...")
tg.run("test", "./...")
tg.run("list", "./...")
tg.grepStdout("pkgs$", "expected package not listed")
tg.grepStdout("pkgs/a", "expected package not listed")
}
...@@ -433,7 +433,7 @@ func setErrorPos(p *Package, importPos []token.Position) *Package { ...@@ -433,7 +433,7 @@ func setErrorPos(p *Package, importPos []token.Position) *Package {
func cleanImport(path string) string { func cleanImport(path string) string {
orig := path orig := path
path = pathpkg.Clean(path) path = pathpkg.Clean(path)
if strings.HasPrefix(orig, "./") && path != ".." && path != "." && !strings.HasPrefix(path, "../") { if strings.HasPrefix(orig, "./") && path != ".." && !strings.HasPrefix(path, "../") {
path = "./" + path path = "./" + path
} }
return path return path
......
...@@ -1357,16 +1357,7 @@ func (db *DB) begin(ctx context.Context, opts *TxOptions, strategy connReuseStra ...@@ -1357,16 +1357,7 @@ func (db *DB) begin(ctx context.Context, opts *TxOptions, strategy connReuseStra
cancel: cancel, cancel: cancel,
ctx: ctx, ctx: ctx,
} }
go func(tx *Tx) { go tx.awaitDone()
select {
case <-tx.ctx.Done():
if !tx.isDone() {
// Discard and close the connection used to ensure the transaction
// is closed and the resources are released.
tx.rollback(true)
}
}
}(tx)
return tx, nil return tx, nil
} }
...@@ -1388,6 +1379,11 @@ func (db *DB) Driver() driver.Driver { ...@@ -1388,6 +1379,11 @@ func (db *DB) Driver() driver.Driver {
type Tx struct { type Tx struct {
db *DB db *DB
// closemu prevents the transaction from closing while there
// is an active query. It is held for read during queries
// and exclusively during close.
closemu sync.RWMutex
// dc is owned exclusively until Commit or Rollback, at which point // dc is owned exclusively until Commit or Rollback, at which point
// it's returned with putConn. // it's returned with putConn.
dc *driverConn dc *driverConn
...@@ -1413,6 +1409,20 @@ type Tx struct { ...@@ -1413,6 +1409,20 @@ type Tx struct {
ctx context.Context ctx context.Context
} }
// awaitDone blocks until the context in Tx is canceled and rolls back
// the transaction if it's not already done.
func (tx *Tx) awaitDone() {
// Wait for either the transaction to be committed or rolled
// back, or for the associated context to be closed.
<-tx.ctx.Done()
// Discard and close the connection used to ensure the
// transaction is closed and the resources are released. This
// rollback does nothing if the transaction has already been
// committed or rolled back.
tx.rollback(true)
}
func (tx *Tx) isDone() bool { func (tx *Tx) isDone() bool {
return atomic.LoadInt32(&tx.done) != 0 return atomic.LoadInt32(&tx.done) != 0
} }
...@@ -1424,16 +1434,31 @@ var ErrTxDone = errors.New("sql: Transaction has already been committed or rolle ...@@ -1424,16 +1434,31 @@ var ErrTxDone = errors.New("sql: Transaction has already been committed or rolle
// close returns the connection to the pool and // close returns the connection to the pool and
// must only be called by Tx.rollback or Tx.Commit. // must only be called by Tx.rollback or Tx.Commit.
func (tx *Tx) close(err error) { func (tx *Tx) close(err error) {
tx.closemu.Lock()
defer tx.closemu.Unlock()
tx.db.putConn(tx.dc, err) tx.db.putConn(tx.dc, err)
tx.cancel() tx.cancel()
tx.dc = nil tx.dc = nil
tx.txi = nil tx.txi = nil
} }
// hookTxGrabConn specifies an optional hook to be called on
// a successful call to (*Tx).grabConn. For tests.
var hookTxGrabConn func()
func (tx *Tx) grabConn(ctx context.Context) (*driverConn, error) { func (tx *Tx) grabConn(ctx context.Context) (*driverConn, error) {
select {
default:
case <-ctx.Done():
return nil, ctx.Err()
}
if tx.isDone() { if tx.isDone() {
return nil, ErrTxDone return nil, ErrTxDone
} }
if hookTxGrabConn != nil { // test hook
hookTxGrabConn()
}
return tx.dc, nil return tx.dc, nil
} }
...@@ -1503,6 +1528,9 @@ func (tx *Tx) Rollback() error { ...@@ -1503,6 +1528,9 @@ func (tx *Tx) Rollback() error {
// for the execution of the returned statement. The returned statement // for the execution of the returned statement. The returned statement
// will run in the transaction context. // will run in the transaction context.
func (tx *Tx) PrepareContext(ctx context.Context, query string) (*Stmt, error) { func (tx *Tx) PrepareContext(ctx context.Context, query string) (*Stmt, error) {
tx.closemu.RLock()
defer tx.closemu.RUnlock()
// TODO(bradfitz): We could be more efficient here and either // TODO(bradfitz): We could be more efficient here and either
// provide a method to take an existing Stmt (created on // provide a method to take an existing Stmt (created on
// perhaps a different Conn), and re-create it on this Conn if // perhaps a different Conn), and re-create it on this Conn if
...@@ -1567,6 +1595,9 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) { ...@@ -1567,6 +1595,9 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) {
// The returned statement operates within the transaction and will be closed // The returned statement operates within the transaction and will be closed
// when the transaction has been committed or rolled back. // when the transaction has been committed or rolled back.
func (tx *Tx) StmtContext(ctx context.Context, stmt *Stmt) *Stmt { func (tx *Tx) StmtContext(ctx context.Context, stmt *Stmt) *Stmt {
tx.closemu.RLock()
defer tx.closemu.RUnlock()
// TODO(bradfitz): optimize this. Currently this re-prepares // TODO(bradfitz): optimize this. Currently this re-prepares
// each time. This is fine for now to illustrate the API but // each time. This is fine for now to illustrate the API but
// we should really cache already-prepared statements // we should really cache already-prepared statements
...@@ -1618,6 +1649,9 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt { ...@@ -1618,6 +1649,9 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
// ExecContext executes a query that doesn't return rows. // ExecContext executes a query that doesn't return rows.
// For example: an INSERT and UPDATE. // For example: an INSERT and UPDATE.
func (tx *Tx) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error) { func (tx *Tx) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error) {
tx.closemu.RLock()
defer tx.closemu.RUnlock()
dc, err := tx.grabConn(ctx) dc, err := tx.grabConn(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -1661,6 +1695,9 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) { ...@@ -1661,6 +1695,9 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
// QueryContext executes a query that returns rows, typically a SELECT. // QueryContext executes a query that returns rows, typically a SELECT.
func (tx *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) { func (tx *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
tx.closemu.RLock()
defer tx.closemu.RUnlock()
dc, err := tx.grabConn(ctx) dc, err := tx.grabConn(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -2038,25 +2075,21 @@ type Rows struct { ...@@ -2038,25 +2075,21 @@ type Rows struct {
// closed value is 1 when the Rows is closed. // closed value is 1 when the Rows is closed.
// Use atomic operations on value when checking value. // Use atomic operations on value when checking value.
closed int32 closed int32
ctxClose chan struct{} // closed when Rows is closed, may be null. cancel func() // called when Rows is closed, may be nil.
lastcols []driver.Value lastcols []driver.Value
lasterr error // non-nil only if closed is true lasterr error // non-nil only if closed is true
closeStmt *driverStmt // if non-nil, statement to Close on close closeStmt *driverStmt // if non-nil, statement to Close on close
} }
func (rs *Rows) initContextClose(ctx context.Context) { func (rs *Rows) initContextClose(ctx context.Context) {
if ctx.Done() == context.Background().Done() { ctx, rs.cancel = context.WithCancel(ctx)
return go rs.awaitDone(ctx)
} }
rs.ctxClose = make(chan struct{}) // awaitDone blocks until the rows are closed or the context canceled.
go func() { func (rs *Rows) awaitDone(ctx context.Context) {
select { <-ctx.Done()
case <-ctx.Done(): rs.Close()
rs.Close()
case <-rs.ctxClose:
}
}()
} }
// Next prepares the next result row for reading with the Scan method. It // Next prepares the next result row for reading with the Scan method. It
...@@ -2314,7 +2347,9 @@ func (rs *Rows) Scan(dest ...interface{}) error { ...@@ -2314,7 +2347,9 @@ func (rs *Rows) Scan(dest ...interface{}) error {
return nil return nil
} }
var rowsCloseHook func(*Rows, *error) // rowsCloseHook returns a function so tests may install the
// hook throug a test only mutex.
var rowsCloseHook = func() func(*Rows, *error) { return nil }
func (rs *Rows) isClosed() bool { func (rs *Rows) isClosed() bool {
return atomic.LoadInt32(&rs.closed) != 0 return atomic.LoadInt32(&rs.closed) != 0
...@@ -2328,13 +2363,15 @@ func (rs *Rows) Close() error { ...@@ -2328,13 +2363,15 @@ func (rs *Rows) Close() error {
if !atomic.CompareAndSwapInt32(&rs.closed, 0, 1) { if !atomic.CompareAndSwapInt32(&rs.closed, 0, 1) {
return nil return nil
} }
if rs.ctxClose != nil {
close(rs.ctxClose)
}
err := rs.rowsi.Close() err := rs.rowsi.Close()
if fn := rowsCloseHook; fn != nil { if fn := rowsCloseHook(); fn != nil {
fn(rs, &err) fn(rs, &err)
} }
if rs.cancel != nil {
rs.cancel()
}
if rs.closeStmt != nil { if rs.closeStmt != nil {
rs.closeStmt.Close() rs.closeStmt.Close()
} }
......
...@@ -14,6 +14,7 @@ import ( ...@@ -14,6 +14,7 @@ import (
"runtime" "runtime"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"testing" "testing"
"time" "time"
) )
...@@ -326,9 +327,7 @@ func TestQueryContext(t *testing.T) { ...@@ -326,9 +327,7 @@ func TestQueryContext(t *testing.T) {
// And verify that the final rows.Next() call, which hit EOF, // And verify that the final rows.Next() call, which hit EOF,
// also closed the rows connection. // also closed the rows connection.
if n := db.numFreeConns(); n != 1 { waitForFree(t, db, 5*time.Second, 1)
t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
}
if prepares := numPrepares(t, db) - prepares0; prepares != 1 { if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares) t.Errorf("executed %d Prepare statements; want 1", prepares)
} }
...@@ -345,6 +344,18 @@ func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool { ...@@ -345,6 +344,18 @@ func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
return false return false
} }
// waitForFree checks db.numFreeConns until either it equals want or
// the maxWait time elapses.
func waitForFree(t *testing.T, db *DB, maxWait time.Duration, want int) {
var numFree int
if !waitCondition(maxWait, 5*time.Millisecond, func() bool {
numFree = db.numFreeConns()
return numFree == want
}) {
t.Fatalf("free conns after hitting EOF = %d; want %d", numFree, want)
}
}
func TestQueryContextWait(t *testing.T) { func TestQueryContextWait(t *testing.T) {
db := newTestDB(t, "people") db := newTestDB(t, "people")
defer closeDB(t, db) defer closeDB(t, db)
...@@ -361,9 +372,7 @@ func TestQueryContextWait(t *testing.T) { ...@@ -361,9 +372,7 @@ func TestQueryContextWait(t *testing.T) {
} }
// Verify closed rows connection after error condition. // Verify closed rows connection after error condition.
if n := db.numFreeConns(); n != 1 { waitForFree(t, db, 5*time.Second, 1)
t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
}
if prepares := numPrepares(t, db) - prepares0; prepares != 1 { if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares) t.Errorf("executed %d Prepare statements; want 1", prepares)
} }
...@@ -388,13 +397,7 @@ func TestTxContextWait(t *testing.T) { ...@@ -388,13 +397,7 @@ func TestTxContextWait(t *testing.T) {
t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err) t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
} }
var numFree int waitForFree(t, db, 5*time.Second, 0)
if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
numFree = db.numFreeConns()
return numFree == 0
}) {
t.Fatalf("free conns after hitting EOF = %d; want 0", numFree)
}
// Ensure the dropped connection allows more connections to be made. // Ensure the dropped connection allows more connections to be made.
// Checked on DB Close. // Checked on DB Close.
...@@ -471,9 +474,7 @@ func TestMultiResultSetQuery(t *testing.T) { ...@@ -471,9 +474,7 @@ func TestMultiResultSetQuery(t *testing.T) {
// And verify that the final rows.Next() call, which hit EOF, // And verify that the final rows.Next() call, which hit EOF,
// also closed the rows connection. // also closed the rows connection.
if n := db.numFreeConns(); n != 1 { waitForFree(t, db, 5*time.Second, 1)
t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
}
if prepares := numPrepares(t, db) - prepares0; prepares != 1 { if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares) t.Errorf("executed %d Prepare statements; want 1", prepares)
} }
...@@ -1135,6 +1136,24 @@ func TestQueryRowClosingStmt(t *testing.T) { ...@@ -1135,6 +1136,24 @@ func TestQueryRowClosingStmt(t *testing.T) {
} }
} }
var atomicRowsCloseHook atomic.Value // of func(*Rows, *error)
func init() {
rowsCloseHook = func() func(*Rows, *error) {
fn, _ := atomicRowsCloseHook.Load().(func(*Rows, *error))
return fn
}
}
func setRowsCloseHook(fn func(*Rows, *error)) {
if fn == nil {
// Can't change an atomic.Value back to nil, so set it to this
// no-op func instead.
fn = func(*Rows, *error) {}
}
atomicRowsCloseHook.Store(fn)
}
// Test issue 6651 // Test issue 6651
func TestIssue6651(t *testing.T) { func TestIssue6651(t *testing.T) {
db := newTestDB(t, "people") db := newTestDB(t, "people")
...@@ -1147,6 +1166,7 @@ func TestIssue6651(t *testing.T) { ...@@ -1147,6 +1166,7 @@ func TestIssue6651(t *testing.T) {
return fmt.Errorf(want) return fmt.Errorf(want)
} }
defer func() { rowsCursorNextHook = nil }() defer func() { rowsCursorNextHook = nil }()
err := db.QueryRow("SELECT|people|name|").Scan(&v) err := db.QueryRow("SELECT|people|name|").Scan(&v)
if err == nil || err.Error() != want { if err == nil || err.Error() != want {
t.Errorf("error = %q; want %q", err, want) t.Errorf("error = %q; want %q", err, want)
...@@ -1154,10 +1174,10 @@ func TestIssue6651(t *testing.T) { ...@@ -1154,10 +1174,10 @@ func TestIssue6651(t *testing.T) {
rowsCursorNextHook = nil rowsCursorNextHook = nil
want = "error in rows.Close" want = "error in rows.Close"
rowsCloseHook = func(rows *Rows, err *error) { setRowsCloseHook(func(rows *Rows, err *error) {
*err = fmt.Errorf(want) *err = fmt.Errorf(want)
} })
defer func() { rowsCloseHook = nil }() defer setRowsCloseHook(nil)
err = db.QueryRow("SELECT|people|name|").Scan(&v) err = db.QueryRow("SELECT|people|name|").Scan(&v)
if err == nil || err.Error() != want { if err == nil || err.Error() != want {
t.Errorf("error = %q; want %q", err, want) t.Errorf("error = %q; want %q", err, want)
...@@ -1830,7 +1850,9 @@ func TestStmtCloseDeps(t *testing.T) { ...@@ -1830,7 +1850,9 @@ func TestStmtCloseDeps(t *testing.T) {
db.dumpDeps(t) db.dumpDeps(t)
} }
if len(stmt.css) > nquery { if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
return len(stmt.css) <= nquery
}) {
t.Errorf("len(stmt.css) = %d; want <= %d", len(stmt.css), nquery) t.Errorf("len(stmt.css) = %d; want <= %d", len(stmt.css), nquery)
} }
...@@ -2576,10 +2598,10 @@ func TestIssue6081(t *testing.T) { ...@@ -2576,10 +2598,10 @@ func TestIssue6081(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
rowsCloseHook = func(rows *Rows, err *error) { setRowsCloseHook(func(rows *Rows, err *error) {
*err = driver.ErrBadConn *err = driver.ErrBadConn
} })
defer func() { rowsCloseHook = nil }() defer setRowsCloseHook(nil)
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
rows, err := stmt.Query() rows, err := stmt.Query()
if err != nil { if err != nil {
...@@ -2642,7 +2664,10 @@ func TestIssue18429(t *testing.T) { ...@@ -2642,7 +2664,10 @@ func TestIssue18429(t *testing.T) {
if err != nil { if err != nil {
return return
} }
rows, err := tx.QueryContext(ctx, "WAIT|"+qwait+"|SELECT|people|name|") // This is expected to give a cancel error many, but not all the time.
// Test failure will happen with a panic or other race condition being
// reported.
rows, _ := tx.QueryContext(ctx, "WAIT|"+qwait+"|SELECT|people|name|")
if rows != nil { if rows != nil {
rows.Close() rows.Close()
} }
...@@ -2655,6 +2680,56 @@ func TestIssue18429(t *testing.T) { ...@@ -2655,6 +2680,56 @@ func TestIssue18429(t *testing.T) {
time.Sleep(milliWait * 3 * time.Millisecond) time.Sleep(milliWait * 3 * time.Millisecond)
} }
// TestIssue18719 closes the context right before use. The sql.driverConn
// will nil out the ci on close in a lock, but if another process uses it right after
// it will panic with on the nil ref.
//
// See https://golang.org/cl/35550 .
func TestIssue18719(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tx, err := db.BeginTx(ctx, nil)
if err != nil {
t.Fatal(err)
}
hookTxGrabConn = func() {
cancel()
// Wait for the context to cancel and tx to rollback.
for tx.isDone() == false {
time.Sleep(time.Millisecond * 3)
}
}
defer func() { hookTxGrabConn = nil }()
// This call will grab the connection and cancel the context
// after it has done so. Code after must deal with the canceled state.
rows, err := tx.QueryContext(ctx, "SELECT|people|name|")
if err != nil {
rows.Close()
t.Fatalf("expected error %v but got %v", nil, err)
}
// Rows may be ignored because it will be closed when the context is canceled.
// Do not explicitly rollback. The rollback will happen from the
// canceled context.
// Wait for connections to return to pool.
var numOpen int
if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
numOpen = db.numOpenConns()
return numOpen == 0
}) {
t.Fatalf("open conns after hitting EOF = %d; want 0", numOpen)
}
}
func TestConcurrency(t *testing.T) { func TestConcurrency(t *testing.T) {
doConcurrentTest(t, new(concurrentDBQueryTest)) doConcurrentTest(t, new(concurrentDBQueryTest))
doConcurrentTest(t, new(concurrentDBExecTest)) doConcurrentTest(t, new(concurrentDBExecTest))
......
...@@ -733,7 +733,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { ...@@ -733,7 +733,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
case *ast.FuncLit: case *ast.FuncLit:
p.expr(x.Type) p.expr(x.Type)
p.adjBlock(p.distanceFrom(x.Type.Pos()), blank, x.Body) p.funcBody(p.distanceFrom(x.Type.Pos()), blank, x.Body)
case *ast.ParenExpr: case *ast.ParenExpr:
if _, hasParens := x.X.(*ast.ParenExpr); hasParens { if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
...@@ -825,6 +825,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { ...@@ -825,6 +825,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
if x.Type != nil { if x.Type != nil {
p.expr1(x.Type, token.HighestPrec, depth) p.expr1(x.Type, token.HighestPrec, depth)
} }
p.level++
p.print(x.Lbrace, token.LBRACE) p.print(x.Lbrace, token.LBRACE)
p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace) p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace)
// do not insert extra line break following a /*-style comment // do not insert extra line break following a /*-style comment
...@@ -837,6 +838,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { ...@@ -837,6 +838,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
mode |= noExtraBlank mode |= noExtraBlank
} }
p.print(mode, x.Rbrace, token.RBRACE, mode) p.print(mode, x.Rbrace, token.RBRACE, mode)
p.level--
case *ast.Ellipsis: case *ast.Ellipsis:
p.print(token.ELLIPSIS) p.print(token.ELLIPSIS)
...@@ -1557,18 +1559,23 @@ func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int { ...@@ -1557,18 +1559,23 @@ func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
return bodySize return bodySize
} }
// adjBlock prints an "adjacent" block (e.g., a for-loop or function body) following // funcBody prints a function body following a function header of given headerSize.
// a header (e.g., a for-loop control clause or function signature) of given headerSize.
// If the header's and block's size are "small enough" and the block is "simple enough", // If the header's and block's size are "small enough" and the block is "simple enough",
// the block is printed on the current line, without line breaks, spaced from the header // the block is printed on the current line, without line breaks, spaced from the header
// by sep. Otherwise the block's opening "{" is printed on the current line, followed by // by sep. Otherwise the block's opening "{" is printed on the current line, followed by
// lines for the block's statements and its closing "}". // lines for the block's statements and its closing "}".
// //
func (p *printer) adjBlock(headerSize int, sep whiteSpace, b *ast.BlockStmt) { func (p *printer) funcBody(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
if b == nil { if b == nil {
return return
} }
// save/restore composite literal nesting level
defer func(level int) {
p.level = level
}(p.level)
p.level = 0
const maxSize = 100 const maxSize = 100
if headerSize+p.bodySize(b, maxSize) <= maxSize { if headerSize+p.bodySize(b, maxSize) <= maxSize {
p.print(sep, b.Lbrace, token.LBRACE) p.print(sep, b.Lbrace, token.LBRACE)
...@@ -1613,7 +1620,7 @@ func (p *printer) funcDecl(d *ast.FuncDecl) { ...@@ -1613,7 +1620,7 @@ func (p *printer) funcDecl(d *ast.FuncDecl) {
} }
p.expr(d.Name) p.expr(d.Name)
p.signature(d.Type.Params, d.Type.Results) p.signature(d.Type.Params, d.Type.Results)
p.adjBlock(p.distanceFrom(d.Pos()), vtab, d.Body) p.funcBody(p.distanceFrom(d.Pos()), vtab, d.Body)
} }
func (p *printer) decl(decl ast.Decl) { func (p *printer) decl(decl ast.Decl) {
......
...@@ -58,6 +58,7 @@ type printer struct { ...@@ -58,6 +58,7 @@ type printer struct {
// Current state // Current state
output []byte // raw printer result output []byte // raw printer result
indent int // current indentation indent int // current indentation
level int // level == 0: outside composite literal; level > 0: inside composite literal
mode pmode // current printer mode mode pmode // current printer mode
impliedSemi bool // if set, a linebreak implies a semicolon impliedSemi bool // if set, a linebreak implies a semicolon
lastTok token.Token // last token printed (token.ILLEGAL if it's whitespace) lastTok token.Token // last token printed (token.ILLEGAL if it's whitespace)
...@@ -744,15 +745,19 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro ...@@ -744,15 +745,19 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro
// follows on the same line but is not a comma, and not a "closing" // follows on the same line but is not a comma, and not a "closing"
// token immediately following its corresponding "opening" token, // token immediately following its corresponding "opening" token,
// add an extra separator unless explicitly disabled. Use a blank // add an extra separator unless explicitly disabled. Use a blank
// as separator unless we have pending linebreaks and they are not // as separator unless we have pending linebreaks, they are not
// disabled, in which case we want a linebreak (issue 15137). // disabled, and we are outside a composite literal, in which case
// we want a linebreak (issue 15137).
// TODO(gri) This has become overly complicated. We should be able
// to track whether we're inside an expression or statement and
// use that information to decide more directly.
needsLinebreak := false needsLinebreak := false
if p.mode&noExtraBlank == 0 && if p.mode&noExtraBlank == 0 &&
last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line && last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line &&
tok != token.COMMA && tok != token.COMMA &&
(tok != token.RPAREN || p.prevOpen == token.LPAREN) && (tok != token.RPAREN || p.prevOpen == token.LPAREN) &&
(tok != token.RBRACK || p.prevOpen == token.LBRACK) { (tok != token.RBRACK || p.prevOpen == token.LBRACK) {
if p.containsLinebreak() && p.mode&noExtraLinebreak == 0 { if p.containsLinebreak() && p.mode&noExtraLinebreak == 0 && p.level == 0 {
needsLinebreak = true needsLinebreak = true
} else { } else {
p.writeByte(' ', 1) p.writeByte(' ', 1)
......
...@@ -103,3 +103,62 @@ label: ...@@ -103,3 +103,62 @@ label:
mask := uint64(1)<<c - 1 // Allocation mask mask := uint64(1)<<c - 1 // Allocation mask
used := atomic.LoadUint64(&h.used) // Current allocations used := atomic.LoadUint64(&h.used) // Current allocations
} }
// Test cases for issue 18782
var _ = [][]int{
/* a, b, c, d, e */
/* a */ {0, 0, 0, 0, 0},
/* b */ {0, 5, 4, 4, 4},
/* c */ {0, 4, 5, 4, 4},
/* d */ {0, 4, 4, 5, 4},
/* e */ {0, 4, 4, 4, 5},
}
var _ = T{ /* a */ 0}
var _ = T{ /* a */ /* b */ 0}
var _ = T{ /* a */ /* b */
/* c */ 0,
}
var _ = T{ /* a */ /* b */
/* c */
/* d */ 0,
}
var _ = T{
/* a */
/* b */ 0,
}
var _ = T{ /* a */ {}}
var _ = T{ /* a */ /* b */ {}}
var _ = T{ /* a */ /* b */
/* c */ {},
}
var _ = T{ /* a */ /* b */
/* c */
/* d */ {},
}
var _ = T{
/* a */
/* b */ {},
}
var _ = []T{
func() {
var _ = [][]int{
/* a, b, c, d, e */
/* a */ {0, 0, 0, 0, 0},
/* b */ {0, 5, 4, 4, 4},
/* c */ {0, 4, 5, 4, 4},
/* d */ {0, 4, 4, 5, 4},
/* e */ {0, 4, 4, 4, 5},
}
},
}
...@@ -103,3 +103,66 @@ label: ...@@ -103,3 +103,66 @@ label:
mask := uint64(1)<<c - 1 // Allocation mask mask := uint64(1)<<c - 1 // Allocation mask
used := atomic.LoadUint64(&h.used) // Current allocations used := atomic.LoadUint64(&h.used) // Current allocations
} }
// Test cases for issue 18782
var _ = [][]int{
/* a, b, c, d, e */
/* a */ {0, 0, 0, 0, 0},
/* b */ {0, 5, 4, 4, 4},
/* c */ {0, 4, 5, 4, 4},
/* d */ {0, 4, 4, 5, 4},
/* e */ {0, 4, 4, 4, 5},
}
var _ = T{ /* a */ 0,
}
var _ = T{ /* a */ /* b */ 0,
}
var _ = T{ /* a */ /* b */
/* c */ 0,
}
var _ = T{ /* a */ /* b */
/* c */
/* d */ 0,
}
var _ = T{
/* a */
/* b */ 0,
}
var _ = T{ /* a */ {},
}
var _ = T{ /* a */ /* b */ {},
}
var _ = T{ /* a */ /* b */
/* c */ {},
}
var _ = T{ /* a */ /* b */
/* c */
/* d */ {},
}
var _ = T{
/* a */
/* b */ {},
}
var _ = []T{
func() {
var _ = [][]int{
/* a, b, c, d, e */
/* a */ {0, 0, 0, 0, 0},
/* b */ {0, 5, 4, 4, 4},
/* c */ {0, 4, 5, 4, 4},
/* d */ {0, 4, 4, 5, 4},
/* e */ {0, 4, 4, 4, 5},
}
},
}
...@@ -413,11 +413,12 @@ func (c *Client) checkRedirect(req *Request, via []*Request) error { ...@@ -413,11 +413,12 @@ func (c *Client) checkRedirect(req *Request, via []*Request) error {
// redirectBehavior describes what should happen when the // redirectBehavior describes what should happen when the
// client encounters a 3xx status code from the server // client encounters a 3xx status code from the server
func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirectMethod string, shouldRedirect bool) { func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirectMethod string, shouldRedirect, includeBody bool) {
switch resp.StatusCode { switch resp.StatusCode {
case 301, 302, 303: case 301, 302, 303:
redirectMethod = reqMethod redirectMethod = reqMethod
shouldRedirect = true shouldRedirect = true
includeBody = false
// RFC 2616 allowed automatic redirection only with GET and // RFC 2616 allowed automatic redirection only with GET and
// HEAD requests. RFC 7231 lifts this restriction, but we still // HEAD requests. RFC 7231 lifts this restriction, but we still
...@@ -429,6 +430,7 @@ func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirect ...@@ -429,6 +430,7 @@ func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirect
case 307, 308: case 307, 308:
redirectMethod = reqMethod redirectMethod = reqMethod
shouldRedirect = true shouldRedirect = true
includeBody = true
// Treat 307 and 308 specially, since they're new in // Treat 307 and 308 specially, since they're new in
// Go 1.8, and they also require re-sending the request body. // Go 1.8, and they also require re-sending the request body.
...@@ -449,7 +451,7 @@ func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirect ...@@ -449,7 +451,7 @@ func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirect
shouldRedirect = false shouldRedirect = false
} }
} }
return redirectMethod, shouldRedirect return redirectMethod, shouldRedirect, includeBody
} }
// Do sends an HTTP request and returns an HTTP response, following // Do sends an HTTP request and returns an HTTP response, following
...@@ -492,11 +494,14 @@ func (c *Client) Do(req *Request) (*Response, error) { ...@@ -492,11 +494,14 @@ func (c *Client) Do(req *Request) (*Response, error) {
} }
var ( var (
deadline = c.deadline() deadline = c.deadline()
reqs []*Request reqs []*Request
resp *Response resp *Response
copyHeaders = c.makeHeadersCopier(req) copyHeaders = c.makeHeadersCopier(req)
// Redirect behavior:
redirectMethod string redirectMethod string
includeBody bool
) )
uerr := func(err error) error { uerr := func(err error) error {
req.closeBody() req.closeBody()
...@@ -534,7 +539,7 @@ func (c *Client) Do(req *Request) (*Response, error) { ...@@ -534,7 +539,7 @@ func (c *Client) Do(req *Request) (*Response, error) {
Cancel: ireq.Cancel, Cancel: ireq.Cancel,
ctx: ireq.ctx, ctx: ireq.ctx,
} }
if ireq.GetBody != nil { if includeBody && ireq.GetBody != nil {
req.Body, err = ireq.GetBody() req.Body, err = ireq.GetBody()
if err != nil { if err != nil {
return nil, uerr(err) return nil, uerr(err)
...@@ -598,7 +603,7 @@ func (c *Client) Do(req *Request) (*Response, error) { ...@@ -598,7 +603,7 @@ func (c *Client) Do(req *Request) (*Response, error) {
} }
var shouldRedirect bool var shouldRedirect bool
redirectMethod, shouldRedirect = redirectBehavior(req.Method, resp, reqs[0]) redirectMethod, shouldRedirect, includeBody = redirectBehavior(req.Method, resp, reqs[0])
if !shouldRedirect { if !shouldRedirect {
return resp, nil return resp, nil
} }
......
...@@ -360,25 +360,25 @@ func TestPostRedirects(t *testing.T) { ...@@ -360,25 +360,25 @@ func TestPostRedirects(t *testing.T) {
wantSegments := []string{ wantSegments := []string{
`POST / "first"`, `POST / "first"`,
`POST /?code=301&next=302 "c301"`, `POST /?code=301&next=302 "c301"`,
`GET /?code=302 "c301"`, `GET /?code=302 ""`,
`GET / "c301"`, `GET / ""`,
`POST /?code=302&next=302 "c302"`, `POST /?code=302&next=302 "c302"`,
`GET /?code=302 "c302"`, `GET /?code=302 ""`,
`GET / "c302"`, `GET / ""`,
`POST /?code=303&next=301 "c303wc301"`, `POST /?code=303&next=301 "c303wc301"`,
`GET /?code=301 "c303wc301"`, `GET /?code=301 ""`,
`GET / "c303wc301"`, `GET / ""`,
`POST /?code=304 "c304"`, `POST /?code=304 "c304"`,
`POST /?code=305 "c305"`, `POST /?code=305 "c305"`,
`POST /?code=307&next=303,308,302 "c307"`, `POST /?code=307&next=303,308,302 "c307"`,
`POST /?code=303&next=308,302 "c307"`, `POST /?code=303&next=308,302 "c307"`,
`GET /?code=308&next=302 "c307"`, `GET /?code=308&next=302 ""`,
`GET /?code=302 "c307"`, `GET /?code=302 "c307"`,
`GET / "c307"`, `GET / ""`,
`POST /?code=308&next=302,301 "c308"`, `POST /?code=308&next=302,301 "c308"`,
`POST /?code=302&next=301 "c308"`, `POST /?code=302&next=301 "c308"`,
`GET /?code=301 "c308"`, `GET /?code=301 ""`,
`GET / "c308"`, `GET / ""`,
`POST /?code=404 "c404"`, `POST /?code=404 "c404"`,
} }
want := strings.Join(wantSegments, "\n") want := strings.Join(wantSegments, "\n")
...@@ -399,20 +399,20 @@ func TestDeleteRedirects(t *testing.T) { ...@@ -399,20 +399,20 @@ func TestDeleteRedirects(t *testing.T) {
wantSegments := []string{ wantSegments := []string{
`DELETE / "first"`, `DELETE / "first"`,
`DELETE /?code=301&next=302,308 "c301"`, `DELETE /?code=301&next=302,308 "c301"`,
`GET /?code=302&next=308 "c301"`, `GET /?code=302&next=308 ""`,
`GET /?code=308 "c301"`, `GET /?code=308 ""`,
`GET / "c301"`, `GET / "c301"`,
`DELETE /?code=302&next=302 "c302"`, `DELETE /?code=302&next=302 "c302"`,
`GET /?code=302 "c302"`, `GET /?code=302 ""`,
`GET / "c302"`, `GET / ""`,
`DELETE /?code=303 "c303"`, `DELETE /?code=303 "c303"`,
`GET / "c303"`, `GET / ""`,
`DELETE /?code=307&next=301,308,303,302,304 "c307"`, `DELETE /?code=307&next=301,308,303,302,304 "c307"`,
`DELETE /?code=301&next=308,303,302,304 "c307"`, `DELETE /?code=301&next=308,303,302,304 "c307"`,
`GET /?code=308&next=303,302,304 "c307"`, `GET /?code=308&next=303,302,304 ""`,
`GET /?code=303&next=302,304 "c307"`, `GET /?code=303&next=302,304 "c307"`,
`GET /?code=302&next=304 "c307"`, `GET /?code=302&next=304 ""`,
`GET /?code=304 "c307"`, `GET /?code=304 ""`,
`DELETE /?code=308&next=307 "c308"`, `DELETE /?code=308&next=307 "c308"`,
`DELETE /?code=307 "c308"`, `DELETE /?code=307 "c308"`,
`DELETE / "c308"`, `DELETE / "c308"`,
...@@ -432,7 +432,11 @@ func testRedirectsByMethod(t *testing.T, method string, table []redirectTest, wa ...@@ -432,7 +432,11 @@ func testRedirectsByMethod(t *testing.T, method string, table []redirectTest, wa
ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
log.Lock() log.Lock()
slurp, _ := ioutil.ReadAll(r.Body) slurp, _ := ioutil.ReadAll(r.Body)
fmt.Fprintf(&log.Buffer, "%s %s %q\n", r.Method, r.RequestURI, slurp) fmt.Fprintf(&log.Buffer, "%s %s %q", r.Method, r.RequestURI, slurp)
if cl := r.Header.Get("Content-Length"); r.Method == "GET" && len(slurp) == 0 && (r.ContentLength != 0 || cl != "") {
fmt.Fprintf(&log.Buffer, " (but with body=%T, content-length = %v, %q)", r.Body, r.ContentLength, cl)
}
log.WriteByte('\n')
log.Unlock() log.Unlock()
urlQuery := r.URL.Query() urlQuery := r.URL.Query()
if v := urlQuery.Get("code"); v != "" { if v := urlQuery.Get("code"); v != "" {
...@@ -475,7 +479,24 @@ func testRedirectsByMethod(t *testing.T, method string, table []redirectTest, wa ...@@ -475,7 +479,24 @@ func testRedirectsByMethod(t *testing.T, method string, table []redirectTest, wa
want = strings.TrimSpace(want) want = strings.TrimSpace(want)
if got != want { if got != want {
t.Errorf("Log differs.\n Got:\n%s\nWant:\n%s\n", got, want) got, want, lines := removeCommonLines(got, want)
t.Errorf("Log differs after %d common lines.\n\nGot:\n%s\n\nWant:\n%s\n", lines, got, want)
}
}
func removeCommonLines(a, b string) (asuffix, bsuffix string, commonLines int) {
for {
nl := strings.IndexByte(a, '\n')
if nl < 0 {
return a, b, commonLines
}
line := a[:nl+1]
if !strings.HasPrefix(b, line) {
return a, b, commonLines
}
commonLines++
a = a[len(line):]
b = b[len(line):]
} }
} }
......
...@@ -5277,7 +5277,7 @@ func TestServerHijackGetsBackgroundByte_big(t *testing.T) { ...@@ -5277,7 +5277,7 @@ func TestServerHijackGetsBackgroundByte_big(t *testing.T) {
defer conn.Close() defer conn.Close()
slurp, err := ioutil.ReadAll(buf.Reader) slurp, err := ioutil.ReadAll(buf.Reader)
if err != nil { if err != nil {
t.Error("Copy: %v", err) t.Errorf("Copy: %v", err)
} }
allX := true allX := true
for _, v := range slurp { for _, v := range slurp {
......
...@@ -71,7 +71,9 @@ merge() { ...@@ -71,7 +71,9 @@ merge() {
elif test -f ${old}; then elif test -f ${old}; then
# The file exists in the old version. # The file exists in the old version.
if ! test -f ${libgo}; then if ! test -f ${libgo}; then
echo "merge.sh: $name: skipping: exists in old and new git, but not in libgo" if ! cmp -s ${old} ${new}; then
echo "merge.sh: $name: skipping: exists in old and new git, but not in libgo"
fi
continue continue
fi fi
if cmp -s ${old} ${libgo}; then if cmp -s ${old} ${libgo}; then
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment