将项目添加到 Go AST 后评论乱序

以下测试尝试使用 AST 向结构添加字段。字段添加正确,但注释添加顺序不正确。我认为可能需要手动指定位置,但到目前为止我已经找到了答案。


这是一个失败的测试:http : //play.golang.org/p/RID4N30FZK


这是代码:


package generator


import (

    "bytes"

    "fmt"

    "go/ast"

    "go/parser"

    "go/printer"

    "go/token"

    "testing"

)


func TestAst(t *testing.T) {


    source := `package a


// B comment

type B struct {

    // C comment

    C string

}`


    fset := token.NewFileSet()

    file, err := parser.ParseFile(fset, "", []byte(source), parser.ParseComments)

    if err != nil {

        t.Error(err)

    }


    v := &visitor{

        file: file,

    }

    ast.Walk(v, file)


    var output []byte

    buf := bytes.NewBuffer(output)

    if err := printer.Fprint(buf, fset, file); err != nil {

        t.Error(err)

    }


    expected := `package a


// B comment

type B struct {

    // C comment

    C string

    // D comment

    D int

    // E comment

    E float64

}

`


    if buf.String() != expected {

        t.Error(fmt.Sprintf("Test failed. Expected:\n%s\nGot:\n%s", expected, buf.String()))

    }


    /*

    actual output = `package a


// B comment

type B struct {

    // C comment

    // D comment

    // E comment

    C   string

    D   int

    E   float64

}

`

    */


}


type visitor struct {

    file *ast.File

}


func (v *visitor) Visit(node ast.Node) (w ast.Visitor) {


    if node == nil {

        return v

    }


    switch n := node.(type) {

    case *ast.GenDecl:

        if n.Tok != token.TYPE {

            break

        }

        ts := n.Specs[0].(*ast.TypeSpec)

        if ts.Name.Name == "B" {

            fields := ts.Type.(*ast.StructType).Fields

            addStructField(fields, v.file, "int", "D", "D comment")

            addStructField(fields, v.file, "float64", "E", "E comment")

        }

    }


    return v

}



慕妹3242003
浏览 161回答 2
2回答

明月笑刀无情

我相信我已经让它发挥作用了。正如我在上面的评论中所述,所需的要点是:具体设置缓冲区位置包括Slash和NamePos用于token.File.AddLine在特定偏移处添加新行(使用第 1 项中的位置计算)过度分配源缓冲区token.File.Position(由源缓冲区使用,printer.Printer并且token.File.Addline不会使源缓冲区的范围检查失败代码:package mainimport (    "bytes"    "fmt"    "go/ast"    "go/parser"    "go/printer"    "go/token"    "testing")func main() {    tests := []testing.InternalTest{{"TestAst", TestAst}}    matchAll := func(t string, pat string) (bool, error) { return true, nil }    testing.Main(matchAll, tests, nil, nil)}func TestAst(t *testing.T) {    source := `package a// B commenttype B struct {    // C comment    C string}`    buffer := make([]byte, 1024, 1024)    for idx,_ := range buffer {        buffer[idx] = 0x20    }    copy(buffer[:], source)    fset := token.NewFileSet()    file, err := parser.ParseFile(fset, "", buffer, parser.ParseComments)    if err != nil {        t.Error(err)    }    v := &visitor{        file: file,        fset: fset,    }    ast.Walk(v, file)    var output []byte    buf := bytes.NewBuffer(output)    if err := printer.Fprint(buf, fset, file); err != nil {        t.Error(err)    }    expected := `package a// B commenttype B struct {    // C comment    C   string    // D comment    D   int    // E comment    E   float64}`    if buf.String() != expected {        t.Error(fmt.Sprintf("Test failed. Expected:\n%s\nGot:\n%s", expected, buf.String()))    }}type visitor struct {    file *ast.File    fset *token.FileSet}func (v *visitor) Visit(node ast.Node) (w ast.Visitor) {    if node == nil {        return v    }    switch n := node.(type) {    case *ast.GenDecl:        if n.Tok != token.TYPE {            break        }        ts := n.Specs[0].(*ast.TypeSpec)        if ts.Name.Name == "B" {            fields := ts.Type.(*ast.StructType).Fields            addStructField(v.fset, fields, v.file, "int", "D", "D comment")            addStructField(v.fset, fields, v.file, "float64", "E", "E comment")        }    }    return v}func addStructField(fset *token.FileSet, fields *ast.FieldList, file *ast.File, typ string, name string, comment string) {    prevField := fields.List[fields.NumFields()-1]     c := &ast.Comment{Text: fmt.Sprint("// ", comment), Slash: prevField.End() + 1}    cg := &ast.CommentGroup{List: []*ast.Comment{c}}    o := ast.NewObj(ast.Var, name)    f := &ast.Field{        Doc:   cg,        Names: []*ast.Ident{&ast.Ident{Name: name, Obj: o, NamePos: cg.End() + 1}},    }    o.Decl = f    f.Type = &ast.Ident{Name: typ, NamePos: f.Names[0].End() + 1}    fset.File(c.End()).AddLine(int(c.End()))    fset.File(f.End()).AddLine(int(f.End()))    fields.List = append(fields.List, f)    file.Comments = append(file.Comments, cg)}示例:http : //play.golang.org/p/_q1xh3giHm对于Item (3),将所有过度分配的字节设置为空格 ( 0x20)也很重要,以便打印机在处理它们时不会抱怨空字节。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go