-l
just list files whose formatting differs from gofmt's; generate no other output
unless -w is also set.
+ -r rule
+ apply the rewrite rule to the source before reformatting.
-w
if set, overwrite each input file with its output.
-spaces
-comments=true
print comments; if false, all comments are elided from the output.
+The rewrite rule specified with the -r flag must be a string of the form:
+
+ pattern -> replacement
+
+Both pattern and replacement must be valid Go expressions.
+In the pattern, single-character lowercase identifers serve as
+wildcards matching arbitrary subexpressions; those expressions
+will be substituted for the same identifiers in the replacement.
+
+
+Examples
+
+To check files for unnecessary parentheses:
+
+ gofmt -r '(a) -> a' -l *.go
+
+To remove the parentheses:
+
+ gofmt -r '(a) -> a' -w *.go
+
+To convert the package tree from explicit slice upper bounds to implicit ones:
+
+ gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src/pkg
*/
package documentation
+
+// BUG(rsc): The implementation of -r is a bit slow.
for k := range m {
m[k] = nil, false
}
+ val = apply(f, val);
if match(m, pat, val) {
- return subst(m, repl)
+ val = subst(m, repl, reflect.NewValue(val.Interface().(ast.Node).Pos()))
}
- return apply(f, val);
+ return val;
};
return apply(f, reflect.NewValue(p)).Interface().(*ast.File);
}
var positionType = reflect.Typeof(token.Position{})
-var zeroPosition = reflect.NewValue(token.Position{})
var identType = reflect.Typeof((*ast.Ident)(nil))
func isWildcard(s string) bool {
- rune, _ := utf8.DecodeRuneInString(s);
- return unicode.Is(unicode.Greek, rune) && unicode.IsLower(rune);
+ rune, size := utf8.DecodeRuneInString(s);
+ return size == len(s) && unicode.IsLower(rune);
}
}
-// subst returns a copy of pattern with values from m substituted in place of wildcards.
-// if m == nil, subst returns a copy of pattern.
-// Either way, the returned value has no valid line number information.
-func subst(m map[string]reflect.Value, pattern reflect.Value) reflect.Value {
+// subst returns a copy of pattern with values from m substituted in place
+// of wildcards and pos used as the position of tokens from the pattern.
+// if m == nil, subst returns a copy of pattern and doesn't change the line
+// number information.
+func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) reflect.Value {
if pattern == nil {
return nil
}
name := pattern.Interface().(*ast.Ident).Value;
if isWildcard(name) {
if old, ok := m[name]; ok {
- return subst(nil, old)
+ return subst(nil, old, nil)
}
}
}
- if pattern.Type() == positionType {
- return zeroPosition
+ if pos != nil && pattern.Type() == positionType {
+ return pos
}
// Otherwise copy.
case *reflect.SliceValue:
v := reflect.MakeSlice(p.Type().(*reflect.SliceType), p.Len(), p.Len());
for i := 0; i < p.Len(); i++ {
- v.Elem(i).SetValue(subst(m, p.Elem(i)))
+ v.Elem(i).SetValue(subst(m, p.Elem(i), pos))
}
return v;
case *reflect.StructValue:
v := reflect.MakeZero(p.Type()).(*reflect.StructValue);
for i := 0; i < p.NumField(); i++ {
- v.Field(i).SetValue(subst(m, p.Field(i)))
+ v.Field(i).SetValue(subst(m, p.Field(i), pos))
}
return v;
case *reflect.PtrValue:
v := reflect.MakeZero(p.Type()).(*reflect.PtrValue);
- v.PointTo(subst(m, p.Elem()));
+ v.PointTo(subst(m, p.Elem(), pos));
return v;
case *reflect.InterfaceValue:
v := reflect.MakeZero(p.Type()).(*reflect.InterfaceValue);
- v.SetValue(subst(m, p.Elem()));
+ v.SetValue(subst(m, p.Elem(), pos));
return v;
}
n = max
}
- // TODO(gri): try to avoid direct manipulation of p.pos
- // demo of why this is necessary: run gofmt -r 'i < i -> i < j' x.go on this x.go:
- // package main
- // func main() {
- // i < i;
- // j < 10;
- // }
- //
- p.pos.Line += n;
-
if n > 0 {
p.print(ws);
if newSection {
switch l := e.X.(type) {
case *ast.BinaryExpr:
+ if l.Op.Precedence() < e.Op.Precedence() {
+ // parens will be inserted.
+ // pretend this is an *ast.ParenExpr and do nothing.
+ break
+ }
h5, h6, mp := walkBinary(l);
has5 = has5 || h5;
has6 = has6 || h6;
switch r := e.Y.(type) {
case *ast.BinaryExpr:
+ if r.Op.Precedence() <= e.Op.Precedence() {
+ // parens will be inserted.
+ // pretend this is an *ast.ParenExpr and do nothing.
+ break
+ }
h5, h6, mp := walkBinary(r);
has5 = has5 || h5;
has6 = has6 || h6;
if printBlank {
p.print(blank)
}
- p.expr1(x.Y, prec, depth+1, 0, multiLine);
+ p.expr1(x.Y, prec+1, depth+1, 0, multiLine);
if ws == ignore {
p.print(unindent)
}
p.expr(x.Value, multiLine);
case *ast.StarExpr:
- p.print(token.MUL);
- optSemi = p.expr(x.X, multiLine);
+ const prec = token.UnaryPrec;
+ if prec < prec1 {
+ // parenthesis needed
+ p.print(token.LPAREN);
+ p.print(token.MUL);
+ optSemi = p.expr(x.X, multiLine);
+ p.print(token.RPAREN);
+ } else {
+ // no parenthesis needed
+ p.print(token.MUL);
+ optSemi = p.expr(x.X, multiLine);
+ }
case *ast.UnaryExpr:
const prec = token.UnaryPrec;