godoc/static: use a stable quote function

Printf(%q) aka strconv.AppendQuote depends on the Unicode tables du
jour, which means gen_test breaks after a Go release in which Unicode
evolves. This change uses a very dumb quotation function that emits
only ASCII and is independent of the Go release.

Perhaps an even better fix would have been to parse the generated file.

Change-Id: I197942f1c36a8b53d6a37be4bb2b1e63a208f7e2
Reviewed-on: https://go-review.googlesource.com/119157
Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
Alan Donovan 2018-06-15 15:06:09 -04:00
parent c06a8d8ed1
commit e10d6c9a84
3 changed files with 89 additions and 399 deletions

View File

@ -12,7 +12,7 @@ import (
"go/format"
"io/ioutil"
"time"
"unicode/utf8"
"unicode"
)
var files = []string{
@ -80,35 +80,28 @@ func Generate() ([]byte, error) {
return b, err
}
fmt.Fprintf(buf, "\t%q: ", fn)
if utf8.Valid(b) {
fmt.Fprintf(buf, "`%s`", sanitize(b))
} else {
fmt.Fprintf(buf, "%q", b)
}
appendQuote(buf, b)
fmt.Fprintf(buf, ",\n\n")
}
fmt.Fprintln(buf, "}")
b := buf.Bytes()
// The 鿥 U+9FE5 character became printable in go 1.10,
// causing it to appear literally in newer output.
// Force the old behavior.
b = bytes.Replace(b, []byte("\u9fe5"), []byte(`\u9fe5`), -1)
return format.Source(b)
return format.Source(buf.Bytes())
}
// sanitize prepares a valid UTF-8 string as a raw string constant.
func sanitize(b []byte) []byte {
// Replace ` with `+"`"+`
b = bytes.Replace(b, []byte("`"), []byte("`+\"`\"+`"), -1)
// Replace BOM with `+"\xEF\xBB\xBF"+`
// (A BOM is valid UTF-8 but not permitted in Go source files.
// I wouldn't bother handling this, but for some insane reason
// jquery.js has a BOM somewhere in the middle.)
return bytes.Replace(b, []byte("\xEF\xBB\xBF"), []byte("`+\"\\xEF\\xBB\\xBF\"+`"), -1)
// appendQuote is like strconv.AppendQuote, but we avoid the latter
// because it changes when Unicode evolves, breaking gen_test.go.
func appendQuote(out *bytes.Buffer, data []byte) {
out.WriteByte('"')
for _, b := range data {
if b == '\\' || b == '"' {
out.WriteByte('\\')
out.WriteByte(b)
} else if b <= unicode.MaxASCII && unicode.IsPrint(rune(b)) && !unicode.IsSpace(rune(b)) {
out.WriteByte(b)
} else {
fmt.Fprintf(out, "\\x%02x", b)
}
}
out.WriteByte('"')
}
const warning = `// Code generated by "makestatic"; DO NOT EDIT.`

View File

@ -7,7 +7,9 @@ package static
import (
"bytes"
"io/ioutil"
"strconv"
"testing"
"unicode"
)
func TestStaticIsUpToDate(t *testing.T) {
@ -29,3 +31,19 @@ to see the differences.`)
}
}
// TestAppendQuote ensures that AppendQuote produces a valid literal.
func TestAppendQuote(t *testing.T) {
var in, out bytes.Buffer
for r := rune(0); r < unicode.MaxRune; r++ {
in.WriteRune(r)
}
appendQuote(&out, in.Bytes())
in2, err := strconv.Unquote(out.String())
if err != nil {
t.Fatalf("AppendQuote produced invalid string literal: %v", err)
}
if got, want := in2, in.String(); got != want {
t.Fatal("AppendQuote modified string") // no point printing got/want: huge
}
}

File diff suppressed because one or more lines are too long