diff --git a/minifier.go b/minifier.go index 0532357..5abe5d1 100644 --- a/minifier.go +++ b/minifier.go @@ -217,6 +217,9 @@ func (m *minifier) printExpr(n ast.Expr) { case *ast.InterfaceType: m.printInterfaceType(n) + case *ast.IndexListExpr: + m.printIndexListExpr(n) + default: m.panicUnhandled("printExpr", n) } @@ -476,6 +479,11 @@ func (m *minifier) printGenDecl(n *ast.GenDecl) { } case *ast.TypeSpec: m.out.WriteString(spec.Name.Name) + if spec.TypeParams != nil { + m.out.WriteString("[") + m.printFieldList(spec.TypeParams, ',') + m.out.WriteString("]") + } if spec.Assign != token.NoPos { m.out.WriteByte('=') } else { @@ -514,6 +522,12 @@ func (m *minifier) printBinaryExpr(n *ast.BinaryExpr) { } } + // Handle `x / Y` so we don't output it as `x/Y` (where Y is an expression). + // TODO(cristaloleg): handle only a case when we dereference Y: `x / *y`. + if n.Op == token.QUO { + spaceBeforeY = true + } + m.printExpr(n.X) m.out.WriteString(n.Op.String()) if spaceBeforeY { @@ -523,6 +537,11 @@ func (m *minifier) printBinaryExpr(n *ast.BinaryExpr) { } func (m *minifier) printFuncType(n *ast.FuncType) { + if n.TypeParams != nil { + m.out.WriteString("[") + m.printFieldList(n.TypeParams, ',') + m.out.WriteString("]") + } m.out.WriteString("(") m.printFieldList(n.Params, ',') m.out.WriteString(")") @@ -560,6 +579,20 @@ func (m *minifier) printInterfaceType(n *ast.InterfaceType) { m.out.WriteByte('}') } +func (m *minifier) printIndexListExpr(n *ast.IndexListExpr) { + m.printExpr(n.X) + m.out.WriteString("[") + + for i, ind := range n.Indices { + if i > 0 { + m.out.WriteByte(',') + } + m.printExpr(ind) + } + + m.out.WriteByte(']') +} + func (m *minifier) printFieldList(n *ast.FieldList, sep byte) { for j, field := range n.List { for j, ident := range field.Names { diff --git a/minifier_test.go b/minifier_test.go index f3c7d89..69b61c2 100644 --- a/minifier_test.go +++ b/minifier_test.go @@ -265,7 +265,7 @@ func TestGoroot(t *testing.T) { fset2 := token.NewFileSet() f2, err := parser.ParseFile(fset2, filename, minified.Bytes(), 0) if err != nil { - return fmt.Errorf("re-parse minified: %w", err) + return fmt.Errorf("re-parse minified: %w\nminified: %s", err, minified.String()) } if diff := astDiff(f, f2); diff != "" { return fmt.Errorf("minified code produced different AST:\n%s", diff) @@ -284,6 +284,18 @@ func TestGoroot(t *testing.T) { if !strings.HasSuffix(info.Name(), ".go") { return nil } + // Skip some testdata dirs where not all files can be parsed. + // TODO(cristaloleg): skip file when we cannot parse it with go/parser. + if strings.Contains(path, "src/cmd/compile/internal/syntax/testdata") || + strings.Contains(path, "src/cmd/compile/internal/types2/testdata") || + strings.Contains(path, "src/cmd/go/internal/modindex/testdata") || + strings.Contains(path, "src/cmd/go/parser/testdata") || + strings.Contains(path, "src/cmd/internal/modindex/testdata") || + strings.Contains(path, "src/go/parser/testdata") || + strings.Contains(path, "src/internal/types/testdata") || + strings.Contains(path, "src/cmd/compile/internal/syntax/testdata/smoketest.go") { + return nil + } if err := visitFile(path); err != nil { return fmt.Errorf("%s: %w", path, err) }