bug fix: do not modify (string) literals in any way even if they

contain tabs or line breaks

R=rsc
DELTA=57  (19 added, 12 deleted, 26 changed)
OCL=35749
CL=35770
This commit is contained in:
Robert Griesemer 2009-10-15 09:29:31 -07:00
Родитель b55e6d1ba5
Коммит 8f8a393b40
3 изменённых файлов: 45 добавлений и 38 удалений

Просмотреть файл

@ -50,6 +50,7 @@ const (
var (
esc = []byte{tabwriter.Escape};
htab = []byte{'\t'};
htabs = [...]byte{'\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t'};
newlines = [...]byte{'\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n'}; // more than maxNewlines
@ -81,6 +82,7 @@ type printer struct {
// Current state
written int; // number of bytes written
indent int; // current indentation
escape bool; // true if in escape sequence
// Buffered whitespace
buffer []whiteSpace;
@ -125,17 +127,11 @@ func (p *printer) write0(data []byte) {
}
type writeMode uint;
const (
writeRaw writeMode = 1<<iota; // do not interpret newline/formfeed characters
setLineTag; // wrap item with a line tag
)
// write interprets data and writes it to p.output. It inserts indentation
// after newline or formfeed if not in writeRaw mode and HTML-escapes characters
// if GenHTML is set. It updates p.pos as a side-effect.
// after a line break unless in a tabwriter escape sequence, and it HTML-
// escapes characters if GenHTML is set. It updates p.pos as a side-effect.
//
func (p *printer) write(data []byte, mode writeMode) {
func (p *printer) write(data []byte) {
i0 := 0;
for i, b := range data {
switch b {
@ -148,7 +144,7 @@ func (p *printer) write(data []byte, mode writeMode) {
p.pos.Line++;
p.pos.Column = 1;
if mode & writeRaw == 0 {
if !p.escape {
// write indentation
// use "hard" htabs - indentation columns
// must not be discarded by the tabwriter
@ -188,6 +184,9 @@ func (p *printer) write(data []byte, mode writeMode) {
// next segment start
i0 = i+1;
}
case tabwriter.Escape:
p.escape = !p.escape;
}
}
@ -206,7 +205,7 @@ func (p *printer) writeNewlines(n int) {
if n > maxNewlines {
n = maxNewlines;
}
p.write(newlines[0 : n], 0);
p.write(newlines[0 : n]);
}
}
@ -214,11 +213,11 @@ func (p *printer) writeNewlines(n int) {
// writeItem writes data at position pos. data is the text corresponding to
// a single lexical token, but may also be comment text. pos is the actual
// (or at least very accurately estimated) position of the data in the original
// source text. The data may be tagged, depending on p.mode and the mode
// source text. The data may be tagged, depending on p.mode and the setLineTag
// parameter. writeItem updates p.last to the position immediately following
// the data.
//
func (p *printer) writeItem(pos token.Position, data []byte, mode writeMode) {
func (p *printer) writeItem(pos token.Position, data []byte, setLineTag bool) {
p.pos = pos;
if debug {
// do not update p.pos - use write0
@ -226,7 +225,7 @@ func (p *printer) writeItem(pos token.Position, data []byte, mode writeMode) {
}
if p.mode & GenHTML != 0 {
// no html-escaping and no p.pos update for tags - use write0
if mode & setLineTag != 0 && pos.Line > p.lastTaggedLine {
if setLineTag && pos.Line > p.lastTaggedLine {
// id's must be unique within a document: set
// line tag only if line number has increased
// (note: for now write complete start and end
@ -240,14 +239,14 @@ func (p *printer) writeItem(pos token.Position, data []byte, mode writeMode) {
p.write0(strings.Bytes(p.tag.start));
p.tag.start = ""; // tag consumed
}
p.write(data, mode);
p.write(data);
// write end tag, if any
if p.tag.end != "" {
p.write0(strings.Bytes(p.tag.end));
p.tag.end = ""; // tag consumed
}
} else {
p.write(data, mode);
p.write(data);
}
p.last = p.pos;
}
@ -295,7 +294,7 @@ func (p *printer) writeCommentPrefix(line int, isFirst bool) {
}
// make sure there is at least one tab
if !hasTab {
p.write(htab, 0);
p.write(htab);
}
} else {
@ -352,7 +351,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
}
// write comment
p.writeItem(comment.Pos(), text, 0);
p.writeItem(comment.Pos(), text, false);
}
@ -382,7 +381,7 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) {
// make sure we have a line break
if needsLinebreak {
p.write([]byte{'\n'}, 0);
p.write([]byte{'\n'});
}
}
@ -441,7 +440,7 @@ func (p *printer) writeWhitespace(n int) {
fallthrough;
default:
data[0] = byte(ch);
p.write(&data, 0);
p.write(&data);
}
}
@ -470,7 +469,7 @@ func (p *printer) writeWhitespace(n int) {
// printed, followed by the actual token.
//
func (p *printer) print(args ...) {
var mode writeMode;
setLineTag := false;
v := reflect.NewValue(args).(*reflect.StructValue);
for i := 0; i < v.NumField(); i++ {
f := v.Field(i);
@ -491,10 +490,6 @@ func (p *printer) print(args ...) {
p.buffer[i] = x;
case []byte:
data = x;
// do not modify multi-line `` strings!
if len(x) > 0 && x[0] == '`' && x[len(x)-1] == '`' {
mode |= writeRaw;
}
case string:
data = strings.Bytes(x);
case token.Token:
@ -507,7 +502,7 @@ func (p *printer) print(args ...) {
pos := token.Position(x);
if pos.IsValid() {
next = pos; // accurate position of next item
mode |= setLineTag;
setLineTag = true;
}
case htmlTag:
p.tag = x; // tag surrounding next item
@ -524,8 +519,8 @@ func (p *printer) print(args ...) {
// at the end of a file)
p.writeNewlines(next.Line - p.pos.Line);
p.writeItem(next, data, mode);
mode = 0;
p.writeItem(next, data, setLineTag);
setLineTag = false;
}
}
}
@ -976,8 +971,16 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
}
case *ast.BasicLit:
// TODO(gri): string contents must remain unchanged through tabwriter!
p.print(x.Value);
if p.mode & RawFormat == 0 {
// tabwriter is used: escape all literals
// so they pass through unchanged
// (note that a legal Go program cannot contain an '\xff' byte in
// literal source text since '\xff' is not a legal byte in correct
// UTF-8 encoded text)
p.print(esc, x.Value, esc);
} else {
p.print(x.Value);
}
case *ast.StringList:
p.stringList(x.Strings);

10
src/pkg/go/printer/testdata/expressions.go поставляемый
Просмотреть файл

@ -105,10 +105,13 @@ func _() {
func _() {
// do not modify `` strings
// do not modify literals
_ = "tab1 tab2 tab3 end"; // string contains 3 tabs
_ = "tab1 tab2 tab3 end"; // same string with 3 blanks - may be unaligned because editors see tabs in strings
_ = ""; // this comment should be aligned with the one on the previous line
_ = ``;
_ = `
`; // TODO(gri): fix line breaks here
`;
_ = `foo
bar`;
}
@ -138,8 +141,7 @@ func _() {
b < a;
_ = "1234567890"
"1234567890";
// TODO(gri): add more test cases
// TODO(gri): these comments should be indented
// this comment should be indented
}

Просмотреть файл

@ -105,10 +105,13 @@ func _() {
func _() {
// do not modify `` strings
// do not modify literals
_ = "tab1 tab2 tab3 end"; // string contains 3 tabs
_ = "tab1 tab2 tab3 end"; // same string with 3 blanks - may be unaligned because editors see tabs in strings
_ = ""; // this comment should be aligned with the one on the previous line
_ = ``;
_ = `
`; // TODO(gri): fix line breaks here
`;
_ = `foo
bar`;
}
@ -138,8 +141,7 @@ func _() {
b < a;
_ = "1234567890"
"1234567890";
// TODO(gri): add more test cases
// TODO(gri): these comments should be indented
// this comment should be indented
}