Skip to content

Commit 5515ee9

Browse files
committed
tools/fix: move fix functionality to separate package
This will allow users to apply fixes programmatically. Also handy for tests. Change-Id: Iec662c773d0957abf3aa79e7473a3408bb971c80 Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6202 Reviewed-by: Marcel van Lohuizen <[email protected]>
1 parent 1d213a3 commit 5515ee9

File tree

13 files changed

+280
-253
lines changed

13 files changed

+280
-253
lines changed

cmd/cue/cmd/fix.go

Lines changed: 2 additions & 240 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,17 @@
1515
package cmd
1616

1717
import (
18-
"fmt"
1918
"io/ioutil"
2019
"os"
2120
"path/filepath"
2221
"strings"
2322

2423
"cuelang.org/go/cue/ast"
25-
"cuelang.org/go/cue/ast/astutil"
2624
"cuelang.org/go/cue/errors"
2725
"cuelang.org/go/cue/format"
2826
"cuelang.org/go/cue/load"
29-
"cuelang.org/go/cue/parser"
3027
"cuelang.org/go/cue/token"
31-
"cuelang.org/go/internal"
28+
"cuelang.org/go/tools/fix"
3229
"github.com/spf13/cobra"
3330
)
3431

@@ -87,7 +84,7 @@ func runFixAll(cmd *Command, args []string) error {
8784
}
8885
}
8986

90-
errs := fixAll(instances)
87+
errs := fix.Instances(instances)
9188

9289
if errs != nil && flagForce.Bool(cmd) {
9390
return errs
@@ -129,238 +126,3 @@ func appendDirs(a []string, base string) []string {
129126
})
130127
return a
131128
}
132-
133-
func fix(f *ast.File) *ast.File {
134-
// Isolate bulk optional fields into a single struct.
135-
ast.Walk(f, func(n ast.Node) bool {
136-
var decls []ast.Decl
137-
switch x := n.(type) {
138-
case *ast.StructLit:
139-
decls = x.Elts
140-
case *ast.File:
141-
decls = x.Decls
142-
}
143-
144-
if len(decls) <= 1 {
145-
return true
146-
}
147-
148-
for i, d := range decls {
149-
if internal.IsBulkField(d) {
150-
decls[i] = internal.EmbedStruct(ast.NewStruct(d))
151-
}
152-
}
153-
154-
return true
155-
}, nil)
156-
157-
// Rewrite an old-style alias to a let clause.
158-
ast.Walk(f, func(n ast.Node) bool {
159-
var decls []ast.Decl
160-
switch x := n.(type) {
161-
case *ast.StructLit:
162-
decls = x.Elts
163-
case *ast.File:
164-
decls = x.Decls
165-
}
166-
for i, d := range decls {
167-
if a, ok := d.(*ast.Alias); ok {
168-
x := &ast.LetClause{
169-
Ident: a.Ident,
170-
Equal: a.Equal,
171-
Expr: a.Expr,
172-
}
173-
astutil.CopyMeta(x, a)
174-
decls[i] = x
175-
}
176-
}
177-
return true
178-
}, nil)
179-
180-
// Rewrite block comments to regular comments.
181-
ast.Walk(f, func(n ast.Node) bool {
182-
switch x := n.(type) {
183-
case *ast.CommentGroup:
184-
comments := []*ast.Comment{}
185-
for _, c := range x.List {
186-
s := c.Text
187-
if !strings.HasPrefix(s, "/*") || !strings.HasSuffix(s, "*/") {
188-
comments = append(comments, c)
189-
continue
190-
}
191-
if x.Position > 0 {
192-
// Moving to the end doesn't work, as it still
193-
// may inject at a false line break position.
194-
x.Position = 0
195-
x.Doc = true
196-
}
197-
s = strings.TrimSpace(s[2 : len(s)-2])
198-
for _, s := range strings.Split(s, "\n") {
199-
for i := 0; i < 3; i++ {
200-
if strings.HasPrefix(s, " ") || strings.HasPrefix(s, "*") {
201-
s = s[1:]
202-
}
203-
}
204-
comments = append(comments, &ast.Comment{Text: "// " + s})
205-
}
206-
}
207-
x.List = comments
208-
return false
209-
}
210-
return true
211-
}, nil)
212-
213-
// Referred nodes and used identifiers.
214-
referred := map[ast.Node]string{}
215-
used := map[string]bool{}
216-
replacement := map[ast.Node]string{}
217-
218-
ast.Walk(f, func(n ast.Node) bool {
219-
if i, ok := n.(*ast.Ident); ok {
220-
str, err := ast.ParseIdent(i)
221-
if err != nil {
222-
return false
223-
}
224-
referred[i.Node] = str
225-
used[str] = true
226-
}
227-
return true
228-
}, nil)
229-
230-
num := 0
231-
newIdent := func() string {
232-
for num++; ; num++ {
233-
str := fmt.Sprintf("X%d", num)
234-
if !used[str] {
235-
used[str] = true
236-
return str
237-
}
238-
}
239-
}
240-
241-
// Rewrite TemplateLabel to ListLit.
242-
// Note: there is a chance that the name will clash with the
243-
// scope in which it is defined. We drop the alias if it is not
244-
// used to mitigate this issue.
245-
f = astutil.Apply(f, func(c astutil.Cursor) bool {
246-
n := c.Node()
247-
switch x := n.(type) {
248-
case *ast.TemplateLabel:
249-
var expr ast.Expr = ast.NewIdent("string")
250-
if _, ok := referred[x]; ok {
251-
expr = &ast.Alias{
252-
Ident: x.Ident,
253-
Expr: ast.NewIdent("_"),
254-
}
255-
}
256-
c.Replace(ast.NewList(expr))
257-
}
258-
return true
259-
}, nil).(*ast.File)
260-
261-
// Rewrite quoted identifier fields that are referenced.
262-
f = astutil.Apply(f, func(c astutil.Cursor) bool {
263-
n := c.Node()
264-
switch x := n.(type) {
265-
case *ast.Field:
266-
m, ok := referred[x.Value]
267-
if !ok {
268-
break
269-
}
270-
271-
if b, ok := x.Label.(*ast.Ident); ok {
272-
str, err := ast.ParseIdent(b)
273-
var expr ast.Expr = b
274-
275-
switch {
276-
case token.Lookup(str) != token.IDENT:
277-
// quote keywords
278-
expr = ast.NewString(b.Name)
279-
280-
case err != nil || str != m || str == b.Name:
281-
return true
282-
283-
case ast.IsValidIdent(str):
284-
x.Label = astutil.CopyMeta(ast.NewIdent(str), x.Label).(ast.Label)
285-
return true
286-
}
287-
288-
ident := newIdent()
289-
replacement[x.Value] = ident
290-
expr = &ast.Alias{Ident: ast.NewIdent(ident), Expr: expr}
291-
ast.SetRelPos(x.Label, token.NoRelPos)
292-
x.Label = astutil.CopyMeta(expr, x.Label).(ast.Label)
293-
}
294-
}
295-
return true
296-
}, nil).(*ast.File)
297-
298-
// Replace quoted references with their alias identifier.
299-
astutil.Apply(f, func(c astutil.Cursor) bool {
300-
n := c.Node()
301-
switch x := n.(type) {
302-
case *ast.Ident:
303-
if r, ok := replacement[x.Node]; ok {
304-
c.Replace(astutil.CopyMeta(ast.NewIdent(r), n))
305-
break
306-
}
307-
str, err := ast.ParseIdent(x)
308-
if err != nil || str == x.Name {
309-
break
310-
}
311-
// Either the identifier is valid, in which can be replaced simply
312-
// as here, or it is a complicated identifier and the original
313-
// destination must have been quoted, in which case it is handled
314-
// above.
315-
if ast.IsValidIdent(str) && token.Lookup(str) == token.IDENT {
316-
c.Replace(astutil.CopyMeta(ast.NewIdent(str), n))
317-
}
318-
}
319-
return true
320-
}, nil)
321-
322-
// TODO: we are probably reintroducing slices. Disable for now.
323-
//
324-
// Rewrite slice expression.
325-
// f = astutil.Apply(f, func(c astutil.Cursor) bool {
326-
// n := c.Node()
327-
// getVal := func(n ast.Expr) ast.Expr {
328-
// if n == nil {
329-
// return nil
330-
// }
331-
// if id, ok := n.(*ast.Ident); ok && id.Name == "_" {
332-
// return nil
333-
// }
334-
// return n
335-
// }
336-
// switch x := n.(type) {
337-
// case *ast.SliceExpr:
338-
// ast.SetRelPos(x.X, token.NoRelPos)
339-
340-
// lo := getVal(x.Low)
341-
// hi := getVal(x.High)
342-
// if lo == nil { // a[:j]
343-
// lo = mustParseExpr("0")
344-
// astutil.CopyMeta(lo, x.Low)
345-
// }
346-
// if hi == nil { // a[i:]
347-
// hi = ast.NewCall(ast.NewIdent("len"), x.X)
348-
// astutil.CopyMeta(lo, x.High)
349-
// }
350-
// if pkg := c.Import("list"); pkg != nil {
351-
// c.Replace(ast.NewCall(ast.NewSel(pkg, "Slice"), x.X, lo, hi))
352-
// }
353-
// }
354-
// return true
355-
// }, nil).(*ast.File)
356-
357-
return f
358-
}
359-
360-
func mustParseExpr(expr string) ast.Expr {
361-
ex, err := parser.ParseExpr("fix", expr)
362-
if err != nil {
363-
panic(err)
364-
}
365-
return ex
366-
}

cmd/cue/cmd/fmt.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"cuelang.org/go/cue/format"
2323
"cuelang.org/go/cue/load"
2424
"cuelang.org/go/internal/encoding"
25+
"cuelang.org/go/tools/fix"
2526
)
2627

2728
func newFmtCmd(c *Command) *cobra.Command {
@@ -67,7 +68,7 @@ func newFmtCmd(c *Command) *cobra.Command {
6768
f := d.File()
6869

6970
if file.Encoding == build.CUE {
70-
f = fix(f)
71+
f = fix.File(f)
7172
}
7273

7374
files = append(files, f)

0 commit comments

Comments
 (0)