如何插入Goast中正确的位置?

问题描述 投票:0回答:1

我写了一个可以自动静音的程序。在运行时,我读取源代码,使用 go/parser 对其进行解析,在 ast 中搜索正确的位置,创建一些 go/ast.Expr,将它们插入到 ast 中并使用 go/format 格式化新的 ast。

我的问题是代码没有出现在我插入的位置。

这是我插入的地方:

var passwd = []*PwEntry{}
// line 1 after
// line 2 after

我正在搜索 ast.CompositeLit 并将新的 Exprs 插入 Elts 中。我的期望是,新代码显示在大括号之间,并且注释“line 1 after”保留在右大括号后面。但事实并非如此。这是 go/format 生成的代码:

var passwd = []*PwEntry{{Login:
// line 1 after
// line 2 after
"abc", Salt: []byte{214, 194, 249, 8, 11, 40, 37, 195, 65, 130, 142, 86, 68, 78, 185, 33}, Algo: 1, Hash: []byte{70, 8, 114, 178, 255, 193, 204, 112, 83, 209, 249, 153, 213, 253, 151, 47, 49, 99, 133, 139, 203, 184, 243, 15, 203, 16, 6, 235, 29, 236, 249, 57}}}

如何插入新的 ast.Expr 以使以下行保留在后面?

这是完整的示例:

package main

import (
    "crypto/rand"
    "fmt"
    "github.com/peterh/liner"
    "go/ast"
    "go/format"
    "go/parser"
    "go/token"
    "golang.org/x/crypto/argon2"
    "os"
    "runtime"
)

//======================================================================
// Terminal input
//----------------------------------------------------------------------

func ReadLogin(term *liner.State) string {
    login, err := term.Prompt("Login: ")
    if err != nil {
        panic(err)
    }
    return login
}

func ReadPassword(term *liner.State) string {
    password, err := term.PasswordPrompt("Password: ")
    if err != nil {
        panic(err)
    }
    return password
}

//======================================================================
// Mutate source file
//----------------------------------------------------------------------

type Source struct {
    FileName string
    FileSet  *token.FileSet
    AstFile  *ast.File
}

func NewSource() (this *Source) {
    this = new(Source)
    _, fn, _, ok := runtime.Caller(0)
    if !ok {
        panic("Can not read source.")
    }
    this.FileName = fn
    this.FileSet = token.NewFileSet()
    return
}

func (this *Source) Read() {
    var err error
    this.AstFile, err = parser.ParseFile(
        this.FileSet, this.FileName, nil, parser.ParseComments)
    if err != nil {
        panic(err)
    }
}

func (this *Source) Dump() {
    ast.Print(this.FileSet, this.AstFile)
}

// Search for top level decleration "var passwd = []*PwEntry{...}".
func (this *Source) FindPasswdDecl() *ast.CompositeLit {
    for _, node := range this.AstFile.Decls {
        switch decl := node.(type) {
        case *ast.GenDecl:
            if decl.Tok == token.VAR {
                // fmt.Println("found: var")
                for _, spec := range decl.Specs {
                    switch vspec := spec.(type) {
                    case *ast.ValueSpec:
                        if len(vspec.Names) == 1 && vspec.Names[0].Name == "passwd" {
                            // fmt.Println("found: passwd")
                            if len(vspec.Values) == 1 {
                                switch cl := vspec.Values[0].(type) {
                                case *ast.CompositeLit:
                                    switch clt := cl.Type.(type) {
                                    case *ast.ArrayType:
                                        // fmt.Println("found: []")
                                        switch se := clt.Elt.(type) {
                                        case *ast.StarExpr:
                                            // fmt.Println("found: *")
                                            switch sx := se.X.(type) {
                                            case *ast.Ident:
                                                if sx.Name == "PwEntry" {
                                                    // fmt.Println("found: PwEntry")
                                                    return cl
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    return nil
}

func AstString(s string) ast.Expr {
    return &ast.BasicLit{
        Kind: token.STRING,
        Value: fmt.Sprintf("%q", s)}
}

func AstInt(i int) ast.Expr {
    return &ast.BasicLit{
      Kind: token.INT,
        Value: fmt.Sprintf("%d", i)}
}

func AstByteArray(ba []byte) ast.Expr {
    cl := &ast.CompositeLit{
        Type: &ast.ArrayType{
            Elt: ast.NewIdent("byte")}}
    cl.Elts = make([]ast.Expr, len(ba))
    for i, b := range ba {
        cl.Elts[i] = AstInt(int(b))
    }
    return cl
}

func AstPwEntryElts(entry *PwEntry) []ast.Expr {
    return []ast.Expr{
        &ast.CompositeLit{
            Elts: []ast.Expr{
                &ast.KeyValueExpr{
                    Key: ast.NewIdent("Login"),
                    Value: AstString(entry.Login)},
                &ast.KeyValueExpr{
                    Key: ast.NewIdent("Salt"),
                    Value: AstByteArray(entry.Salt)},
                &ast.KeyValueExpr{
                    Key: ast.NewIdent("Algo"),
                    Value: AstInt(entry.Algo)},
                &ast.KeyValueExpr{
                    Key: ast.NewIdent("Hash"),
                    Value: AstByteArray(entry.Hash)}}}}
}

func (this *Source) SetPwEntry(entry *PwEntry) {
    cl := this.FindPasswdDecl()
    if cl.Elts == nil {
        cl.Elts = AstPwEntryElts(entry)
    } else {
        for i, elt := range cl.Elts {
            switch nelt := elt.(type) {
            case *ast.CompositeLit:
                if len(nelt.Elts) == 4 {
                    fmt.Println("found: 4 in %d", i)
                    return
                }
            }
        }
    }
}

func (this *Source) Write() {
    output, err := os.Create("tmp.go")
    if err != nil {
        panic(err)
    }
    err = format.Node(output, this.FileSet, this.AstFile)
    if err != nil {
        panic(err)
    }
}

//======================================================================
// Password entry
//----------------------------------------------------------------------

const SaltLength = 16
const (
    AlgoNil = iota
    AlgoArgon2id
)

type PwEntry struct {
    Login string
    Salt  []byte
    Algo  int
    Hash  []byte
}

func NewPwEntry() *PwEntry {
    this := new(PwEntry)
    this.Salt = make([]byte, SaltLength)
    _, err := rand.Read(this.Salt)
    if err != nil {
        panic(err)
    }
    return this
}

func (this *PwEntry) SetPassword(password []byte) {
    this.Algo = AlgoArgon2id
    this.Hash = argon2.IDKey(password, this.Salt, 1, 64*1024, 4, 32)
}

func (this *PwEntry) AlgoName() string {
    switch this.Algo {
    case AlgoNil:
        return "nil"
    case AlgoArgon2id:
        return "Argon2id"
    default:
        return "undefined"
    }
}

func (this *PwEntry) String() string {
    return fmt.Sprintf(`{"%s":"%s","%s":"%x","%s":"%s","%s":"%x"}`,
        "login", this.Login,
        "salt", this.Salt,
        "algo", this.AlgoName(),
        "hash", this.Hash,
    )
}

//======================================================================
// Password entries
//----------------------------------------------------------------------

var passwd = []*PwEntry{}
// line 1 after
// line 2 after

func SearchPwEntry(login string) *PwEntry {
    for _, entry := range passwd {
        if entry.Login == login {
            return entry
        }
    }
    return nil
}

func GetPwEntry(login string) *PwEntry {
    entry := SearchPwEntry(login)
    if entry == nil {
        entry = NewPwEntry()
        entry.Login = login
    }
    return entry
}

//======================================================================
// Main
//----------------------------------------------------------------------

func Passwd() {
    term := liner.NewLiner()
    defer term.Close()
    src := NewSource()
    defer src.Write()
    src.Read()
    //src.Dump()
    login := ReadLogin(term)
    entry := GetPwEntry(login)
    entry.SetPassword([]byte(ReadPassword(term)))
    fmt.Println(entry)
    src.SetPwEntry(entry)
}

func main() {
    Passwd()
}
go abstract-syntax-tree
1个回答
0
投票

这似乎是一个未解决的错误:https://github.com/golang/go/issues/20744

123

© www.soinside.com 2019 - 2024. All rights reserved.