Compare commits

...

14 Commits

Author SHA1 Message Date
Andrew Gerrand
5464bfebe7 [release-branch.r60] doc: release.r60
««« CL 4981047 / c0fea223bc90
doc: release.r60

R=dsymonds, r, rsc
CC=golang-dev
https://golang.org/cl/4981047
»»»

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4961072
2011-09-08 12:16:42 +10:00
Andrew Gerrand
8b228654d0 [release-branch.r60] template: indirect or dereference function arguments if necessary to match the type of the formal.
««« CL 4967056 / a163a464ef59
template: indirect or dereference function arguments if necessary to match the type of the formal.
Fixes #2235

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4967056
»»»

R=dsymonds
CC=golang-dev
https://golang.org/cl/4992043
2011-09-08 11:15:38 +10:00
Andrew Gerrand
2800956682 [release-branch.r60] template: fix deadlock.
««« CL 4963054 / c581abafc917
template: fix deadlock.
No need for lexInsideAction to loop.
Fixes #2217.

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/4963054
»»»

R=dsymonds
CC=golang-dev
https://golang.org/cl/4974068
2011-09-08 11:12:11 +10:00
Andrew Gerrand
1adba86fdd [release-branch.r60] template: range over channel
««« CL 4951046 / 379452c085ab
template: range over channel

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4951046
»»»

R=dsymonds
CC=golang-dev
https://golang.org/cl/4983052
2011-09-08 11:06:22 +10:00
Andrew Gerrand
dda8180c96 [release-branch.r60] template: Grammar fix for template documentation.
««« CL 4944043 / 6577cd4b870f
template: Grammar fix for template documentation.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/4944043

»»»

R=dsymonds
CC=golang-dev
https://golang.org/cl/4978058
2011-09-08 11:02:39 +10:00
Andrew Gerrand
1fc3d23ca0 [release-branch.r60] exp/template: remove else and end nodes from public view.
««« CL 4905052 / 508513bbf607
exp/template: remove else and end nodes from public view.
They are used internally and do not appear in the final parse tree.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4905052
»»»

R=dsymonds
CC=golang-dev
https://golang.org/cl/4951069
2011-09-08 10:59:11 +10:00
Andrew Gerrand
b8f72d6110 [release-branch.r60] gc: fix pc/line table
««« CL 4938042 / 5671737303a0
gc: fix pc/line table

When a line directive was encountered we would push a new 'z' entry into
the history to indicate the start of new file attributation, and a 'Z'
entry to change line numbering.  However we didn't pop the 'z' entry, so
we were actually corrupting the history stack.  The most obvious
occurance of this was in the code that build the symbol tables for the
DWARF information - where an internal stack in the linker would overflow
when more than a few line directives were encountered in a single stack
(Issue 1878).  So now we pop the 'z' entry when we encounter the end of
the file that the directive was in, which maintains the history stack
integrity.

Also, although new 'z' entries for new files had relative paths
expanded, the same was not done for line directives.  Now we do it for
line directives also - so that the now correct DWARF information has the
full path available.

Fixes #1878.

R=rsc
CC=golang-dev
https://golang.org/cl/4938042

»»»

R=golang-dev
CC=golang-dev
https://golang.org/cl/4973069
2011-09-08 10:55:54 +10:00
Andrew Gerrand
616801951b [release-branch.r60] gofix: do not convert url in field names
««« CL 4972052 / 0f7a647510f9
gofix: do not convert url in field names

There's some ambiguity in the U{url: url} case as it could be
both a map or a struct literal, but given context it's more
likely a struct, so U{url: url_} rather than U{url_: url_}.
At least that was the case for me.

R=golang-dev, rsc, adg
CC=golang-dev
https://golang.org/cl/4972052
»»»

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4962058
2011-09-08 10:52:28 +10:00
Andrew Gerrand
e84d4effa1 [release-branch.r60] gofix: forgot to rename the URL type
««« CL 4952041 / c51a2f3f897a
gofix: forgot to rename the URL type
Fixes #2182

R=golang-dev, gri
CC=golang-dev
https://golang.org/cl/4952041
»»»

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4951068
2011-09-08 10:51:09 +10:00
Andrew Gerrand
d28f559503 [release-branch.r60] gofix: osopen: fixed=true when changing O_CREAT
««« CL 4921047 / 15cefddbe963
gofix: osopen: fixed=true when changing O_CREAT

R=rsc, r
CC=golang-dev
https://golang.org/cl/4921047

»»»

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4961071
2011-09-08 10:49:40 +10:00
Andrew Gerrand
f7e76c75f1 [release-branch.r60] goinstall: select the tag that is closest to runtime.Version
««« CL 4873057 / db63f3a1f992
goinstall: select the tag that is closest to runtime.Version

release.r50 looks for newest tag <= go.r50
weekly.2010-10-10 looks for newest tag <= go.2010-10-10

Implements behavior for hg, git, and bzr.

R=dsymonds, rsc, n13m3y3r
CC=golang-dev
https://golang.org/cl/4873057
»»»

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4974067
2011-09-08 10:48:19 +10:00
Andrew Gerrand
adc856a211 [release-branch.r60] goinstall: report lack of $GOPATH on errors
««« CL 4929047 / 7bfbbfb1ad42
goinstall: report lack of $GOPATH on errors

Fixes #2175.

R=alex.brainman, rsc, gustavo, adg
CC=golang-dev
https://golang.org/cl/4929047
»»»

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4964068
2011-09-08 10:45:04 +10:00
Andrew Gerrand
6a571c5191 [release-branch.r60] goinstall: error out with paths that end with '/'
««« CL 4807048 / ae3b2b092cf7
goinstall: error out with paths that end with '/'

R=adg, rsc, tarmigan+golang
CC=golang-dev
https://golang.org/cl/4807048

»»»

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4968077
2011-09-08 10:43:40 +10:00
Andrew Gerrand
808bb141a6 create release-branch.r60 2011-09-08 10:39:39 +10:00
21 changed files with 723 additions and 187 deletions

View File

@@ -14,6 +14,81 @@ hg pull
hg update release.r<i>NN</i>
</pre>
<h2 id="r60">r60 (released 2011/09/07)</h2>
<p>
The r60 release corresponds to
<code><a href="weekly.html#2011-08-17">weekly.2011-08-17</a></code>.
This section highlights the most significant changes in this release.
For a more detailed summary, see the
<a href="weekly.html#2011-08-17">weekly release notes</a>.
For complete information, see the
<a href="http://code.google.com/p/go/source/list?r=release-branch.r60">Mercurial change list</a>.
</p>
<h3 id="r60.lang">Language</h3>
<p>
An "else" block is now required to have braces except if the body of the "else"
is another "if". Since gofmt always puts those braces in anyway,
gofmt-formatted programs will not be affected.
To fix other programs, run gofmt.
</p>
<h3 id="r60.pkg">Packages</h3>
<p>
<a href="/pkg/http/">Package http</a>'s URL parsing and query escaping code
(such as <code>ParseURL</code> and <code>URLEscape</code>) has been moved to
the new <a href="/pkg/url/">url package</a>, with several simplifications to
the names. Client code can be updated automatically with gofix.
</p>
<p>
<a href="/pkg/image/">Package image</a> has had significant changes made to the
<code>Pix</code> field of struct types such as
<a href="/pkg/image/#RGBA">image.RGBA</a> and
<a href="/pkg/image/#NRGBA">image.NRGBA</a>.
The <a href="/pkg/image/#Image">image.Image</a> interface type has not changed,
though, and you should not need to change your code if you don't explicitly
refer to <code>Pix</code> fields. For example, if you decode a number of images
using the <a href="/pkg/image/jpeg/">image/jpeg</a> package, compose them using
<a href="/pkg/image/draw/">image/draw</a>, and then encode the result using
<a href="/pkg/img/png">image/png</a>, then your code should still work as
before.
If your code <i>does</i> refer to <code>Pix</code> fields see the
<a href="/doc/devel/weekly.html#2011-07-19">weekly.2011-07-19</a>
snapshot notes for how to update your code.
</p>
<p>
<a href="/pkg/template/">Package template</a> has been replaced with a new
templating package (formerly <code>exp/template</code>). The original template
package is still available as <a href="/pkg/old/template/">old/template</a>.
The <code>old/template</code> package is deprecated and will be removed.
The Go tree has been updated to use the new template package. We encourage
users of the old template package to switch to the new one. Code that uses
<code>template</code> or <code>exp/template</code> will need to change its
import lines to <code>"old/template"</code> or <code>"template"</code>,
respectively.
</p>
<h3 id="r60.cmd">Tools</h3>
<p>
<a href="/cmd/goinstall/">Goinstall</a> now uses a new tag selection scheme.
When downloading or updating, goinstall looks for a tag or branch with the
<code>"go."</code> prefix that corresponds to the local Go version. For Go
<code>release.r58</code> it looks for <code>go.r58</code>. For
<code>weekly.2011-06-03</code> it looks for <code>go.weekly.2011-06-03</code>.
If the specific <code>go.X</code> tag or branch is not found, it chooses the
closest earlier version. If an appropriate tag or branch is found, goinstall
uses that version of the code. Otherwise it uses the default version selected
by the version control system. Library authors are encouraged to use the
appropriate tag or branch names in their repositories to make their libraries
more accessible.
</p>
<h2 id="r59">r59 (released 2011/08/01)</h2>
<p>
@@ -323,7 +398,7 @@ Remember that gofix will handle the bulk of the rewrites
necessary for these changes to package APIs.
</p>
<h3 id="r57.tool">Tools</h3>
<h3 id="r57.cmd">Tools</h3>
<p><a href="/cmd/gofix/">Gofix</a>, a new command, is described above.</p>

View File

@@ -14,7 +14,195 @@ hg pull
hg update weekly.<i>YYYY-MM-DD</i>
</pre>
<h2 id="2011-08-17">2011-08-17</h2>
<h2 id="2011-09-07">2011-09-07</h2>
<pre>
This weekly snapshot consists of improvements and bug fixes, including fixes
for issues introduced by escape analysis changes in the gc compiler.
* build: clear execute bit from Go files (thanks Mike Rosset),
error out if problem with sudo.bash /usr/local/bin (thanks Mike Rosset).
* exp/norm: add Reader and Writer,
performance improvements of quickSpan.
* exp/regexp: bug fixes and RE2 tests.
* exp/template/html: string replacement refactoring,
tweaks to js{,_test}.go.
* gc: add -p flag to catch import cycles earlier,
fix label recursion bugs,
fix zero-length struct eval,
zero stack-allocated slice backing arrays,
* gc, ld: fix Windows file paths (thanks Hector Chu).
* go/parser: accept corner cases of signature syntax.
* gobuilder: ignore _test.go files when looking for docs, more logging.
* godoc: minor tweaks for App Engine use.
* gofix: do not convert url in field names (thanks Gustavo Niemeyer).
* gofmt: indent multi-line signatures.
* gopprof: regexp fixes (thanks Hector Chu).
* image/png: check zlib checksum during Decode.
* libmach: fix incorrect use of memset (thanks Dave Cheney).
* misc/goplay: fix template output.
* net: ParseCIDR returns IPNet instead of IPMask (thanks Mikio Hara),
sync CIDRMask code, doc.
* os: use GetFileAttributesEx to implement Stat on windows (thanks Alex Brainman).
* runtime: fix openbsd 386 raisesigpipe,
implement exception handling on windows/amd64 (thanks Hector Chu),
test for concurrent channel consumers (thanks Christopher Wedgwood).
* sort: use heapsort to bail out quicksort (thanks Ziad Hatahet).
* sync/atomic: add LoadUintptr, add Store functions.
* syscall: update routing message attributes handling (thanks Mikio Hara).
* template: fix deadlock,
indirect or dereference function arguments if necessary,
slightly simplify the test for assignability of arguments.
* url: handle ; in ParseQuery.
* websocket: fix incorrect prints found by govet (thanks Robert Hencke).
</pre>
<h2 id="2011-09-01">2011-09-01</h2>
<pre>
This weekly contains performance improvements and bug fixes.
The gc compiler now does escape analysis, which improves program performance
by placing variables on the call stack instead of the heap when it is safe to
do so.
The container/vector package is deprecated and will be removed at some point
in the future.
Other changes:
* archive/tar: support symlinks. (thanks Mike Rosset)
* big: fix nat.scan bug. (thanks Evan Shaw)
* bufio: handle a "\r\n" that straddles the buffer.
add openbsd.
avoid redundant bss declarations.
fix unused parameters.
fix windows/amd64 build with newest mingw-w64. (thanks Hector Chu)
* bytes: clarify that NewBuffer is not for beginners.
* cgo: explain how to free something.
fix GoBytes. (thanks Gustavo Niemeyer)
fixes callback for windows amd64. (thanks Wei Guangjing)
note that CString result must be freed. (thanks Gustavo Niemeyer)
* cov: remove tautological #defines. (thanks Lucio De Re)
* dashboard: yet another utf-8 fix.
* doc/codelab/wiki: fix Makefile.
* doc/progs: fix windows/amd64. (thanks Jaroslavas Počepko)
* doc/tmpltohtml: update to new template package.
* doc: emphasize that environment variables are optional.
* effective_go: convert to use tmpltohtml.
* exp/norm: reduced the size of the byte buffer used by reorderBuffer by half by reusing space when combining.
a few minor fixes to support the implementation of norm.
added implementation for []byte versions of methods.
* exp/template/html: add some tests for ">" attributes.
added handling for URL attributes.
differentiate URL-valued attributes (such as href).
reworked escapeText to recognize attr boundaries.
* exp/wingui: made compatible with windows/amd64. (thanks Jaroslavas Počepko)
* flag: add Parsed, restore Usage.
* gc: add openbsd.
escape analysis.
fix build on Plan 9. (thanks Lucio De Re)
fix div bug.
fix pc/line table. (thanks Julian Phillips)
fix some spurious leaks.
make static initialization more static.
remove JCXZ; add JCXZW, JCXZL, and JCXZQ instructions. (thanks Jaroslavas Počepko)
shuffle #includes.
simplify escape analysis recursion.
tweak and enable escape analysis.
* go/ast cleanup: base File/PackageExports on FilterFile/FilterPackage code.
adjustments to filter function.
fix ast.MergePackageFiles to collect infos about imports. (thanks Sebastien Binet)
generalize ast.FilterFile.
* go/build: add test support & use in gotest.
separate test imports out when scanning. (thanks Gustavo Niemeyer)
* go/parser: fix type switch scoping.
fix type switch scoping.
* gob: explain that Debug isn't useful unless it's compiled in.
* gobuilder: increase log limit.
* godashboard: fix utf-8 in user names.
* godoc: first step towards reducing index size.
add dummy playground.js to silence godoc warning at start-up.
added systematic throttling to indexing goroutine.
fix bug in zip.go.
support for reading/writing (splitted) index files.
use virtual file system when generating package synopses.
* gofix: forgot to rename the URL type.
osopen: fixed=true when changing O_CREAT. (thanks Tarmigan Casebolt)
* goinstall: error out with paths that end with '/'. (thanks Tarmigan Casebolt)
report lack of $GOPATH on errors. (thanks Gustavo Niemeyer)
select the tag that is closest to runtime.Version.
* gotry: add missing $. (thanks Tarmigan Casebolt)
* http: add MaxBytesReader to limit request body size.
add file protocol transport.
adjust test threshold for larger suse buffers.
delete error kludge.
on invalid request, send 400 response.
return 413 instead of 400 when the request body is too large. (thanks Dave Cheney)
support setting Transport's TLS client config.
* image/tiff: add a decode benchmark. (thanks Benny Siegert)
decoder optimization. (thanks Benny Siegert)
* image: add PalettedImage interface, and make image/png recognize it. (thanks Jaroslavas Počepko)
* io: add TeeReader. (thanks Hector Chu)
* json: add struct tag option to wrap literals in strings.
calculate Offset for Indent correctly. (thanks Jeff Hodges)
fix decode bug with struct tag names with ,opts being ignored.
* ld: handle Plan 9 ar format. (thanks Lucio De Re)
remove duplicate bss definitions.
* libmach: support reading symbols from Windows .exe for nm. (thanks Mateusz Czapliński)
* math: fix Pow10 loop. (thanks Volker Dobler)
* mime: ParseMediaType returns os.Error now, not a nil map.
media type formatter. (thanks Pascal S. de Kloe)
text charset defaults. (thanks Pascal S. de Kloe)
* misc/dashboard: remove limit for json package list.
* misc/emacs: refine label detection.
* net: add ParseMAC function. (thanks Paul Borman)
change the internal form of IPMask for IPv4. (thanks Mikio Hara)
disable "tcp" test on openbsd.
fix windows build. (thanks Alex Brainman)
join and leave a IPv6 group address, on a specific interface. (thanks Mikio Hara)
make use of IPv4len, IPv6len. (thanks Mikio Hara)
move internal string manipulation routines to parse.go. (thanks Mikio Hara)
* os: disable Hostname test on OpenBSD.
fix WNOHANG Waitmsg. (thanks Gustavo Niemeyer)
* reflect: add Value.Bytes, Value.SetBytes methods.
* rpc: add benchmark for async rpc calls.
* runtime: add openbsd 386 defs.h.
add runtime support for openbsd 386.
add runtime· prefix to showframe.
ctrlhandler for windows amd64. (thanks Wei Guangjing)
fix stack cleanup on windows/amd64. (thanks Hector Chu)
fix void warnings.
go interface to cdecl calbacks. (thanks Jaroslavas Počepko)
handle string + char literals in goc2c.
make arm work on Ubuntu Natty qemu.
openbsd thread tweaks.
simplify stack traces.
speed up cgo calls. (thanks Alex Brainman)
use cgo runtime functions to call windows syscalls. (thanks Alex Brainman)
windows/amd64 callbacks fixed and syscall fixed to allow using it in callbacks. (thanks Jaroslavas Počepko)
* strconv: put decimal on stack.
* spec: update section on Implementation Differences.
* syscall: SOMAXCONN should be 0x7fffffff at winsock2. (thanks Yasuhiro Matsumoto)
add openbsd 386.
handle RTM_NEWROUTE in ParseNetlinkRouteAttr on Linux. (thanks Albert Strasheim)
handle routing entry in ParseRoutingSockaddr on BSD variants. (thanks Mikio Hara)
openbsd amd64 syscall support.
use the vdso page on linux x86 for faster syscalls instead of int $0x80. (thanks Yuval Pavel Zholkover)
* template/parse: give if, range, and with a common representation.
* template: grammar fix for template documentation. (thanks Bill Neubauer)
range over channel.
remove else and end nodes from public view.
* test: put GOROOT/bin before all others in run.
* time: fix Plan 9 build. (thanks Fazlul Shahriar)
fix zone during windows test.
* type switches: test for pathological case.
* version.bash: update VERSION on -save if already present. (thanks Gustavo Niemeyer)
* websocket: implements new version of WebSocket protocol. (thanks Fumitoshi Ukai)
* windows/386: clean stack after syscall. (thanks Jaroslavas Počepko)
* xml: marshal "parent>child" tags correctly. (thanks Ross Light)
</pre>
<h2 id="2011-08-17">2011-08-17 (<a href="release.html#r60">base for r60</a>)</h2>
<pre>
This weekly contains some package re-shuffling. Users of the http and

View File

@@ -127,6 +127,7 @@ static void
outhist(Biobuf *b)
{
Hist *h;
int i, depth = 0;
char *p, ds[] = {'c', ':', '/', 0};
for(h = hist; h != H; h = h->link) {
@@ -156,13 +157,21 @@ outhist(Biobuf *b)
outzfile(b, p+1);
} else {
// relative name, like dir/file.go
if(h->offset == 0 && pathname && pathname[0] == '/') {
if(h->offset >= 0 && pathname && pathname[0] == '/') {
zfile(b, "/", 1); // leading "/"
outzfile(b, pathname+1);
}
outzfile(b, p);
}
}
if(h->offset > 0) {
//line directive
depth++;
}
} else if(depth > 0) {
for(i = 0; i < depth; i++)
zhist(b, h->line, h->offset);
depth = 0;
}
zhist(b, h->line, h->offset);
}

View File

@@ -31,6 +31,7 @@ func osopen(f *ast.File) bool {
// Rename O_CREAT to O_CREATE.
if expr, ok := n.(ast.Expr); ok && isPkgDot(expr, "os", "O_CREAT") {
expr.(*ast.SelectorExpr).Sel.Name = "O_CREATE"
fixed = true
return
}

View File

@@ -54,6 +54,29 @@ func f() {
os.OpenFile(a, os.O_SURPRISE|os.O_CREATE, 0666)
_ = os.O_CREATE
}
`,
},
{
Name: "osopen.1",
In: `package main
import (
"os"
)
func f() {
_ = os.O_CREAT
}
`,
Out: `package main
import (
"os"
)
func f() {
_ = os.O_CREATE
}
`,
},
}

View File

@@ -27,6 +27,7 @@ func init() {
}
var urlRenames = []struct{ in, out string }{
{"URL", "URL"},
{"ParseURL", "Parse"},
{"ParseURLReference", "ParseWithReference"},
{"ParseQuery", "ParseQuery"},
@@ -45,7 +46,12 @@ func url(f *ast.File) bool {
fixed := false
// Update URL code.
var skip interface{}
urlWalk := func(n interface{}) {
if n == skip {
skip = nil
return
}
// Is it an identifier?
if ident, ok := n.(*ast.Ident); ok && ident.Name == "url" {
ident.Name = "url_"
@@ -56,6 +62,12 @@ func url(f *ast.File) bool {
fixed = urlDoFields(fn.Params) || fixed
fixed = urlDoFields(fn.Results) || fixed
}
// U{url: ...} is likely a struct field.
if kv, ok := n.(*ast.KeyValueExpr); ok {
if ident, ok := kv.Key.(*ast.Ident); ok && ident.Name == "url" {
skip = ident
}
}
}
// Fix up URL code and add import, at most once.
@@ -63,7 +75,7 @@ func url(f *ast.File) bool {
if fixed {
return
}
walk(f, urlWalk)
walkBeforeAfter(f, urlWalk, nop)
addImport(f, "url")
fixed = true
}

View File

@@ -18,6 +18,7 @@ import (
)
func f() {
var _ http.URL
http.ParseURL(a)
http.ParseURLReference(a)
http.ParseQuery(a)
@@ -33,6 +34,7 @@ func f() {
import "url"
func f() {
var _ url.URL
url.Parse(a)
url.ParseWithReference(a)
url.ParseQuery(a)
@@ -78,10 +80,15 @@ import (
"http"
)
type U struct{ url int }
type M map[int]int
func f() {
http.ParseURL(a)
var url = 23
url, x := 45, y
_ = U{url: url}
_ = M{url + 1: url}
}
func g(url string) string {
@@ -96,10 +103,15 @@ func h() (url string) {
import "url"
type U struct{ url int }
type M map[int]int
func f() {
url.Parse(a)
var url_ = 23
url_, x := 45, y
_ = U{url: url_}
_ = M{url_ + 1: url_}
}
func g(url_ string) string {

View File

@@ -11,3 +11,9 @@ GOFILES=\
make.go\
include ../../Make.cmd
test:
gotest
testshort:
gotest -test.short

View File

@@ -94,8 +94,11 @@ attempt to fetch updates. The -u flag changes this behavior,
causing goinstall to update all remote packages encountered during
the installation.
When downloading or updating, goinstall first looks for a tag or branch
named "release". If there is one, it uses that version of the code.
When downloading or updating, goinstall looks for a tag with the "go." prefix
that corresponds to the local Go version. For Go "release.r58" it looks for a
tag named "go.r58". For "weekly.2011-06-03" it looks for "go.weekly.2011-06-03".
If the specific "go.X" tag is not found, it chooses the closest earlier version.
If an appropriate tag is found, goinstall uses that version of the code.
Otherwise it uses the default version selected by the version control
system, typically HEAD for git, tip for Mercurial.

View File

@@ -7,12 +7,15 @@
package main
import (
"bytes"
"exec"
"fmt"
"http"
"os"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
)
@@ -36,22 +39,21 @@ func maybeReportToDashboard(path string) {
// a vcs represents a version control system
// like Mercurial, Git, or Subversion.
type vcs struct {
name string
cmd string
metadir string
checkout string
clone string
update string
updateReleaseFlag string
pull string
pullForceFlag string
log string
logLimitFlag string
logReleaseFlag string
check string
protocols []string
suffix string
defaultHosts []host
name string
cmd string
metadir string
checkout string
clone string
update string
updateRevFlag string
pull string
pullForceFlag string
tagList string
tagListRe *regexp.Regexp
check string
protocols []string
suffix string
defaultHosts []host
}
type host struct {
@@ -61,20 +63,18 @@ type host struct {
}
var hg = vcs{
name: "Mercurial",
cmd: "hg",
metadir: ".hg",
checkout: "checkout",
clone: "clone",
update: "update",
updateReleaseFlag: "release",
pull: "pull",
log: "log",
logLimitFlag: "-l1",
logReleaseFlag: "-rrelease",
check: "identify",
protocols: []string{"https", "http"},
suffix: ".hg",
name: "Mercurial",
cmd: "hg",
metadir: ".hg",
checkout: "checkout",
clone: "clone",
update: "update",
pull: "pull",
tagList: "tags",
tagListRe: regexp.MustCompile("([^ ]+)[^\n]+\n"),
check: "identify",
protocols: []string{"https", "http"},
suffix: ".hg",
defaultHosts: []host{
{regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/hg)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
{regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ""},
@@ -82,20 +82,18 @@ var hg = vcs{
}
var git = vcs{
name: "Git",
cmd: "git",
metadir: ".git",
checkout: "checkout",
clone: "clone",
update: "pull",
updateReleaseFlag: "release",
pull: "fetch",
log: "show-ref",
logLimitFlag: "",
logReleaseFlag: "release",
check: "ls-remote",
protocols: []string{"git", "https", "http"},
suffix: ".git",
name: "Git",
cmd: "git",
metadir: ".git",
checkout: "checkout",
clone: "clone",
update: "pull",
pull: "fetch",
tagList: "tag",
tagListRe: regexp.MustCompile("([^\n]+)\n"),
check: "ls-remote",
protocols: []string{"git", "https", "http"},
suffix: ".git",
defaultHosts: []host{
{regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/git)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
{regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ".git"},
@@ -103,40 +101,35 @@ var git = vcs{
}
var svn = vcs{
name: "Subversion",
cmd: "svn",
metadir: ".svn",
checkout: "checkout",
clone: "checkout",
update: "update",
updateReleaseFlag: "release",
log: "log",
logLimitFlag: "-l1",
logReleaseFlag: "release",
check: "info",
protocols: []string{"https", "http", "svn"},
suffix: ".svn",
name: "Subversion",
cmd: "svn",
metadir: ".svn",
checkout: "checkout",
clone: "checkout",
update: "update",
check: "info",
protocols: []string{"https", "http", "svn"},
suffix: ".svn",
defaultHosts: []host{
{regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/svn)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
},
}
var bzr = vcs{
name: "Bazaar",
cmd: "bzr",
metadir: ".bzr",
checkout: "update",
clone: "branch",
update: "update",
updateReleaseFlag: "-rrelease",
pull: "pull",
pullForceFlag: "--overwrite",
log: "log",
logLimitFlag: "-l1",
logReleaseFlag: "-rrelease",
check: "info",
protocols: []string{"https", "http", "bzr"},
suffix: ".bzr",
name: "Bazaar",
cmd: "bzr",
metadir: ".bzr",
checkout: "update",
clone: "branch",
update: "update",
updateRevFlag: "-r",
pull: "pull",
pullForceFlag: "--overwrite",
tagList: "tags",
tagListRe: regexp.MustCompile("([^ ]+)[^\n]+\n"),
check: "info",
protocols: []string{"https", "http", "bzr"},
suffix: ".bzr",
defaultHosts: []host{
{regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`), "https", ""},
},
@@ -240,20 +233,84 @@ func download(pkg, srcDir string) (public bool, err os.Error) {
return
}
// Try to detect if a "release" tag exists. If it does, update
// to the tagged version, otherwise just update the current branch.
// NOTE(_nil): svn will always fail because it is trying to get
// the revision history of a file named "release" instead of
// looking for a commit with a release tag
// updateRepo gets a list of tags in the repository and
// checks out the tag closest to the current runtime.Version.
// If no matching tag is found, it just updates to tip.
func (v *vcs) updateRepo(dst string) os.Error {
if err := quietRun(dst, nil, v.cmd, v.log, v.logLimitFlag, v.logReleaseFlag); err == nil {
if err := run(dst, nil, v.cmd, v.checkout, v.updateReleaseFlag); err != nil {
return err
}
} else if err := run(dst, nil, v.cmd, v.update); err != nil {
if v.tagList == "" || v.tagListRe == nil {
// TODO(adg): fix for svn
return run(dst, nil, v.cmd, v.update)
}
// Get tag list.
stderr := new(bytes.Buffer)
cmd := exec.Command(v.cmd, v.tagList)
cmd.Dir = dst
cmd.Stderr = stderr
b, err := cmd.Output()
if err != nil {
errorf("%s %s: %s\n", v.cmd, v.tagList, stderr)
return err
}
return nil
var tags []string
for _, m := range v.tagListRe.FindAllStringSubmatch(string(b), -1) {
tags = append(tags, m[1])
}
// Only use the tag component of runtime.Version.
ver := strings.Split(runtime.Version(), " ")[0]
// Select tag.
if tag := selectTag(ver, tags); tag != "" {
printf("selecting revision %q\n", tag)
return run(dst, nil, v.cmd, v.checkout, v.updateRevFlag+tag)
}
// No matching tag found, make default selection.
printf("selecting tip\n")
return run(dst, nil, v.cmd, v.update)
}
// selectTag returns the closest matching tag for a given version.
// Closest means the latest one that is not after the current release.
// Version "release.rN" matches tags of the form "go.rN" (N being a decimal).
// Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD".
func selectTag(goVersion string, tags []string) (match string) {
const rPrefix = "release.r"
if strings.HasPrefix(goVersion, rPrefix) {
p := "go.r"
v, err := strconv.Atof64(goVersion[len(rPrefix):])
if err != nil {
return ""
}
var matchf float64
for _, t := range tags {
if !strings.HasPrefix(t, p) {
continue
}
tf, err := strconv.Atof64(t[len(p):])
if err != nil {
continue
}
if matchf < tf && tf <= v {
match, matchf = t, tf
}
}
}
const wPrefix = "weekly."
if strings.HasPrefix(goVersion, wPrefix) {
p := "go.weekly."
v := goVersion[len(wPrefix):]
for _, t := range tags {
if !strings.HasPrefix(t, p) {
continue
}
if match < t && t[len(p):] <= v {
match = t
}
}
}
return match
}
// checkoutRepo checks out repo into dst using vcs.

View File

@@ -71,6 +71,13 @@ func errorf(format string, args ...interface{}) {
logf(format, args...)
}
func terrorf(tree *build.Tree, format string, args ...interface{}) {
if tree != nil && tree.Goroot && os.Getenv("GOPATH") == "" {
format = strings.TrimRight(format, "\n") + " ($GOPATH not set)\n"
}
errorf(format, args...)
}
func main() {
flag.Usage = usage
flag.Parse()
@@ -156,7 +163,7 @@ func logPackage(pkg string, tree *build.Tree) (logged bool) {
name := filepath.Join(tree.Path, logfile)
fout, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
logf("%s\n", err)
terrorf(tree, "package log: %s\n", err)
return false
}
fmt.Fprintf(fout, "%s\n", pkg)
@@ -182,6 +189,12 @@ func install(pkg, parent string) {
visit[pkg] = done
}()
// Don't allow trailing '/'
if _, f := filepath.Split(pkg); f == "" {
errorf("%s should not have trailing '/'\n", pkg)
return
}
// Check whether package is local or remote.
// If remote, download or update it.
tree, pkg, err := build.FindTree(pkg)
@@ -209,7 +222,7 @@ func install(pkg, parent string) {
}
}
if err != nil {
errorf("%s: %v\n", pkg, err)
terrorf(tree, "%s: %v\n", pkg, err)
return
}
dir := filepath.Join(tree.SrcDir(), pkg)
@@ -217,11 +230,11 @@ func install(pkg, parent string) {
// Install prerequisites.
dirInfo, err := build.ScanDir(dir, parent == "")
if err != nil {
errorf("%s: %v\n", pkg, err)
terrorf(tree, "%s: %v\n", pkg, err)
return
}
if len(dirInfo.GoFiles)+len(dirInfo.CgoFiles) == 0 {
errorf("%s: package has no files\n", pkg)
terrorf(tree, "%s: package has no files\n", pkg)
return
}
for _, p := range dirInfo.Imports {
@@ -237,13 +250,13 @@ func install(pkg, parent string) {
if *useMake {
err := domake(dir, pkg, tree, dirInfo.IsCommand())
if err != nil {
errorf("%s: install: %v\n", pkg, err)
terrorf(tree, "%s: install: %v\n", pkg, err)
return
}
} else {
script, err := build.Build(tree, pkg, dirInfo)
if err != nil {
errorf("%s: install: %v\n", pkg, err)
terrorf(tree, "%s: install: %v\n", pkg, err)
return
}
if *nuke {
@@ -257,7 +270,7 @@ func install(pkg, parent string) {
if script.Stale() {
printf("%s: install\n", pkg)
if err := script.Run(); err != nil {
errorf("%s: install: %v\n", pkg, err)
terrorf(tree, "%s: install: %v\n", pkg, err)
return
}
} else {

View File

@@ -0,0 +1,73 @@
// Copyright 2011 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 "testing"
var selectTagTestTags = []string{
"go.r58",
"go.r58.1",
"go.r59",
"go.r59.1",
"go.r61",
"go.r61.1",
"go.weekly.2010-01-02",
"go.weekly.2011-10-12",
"go.weekly.2011-10-12.1",
"go.weekly.2011-10-14",
"go.weekly.2011-11-01",
// these should be ignored:
"release.r59",
"release.r59.1",
"release",
"weekly.2011-10-12",
"weekly.2011-10-12.1",
"weekly",
"foo",
"bar",
"go.f00",
"go!r60",
"go.1999-01-01",
}
var selectTagTests = []struct {
version string
selected string
}{
{"release.r57", ""},
{"release.r58.2", "go.r58.1"},
{"release.r59", "go.r59"},
{"release.r59.1", "go.r59.1"},
{"release.r60", "go.r59.1"},
{"release.r60.1", "go.r59.1"},
{"release.r61", "go.r61"},
{"release.r66", "go.r61.1"},
{"weekly.2010-01-01", ""},
{"weekly.2010-01-02", "go.weekly.2010-01-02"},
{"weekly.2010-01-02.1", "go.weekly.2010-01-02"},
{"weekly.2010-01-03", "go.weekly.2010-01-02"},
{"weekly.2011-10-12", "go.weekly.2011-10-12"},
{"weekly.2011-10-12.1", "go.weekly.2011-10-12.1"},
{"weekly.2011-10-13", "go.weekly.2011-10-12.1"},
{"weekly.2011-10-14", "go.weekly.2011-10-14"},
{"weekly.2011-10-14.1", "go.weekly.2011-10-14"},
{"weekly.2011-11-01", "go.weekly.2011-11-01"},
{"weekly.2014-01-01", "go.weekly.2011-11-01"},
{"weekly.3000-01-01", "go.weekly.2011-11-01"},
// faulty versions:
{"release.f00", ""},
{"weekly.1999-01-01", ""},
{"junk", ""},
{"", ""},
}
func TestSelectTag(t *testing.T) {
for _, c := range selectTagTests {
selected := selectTag(c.version, selectTagTestTags)
if selected != c.selected {
t.Errorf("selectTag(%q) = %q, want %q", c.version, selected, c.selected)
}
}
}

View File

@@ -240,7 +240,6 @@ NOTEST+=\
../cmd/cgo\
../cmd/ebnflint\
../cmd/godoc\
../cmd/goinstall\
../cmd/gotest\
../cmd/goyacc\
../cmd/hgpatch\

View File

@@ -294,7 +294,7 @@ This defines two templates, T1 and T2, and a third T3 that invokes the other two
when it is executed.
The second way to build a template set is to use Set's Add method to add a
parsed template to a set. A template may be bound at most one set. If it's
parsed template to a set. A template may be bound to at most one set. If it's
necessary to have a template in multiple sets, the template definition must be
parsed multiple times to create distinct *Template values.

View File

@@ -196,23 +196,25 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
val, _ := indirect(s.evalPipeline(dot, r.Pipe))
// mark top of stack before any variables in the body are pushed.
mark := s.mark()
oneIteration := func(index, elem reflect.Value) {
// Set top var (lexically the second if there are two) to the element.
if len(r.Pipe.Decl) > 0 {
s.setVar(1, elem)
}
// Set next var (lexically the first if there are two) to the index.
if len(r.Pipe.Decl) > 1 {
s.setVar(2, index)
}
s.walk(elem, r.List)
s.pop(mark)
}
switch val.Kind() {
case reflect.Array, reflect.Slice:
if val.Len() == 0 {
break
}
for i := 0; i < val.Len(); i++ {
elem := val.Index(i)
// Set top var (lexically the second if there are two) to the element.
if len(r.Pipe.Decl) > 0 {
s.setVar(1, elem)
}
// Set next var (lexically the first if there are two) to the index.
if len(r.Pipe.Decl) > 1 {
s.setVar(2, reflect.ValueOf(i))
}
s.walk(elem, r.List)
s.pop(mark)
oneIteration(reflect.ValueOf(i), val.Index(i))
}
return
case reflect.Map:
@@ -220,17 +222,23 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
break
}
for _, key := range val.MapKeys() {
elem := val.MapIndex(key)
// Set top var (lexically the second if there are two) to the element.
if len(r.Pipe.Decl) > 0 {
s.setVar(1, elem)
oneIteration(key, val.MapIndex(key))
}
return
case reflect.Chan:
if val.IsNil() {
break
}
i := 0
for ; ; i++ {
elem, ok := val.Recv()
if !ok {
break
}
// Set next var (lexically the first if there are two) to the key.
if len(r.Pipe.Decl) > 1 {
s.setVar(2, key)
}
s.walk(elem, r.List)
s.pop(mark)
oneIteration(reflect.ValueOf(i), elem)
}
if i == 0 {
break
}
return
case reflect.Invalid:
@@ -498,7 +506,18 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu
s.errorf("invalid value; expected %s", typ)
}
if !value.Type().AssignableTo(typ) {
s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
// Does one dereference or indirection work? We could do more, as we
// do with method receivers, but that gets messy and method receivers
// are much more constrained, so it makes more sense there than here.
// Besides, one is almost always all you need.
switch {
case value.Kind() == reflect.Ptr && value.Elem().Type().AssignableTo(typ):
value = value.Elem()
case reflect.PtrTo(value.Type()).AssignableTo(typ) && value.CanAddr():
value = value.Addr()
default:
s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
}
}
return value
}

View File

@@ -391,6 +391,8 @@ var execTests = []execTest{
{"range $x $y MSIone", "{{range $x, $y := .MSIone}}<{{$x}}={{$y}}>{{end}}", "<one=1>", tVal, true},
{"range $x PSI", "{{range $x := .PSI}}<{{$x}}>{{end}}", "<21><22><23>", tVal, true},
{"declare in range", "{{range $x := .PSI}}<{{$foo:=$x}}{{$x}}>{{end}}", "<21><22><23>", tVal, true},
{"range count", `{{range $i, $x := count 5}}[{{$i}}]{{$x}}{{end}}`, "[0]a[1]b[2]c[3]d[4]e", tVal, true},
{"range nil count", `{{range $i, $x := count 0}}{{else}}empty{{end}}`, "empty", tVal, true},
// Cute examples.
{"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true},
@@ -414,6 +416,11 @@ var execTests = []execTest{
{"bug4", "{{if .Empty0}}non-nil{{else}}nil{{end}}", "nil", tVal, true},
// Stringer.
{"bug5", "{{.Str}}", "foozle", tVal, true},
// Args need to be indirected and dereferenced sometimes.
{"bug6a", "{{vfunc .V0 .V1}}", "vfunc", tVal, true},
{"bug6b", "{{vfunc .V0 .V0}}", "vfunc", tVal, true},
{"bug6c", "{{vfunc .V1 .V0}}", "vfunc", tVal, true},
{"bug6d", "{{vfunc .V1 .V1}}", "vfunc", tVal, true},
}
func zeroArgs() string {
@@ -424,9 +431,35 @@ func oneArg(a string) string {
return "oneArg=" + a
}
// count returns a channel that will deliver n sequential 1-letter strings starting at "a"
func count(n int) chan string {
if n == 0 {
return nil
}
c := make(chan string)
go func() {
for i := 0; i < n; i++ {
c <- "abcdefghijklmnop"[i : i+1]
}
close(c)
}()
return c
}
// vfunc takes a *V and a V
func vfunc(V, *V) string {
return "vfunc"
}
func testExecute(execTests []execTest, set *Set, t *testing.T) {
b := new(bytes.Buffer)
funcs := FuncMap{"zeroArgs": zeroArgs, "oneArg": oneArg, "typeOf": typeOf}
funcs := FuncMap{
"count": count,
"oneArg": oneArg,
"typeOf": typeOf,
"vfunc": vfunc,
"zeroArgs": zeroArgs,
}
for _, test := range execTests {
tmpl := New(test.name).Funcs(funcs)
_, err := tmpl.ParseInSet(test.input, set)

View File

@@ -278,53 +278,51 @@ func lexInsideAction(l *lexer) stateFn {
// Either number, quoted string, or identifier.
// Spaces separate and are ignored.
// Pipe symbols separate and are emitted.
for {
if strings.HasPrefix(l.input[l.pos:], rightDelim) {
return lexRightDelim
}
switch r := l.next(); {
case r == eof || r == '\n':
return l.errorf("unclosed action")
case isSpace(r):
l.ignore()
case r == ':':
if l.next() != '=' {
return l.errorf("expected :=")
}
l.emit(itemColonEquals)
case r == '|':
l.emit(itemPipe)
case r == '"':
return lexQuote
case r == '`':
return lexRawQuote
case r == '$':
return lexIdentifier
case r == '\'':
return lexChar
case r == '.':
// special look-ahead for ".field" so we don't break l.backup().
if l.pos < len(l.input) {
r := l.input[l.pos]
if r < '0' || '9' < r {
return lexIdentifier // itemDot comes from the keyword table.
}
}
fallthrough // '.' can start a number.
case r == '+' || r == '-' || ('0' <= r && r <= '9'):
l.backup()
return lexNumber
case isAlphaNumeric(r):
l.backup()
return lexIdentifier
case r <= unicode.MaxASCII && unicode.IsPrint(r):
l.emit(itemChar)
return lexInsideAction
default:
return l.errorf("unrecognized character in action: %#U", r)
}
if strings.HasPrefix(l.input[l.pos:], rightDelim) {
return lexRightDelim
}
return nil
switch r := l.next(); {
case r == eof || r == '\n':
return l.errorf("unclosed action")
case isSpace(r):
l.ignore()
case r == ':':
if l.next() != '=' {
return l.errorf("expected :=")
}
l.emit(itemColonEquals)
case r == '|':
l.emit(itemPipe)
case r == '"':
return lexQuote
case r == '`':
return lexRawQuote
case r == '$':
return lexIdentifier
case r == '\'':
return lexChar
case r == '.':
// special look-ahead for ".field" so we don't break l.backup().
if l.pos < len(l.input) {
r := l.input[l.pos]
if r < '0' || '9' < r {
return lexIdentifier // itemDot comes from the keyword table.
}
}
fallthrough // '.' can start a number.
case r == '+' || r == '-' || ('0' <= r && r <= '9'):
l.backup()
return lexNumber
case isAlphaNumeric(r):
l.backup()
return lexIdentifier
case r <= unicode.MaxASCII && unicode.IsPrint(r):
l.emit(itemChar)
return lexInsideAction
default:
return l.errorf("unrecognized character in action: %#U", r)
}
return lexInsideAction
}
// lexIdentifier scans an alphanumeric or field.

View File

@@ -184,6 +184,20 @@ var lexTests = []lexTest{
tLeft,
{itemError, `bad number syntax: "3k"`},
}},
// Fixed bugs
// Many elements in an action blew the lookahead until
// we made lexInsideAction not loop.
{"long pipeline deadlock", "{{|||||}}", []item{
tLeft,
tPipe,
tPipe,
tPipe,
tPipe,
tPipe,
tRight,
tEOF,
}},
}
// collect gathers the emitted items into a slice.

View File

@@ -35,8 +35,8 @@ const (
NodeBool // A boolean constant.
NodeCommand // An element of a pipeline.
NodeDot // The cursor, dot.
NodeElse // An else action.
NodeEnd // An end action.
nodeElse // An else action. Not added to tree.
nodeEnd // An end action. Not added to tree.
NodeField // A field or method name.
NodeIdentifier // An identifier; always a function name.
NodeIf // An if action.
@@ -356,36 +356,37 @@ func (s *StringNode) String() string {
return fmt.Sprintf("S=%#q", s.Text)
}
// EndNode represents an {{end}} action. It is represented by a nil pointer.
type EndNode bool
// endNode represents an {{end}} action. It is represented by a nil pointer.
// It does not appear in the final parse tree.
type endNode bool
func newEnd() *EndNode {
func newEnd() *endNode {
return nil
}
func (e *EndNode) Type() NodeType {
return NodeEnd
func (e *endNode) Type() NodeType {
return nodeEnd
}
func (e *EndNode) String() string {
func (e *endNode) String() string {
return "{{end}}"
}
// ElseNode represents an {{else}} action.
type ElseNode struct {
// elseNode represents an {{else}} action. Does not appear in the final tree.
type elseNode struct {
NodeType
Line int // The line number in the input.
}
func newElse(line int) *ElseNode {
return &ElseNode{NodeType: NodeElse, Line: line}
func newElse(line int) *elseNode {
return &elseNode{NodeType: nodeElse, Line: line}
}
func (e *ElseNode) Type() NodeType {
return NodeElse
func (e *elseNode) Type() NodeType {
return nodeElse
}
func (e *ElseNode) String() string {
func (e *elseNode) String() string {
return "{{else}}"
}

View File

@@ -173,7 +173,7 @@ func (t *Tree) itemList(toEOF bool) (list *ListNode, next Node) {
for t.peek().typ != itemEOF {
n := t.textOrAction()
switch n.Type() {
case NodeEnd, NodeElse:
case nodeEnd, nodeElse:
return list, n
}
list.append(n)
@@ -278,10 +278,10 @@ func (t *Tree) parseControl(context string) (lineNum int, pipe *PipeNode, list,
var next Node
list, next = t.itemList(false)
switch next.Type() {
case NodeEnd: //done
case NodeElse:
case nodeEnd: //done
case nodeElse:
elseList, next = t.itemList(false)
if next.Type() != NodeEnd {
if next.Type() != nodeEnd {
t.errorf("expected end; found %s", next)
}
elseList = elseList

View File

@@ -37,7 +37,7 @@ func Set(text string, funcs ...map[string]interface{}) (tree map[string]*Tree, e
if end == nil {
t.errorf("unexpected EOF in %s", context)
}
if end.Type() != NodeEnd {
if end.Type() != nodeEnd {
t.errorf("unexpected %s in %s", end, context)
}
t.stopParse()