Golang中将AST转换为代码时注释位置错乱问题

Golang中将AST转换为代码时注释位置错乱问题 当我将AST转换回Go程序时,原始Go程序中的所有注释都跑到了最后:

这是测试文件:test.go

package main
import "fmt"

func main() {
    // comment here
    if true{
	  fmt.Println("needs to be treated")
    } else {
      fmt.Println("you done it")
   }
}

这是我得到的输出:output.go

package main
import "fmt"

func main() {
        if true {
                fmt.Println("needs to be treated")
        } else {
                fmt.Println("you done it")
        }
}
// comment here

这是我的代码:main.go

package main
import (
	"bytes"
	"fmt"
	"go/format"
	"go/parser"
	"go/token"
	"log"
)

func main() {
	fs := token.NewFileSet()
	f, err := parser.ParseFile(fs, "test.go", nil, parser.ParseComments)
	if err != nil {
		log.Fatal(err)
	}

	fset := token.NewFileSet()

	var buf bytes.Buffer
	err = format.Node(&buf, fset, f)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(buf.String())
}

要运行此代码,只需在终端中执行:go run ./main.go > output.go

执行后,为什么注释会跑到下面去?Go中是否有其他格式化函数不会这样做? 如果我查看test.go,我发现格式化函数读取的是AST文件中位于最后的注释部分。因此注释被放到了下面。 那么,有人能建议Go中是否有其他格式化函数可以将注释放在正确的位置吗?


更多关于Golang中将AST转换为代码时注释位置错乱问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

go/ast 包在处理注释方面存在问题。相关问题的讨论可以查看这里。有一个名为 dst 的包旨在解决这个问题。

更多关于Golang中将AST转换为代码时注释位置错乱问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,处理AST时保留注释位置需要使用正确的配置和API。问题在于format.Node函数默认不会处理注释位置,需要使用go/format包的Source函数配合parser.ParseComments模式。

以下是修正后的代码:

package main

import (
	"fmt"
	"go/format"
	"go/parser"
	"go/token"
	"log"
	"os"
)

func main() {
	// 读取源文件
	src, err := os.ReadFile("test.go")
	if err != nil {
		log.Fatal(err)
	}

	// 解析文件并保留注释
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "test.go", src, parser.ParseComments)
	if err != nil {
		log.Fatal(err)
	}

	// 使用format.Source处理,它会保留注释位置
	formatted, err := format.Source(src)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(formatted))
}

或者,如果你需要操作AST后再格式化,应该这样做:

package main

import (
	"bytes"
	"fmt"
	"go/format"
	"go/parser"
	"go/token"
	"log"
)

func main() {
	// 创建文件集
	fset := token.NewFileSet()
	
	// 解析文件并保留注释
	f, err := parser.ParseFile(fset, "test.go", nil, parser.ParseComments)
	if err != nil {
		log.Fatal(err)
	}

	// 这里可以对AST进行操作
	// 例如修改函数、添加语句等

	// 使用bytes.Buffer和format.Node,但需要正确的文件集
	var buf bytes.Buffer
	if err := format.Node(&buf, fset, f); err != nil {
		log.Fatal(err)
	}

	fmt.Println(buf.String())
}

关键点:

  1. 使用parser.ParseFile时必须包含parser.ParseComments标志
  2. format.Node需要传入正确的fset(包含位置信息的文件集)
  3. 如果只是格式化源代码而不修改AST,使用format.Source更简单

对于你的测试文件,修正后的代码会输出:

package main

import "fmt"

func main() {
	// comment here
	if true {
		fmt.Println("needs to be treated")
	} else {
		fmt.Println("you done it")
	}
}

注释会保持在原来的位置。

回到顶部