[dev.boringcrypto.go1.9] all: merge go1.9.7 into dev.boringcrypto.go1.9

Change-Id: I1f9769a0c2c7c090886afa31c86c403da29d2013
This commit is contained in:
Filippo Valsorda 2018-07-02 17:50:42 -04:00
commit 0bad1bef40
43 changed files with 1048 additions and 166 deletions

View File

@ -63,6 +63,29 @@ See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.9.4">Go
1.9.4</a> milestone on our issue tracker for details. 1.9.4</a> milestone on our issue tracker for details.
</p> </p>
<p>
go1.9.5 (released 2018/03/28) includes fixes to the compiler, go command, and
<code>net/http/pprof</code> package.
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.9.5">Go
1.9.5 milestone</a> on our issue tracker for details.
</p>
<p>
go1.9.6 (released 2018/05/01) includes fixes to the compiler and go command.
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.9.6">Go
1.9.6 milestone</a> on our issue tracker for details.
</p>
<p>
go1.9.7 (released 2018/06/05) includes fixes to the go command, and the
<code>crypto/x509</code>, and <code>strings</code> packages.
In particular, it adds <a href="https://go.googlesource.com/go/+/d4e21288e444d3ffd30d1a0737f15ea3fc3b8ad9">
minimal support to the go command for the vgo transition</a>.
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.9.7">Go
1.9.7 milestone</a> on our issue tracker for details.
</p>
<h2 id="go1.8">go1.8 (released 2017/02/16)</h2> <h2 id="go1.8">go1.8 (released 2017/02/16)</h2>
<p> <p>

View File

@ -9,7 +9,7 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#define fd (100) #define fd (30)
// Tests libgo2.so, which does not export any functions. // Tests libgo2.so, which does not export any functions.
// Read a string from the file descriptor and print it. // Read a string from the file descriptor and print it.

View File

@ -21,7 +21,7 @@ import (
// that the C code can also use. // that the C code can also use.
const ( const (
fd = 100 fd = 30
) )
func init() { func init() {

View File

@ -909,6 +909,20 @@ var linuxAMD64Tests = []*asmTest{
`, `,
[]string{"b\\+40\\(SP\\)"}, []string{"b\\+40\\(SP\\)"},
}, },
{
`
func f73(a,b [3]int16) bool {
return a == b
}`,
[]string{"\tCMPL\t[A-Z]"},
},
{
`
func f74(a,b [12]int8) bool {
return a == b
}`,
[]string{"\tCMPQ\t[A-Z]", "\tCMPL\t[A-Z]"},
},
} }
var linux386Tests = []*asmTest{ var linux386Tests = []*asmTest{

View File

@ -1190,8 +1190,22 @@ func (p *noder) pragma(pos src.Pos, text string) syntax.Pragma {
} }
p.linknames = append(p.linknames, linkname{pos, f[1], f[2]}) p.linknames = append(p.linknames, linkname{pos, f[1], f[2]})
case strings.HasPrefix(text, "go:cgo_import_dynamic "):
// This is permitted for general use because Solaris
// code relies on it in golang.org/x/sys/unix and others.
fields := pragmaFields(text)
if len(fields) >= 4 {
lib := strings.Trim(fields[3], `"`)
if lib != "" && !safeArg(lib) && !isCgoGeneratedFile(pos) {
p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("invalid library name %q in cgo_import_dynamic directive", lib)})
}
p.pragcgobuf += p.pragcgo(pos, text)
return pragmaValue("go:cgo_import_dynamic")
}
fallthrough
case strings.HasPrefix(text, "go:cgo_"): case strings.HasPrefix(text, "go:cgo_"):
// For security, we disallow //go:cgo_* directives outside cgo-generated files. // For security, we disallow //go:cgo_* directives other
// than cgo_import_dynamic outside cgo-generated files.
// Exception: they are allowed in the standard library, for runtime and syscall. // Exception: they are allowed in the standard library, for runtime and syscall.
if !isCgoGeneratedFile(pos) && !compiling_std { if !isCgoGeneratedFile(pos) && !compiling_std {
p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in cgo-generated code", text)}) p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in cgo-generated code", text)})
@ -1227,6 +1241,18 @@ func isCgoGeneratedFile(pos src.Pos) bool {
return strings.HasPrefix(filepath.Base(filepath.Clean(pos.AbsFilename())), "_cgo_") return strings.HasPrefix(filepath.Base(filepath.Clean(pos.AbsFilename())), "_cgo_")
} }
// safeArg reports whether arg is a "safe" command-line argument,
// meaning that when it appears in a command-line, it probably
// doesn't have some special meaning other than its own name.
// This is copied from SafeArg in cmd/go/internal/load/pkg.go.
func safeArg(name string) bool {
if name == "" {
return false
}
c := name[0]
return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf
}
func mkname(sym *types.Sym) *Node { func mkname(sym *types.Sym) *Node {
n := oldname(sym) n := oldname(sym)
if n.Name != nil && n.Name.Pack != nil { if n.Name != nil && n.Name.Pack != nil {

View File

@ -1272,6 +1272,13 @@ opswitch:
} }
if cs != nil { if cs != nil {
cmp := Op(n.Etype) cmp := Op(n.Etype)
// Our comparison below assumes that the non-constant string
// is on the left hand side, so rewrite "" cmp x to x cmp "".
// See issue 24817.
if Isconst(n.Left, CTSTR) {
cmp = brrev(cmp)
}
// maxRewriteLen was chosen empirically. // maxRewriteLen was chosen empirically.
// It is the value that minimizes cmd/go file size // It is the value that minimizes cmd/go file size
// across most architectures. // across most architectures.
@ -3390,18 +3397,23 @@ func walkcompare(n *Node, init *Nodes) *Node {
i++ i++
remains -= t.Elem().Width remains -= t.Elem().Width
} else { } else {
elemType := t.Elem().ToUnsigned()
cmplw := nod(OINDEX, cmpl, nodintconst(int64(i))) cmplw := nod(OINDEX, cmpl, nodintconst(int64(i)))
cmplw = conv(cmplw, convType) cmplw = conv(cmplw, elemType) // convert to unsigned
cmplw = conv(cmplw, convType) // widen
cmprw := nod(OINDEX, cmpr, nodintconst(int64(i))) cmprw := nod(OINDEX, cmpr, nodintconst(int64(i)))
cmprw = conv(cmprw, elemType)
cmprw = conv(cmprw, convType) cmprw = conv(cmprw, convType)
// For code like this: uint32(s[0]) | uint32(s[1])<<8 | uint32(s[2])<<16 ... // For code like this: uint32(s[0]) | uint32(s[1])<<8 | uint32(s[2])<<16 ...
// ssa will generate a single large load. // ssa will generate a single large load.
for offset := int64(1); offset < step; offset++ { for offset := int64(1); offset < step; offset++ {
lb := nod(OINDEX, cmpl, nodintconst(int64(i+offset))) lb := nod(OINDEX, cmpl, nodintconst(int64(i+offset)))
lb = conv(lb, elemType)
lb = conv(lb, convType) lb = conv(lb, convType)
lb = nod(OLSH, lb, nodintconst(int64(8*t.Elem().Width*offset))) lb = nod(OLSH, lb, nodintconst(int64(8*t.Elem().Width*offset)))
cmplw = nod(OOR, cmplw, lb) cmplw = nod(OOR, cmplw, lb)
rb := nod(OINDEX, cmpr, nodintconst(int64(i+offset))) rb := nod(OINDEX, cmpr, nodintconst(int64(i+offset)))
rb = conv(rb, elemType)
rb = conv(rb, convType) rb = conv(rb, convType)
rb = nod(OLSH, rb, nodintconst(int64(8*t.Elem().Width*offset))) rb = nod(OLSH, rb, nodintconst(int64(8*t.Elem().Width*offset)))
cmprw = nod(OOR, cmprw, rb) cmprw = nod(OOR, cmprw, rb)

View File

@ -1499,9 +1499,9 @@
(SUBQconst (MOVQconst [d]) [c]) -> (MOVQconst [d-c]) (SUBQconst (MOVQconst [d]) [c]) -> (MOVQconst [d-c])
(SUBQconst (SUBQconst x [d]) [c]) && is32Bit(-c-d) -> (ADDQconst [-c-d] x) (SUBQconst (SUBQconst x [d]) [c]) && is32Bit(-c-d) -> (ADDQconst [-c-d] x)
(SARQconst [c] (MOVQconst [d])) -> (MOVQconst [d>>uint64(c)]) (SARQconst [c] (MOVQconst [d])) -> (MOVQconst [d>>uint64(c)])
(SARLconst [c] (MOVQconst [d])) -> (MOVQconst [d>>uint64(c)]) (SARLconst [c] (MOVQconst [d])) -> (MOVQconst [int64(int32(d))>>uint64(c)])
(SARWconst [c] (MOVQconst [d])) -> (MOVQconst [d>>uint64(c)]) (SARWconst [c] (MOVQconst [d])) -> (MOVQconst [int64(int16(d))>>uint64(c)])
(SARBconst [c] (MOVQconst [d])) -> (MOVQconst [d>>uint64(c)]) (SARBconst [c] (MOVQconst [d])) -> (MOVQconst [int64(int8(d))>>uint64(c)])
(NEGQ (MOVQconst [c])) -> (MOVQconst [-c]) (NEGQ (MOVQconst [c])) -> (MOVQconst [-c])
(NEGL (MOVLconst [c])) -> (MOVLconst [int64(int32(-c))]) (NEGL (MOVLconst [c])) -> (MOVLconst [int64(int32(-c))])
(MULQconst [c] (MOVQconst [d])) -> (MOVQconst [c*d]) (MULQconst [c] (MOVQconst [d])) -> (MOVQconst [c*d])

View File

@ -268,22 +268,22 @@ func init() {
// Note: x86 is weird, the 16 and 8 byte shifts still use all 5 bits of shift amount! // Note: x86 is weird, the 16 and 8 byte shifts still use all 5 bits of shift amount!
{name: "SHRQ", argLength: 2, reg: gp21shift, asm: "SHRQ", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> arg1, shift amount is mod 64 {name: "SHRQ", argLength: 2, reg: gp21shift, asm: "SHRQ", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> arg1, shift amount is mod 64
{name: "SHRL", argLength: 2, reg: gp21shift, asm: "SHRL", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> arg1, shift amount is mod 32 {name: "SHRL", argLength: 2, reg: gp21shift, asm: "SHRL", resultInArg0: true, clobberFlags: true}, // unsigned uint32(arg0) >> arg1, shift amount is mod 32
{name: "SHRW", argLength: 2, reg: gp21shift, asm: "SHRW", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> arg1, shift amount is mod 32 {name: "SHRW", argLength: 2, reg: gp21shift, asm: "SHRW", resultInArg0: true, clobberFlags: true}, // unsigned uint16(arg0) >> arg1, shift amount is mod 32
{name: "SHRB", argLength: 2, reg: gp21shift, asm: "SHRB", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> arg1, shift amount is mod 32 {name: "SHRB", argLength: 2, reg: gp21shift, asm: "SHRB", resultInArg0: true, clobberFlags: true}, // unsigned uint8(arg0) >> arg1, shift amount is mod 32
{name: "SHRQconst", argLength: 1, reg: gp11, asm: "SHRQ", aux: "Int8", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> auxint, shift amount 0-63 {name: "SHRQconst", argLength: 1, reg: gp11, asm: "SHRQ", aux: "Int8", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> auxint, shift amount 0-63
{name: "SHRLconst", argLength: 1, reg: gp11, asm: "SHRL", aux: "Int8", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> auxint, shift amount 0-31 {name: "SHRLconst", argLength: 1, reg: gp11, asm: "SHRL", aux: "Int8", resultInArg0: true, clobberFlags: true}, // unsigned uint32(arg0) >> auxint, shift amount 0-31
{name: "SHRWconst", argLength: 1, reg: gp11, asm: "SHRW", aux: "Int8", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> auxint, shift amount 0-15 {name: "SHRWconst", argLength: 1, reg: gp11, asm: "SHRW", aux: "Int8", resultInArg0: true, clobberFlags: true}, // unsigned uint16(arg0) >> auxint, shift amount 0-15
{name: "SHRBconst", argLength: 1, reg: gp11, asm: "SHRB", aux: "Int8", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> auxint, shift amount 0-7 {name: "SHRBconst", argLength: 1, reg: gp11, asm: "SHRB", aux: "Int8", resultInArg0: true, clobberFlags: true}, // unsigned uint8(arg0) >> auxint, shift amount 0-7
{name: "SARQ", argLength: 2, reg: gp21shift, asm: "SARQ", resultInArg0: true, clobberFlags: true}, // signed arg0 >> arg1, shift amount is mod 64 {name: "SARQ", argLength: 2, reg: gp21shift, asm: "SARQ", resultInArg0: true, clobberFlags: true}, // signed arg0 >> arg1, shift amount is mod 64
{name: "SARL", argLength: 2, reg: gp21shift, asm: "SARL", resultInArg0: true, clobberFlags: true}, // signed arg0 >> arg1, shift amount is mod 32 {name: "SARL", argLength: 2, reg: gp21shift, asm: "SARL", resultInArg0: true, clobberFlags: true}, // signed int32(arg0) >> arg1, shift amount is mod 32
{name: "SARW", argLength: 2, reg: gp21shift, asm: "SARW", resultInArg0: true, clobberFlags: true}, // signed arg0 >> arg1, shift amount is mod 32 {name: "SARW", argLength: 2, reg: gp21shift, asm: "SARW", resultInArg0: true, clobberFlags: true}, // signed int16(arg0) >> arg1, shift amount is mod 32
{name: "SARB", argLength: 2, reg: gp21shift, asm: "SARB", resultInArg0: true, clobberFlags: true}, // signed arg0 >> arg1, shift amount is mod 32 {name: "SARB", argLength: 2, reg: gp21shift, asm: "SARB", resultInArg0: true, clobberFlags: true}, // signed int8(arg0) >> arg1, shift amount is mod 32
{name: "SARQconst", argLength: 1, reg: gp11, asm: "SARQ", aux: "Int8", resultInArg0: true, clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-63 {name: "SARQconst", argLength: 1, reg: gp11, asm: "SARQ", aux: "Int8", resultInArg0: true, clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-63
{name: "SARLconst", argLength: 1, reg: gp11, asm: "SARL", aux: "Int8", resultInArg0: true, clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-31 {name: "SARLconst", argLength: 1, reg: gp11, asm: "SARL", aux: "Int8", resultInArg0: true, clobberFlags: true}, // signed int32(arg0) >> auxint, shift amount 0-31
{name: "SARWconst", argLength: 1, reg: gp11, asm: "SARW", aux: "Int8", resultInArg0: true, clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-15 {name: "SARWconst", argLength: 1, reg: gp11, asm: "SARW", aux: "Int8", resultInArg0: true, clobberFlags: true}, // signed int16(arg0) >> auxint, shift amount 0-15
{name: "SARBconst", argLength: 1, reg: gp11, asm: "SARB", aux: "Int8", resultInArg0: true, clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-7 {name: "SARBconst", argLength: 1, reg: gp11, asm: "SARB", aux: "Int8", resultInArg0: true, clobberFlags: true}, // signed int8(arg0) >> auxint, shift amount 0-7
{name: "ROLQ", argLength: 2, reg: gp21shift, asm: "ROLQ", resultInArg0: true, clobberFlags: true}, // arg0 rotate left arg1 bits. {name: "ROLQ", argLength: 2, reg: gp21shift, asm: "ROLQ", resultInArg0: true, clobberFlags: true}, // arg0 rotate left arg1 bits.
{name: "ROLL", argLength: 2, reg: gp21shift, asm: "ROLL", resultInArg0: true, clobberFlags: true}, // arg0 rotate left arg1 bits. {name: "ROLL", argLength: 2, reg: gp21shift, asm: "ROLL", resultInArg0: true, clobberFlags: true}, // arg0 rotate left arg1 bits.

View File

@ -978,7 +978,7 @@
(SUBconst (MOVDconst [d]) [c]) -> (MOVDconst [d-c]) (SUBconst (MOVDconst [d]) [c]) -> (MOVDconst [d-c])
(SUBconst (SUBconst x [d]) [c]) && is32Bit(-c-d) -> (ADDconst [-c-d] x) (SUBconst (SUBconst x [d]) [c]) && is32Bit(-c-d) -> (ADDconst [-c-d] x)
(SRADconst [c] (MOVDconst [d])) -> (MOVDconst [d>>uint64(c)]) (SRADconst [c] (MOVDconst [d])) -> (MOVDconst [d>>uint64(c)])
(SRAWconst [c] (MOVDconst [d])) -> (MOVDconst [d>>uint64(c)]) (SRAWconst [c] (MOVDconst [d])) -> (MOVDconst [int64(int32(d))>>uint64(c)])
(NEG (MOVDconst [c])) -> (MOVDconst [-c]) (NEG (MOVDconst [c])) -> (MOVDconst [-c])
(NEGW (MOVDconst [c])) -> (MOVDconst [int64(int32(-c))]) (NEGW (MOVDconst [c])) -> (MOVDconst [int64(int32(-c))])
(MULLDconst [c] (MOVDconst [d])) -> (MOVDconst [c*d]) (MULLDconst [c] (MOVDconst [d])) -> (MOVDconst [c*d])

View File

@ -295,15 +295,15 @@ func init() {
{name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int8"}, // arg0 << auxint, shift amount 0-31 {name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int8"}, // arg0 << auxint, shift amount 0-31
{name: "SRD", argLength: 2, reg: sh21, asm: "SRD"}, // unsigned arg0 >> arg1, shift amount is mod 64 {name: "SRD", argLength: 2, reg: sh21, asm: "SRD"}, // unsigned arg0 >> arg1, shift amount is mod 64
{name: "SRW", argLength: 2, reg: sh21, asm: "SRW"}, // unsigned arg0 >> arg1, shift amount is mod 32 {name: "SRW", argLength: 2, reg: sh21, asm: "SRW"}, // unsigned uint32(arg0) >> arg1, shift amount is mod 32
{name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int8"}, // unsigned arg0 >> auxint, shift amount 0-63 {name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int8"}, // unsigned arg0 >> auxint, shift amount 0-63
{name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int8"}, // unsigned arg0 >> auxint, shift amount 0-31 {name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int8"}, // unsigned uint32(arg0) >> auxint, shift amount 0-31
// Arithmetic shifts clobber flags. // Arithmetic shifts clobber flags.
{name: "SRAD", argLength: 2, reg: sh21, asm: "SRAD", clobberFlags: true}, // signed arg0 >> arg1, shift amount is mod 64 {name: "SRAD", argLength: 2, reg: sh21, asm: "SRAD", clobberFlags: true}, // signed arg0 >> arg1, shift amount is mod 64
{name: "SRAW", argLength: 2, reg: sh21, asm: "SRAW", clobberFlags: true}, // signed arg0 >> arg1, shift amount is mod 32 {name: "SRAW", argLength: 2, reg: sh21, asm: "SRAW", clobberFlags: true}, // signed int32(arg0) >> arg1, shift amount is mod 32
{name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int8", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-63 {name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int8", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-63
{name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int8", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-31 {name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int8", clobberFlags: true}, // signed int32(arg0) >> auxint, shift amount 0-31
{name: "RLLGconst", argLength: 1, reg: gp11, asm: "RLLG", aux: "Int8"}, // arg0 rotate left auxint, rotate amount 0-63 {name: "RLLGconst", argLength: 1, reg: gp11, asm: "RLLG", aux: "Int8"}, // arg0 rotate left auxint, rotate amount 0-63
{name: "RLLconst", argLength: 1, reg: gp11, asm: "RLL", aux: "Int8"}, // arg0 rotate left auxint, rotate amount 0-31 {name: "RLLconst", argLength: 1, reg: gp11, asm: "RLL", aux: "Int8"}, // arg0 rotate left auxint, rotate amount 0-31

View File

@ -33234,7 +33234,7 @@ func rewriteValueAMD64_OpAMD64SARBconst_0(v *Value) bool {
} }
// match: (SARBconst [c] (MOVQconst [d])) // match: (SARBconst [c] (MOVQconst [d]))
// cond: // cond:
// result: (MOVQconst [d>>uint64(c)]) // result: (MOVQconst [int64(int8(d))>>uint64(c)])
for { for {
c := v.AuxInt c := v.AuxInt
v_0 := v.Args[0] v_0 := v.Args[0]
@ -33243,7 +33243,7 @@ func rewriteValueAMD64_OpAMD64SARBconst_0(v *Value) bool {
} }
d := v_0.AuxInt d := v_0.AuxInt
v.reset(OpAMD64MOVQconst) v.reset(OpAMD64MOVQconst)
v.AuxInt = d >> uint64(c) v.AuxInt = int64(int8(d)) >> uint64(c)
return true return true
} }
return false return false
@ -33489,7 +33489,7 @@ func rewriteValueAMD64_OpAMD64SARLconst_0(v *Value) bool {
} }
// match: (SARLconst [c] (MOVQconst [d])) // match: (SARLconst [c] (MOVQconst [d]))
// cond: // cond:
// result: (MOVQconst [d>>uint64(c)]) // result: (MOVQconst [int64(int32(d))>>uint64(c)])
for { for {
c := v.AuxInt c := v.AuxInt
v_0 := v.Args[0] v_0 := v.Args[0]
@ -33498,7 +33498,7 @@ func rewriteValueAMD64_OpAMD64SARLconst_0(v *Value) bool {
} }
d := v_0.AuxInt d := v_0.AuxInt
v.reset(OpAMD64MOVQconst) v.reset(OpAMD64MOVQconst)
v.AuxInt = d >> uint64(c) v.AuxInt = int64(int32(d)) >> uint64(c)
return true return true
} }
return false return false
@ -33809,7 +33809,7 @@ func rewriteValueAMD64_OpAMD64SARWconst_0(v *Value) bool {
} }
// match: (SARWconst [c] (MOVQconst [d])) // match: (SARWconst [c] (MOVQconst [d]))
// cond: // cond:
// result: (MOVQconst [d>>uint64(c)]) // result: (MOVQconst [int64(int16(d))>>uint64(c)])
for { for {
c := v.AuxInt c := v.AuxInt
v_0 := v.Args[0] v_0 := v.Args[0]
@ -33818,7 +33818,7 @@ func rewriteValueAMD64_OpAMD64SARWconst_0(v *Value) bool {
} }
d := v_0.AuxInt d := v_0.AuxInt
v.reset(OpAMD64MOVQconst) v.reset(OpAMD64MOVQconst)
v.AuxInt = d >> uint64(c) v.AuxInt = int64(int16(d)) >> uint64(c)
return true return true
} }
return false return false

View File

@ -34589,7 +34589,7 @@ func rewriteValueS390X_OpS390XSRAW_0(v *Value) bool {
func rewriteValueS390X_OpS390XSRAWconst_0(v *Value) bool { func rewriteValueS390X_OpS390XSRAWconst_0(v *Value) bool {
// match: (SRAWconst [c] (MOVDconst [d])) // match: (SRAWconst [c] (MOVDconst [d]))
// cond: // cond:
// result: (MOVDconst [d>>uint64(c)]) // result: (MOVDconst [int64(int32(d))>>uint64(c)])
for { for {
c := v.AuxInt c := v.AuxInt
v_0 := v.Args[0] v_0 := v.Args[0]
@ -34598,7 +34598,7 @@ func rewriteValueS390X_OpS390XSRAWconst_0(v *Value) bool {
} }
d := v_0.AuxInt d := v_0.AuxInt
v.reset(OpS390XMOVDconst) v.reset(OpS390XMOVDconst)
v.AuxInt = d >> uint64(c) v.AuxInt = int64(int32(d)) >> uint64(c)
return true return true
} }
return false return false

View File

@ -4500,3 +4500,36 @@ func TestBadCgoDirectives(t *testing.T) {
tg.run("build", "-n", "x") tg.run("build", "-n", "x")
tg.grepStderr("-D@foo", "did not find -D@foo in commands") tg.grepStderr("-D@foo", "did not find -D@foo in commands")
} }
func TestTwoPkgConfigs(t *testing.T) {
if !canCgo {
t.Skip("no cgo")
}
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
t.Skipf("no shell scripts on %s", runtime.GOOS)
}
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.tempFile("src/x/a.go", `package x
// #cgo pkg-config: --static a
import "C"
`)
tg.tempFile("src/x/b.go", `package x
// #cgo pkg-config: --static a
import "C"
`)
tg.tempFile("pkg-config.sh", `#!/bin/sh
echo $* >>`+tg.path("pkg-config.out"))
tg.must(os.Chmod(tg.path("pkg-config.sh"), 0755))
tg.setenv("GOPATH", tg.path("."))
tg.setenv("PKG_CONFIG", tg.path("pkg-config.sh"))
tg.run("build", "x")
out, err := ioutil.ReadFile(tg.path("pkg-config.out"))
tg.must(err)
out = bytes.TrimSpace(out)
want := "--cflags --static --static -- a a\n--libs --static --static -- a a"
if !bytes.Equal(out, []byte(want)) {
t.Errorf("got %q want %q", out, want)
}
}

View File

@ -55,6 +55,13 @@ func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
continue continue
} }
if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 { if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 {
// Ignore VCS type "mod", which is new Go modules.
// This code is for old go get and must ignore the new mod lines.
// Otherwise matchGoImport will complain about two
// different metaImport lines for the same Prefix.
if f[1] == "mod" {
continue
}
imports = append(imports, metaImport{ imports = append(imports, metaImport{
Prefix: f[0], Prefix: f[0],
VCS: f[1], VCS: f[1],

View File

@ -210,7 +210,7 @@ var downloadRootCache = map[string]bool{}
// download runs the download half of the get command // download runs the download half of the get command
// for the package named by the argument. // for the package named by the argument.
func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) { func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) {
if mode&load.UseVendor != 0 { if mode&load.ResolveImport != 0 {
// Caller is responsible for expanding vendor paths. // Caller is responsible for expanding vendor paths.
panic("internal error: download mode has useVendor set") panic("internal error: download mode has useVendor set")
} }
@ -218,7 +218,7 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
if parent == nil { if parent == nil {
return load.LoadPackage(path, stk) return load.LoadPackage(path, stk)
} }
return load.LoadImport(path, parent.Dir, parent, stk, nil, mode) return load.LoadImport(path, parent.Dir, parent, stk, nil, mode|load.ResolveModule)
} }
p := load1(arg, mode) p := load1(arg, mode)
@ -347,12 +347,12 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
base.Errorf("%s", err) base.Errorf("%s", err)
continue continue
} }
// If this is a test import, apply vendor lookup now. // If this is a test import, apply module and vendor lookup now.
// We cannot pass useVendor to download, because // We cannot pass ResolveImport to download, because
// download does caching based on the value of path, // download does caching based on the value of path,
// so it must be the fully qualified path already. // so it must be the fully qualified path already.
if i >= len(p.Imports) { if i >= len(p.Imports) {
path = load.VendoredImportPath(p, path) path = load.ResolveImportPath(p, path)
} }
download(path, p, stk, 0) download(path, p, stk, 0)
} }

View File

@ -47,6 +47,20 @@ var parseMetaGoImportsTests = []struct {
{"baz/quux", "git", "http://github.com/rsc/baz/quux"}, {"baz/quux", "git", "http://github.com/rsc/baz/quux"},
}, },
}, },
{
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">`,
[]metaImport{
{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
},
},
{
`<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">
<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
[]metaImport{
{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
},
},
{ {
`<head> `<head>
<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar"> <meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">

View File

@ -767,8 +767,8 @@ func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*re
} }
} }
if !strings.Contains(mmi.RepoRoot, "://") { if err := validateRepoRootScheme(mmi.RepoRoot); err != nil {
return nil, fmt.Errorf("%s: invalid repo root %q; no scheme", urlStr, mmi.RepoRoot) return nil, fmt.Errorf("%s: invalid repo root %q: %v", urlStr, mmi.RepoRoot, err)
} }
rr := &repoRoot{ rr := &repoRoot{
vcs: vcsByCmd(mmi.VCS), vcs: vcsByCmd(mmi.VCS),
@ -782,6 +782,36 @@ func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*re
return rr, nil return rr, nil
} }
// validateRepoRootScheme returns an error if repoRoot does not seem
// to have a valid URL scheme. At this point we permit things that
// aren't valid URLs, although later, if not using -insecure, we will
// restrict repoRoots to be valid URLs. This is only because we've
// historically permitted them, and people may depend on that.
func validateRepoRootScheme(repoRoot string) error {
end := strings.Index(repoRoot, "://")
if end <= 0 {
return errors.New("no scheme")
}
// RFC 3986 section 3.1.
for i := 0; i < end; i++ {
c := repoRoot[i]
switch {
case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
// OK.
case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.':
// OK except at start.
if i == 0 {
return errors.New("invalid scheme")
}
default:
return errors.New("invalid scheme")
}
}
return nil
}
var fetchGroup singleflight.Group var fetchGroup singleflight.Group
var ( var (
fetchCacheMu sync.Mutex fetchCacheMu sync.Mutex

View File

@ -390,3 +390,46 @@ func TestMatchGoImport(t *testing.T) {
} }
} }
} }
func TestValidateRepoRootScheme(t *testing.T) {
tests := []struct {
root string
err string
}{
{
root: "",
err: "no scheme",
},
{
root: "http://",
err: "",
},
{
root: "a://",
err: "",
},
{
root: "a#://",
err: "invalid scheme",
},
{
root: "-config://",
err: "invalid scheme",
},
}
for _, test := range tests {
err := validateRepoRootScheme(test.root)
if err == nil {
if test.err != "" {
t.Errorf("validateRepoRootScheme(%q) = nil, want %q", test.root, test.err)
}
} else if test.err == "" {
if err != nil {
t.Errorf("validateRepoRootScheme(%q) = %q, want nil", test.root, test.err)
}
} else if err.Error() != test.err {
t.Errorf("validateRepoRootScheme(%q) = %q, want %q", test.root, err, test.err)
}
}
}

View File

@ -201,8 +201,8 @@ func runList(cmd *base.Command, args []string) {
for _, pkg := range loadpkgs(args) { for _, pkg := range loadpkgs(args) {
// Show vendor-expanded paths in listing // Show vendor-expanded paths in listing
pkg.TestImports = pkg.Vendored(pkg.TestImports) pkg.TestImports = pkg.Resolve(pkg.TestImports)
pkg.XTestImports = pkg.Vendored(pkg.XTestImports) pkg.XTestImports = pkg.Resolve(pkg.XTestImports)
do(&pkg.PackagePublic) do(&pkg.PackagePublic)
} }

View File

@ -6,6 +6,7 @@
package load package load
import ( import (
"bytes"
"crypto/sha1" "crypto/sha1"
"fmt" "fmt"
"go/build" "go/build"
@ -16,6 +17,7 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"sort" "sort"
"strconv"
"strings" "strings"
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
@ -122,8 +124,9 @@ func (p *Package) AllFiles() []string {
type PackageInternal struct { type PackageInternal struct {
// Unexported fields are not part of the public API. // Unexported fields are not part of the public API.
Build *build.Package Build *build.Package
Pkgdir string // overrides build.PkgDir Pkgdir string // overrides build.PkgDir
Imports []*Package Imports []*Package // this package's direct imports
RawImports []string // this package's original imports as they appear in the text of the program
Deps []*Package Deps []*Package
GoFiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths GoFiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths
SFiles []string SFiles []string
@ -169,7 +172,7 @@ func (e *NoGoError) Error() string {
return "no Go files in " + e.Package.Dir return "no Go files in " + e.Package.Dir
} }
// Vendored returns the vendor-resolved version of imports, // Resolve returns the resolved version of imports,
// which should be p.TestImports or p.XTestImports, NOT p.Imports. // which should be p.TestImports or p.XTestImports, NOT p.Imports.
// The imports in p.TestImports and p.XTestImports are not recursively // The imports in p.TestImports and p.XTestImports are not recursively
// loaded during the initial load of p, so they list the imports found in // loaded during the initial load of p, so they list the imports found in
@ -179,14 +182,14 @@ func (e *NoGoError) Error() string {
// can produce better error messages if it starts with the original paths. // can produce better error messages if it starts with the original paths.
// The initial load of p loads all the non-test imports and rewrites // The initial load of p loads all the non-test imports and rewrites
// the vendored paths, so nothing should ever call p.vendored(p.Imports). // the vendored paths, so nothing should ever call p.vendored(p.Imports).
func (p *Package) Vendored(imports []string) []string { func (p *Package) Resolve(imports []string) []string {
if len(imports) > 0 && len(p.Imports) > 0 && &imports[0] == &p.Imports[0] { if len(imports) > 0 && len(p.Imports) > 0 && &imports[0] == &p.Imports[0] {
panic("internal error: p.vendored(p.Imports) called") panic("internal error: p.Resolve(p.Imports) called")
} }
seen := make(map[string]bool) seen := make(map[string]bool)
var all []string var all []string
for _, path := range imports { for _, path := range imports {
path = VendoredImportPath(p, path) path = ResolveImportPath(p, path)
if !seen[path] { if !seen[path] {
seen[path] = true seen[path] = true
all = append(all, path) all = append(all, path)
@ -251,6 +254,7 @@ func (p *Package) copyBuild(pp *build.Package) {
// We modify p.Imports in place, so make copy now. // We modify p.Imports in place, so make copy now.
p.Imports = make([]string, len(pp.Imports)) p.Imports = make([]string, len(pp.Imports))
copy(p.Imports, pp.Imports) copy(p.Imports, pp.Imports)
p.Internal.RawImports = pp.Imports
p.TestGoFiles = pp.TestGoFiles p.TestGoFiles = pp.TestGoFiles
p.TestImports = pp.TestImports p.TestImports = pp.TestImports
p.XTestGoFiles = pp.XTestGoFiles p.XTestGoFiles = pp.XTestGoFiles
@ -386,18 +390,22 @@ func makeImportValid(r rune) rune {
// Mode flags for loadImport and download (in get.go). // Mode flags for loadImport and download (in get.go).
const ( const (
// useVendor means that loadImport should do vendor expansion // ResolveImport means that loadImport should do import path expansion.
// (provided the vendoring experiment is enabled). // That is, ResolveImport means that the import path came from
// That is, useVendor means that the import path came from // a source file and has not been expanded yet to account for
// a source file and has not been vendor-expanded yet. // vendoring or possible module adjustment.
// Every import path should be loaded initially with useVendor, // Every import path should be loaded initially with ResolveImport,
// and then the expanded version (with the /vendor/ in it) gets // and then the expanded version (for example with the /vendor/ in it)
// recorded as the canonical import path. At that point, future loads // gets recorded as the canonical import path. At that point, future loads
// of that package must not pass useVendor, because // of that package must not pass ResolveImport, because
// disallowVendor will reject direct use of paths containing /vendor/. // disallowVendor will reject direct use of paths containing /vendor/.
UseVendor = 1 << iota ResolveImport = 1 << iota
// getTestDeps is for download (part of "go get") and indicates // ResolveModule is for download (part of "go get") and indicates
// that the module adjustment should be done, but not vendor adjustment.
ResolveModule
// GetTestDeps is for download (part of "go get") and indicates
// that test dependencies should be fetched too. // that test dependencies should be fetched too.
GetTestDeps GetTestDeps
) )
@ -420,12 +428,15 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo
isLocal := build.IsLocalImport(path) isLocal := build.IsLocalImport(path)
if isLocal { if isLocal {
importPath = dirToImportPath(filepath.Join(srcDir, path)) importPath = dirToImportPath(filepath.Join(srcDir, path))
} else if mode&UseVendor != 0 { } else if mode&ResolveImport != 0 {
// We do our own vendor resolution, because we want to // We do our own path resolution, because we want to
// find out the key to use in packageCache without the // find out the key to use in packageCache without the
// overhead of repeated calls to buildContext.Import. // overhead of repeated calls to buildContext.Import.
// The code is also needed in a few other places anyway. // The code is also needed in a few other places anyway.
path = VendoredImportPath(parent, path) path = ResolveImportPath(parent, path)
importPath = path
} else if mode&ResolveModule != 0 {
path = ModuleImportPath(parent, path)
importPath = path importPath = path
} }
@ -445,7 +456,7 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo
// TODO: After Go 1, decide when to pass build.AllowBinary here. // TODO: After Go 1, decide when to pass build.AllowBinary here.
// See issue 3268 for mistakes to avoid. // See issue 3268 for mistakes to avoid.
buildMode := build.ImportComment buildMode := build.ImportComment
if mode&UseVendor == 0 || path != origPath { if mode&ResolveImport == 0 || path != origPath {
// Not vendoring, or we already found the vendored path. // Not vendoring, or we already found the vendored path.
buildMode |= build.IgnoreVendor buildMode |= build.IgnoreVendor
} }
@ -476,7 +487,7 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo
if perr := disallowInternal(srcDir, p, stk); perr != p { if perr := disallowInternal(srcDir, p, stk); perr != p {
return setErrorPos(perr, importPos) return setErrorPos(perr, importPos)
} }
if mode&UseVendor != 0 { if mode&ResolveImport != 0 {
if perr := disallowVendor(srcDir, origPath, p, stk); perr != p { if perr := disallowVendor(srcDir, origPath, p, stk); perr != p {
return setErrorPos(perr, importPos) return setErrorPos(perr, importPos)
} }
@ -535,7 +546,50 @@ func isDir(path string) bool {
return result return result
} }
// VendoredImportPath returns the expansion of path when it appears in parent. // ResolveImportPath returns the true meaning of path when it appears in parent.
// There are two different resolutions applied.
// First, there is Go 1.5 vendoring (golang.org/s/go15vendor).
// If vendor expansion doesn't trigger, then the path is also subject to
// Go 1.11 vgo legacy conversion (golang.org/issue/25069).
func ResolveImportPath(parent *Package, path string) (found string) {
found = VendoredImportPath(parent, path)
if found != path {
return found
}
return ModuleImportPath(parent, path)
}
// dirAndRoot returns the source directory and workspace root
// for the package p, guaranteeing that root is a path prefix of dir.
func dirAndRoot(p *Package) (dir, root string) {
dir = filepath.Clean(p.Dir)
root = filepath.Join(p.Root, "src")
if !hasFilePathPrefix(dir, root) || p.ImportPath != "command-line-arguments" && filepath.Join(root, p.ImportPath) != dir {
// Look for symlinks before reporting error.
dir = expandPath(dir)
root = expandPath(root)
}
if !hasFilePathPrefix(dir, root) || len(dir) <= len(root) || dir[len(root)] != filepath.Separator || p.ImportPath != "command-line-arguments" && !p.Internal.Local && filepath.Join(root, p.ImportPath) != dir {
base.Fatalf("unexpected directory layout:\n"+
" import path: %s\n"+
" root: %s\n"+
" dir: %s\n"+
" expand root: %s\n"+
" expand dir: %s\n"+
" separator: %s",
p.ImportPath,
filepath.Join(p.Root, "src"),
filepath.Clean(p.Dir),
root,
dir,
string(filepath.Separator))
}
return dir, root
}
// VendoredImportPath returns the vendor-expansion of path when it appears in parent.
// If parent is x/y/z, then path might expand to x/y/z/vendor/path, x/y/vendor/path, // If parent is x/y/z, then path might expand to x/y/z/vendor/path, x/y/vendor/path,
// x/vendor/path, vendor/path, or else stay path if none of those exist. // x/vendor/path, vendor/path, or else stay path if none of those exist.
// VendoredImportPath returns the expanded path or, if no expansion is found, the original. // VendoredImportPath returns the expanded path or, if no expansion is found, the original.
@ -544,29 +598,7 @@ func VendoredImportPath(parent *Package, path string) (found string) {
return path return path
} }
dir := filepath.Clean(parent.Dir) dir, root := dirAndRoot(parent)
root := filepath.Join(parent.Root, "src")
if !hasFilePathPrefix(dir, root) || parent.ImportPath != "command-line-arguments" && filepath.Join(root, parent.ImportPath) != dir {
// Look for symlinks before reporting error.
dir = expandPath(dir)
root = expandPath(root)
}
if !hasFilePathPrefix(dir, root) || len(dir) <= len(root) || dir[len(root)] != filepath.Separator || parent.ImportPath != "command-line-arguments" && !parent.Internal.Local && filepath.Join(root, parent.ImportPath) != dir {
base.Fatalf("unexpected directory layout:\n"+
" import path: %s\n"+
" root: %s\n"+
" dir: %s\n"+
" expand root: %s\n"+
" expand dir: %s\n"+
" separator: %s",
parent.ImportPath,
filepath.Join(parent.Root, "src"),
filepath.Clean(parent.Dir),
root,
dir,
string(filepath.Separator))
}
vpath := "vendor/" + path vpath := "vendor/" + path
for i := len(dir); i >= len(root); i-- { for i := len(dir); i >= len(root); i-- {
@ -610,6 +642,164 @@ func VendoredImportPath(parent *Package, path string) (found string) {
return path return path
} }
var (
modulePrefix = []byte("\nmodule ")
goModPathCache = make(map[string]string)
)
// goModPath returns the module path in the go.mod in dir, if any.
func goModPath(dir string) (path string) {
path, ok := goModPathCache[dir]
if ok {
return path
}
defer func() {
goModPathCache[dir] = path
}()
data, err := ioutil.ReadFile(filepath.Join(dir, "go.mod"))
if err != nil {
return ""
}
var i int
if bytes.HasPrefix(data, modulePrefix[1:]) {
i = 0
} else {
i = bytes.Index(data, modulePrefix)
if i < 0 {
return ""
}
i++
}
line := data[i:]
// Cut line at \n, drop trailing \r if present.
if j := bytes.IndexByte(line, '\n'); j >= 0 {
line = line[:j]
}
if line[len(line)-1] == '\r' {
line = line[:len(line)-1]
}
line = line[len("module "):]
// If quoted, unquote.
path = strings.TrimSpace(string(line))
if path != "" && path[0] == '"' {
s, err := strconv.Unquote(path)
if err != nil {
return ""
}
path = s
}
return path
}
// findVersionElement returns the slice indices of the final version element /vN in path.
// If there is no such element, it returns -1, -1.
func findVersionElement(path string) (i, j int) {
j = len(path)
for i = len(path) - 1; i >= 0; i-- {
if path[i] == '/' {
if isVersionElement(path[i:j]) {
return i, j
}
j = i
}
}
return -1, -1
}
// isVersionElement reports whether s is a well-formed path version element:
// v2, v3, v10, etc, but not v0, v05, v1.
func isVersionElement(s string) bool {
if len(s) < 3 || s[0] != '/' || s[1] != 'v' || s[2] == '0' || s[2] == '1' && len(s) == 3 {
return false
}
for i := 2; i < len(s); i++ {
if s[i] < '0' || '9' < s[i] {
return false
}
}
return true
}
// ModuleImportPath translates import paths found in go modules
// back down to paths that can be resolved in ordinary builds.
//
// Define “new” code as code with a go.mod file in the same directory
// or a parent directory. If an import in new code says x/y/v2/z but
// x/y/v2/z does not exist and x/y/go.mod says “module x/y/v2”,
// then go build will read the import as x/y/z instead.
// See golang.org/issue/25069.
func ModuleImportPath(parent *Package, path string) (found string) {
if parent == nil || parent.Root == "" {
return path
}
// If there are no vN elements in path, leave it alone.
// (The code below would do the same, but only after
// some other file system accesses that we can avoid
// here by returning early.)
if i, _ := findVersionElement(path); i < 0 {
return path
}
dir, root := dirAndRoot(parent)
// Consider dir and parents, up to and including root.
for i := len(dir); i >= len(root); i-- {
if i < len(dir) && dir[i] != filepath.Separator {
continue
}
if goModPath(dir[:i]) != "" {
goto HaveGoMod
}
}
// This code is not in a tree with a go.mod,
// so apply no changes to the path.
return path
HaveGoMod:
// This import is in a tree with a go.mod.
// Allow it to refer to code in GOPATH/src/x/y/z as x/y/v2/z
// if GOPATH/src/x/y/go.mod says module "x/y/v2",
// If x/y/v2/z exists, use it unmodified.
if bp, _ := cfg.BuildContext.Import(path, "", build.IgnoreVendor); bp.Dir != "" {
return path
}
// Otherwise look for a go.mod supplying a version element.
// Some version-like elements may appear in paths but not
// be module versions; we skip over those to look for module
// versions. For example the module m/v2 might have a
// package m/v2/api/v1/foo.
limit := len(path)
for limit > 0 {
i, j := findVersionElement(path[:limit])
if i < 0 {
return path
}
if bp, _ := cfg.BuildContext.Import(path[:i], "", build.IgnoreVendor); bp.Dir != "" {
if mpath := goModPath(bp.Dir); mpath != "" {
// Found a valid go.mod file, so we're stopping the search.
// If the path is m/v2/p and we found m/go.mod that says
// "module m/v2", then we return "m/p".
if mpath == path[:j] {
return path[:i] + path[j:]
}
// Otherwise just return the original path.
// We didn't find anything worth rewriting,
// and the go.mod indicates that we should
// not consider parent directories.
return path
}
}
limit = i
}
return path
}
// hasGoFiles reports whether dir contains any files with names ending in .go. // hasGoFiles reports whether dir contains any files with names ending in .go.
// For a vendor check we must exclude directories that contain no .go files. // For a vendor check we must exclude directories that contain no .go files.
// Otherwise it is not possible to vendor just a/b/c and still import the // Otherwise it is not possible to vendor just a/b/c and still import the
@ -848,23 +1038,23 @@ const (
// goTools is a map of Go program import path to install target directory. // goTools is a map of Go program import path to install target directory.
var GoTools = map[string]targetDir{ var GoTools = map[string]targetDir{
"cmd/addr2line": ToTool, "cmd/addr2line": ToTool,
"cmd/api": ToTool, "cmd/api": ToTool,
"cmd/asm": ToTool, "cmd/asm": ToTool,
"cmd/compile": ToTool, "cmd/compile": ToTool,
"cmd/cgo": ToTool, "cmd/cgo": ToTool,
"cmd/cover": ToTool, "cmd/cover": ToTool,
"cmd/dist": ToTool, "cmd/dist": ToTool,
"cmd/doc": ToTool, "cmd/doc": ToTool,
"cmd/fix": ToTool, "cmd/fix": ToTool,
"cmd/link": ToTool, "cmd/link": ToTool,
"cmd/newlink": ToTool, "cmd/newlink": ToTool,
"cmd/nm": ToTool, "cmd/nm": ToTool,
"cmd/objdump": ToTool, "cmd/objdump": ToTool,
"cmd/pack": ToTool, "cmd/pack": ToTool,
"cmd/pprof": ToTool, "cmd/pprof": ToTool,
"cmd/trace": ToTool, "cmd/trace": ToTool,
"cmd/vet": ToTool, "cmd/vet": ToTool,
"code.google.com/p/go.tools/cmd/cover": StalePath, "code.google.com/p/go.tools/cmd/cover": StalePath,
"code.google.com/p/go.tools/cmd/godoc": StalePath, "code.google.com/p/go.tools/cmd/godoc": StalePath,
"code.google.com/p/go.tools/cmd/vet": StalePath, "code.google.com/p/go.tools/cmd/vet": StalePath,
@ -1118,7 +1308,7 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) *Package
if path == "C" { if path == "C" {
continue continue
} }
p1 := LoadImport(path, p.Dir, p, stk, p.Internal.Build.ImportPos[path], UseVendor) p1 := LoadImport(path, p.Dir, p, stk, p.Internal.Build.ImportPos[path], ResolveImport)
if p.Standard && p.Error == nil && !p1.Standard && p1.Error == nil { if p.Standard && p.Error == nil && !p1.Standard && p1.Error == nil {
p.Error = &PackageError{ p.Error = &PackageError{
ImportStack: stk.Copy(), ImportStack: stk.Copy(),
@ -1224,6 +1414,7 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) *Package
// GNU binutils flagfile specifiers, sometimes called "response files"). // GNU binutils flagfile specifiers, sometimes called "response files").
// To be conservative, we reject almost any arg beginning with non-alphanumeric ASCII. // To be conservative, we reject almost any arg beginning with non-alphanumeric ASCII.
// We accept leading . _ and / as likely in file system paths. // We accept leading . _ and / as likely in file system paths.
// There is a copy of this function in cmd/compile/internal/gc/noder.go.
func SafeArg(name string) bool { func SafeArg(name string) bool {
if name == "" { if name == "" {
return false return false

View File

@ -429,7 +429,7 @@ var testMainDeps = map[string]bool{
// Dependencies for testmain. // Dependencies for testmain.
"testing": true, "testing": true,
"testing/internal/testdeps": true, "testing/internal/testdeps": true,
"os": true, "os": true,
} }
func runTest(cmd *base.Command, args []string) { func runTest(cmd *base.Command, args []string) {
@ -499,10 +499,10 @@ func runTest(cmd *base.Command, args []string) {
for _, path := range p.Imports { for _, path := range p.Imports {
deps[path] = true deps[path] = true
} }
for _, path := range p.Vendored(p.TestImports) { for _, path := range p.Resolve(p.TestImports) {
deps[path] = true deps[path] = true
} }
for _, path := range p.Vendored(p.XTestImports) { for _, path := range p.Resolve(p.XTestImports) {
deps[path] = true deps[path] = true
} }
} }
@ -715,8 +715,9 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
var imports, ximports []*load.Package var imports, ximports []*load.Package
var stk load.ImportStack var stk load.ImportStack
stk.Push(p.ImportPath + " (test)") stk.Push(p.ImportPath + " (test)")
rawTestImports := str.StringList(p.TestImports)
for i, path := range p.TestImports { for i, path := range p.TestImports {
p1 := load.LoadImport(path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], load.UseVendor) p1 := load.LoadImport(path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], load.ResolveImport)
if p1.Error != nil { if p1.Error != nil {
return nil, nil, nil, p1.Error return nil, nil, nil, p1.Error
} }
@ -742,8 +743,9 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
stk.Pop() stk.Pop()
stk.Push(p.ImportPath + "_test") stk.Push(p.ImportPath + "_test")
pxtestNeedsPtest := false pxtestNeedsPtest := false
rawXTestImports := str.StringList(p.XTestImports)
for i, path := range p.XTestImports { for i, path := range p.XTestImports {
p1 := load.LoadImport(path, p.Dir, p, &stk, p.Internal.Build.XTestImportPos[path], load.UseVendor) p1 := load.LoadImport(path, p.Dir, p, &stk, p.Internal.Build.XTestImportPos[path], load.ResolveImport)
if p1.Error != nil { if p1.Error != nil {
return nil, nil, nil, p1.Error return nil, nil, nil, p1.Error
} }
@ -810,8 +812,20 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...) ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...)
ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...) ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...)
ptest.Internal.Target = "" ptest.Internal.Target = ""
ptest.Imports = str.StringList(p.Imports, p.TestImports) // Note: The preparation of the compiler import map requires that common
ptest.Internal.Imports = append(append([]*load.Package{}, p.Internal.Imports...), imports...) // indexes in ptest.Imports, ptest.Internal.Imports, and ptest.Internal.RawImports
// all line up (but RawImports can be shorter than the others).
// That is, for 0 ≤ i < len(RawImports),
// RawImports[i] is the import string in the program text,
// Imports[i] is the expanded import string (vendoring applied or relative path expanded away),
// and Internal.Imports[i] is the corresponding *Package.
// Any implicitly added imports appear in Imports and Internal.Imports
// but not RawImports (because they were not in the source code).
// We insert TestImports, imports, and rawTestImports at the start of
// these lists to preserve the alignment.
ptest.Imports = str.StringList(p.TestImports, p.Imports)
ptest.Internal.Imports = append(imports, p.Internal.Imports...)
ptest.Internal.RawImports = str.StringList(rawTestImports, p.Internal.RawImports)
ptest.Internal.Pkgdir = testDir ptest.Internal.Pkgdir = testDir
ptest.Internal.Fake = true ptest.Internal.Fake = true
ptest.Internal.ForceLibrary = true ptest.Internal.ForceLibrary = true
@ -856,10 +870,11 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
Build: &build.Package{ Build: &build.Package{
ImportPos: p.Internal.Build.XTestImportPos, ImportPos: p.Internal.Build.XTestImportPos,
}, },
Imports: ximports, Imports: ximports,
Pkgdir: testDir, RawImports: rawXTestImports,
Fake: true, Pkgdir: testDir,
External: true, Fake: true,
External: true,
}, },
} }
if pxtestNeedsPtest { if pxtestNeedsPtest {

View File

@ -1525,16 +1525,29 @@ func splitPkgConfigOutput(out []byte) []string {
// Calls pkg-config if needed and returns the cflags/ldflags needed to build the package. // Calls pkg-config if needed and returns the cflags/ldflags needed to build the package.
func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string, err error) { func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string, err error) {
if pkgs := p.CgoPkgConfig; len(pkgs) > 0 { if pcargs := p.CgoPkgConfig; len(pcargs) > 0 {
// pkg-config permits arguments to appear anywhere in
// the command line. Move them all to the front, before --.
var pcflags []string
var pkgs []string
for _, pcarg := range pcargs {
if pcarg == "--" {
// We're going to add our own "--" argument.
} else if strings.HasPrefix(pcarg, "--") {
pcflags = append(pcflags, pcarg)
} else {
pkgs = append(pkgs, pcarg)
}
}
for _, pkg := range pkgs { for _, pkg := range pkgs {
if !load.SafeArg(pkg) { if !load.SafeArg(pkg) {
return nil, nil, fmt.Errorf("invalid pkg-config package name: %s", pkg) return nil, nil, fmt.Errorf("invalid pkg-config package name: %s", pkg)
} }
} }
var out []byte var out []byte
out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--cflags", "--", pkgs) out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--cflags", pcflags, "--", pkgs)
if err != nil { if err != nil {
b.showOutput(p.Dir, b.PkgconfigCmd()+" --cflags "+strings.Join(pkgs, " "), string(out)) b.showOutput(p.Dir, b.PkgconfigCmd()+" --cflags "+strings.Join(pcflags, " ")+strings.Join(pkgs, " "), string(out))
b.Print(err.Error() + "\n") b.Print(err.Error() + "\n")
return nil, nil, errPrintedOutput return nil, nil, errPrintedOutput
} }
@ -1544,15 +1557,15 @@ func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string,
return nil, nil, err return nil, nil, err
} }
} }
out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--libs", "--", pkgs) out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--libs", pcflags, "--", pkgs)
if err != nil { if err != nil {
b.showOutput(p.Dir, b.PkgconfigCmd()+" --libs "+strings.Join(pkgs, " "), string(out)) b.showOutput(p.Dir, b.PkgconfigCmd()+" --libs "+strings.Join(pcflags, " ")+strings.Join(pkgs, " "), string(out))
b.Print(err.Error() + "\n") b.Print(err.Error() + "\n")
return nil, nil, errPrintedOutput return nil, nil, errPrintedOutput
} }
if len(out) > 0 { if len(out) > 0 {
ldflags = strings.Fields(string(out)) ldflags = strings.Fields(string(out))
if err := checkLinkerFlags("CFLAGS", "pkg-config --cflags", ldflags); err != nil { if err := checkLinkerFlags("LDFLAGS", "pkg-config --libs", ldflags); err != nil {
return nil, nil, err return nil, nil, err
} }
} }
@ -2249,11 +2262,10 @@ func (gcToolchain) gc(b *Builder, p *load.Package, archive, obj string, asmhdr b
gcargs = append(gcargs, "-dwarf=false") gcargs = append(gcargs, "-dwarf=false")
} }
for _, path := range p.Imports { for i, raw := range p.Internal.RawImports {
if i := strings.LastIndex(path, "/vendor/"); i >= 0 { final := p.Imports[i]
gcargs = append(gcargs, "-importmap", path[i+len("/vendor/"):]+"="+path) if final != raw {
} else if strings.HasPrefix(path, "vendor/") { gcargs = append(gcargs, "-importmap", raw+"="+final)
gcargs = append(gcargs, "-importmap", path[len("vendor/"):]+"="+path)
} }
} }

View File

@ -34,38 +34,95 @@ import (
"fmt" "fmt"
"os" "os"
"regexp" "regexp"
"strings"
) )
var re = regexp.MustCompile var re = regexp.MustCompile
var validCompilerFlags = []*regexp.Regexp{ var validCompilerFlags = []*regexp.Regexp{
re(`-D([A-Za-z_].*)`), re(`-D([A-Za-z_].*)`),
re(`-F([^@\-].*)`),
re(`-I([^@\-].*)`), re(`-I([^@\-].*)`),
re(`-O`), re(`-O`),
re(`-O([^@\-].*)`), re(`-O([^@\-].*)`),
re(`-W`), re(`-W`),
re(`-W([^@,]+)`), // -Wall but not -Wa,-foo. re(`-W([^@,]+)`), // -Wall but not -Wa,-foo.
re(`-Wa,-mbig-obj`),
re(`-Wp,-D([A-Za-z_].*)`),
re(`-ansi`),
re(`-f(no-)?asynchronous-unwind-tables`),
re(`-f(no-)?blocks`),
re(`-f(no-)builtin-[a-zA-Z0-9_]*`),
re(`-f(no-)?common`),
re(`-f(no-)?constant-cfstrings`),
re(`-fdiagnostics-show-note-include-stack`),
re(`-f(no-)?eliminate-unused-debug-types`),
re(`-f(no-)?exceptions`),
re(`-f(no-)?fast-math`),
re(`-f(no-)?inline-functions`),
re(`-finput-charset=([^@\-].*)`),
re(`-f(no-)?fat-lto-objects`),
re(`-f(no-)?keep-inline-dllexport`),
re(`-f(no-)?lto`),
re(`-fmacro-backtrace-limit=(.+)`),
re(`-fmessage-length=(.+)`),
re(`-f(no-)?modules`),
re(`-f(no-)?objc-arc`), re(`-f(no-)?objc-arc`),
re(`-f(no-)?objc-nonfragile-abi`),
re(`-f(no-)?objc-legacy-dispatch`),
re(`-f(no-)?omit-frame-pointer`), re(`-f(no-)?omit-frame-pointer`),
re(`-f(no-)?openmp(-simd)?`),
re(`-f(no-)?permissive`),
re(`-f(no-)?(pic|PIC|pie|PIE)`), re(`-f(no-)?(pic|PIC|pie|PIE)`),
re(`-f(no-)?plt`),
re(`-f(no-)?rtti`),
re(`-f(no-)?split-stack`), re(`-f(no-)?split-stack`),
re(`-f(no-)?stack-(.+)`), re(`-f(no-)?stack-(.+)`),
re(`-f(no-)?strict-aliasing`), re(`-f(no-)?strict-aliasing`),
re(`-f(un)signed-char`),
re(`-f(no-)?use-linker-plugin`), // safe if -B is not used; we don't permit -B
re(`-f(no-)?visibility-inlines-hidden`),
re(`-fsanitize=(.+)`), re(`-fsanitize=(.+)`),
re(`-ftemplate-depth-(.+)`),
re(`-fvisibility=(.+)`),
re(`-g([^@\-].*)?`), re(`-g([^@\-].*)?`),
re(`-m(arch|cpu|fpu|tune)=([^@\-].*)`), re(`-m32`),
re(`-m64`),
re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`),
re(`-marm`),
re(`-mfloat-abi=([^@\-].*)`),
re(`-mfpmath=[0-9a-z,+]*`),
re(`-m(no-)?avx[0-9a-z.]*`),
re(`-m(no-)?ms-bitfields`),
re(`-m(no-)?stack-(.+)`), re(`-m(no-)?stack-(.+)`),
re(`-mmacosx-(.+)`), re(`-mmacosx-(.+)`),
re(`-mios-simulator-version-min=(.+)`),
re(`-miphoneos-version-min=(.+)`),
re(`-mnop-fun-dllimport`), re(`-mnop-fun-dllimport`),
re(`-m(no-)?sse[0-9.]*`),
re(`-mthumb(-interwork)?`),
re(`-mthreads`),
re(`-mwindows`),
re(`--param=ssp-buffer-size=[0-9]*`),
re(`-pedantic(-errors)?`),
re(`-pipe`),
re(`-pthread`), re(`-pthread`),
re(`-std=([^@\-].*)`), re(`-?-std=([^@\-].*)`),
re(`-?-stdlib=([^@\-].*)`),
re(`--sysroot=([^@\-].*)`),
re(`-w`),
re(`-x([^@\-].*)`), re(`-x([^@\-].*)`),
} }
var validCompilerFlagsWithNextArg = []string{ var validCompilerFlagsWithNextArg = []string{
"-arch",
"-D", "-D",
"-I", "-I",
"-framework", "-framework",
"-isysroot",
"-isystem",
"--sysroot",
"-target",
"-x", "-x",
} }
@ -73,29 +130,76 @@ var validLinkerFlags = []*regexp.Regexp{
re(`-F([^@\-].*)`), re(`-F([^@\-].*)`),
re(`-l([^@\-].*)`), re(`-l([^@\-].*)`),
re(`-L([^@\-].*)`), re(`-L([^@\-].*)`),
re(`-O`),
re(`-O([^@\-].*)`),
re(`-f(no-)?(pic|PIC|pie|PIE)`), re(`-f(no-)?(pic|PIC|pie|PIE)`),
re(`-f(no-)?openmp(-simd)?`),
re(`-fsanitize=([^@\-].*)`), re(`-fsanitize=([^@\-].*)`),
re(`-g([^@\-].*)?`), re(`-g([^@\-].*)?`),
re(`-m(arch|cpu|fpu|tune)=([^@\-].*)`), re(`-headerpad_max_install_names`),
re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`),
re(`-mfloat-abi=([^@\-].*)`),
re(`-mmacosx-(.+)`),
re(`-mios-simulator-version-min=(.+)`),
re(`-miphoneos-version-min=(.+)`),
re(`-mthreads`),
re(`-mwindows`),
re(`-(pic|PIC|pie|PIE)`), re(`-(pic|PIC|pie|PIE)`),
re(`-pthread`), re(`-pthread`),
re(`-rdynamic`),
re(`-shared`),
re(`-?-static([-a-z0-9+]*)`),
re(`-?-stdlib=([^@\-].*)`),
// Note that any wildcards in -Wl need to exclude comma, // Note that any wildcards in -Wl need to exclude comma,
// since -Wl splits its argument at commas and passes // since -Wl splits its argument at commas and passes
// them all to the linker uninterpreted. Allowing comma // them all to the linker uninterpreted. Allowing comma
// in a wildcard would allow tunnelling arbitrary additional // in a wildcard would allow tunnelling arbitrary additional
// linker arguments through one of these. // linker arguments through one of these.
re(`-Wl,-rpath,([^,@\-][^,]+)`), re(`-Wl,--(no-)?allow-multiple-definition`),
re(`-Wl,--(no-)?allow-shlib-undefined`),
re(`-Wl,--(no-)?as-needed`),
re(`-Wl,-Bdynamic`),
re(`-Wl,-Bstatic`),
re(`-WL,-O([^@,\-][^,]*)?`),
re(`-Wl,-d[ny]`),
re(`-Wl,--disable-new-dtags`),
re(`-Wl,-e[=,][a-zA-Z0-9]*`),
re(`-Wl,--enable-new-dtags`),
re(`-Wl,--end-group`),
re(`-Wl,-framework,[^,@\-][^,]+`),
re(`-Wl,-headerpad_max_install_names`),
re(`-Wl,--no-undefined`),
re(`-Wl,-rpath(-link)?[=,]([^,@\-][^,]+)`),
re(`-Wl,-s`),
re(`-Wl,-search_paths_first`),
re(`-Wl,-sectcreate,([^,@\-][^,]+),([^,@\-][^,]+),([^,@\-][^,]+)`),
re(`-Wl,--start-group`),
re(`-Wl,-?-static`),
re(`-Wl,-?-subsystem,(native|windows|console|posix|xbox)`),
re(`-Wl,-syslibroot[=,]([^,@\-][^,]+)`),
re(`-Wl,-undefined[=,]([^,@\-][^,]+)`),
re(`-Wl,-?-unresolved-symbols=[^,]+`),
re(`-Wl,--(no-)?warn-([^,]+)`), re(`-Wl,--(no-)?warn-([^,]+)`),
re(`-Wl,-z,(no)?execstack`),
re(`-Wl,-z,relro`),
re(`[a-zA-Z0-9_].*\.(o|obj|dll|dylib|so)`), // direct linker inputs: x.o or libfoo.so (but not -foo.o or @foo.o) re(`[a-zA-Z0-9_/].*\.(a|o|obj|dll|dylib|so)`), // direct linker inputs: x.o or libfoo.so (but not -foo.o or @foo.o)
re(`\./.*\.(a|o|obj|dll|dylib|so)`),
} }
var validLinkerFlagsWithNextArg = []string{ var validLinkerFlagsWithNextArg = []string{
"-arch",
"-F", "-F",
"-l", "-l",
"-L", "-L",
"-framework", "-framework",
"-isysroot",
"--sysroot",
"-target",
"-Wl,-framework",
"-Wl,-rpath",
"-Wl,-undefined",
} }
func checkCompilerFlags(name, source string, list []string) error { func checkCompilerFlags(name, source string, list []string) error {
@ -147,10 +251,21 @@ Args:
i++ i++
continue Args continue Args
} }
if i+1 < len(list) {
return fmt.Errorf("invalid flag in %s: %s %s", source, arg, list[i+1]) // Permit -Wl,-framework -Wl,name.
if i+1 < len(list) &&
strings.HasPrefix(arg, "-Wl,") &&
strings.HasPrefix(list[i+1], "-Wl,") &&
load.SafeArg(list[i+1][4:]) &&
!strings.Contains(list[i+1][4:], ",") {
i++
continue Args
} }
return fmt.Errorf("invalid flag in %s: %s without argument", source, arg)
if i+1 < len(list) {
return fmt.Errorf("invalid flag in %s: %s %s (see https://golang.org/s/invalidflag)", source, arg, list[i+1])
}
return fmt.Errorf("invalid flag in %s: %s without argument (see https://golang.org/s/invalidflag)", source, arg)
} }
} }
Bad: Bad:

View File

@ -12,6 +12,7 @@ import (
var goodCompilerFlags = [][]string{ var goodCompilerFlags = [][]string{
{"-DFOO"}, {"-DFOO"},
{"-Dfoo=bar"}, {"-Dfoo=bar"},
{"-F/Qt"},
{"-I/"}, {"-I/"},
{"-I/etc/passwd"}, {"-I/etc/passwd"},
{"-I."}, {"-I."},
@ -62,6 +63,8 @@ var goodCompilerFlags = [][]string{
var badCompilerFlags = [][]string{ var badCompilerFlags = [][]string{
{"-D@X"}, {"-D@X"},
{"-D-X"}, {"-D-X"},
{"-F@dir"},
{"-F-dir"},
{"-I@dir"}, {"-I@dir"},
{"-I-dir"}, {"-I-dir"},
{"-O@1"}, {"-O@1"},
@ -125,6 +128,7 @@ var goodLinkerFlags = [][]string{
{"-Wl,--no-warn-error"}, {"-Wl,--no-warn-error"},
{"foo.so"}, {"foo.so"},
{"_世界.dll"}, {"_世界.dll"},
{"./x.o"},
{"libcgosotest.dylib"}, {"libcgosotest.dylib"},
{"-F", "framework"}, {"-F", "framework"},
{"-l", "."}, {"-l", "."},
@ -132,14 +136,14 @@ var goodLinkerFlags = [][]string{
{"-l", "世界"}, {"-l", "世界"},
{"-L", "framework"}, {"-L", "framework"},
{"-framework", "Chocolate"}, {"-framework", "Chocolate"},
{"-Wl,-framework", "-Wl,Chocolate"},
{"-Wl,-framework,Chocolate"},
{"-Wl,-unresolved-symbols=ignore-all"},
} }
var badLinkerFlags = [][]string{ var badLinkerFlags = [][]string{
{"-DFOO"}, {"-DFOO"},
{"-Dfoo=bar"}, {"-Dfoo=bar"},
{"-O"},
{"-O2"},
{"-Osmall"},
{"-W"}, {"-W"},
{"-Wall"}, {"-Wall"},
{"-fobjc-arc"}, {"-fobjc-arc"},
@ -152,7 +156,6 @@ var badLinkerFlags = [][]string{
{"-fno-stack-xxx"}, {"-fno-stack-xxx"},
{"-mstack-overflow"}, {"-mstack-overflow"},
{"-mno-stack-overflow"}, {"-mno-stack-overflow"},
{"-mmacosx-version"},
{"-mnop-fun-dllimport"}, {"-mnop-fun-dllimport"},
{"-std=c99"}, {"-std=c99"},
{"-xc"}, {"-xc"},
@ -185,9 +188,14 @@ var badLinkerFlags = [][]string{
{"-l", "-foo"}, {"-l", "-foo"},
{"-framework", "-Caffeine"}, {"-framework", "-Caffeine"},
{"-framework", "@Home"}, {"-framework", "@Home"},
{"-Wl,-framework,-Caffeine"},
{"-Wl,-framework", "-Wl,@Home"},
{"-Wl,-framework", "@Home"},
{"-Wl,-framework,Chocolate,@Home"},
{"-x", "--c"}, {"-x", "--c"},
{"-x", "@obj"}, {"-x", "@obj"},
{"-Wl,-rpath,@foo"}, {"-Wl,-rpath,@foo"},
{"../x.o"},
} }
func TestCheckLinkerFlags(t *testing.T) { func TestCheckLinkerFlags(t *testing.T) {

View File

@ -0,0 +1 @@
module "new/v2"

View File

@ -0,0 +1,3 @@
package new
import _ "new/v2/p2"

View File

@ -0,0 +1,7 @@
package p1
import _ "old/p2"
import _ "new/v2"
import _ "new/v2/p2"
import _ "new/sub/v2/x/v1/y" // v2 is module, v1 is directory in module
import _ "new/sub/inner/x" // new/sub/inner/go.mod overrides new/sub/go.mod

View File

@ -0,0 +1 @@
package p2

View File

@ -0,0 +1 @@
module new/sub/v2

View File

@ -0,0 +1 @@
module new/sub/inner

View File

@ -0,0 +1 @@
package x

View File

@ -0,0 +1 @@
package y

View File

@ -0,0 +1,5 @@
package p1
import _ "old/p2"
import _ "new/p1"
import _ "new"

View File

@ -0,0 +1 @@
package p2

View File

@ -10,6 +10,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"internal/testenv" "internal/testenv"
"os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
@ -327,3 +328,75 @@ func TestVendor12156(t *testing.T) {
tg.grepStderrNot("panic", "panicked") tg.grepStderrNot("panic", "panicked")
tg.grepStderr(`cannot find package "x"`, "wrong error") tg.grepStderr(`cannot find package "x"`, "wrong error")
} }
// Module legacy support does path rewriting very similar to vendoring.
func TestModLegacy(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/modlegacy"))
tg.run("list", "-f", "{{.Imports}}", "old/p1")
tg.grepStdout("new/p1", "old/p1 should import new/p1")
tg.run("list", "-f", "{{.Imports}}", "new/p1")
tg.grepStdout("new/p2", "new/p1 should import new/p2 (not new/v2/p2)")
tg.grepStdoutNot("new/v2", "new/p1 should NOT import new/v2*")
tg.grepStdout("new/sub/x/v1/y", "new/p1 should import new/sub/x/v1/y (not new/sub/v2/x/v1/y)")
tg.grepStdoutNot("new/sub/v2", "new/p1 should NOT import new/sub/v2*")
tg.grepStdout("new/sub/inner/x", "new/p1 should import new/sub/inner/x (no rewrites)")
tg.run("build", "old/p1", "new/p1")
}
func TestModLegacyGet(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
tg := testgo(t)
defer tg.cleanup()
tg.makeTempdir()
tg.setenv("GOPATH", tg.path("d1"))
tg.run("get", "vcs-test.golang.org/git/modlegacy1-old.git/p1")
tg.run("list", "-f", "{{.Deps}}", "vcs-test.golang.org/git/modlegacy1-old.git/p1")
tg.grepStdout("new.git/p2", "old/p1 should depend on new/p2")
tg.grepStdoutNot("new.git/v2/p2", "old/p1 should NOT depend on new/v2/p2")
tg.run("build", "vcs-test.golang.org/git/modlegacy1-old.git/p1", "vcs-test.golang.org/git/modlegacy1-new.git/p1")
tg.setenv("GOPATH", tg.path("d2"))
tg.must(os.RemoveAll(tg.path("d2")))
tg.run("get", "github.com/rsc/vgotest5")
tg.run("get", "github.com/rsc/vgotest4")
tg.run("get", "github.com/myitcv/vgo_example_compat")
if testing.Short() {
return
}
tg.must(os.RemoveAll(tg.path("d2")))
tg.run("get", "github.com/rsc/vgotest4")
tg.run("get", "github.com/rsc/vgotest5")
tg.run("get", "github.com/myitcv/vgo_example_compat")
tg.must(os.RemoveAll(tg.path("d2")))
tg.run("get", "github.com/rsc/vgotest4", "github.com/rsc/vgotest5")
tg.run("get", "github.com/myitcv/vgo_example_compat")
tg.must(os.RemoveAll(tg.path("d2")))
tg.run("get", "github.com/rsc/vgotest5", "github.com/rsc/vgotest4")
tg.run("get", "github.com/myitcv/vgo_example_compat")
tg.must(os.RemoveAll(tg.path("d2")))
tg.run("get", "github.com/myitcv/vgo_example_compat")
tg.run("get", "github.com/rsc/vgotest4", "github.com/rsc/vgotest5")
pkgs := []string{"github.com/myitcv/vgo_example_compat", "github.com/rsc/vgotest4", "github.com/rsc/vgotest5"}
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
for k := 0; k < 3; k++ {
if i == j || i == k || k == j {
continue
}
tg.must(os.RemoveAll(tg.path("d2")))
tg.run("get", pkgs[i], pkgs[j], pkgs[k])
}
}
}
}

View File

@ -95,6 +95,12 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e
return nil return nil
} }
type _CertChainPolicyPara struct {
Size uint32
Flags uint32
ExtraPolicyPara unsafe.Pointer
}
// checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for // checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
// use as a certificate chain for a SSL/TLS server. // use as a certificate chain for a SSL/TLS server.
func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error { func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
@ -108,13 +114,13 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex
} }
sslPara.Size = uint32(unsafe.Sizeof(*sslPara)) sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
para := &syscall.CertChainPolicyPara{ para := &_CertChainPolicyPara{
ExtraPolicyPara: uintptr(unsafe.Pointer(sslPara)), ExtraPolicyPara: unsafe.Pointer(sslPara),
} }
para.Size = uint32(unsafe.Sizeof(*para)) para.Size = uint32(unsafe.Sizeof(*para))
status := syscall.CertChainPolicyStatus{} status := syscall.CertChainPolicyStatus{}
err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status) err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, (*syscall.CertChainPolicyPara)(unsafe.Pointer(para)), &status)
if err != nil { if err != nil {
return err return err
} }

View File

@ -80,6 +80,7 @@ func init() {
// command line, with arguments separated by NUL bytes. // command line, with arguments separated by NUL bytes.
// The package initialization registers it as /debug/pprof/cmdline. // The package initialization registers it as /debug/pprof/cmdline.
func Cmdline(w http.ResponseWriter, r *http.Request) { func Cmdline(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprintf(w, strings.Join(os.Args, "\x00")) fmt.Fprintf(w, strings.Join(os.Args, "\x00"))
} }
@ -100,33 +101,36 @@ func durationExceedsWriteTimeout(r *http.Request, seconds float64) bool {
return ok && srv.WriteTimeout != 0 && seconds >= srv.WriteTimeout.Seconds() return ok && srv.WriteTimeout != 0 && seconds >= srv.WriteTimeout.Seconds()
} }
func serveError(w http.ResponseWriter, status int, txt string) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("X-Go-Pprof", "1")
w.Header().Del("Content-Disposition")
w.WriteHeader(status)
fmt.Fprintln(w, txt)
}
// Profile responds with the pprof-formatted cpu profile. // Profile responds with the pprof-formatted cpu profile.
// The package initialization registers it as /debug/pprof/profile. // The package initialization registers it as /debug/pprof/profile.
func Profile(w http.ResponseWriter, r *http.Request) { func Profile(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Content-Type-Options", "nosniff")
sec, _ := strconv.ParseInt(r.FormValue("seconds"), 10, 64) sec, _ := strconv.ParseInt(r.FormValue("seconds"), 10, 64)
if sec == 0 { if sec == 0 {
sec = 30 sec = 30
} }
if durationExceedsWriteTimeout(r, float64(sec)) { if durationExceedsWriteTimeout(r, float64(sec)) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8") serveError(w, http.StatusBadRequest, "profile duration exceeds server's WriteTimeout")
w.Header().Set("X-Go-Pprof", "1")
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(w, "profile duration exceeds server's WriteTimeout")
return return
} }
// Set Content Type assuming StartCPUProfile will work, // Set Content Type assuming StartCPUProfile will work,
// because if it does it starts writing. // because if it does it starts writing.
w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", `attachment; filename="profile"`)
if err := pprof.StartCPUProfile(w); err != nil { if err := pprof.StartCPUProfile(w); err != nil {
// StartCPUProfile failed, so no writes yet. // StartCPUProfile failed, so no writes yet.
// Can change header back to text content serveError(w, http.StatusInternalServerError,
// and send error code. fmt.Sprintf("Could not enable CPU profiling: %s", err))
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("X-Go-Pprof", "1")
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Could not enable CPU profiling: %s\n", err)
return return
} }
sleep(w, time.Duration(sec)*time.Second) sleep(w, time.Duration(sec)*time.Second)
@ -137,29 +141,25 @@ func Profile(w http.ResponseWriter, r *http.Request) {
// Tracing lasts for duration specified in seconds GET parameter, or for 1 second if not specified. // Tracing lasts for duration specified in seconds GET parameter, or for 1 second if not specified.
// The package initialization registers it as /debug/pprof/trace. // The package initialization registers it as /debug/pprof/trace.
func Trace(w http.ResponseWriter, r *http.Request) { func Trace(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Content-Type-Options", "nosniff")
sec, err := strconv.ParseFloat(r.FormValue("seconds"), 64) sec, err := strconv.ParseFloat(r.FormValue("seconds"), 64)
if sec <= 0 || err != nil { if sec <= 0 || err != nil {
sec = 1 sec = 1
} }
if durationExceedsWriteTimeout(r, sec) { if durationExceedsWriteTimeout(r, sec) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8") serveError(w, http.StatusBadRequest, "profile duration exceeds server's WriteTimeout")
w.Header().Set("X-Go-Pprof", "1")
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(w, "profile duration exceeds server's WriteTimeout")
return return
} }
// Set Content Type assuming trace.Start will work, // Set Content Type assuming trace.Start will work,
// because if it does it starts writing. // because if it does it starts writing.
w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", `attachment; filename="trace"`)
if err := trace.Start(w); err != nil { if err := trace.Start(w); err != nil {
// trace.Start failed, so no writes yet. // trace.Start failed, so no writes yet.
// Can change header back to text content and send error code. serveError(w, http.StatusInternalServerError,
w.Header().Set("Content-Type", "text/plain; charset=utf-8") fmt.Sprintf("Could not enable tracing: %s", err))
w.Header().Set("X-Go-Pprof", "1")
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Could not enable tracing: %s\n", err)
return return
} }
sleep(w, time.Duration(sec*float64(time.Second))) sleep(w, time.Duration(sec*float64(time.Second)))
@ -170,6 +170,7 @@ func Trace(w http.ResponseWriter, r *http.Request) {
// responding with a table mapping program counters to function names. // responding with a table mapping program counters to function names.
// The package initialization registers it as /debug/pprof/symbol. // The package initialization registers it as /debug/pprof/symbol.
func Symbol(w http.ResponseWriter, r *http.Request) { func Symbol(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.Header().Set("Content-Type", "text/plain; charset=utf-8")
// We have to read the whole POST body before // We have to read the whole POST body before
@ -222,18 +223,23 @@ func Handler(name string) http.Handler {
type handler string type handler string
func (name handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (name handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.Header().Set("X-Content-Type-Options", "nosniff")
debug, _ := strconv.Atoi(r.FormValue("debug"))
p := pprof.Lookup(string(name)) p := pprof.Lookup(string(name))
if p == nil { if p == nil {
w.WriteHeader(404) serveError(w, http.StatusNotFound, "Unknown profile")
fmt.Fprintf(w, "Unknown profile: %s\n", name)
return return
} }
gc, _ := strconv.Atoi(r.FormValue("gc")) gc, _ := strconv.Atoi(r.FormValue("gc"))
if name == "heap" && gc > 0 { if name == "heap" && gc > 0 {
runtime.GC() runtime.GC()
} }
debug, _ := strconv.Atoi(r.FormValue("debug"))
if debug != 0 {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
} else {
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name))
}
p.WriteTo(w, debug) p.WriteTo(w, debug)
} }

View File

@ -0,0 +1,69 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pprof
import (
"bytes"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)
func TestHandlers(t *testing.T) {
testCases := []struct {
path string
handler http.HandlerFunc
statusCode int
contentType string
contentDisposition string
resp []byte
}{
{"/debug/pprof/<script>scripty<script>", Index, http.StatusNotFound, "text/plain; charset=utf-8", "", []byte("Unknown profile\n")},
{"/debug/pprof/heap", Index, http.StatusOK, "application/octet-stream", `attachment; filename="heap"`, nil},
{"/debug/pprof/heap?debug=1", Index, http.StatusOK, "text/plain; charset=utf-8", "", nil},
{"/debug/pprof/cmdline", Cmdline, http.StatusOK, "text/plain; charset=utf-8", "", nil},
{"/debug/pprof/profile?seconds=1", Profile, http.StatusOK, "application/octet-stream", `attachment; filename="profile"`, nil},
{"/debug/pprof/symbol", Symbol, http.StatusOK, "text/plain; charset=utf-8", "", nil},
{"/debug/pprof/trace", Trace, http.StatusOK, "application/octet-stream", `attachment; filename="trace"`, nil},
}
for _, tc := range testCases {
t.Run(tc.path, func(t *testing.T) {
req := httptest.NewRequest("GET", "http://example.com"+tc.path, nil)
w := httptest.NewRecorder()
tc.handler(w, req)
resp := w.Result()
if got, want := resp.StatusCode, tc.statusCode; got != want {
t.Errorf("status code: got %d; want %d", got, want)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("when reading response body, expected non-nil err; got %v", err)
}
if got, want := resp.Header.Get("X-Content-Type-Options"), "nosniff"; got != want {
t.Errorf("X-Content-Type-Options: got %q; want %q", got, want)
}
if got, want := resp.Header.Get("Content-Type"), tc.contentType; got != want {
t.Errorf("Content-Type: got %q; want %q", got, want)
}
if got, want := resp.Header.Get("Content-Disposition"), tc.contentDisposition; got != want {
t.Errorf("Content-Disposition: got %q; want %q", got, want)
}
if resp.StatusCode == http.StatusOK {
return
}
if got, want := resp.Header.Get("X-Go-Pprof"), "1"; got != want {
t.Errorf("X-Go-Pprof: got %q; want %q", got, want)
}
if !bytes.Equal(body, tc.resp) {
t.Errorf("response: got %q; want %q", body, tc.resp)
}
})
}
}

View File

@ -523,7 +523,7 @@ func Map(mapping func(rune) rune, s string) string {
b = make([]byte, len(s)+utf8.UTFMax) b = make([]byte, len(s)+utf8.UTFMax)
nbytes = copy(b, s[:i]) nbytes = copy(b, s[:i])
if r >= 0 { if r >= 0 {
if r <= utf8.RuneSelf { if r < utf8.RuneSelf {
b[nbytes] = byte(r) b[nbytes] = byte(r)
nbytes++ nbytes++
} else { } else {
@ -553,7 +553,7 @@ func Map(mapping func(rune) rune, s string) string {
r := mapping(c) r := mapping(c)
// common case // common case
if (0 <= r && r <= utf8.RuneSelf) && nbytes < len(b) { if (0 <= r && r < utf8.RuneSelf) && nbytes < len(b) {
b[nbytes] = byte(r) b[nbytes] = byte(r)
nbytes++ nbytes++
continue continue

View File

@ -522,6 +522,7 @@ var upperTests = []StringTest{
{"AbC123", "ABC123"}, {"AbC123", "ABC123"},
{"azAZ09_", "AZAZ09_"}, {"azAZ09_", "AZAZ09_"},
{"\u0250\u0250\u0250\u0250\u0250", "\u2C6F\u2C6F\u2C6F\u2C6F\u2C6F"}, // grows one byte per char {"\u0250\u0250\u0250\u0250\u0250", "\u2C6F\u2C6F\u2C6F\u2C6F\u2C6F"}, // grows one byte per char
{"a\u0080\U0010FFFF", "A\u0080\U0010FFFF"}, // test utf8.RuneSelf and utf8.MaxRune
} }
var lowerTests = []StringTest{ var lowerTests = []StringTest{
@ -530,6 +531,7 @@ var lowerTests = []StringTest{
{"AbC123", "abc123"}, {"AbC123", "abc123"},
{"azAZ09_", "azaz09_"}, {"azAZ09_", "azaz09_"},
{"\u2C6D\u2C6D\u2C6D\u2C6D\u2C6D", "\u0251\u0251\u0251\u0251\u0251"}, // shrinks one byte per char {"\u2C6D\u2C6D\u2C6D\u2C6D\u2C6D", "\u0251\u0251\u0251\u0251\u0251"}, // shrinks one byte per char
{"A\u0080\U0010FFFF", "a\u0080\U0010FFFF"}, // test utf8.RuneSelf and utf8.MaxRune
} }
const space = "\t\v\r\f\n\u0085\u00a0\u2000\u3000" const space = "\t\v\r\f\n\u0085\u00a0\u2000\u3000"
@ -642,6 +644,27 @@ func TestMap(t *testing.T) {
if m != expect { if m != expect {
t.Errorf("replace invalid sequence: expected %q got %q", expect, m) t.Errorf("replace invalid sequence: expected %q got %q", expect, m)
} }
// 8. Check utf8.RuneSelf and utf8.MaxRune encoding
encode := func(r rune) rune {
switch r {
case utf8.RuneSelf:
return unicode.MaxRune
case unicode.MaxRune:
return utf8.RuneSelf
}
return r
}
s := string(utf8.RuneSelf) + string(utf8.MaxRune)
r := string(utf8.MaxRune) + string(utf8.RuneSelf) // reverse of s
m = Map(encode, s)
if m != r {
t.Errorf("encoding not handled correctly: expected %q got %q", r, m)
}
m = Map(encode, r)
if m != s {
t.Errorf("encoding not handled correctly: expected %q got %q", s, m)
}
} }
func TestToUpper(t *testing.T) { runStringTests(t, ToUpper, "ToUpper", upperTests) } func TestToUpper(t *testing.T) { runStringTests(t, ToUpper, "ToUpper", upperTests) }

View File

@ -0,0 +1,42 @@
// run
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
func main() {
v1 := [2]int32{-1, 88}
v2 := [2]int32{-1, 99}
if v1 == v2 {
panic("bad comparison")
}
w1 := [2]int16{-1, 88}
w2 := [2]int16{-1, 99}
if w1 == w2 {
panic("bad comparison")
}
x1 := [4]int16{-1, 88, 88, 88}
x2 := [4]int16{-1, 99, 99, 99}
if x1 == x2 {
panic("bad comparison")
}
a1 := [2]int8{-1, 88}
a2 := [2]int8{-1, 99}
if a1 == a2 {
panic("bad comparison")
}
b1 := [4]int8{-1, 88, 88, 88}
b2 := [4]int8{-1, 99, 99, 99}
if b1 == b2 {
panic("bad comparison")
}
c1 := [8]int8{-1, 88, 88, 88, 88, 88, 88, 88}
c2 := [8]int8{-1, 99, 99, 99, 99, 99, 99, 99}
if c1 == c2 {
panic("bad comparison")
}
}

View File

@ -0,0 +1,34 @@
// run
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import "fmt"
func main() {
want := int32(0x3edae8)
got := foo(1)
if want != got {
panic(fmt.Sprintf("want %x, got %x", want, got))
}
}
func foo(a int32) int32 {
return shr1(int32(shr2(int64(0x14ff6e2207db5d1f), int(a))), 4)
}
func shr1(n int32, m int) int32 { return n >> uint(m) }
func shr2(n int64, m int) int64 {
if m < 0 {
m = -m
}
if m >= 64 {
return n
}
return n >> uint(m)
}

View File

@ -0,0 +1,64 @@
// run
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Check all ways to compare a non-constant string to the empty string.
package main
import (
"fmt"
"os"
)
var (
s = "abc"
e = ""
failed bool
)
func main() {
want(true, "" < s, `"" < s`)
want(false, s < "", `s < ""`)
want(false, "" < e, `"" < e`)
want(false, e < "", `e < ""`)
want(true, "" <= s, `"" <= s`)
want(false, s <= "", `s <= ""`)
want(true, "" <= e, `"" <= e`)
want(true, e <= "", `e <= ""`)
want(false, "" > s, `"" > s`)
want(true, s > "", `s > ""`)
want(false, "" > e, `"" > e`)
want(false, e > "", `e > ""`)
want(false, "" >= s, `"" >= s`)
want(true, s >= "", `s >= ""`)
want(true, "" >= e, `"" >= e`)
want(true, e >= "", `e >= ""`)
want(false, "" == s, `"" == s`)
want(false, s == "", `s == ""`)
want(true, "" == e, `"" == e`)
want(true, e == "", `e == ""`)
want(true, "" != s, `"" != s`)
want(true, s != "", `s != ""`)
want(false, "" != e, `"" != e`)
want(false, e != "", `e != ""`)
if failed {
os.Exit(1)
}
}
//go:noinline
func want(b bool, have bool, msg string) {
if b != have {
fmt.Println(msg)
failed = true
}
}