Posted at

Go のソースコードからコメントを消す

More than 1 year has passed since last update.

go の parser.ParseFile で AST を取得して、この情報からコメントの情報を消して format.Node を使って再度コードに戻すことでコメントだけが消えたソースを生成することが出来ます。

package main

import (
"go/ast"
"go/format"
"go/parser"
"go/token"
"os"
)

var src = `
package p; // p package

/*
* sample
*/
func main() {
a := 1
a++ // increment
}
`

func main() {
fset := token.NewFileSet()
f, _ := parser.ParseFile(fset, "example.go", src, 0)

f.Comments = make([]*ast.CommentGroup, 0)

format.Node(os.Stdout, fset, f)
}

実行結果

package p

func main() {
a := 1
a++
}

コメントが消えたソースが出力されていることを確認出来ました。


仕組み

parser.ParseFile を利用して AST を取得するのですが、このデータの実態は ast.File でその中の情報にソースコードのコメント一覧が格納されているフィールドがあります。( Comments )

https://github.com/golang/go/blob/release-branch.go1.11/src/go/ast/ast.go#L986

type File struct {

Doc *CommentGroup // associated documentation; or nil
Package token.Pos // position of "package" keyword
Name *Ident // package name
Decls []Decl // top-level declarations; or nil
Scope *Scope // package scope (this file only)
Imports []*ImportSpec // imports in this file
Unresolved []*Ident // unresolved identifiers in this file
Comments []*CommentGroup // list of all comments in the source file
}

format.Node を使って ast.File をソースコードに変換する際に、コメント情報が Comments から取られるので Comments を何かしらの方法で値が空の状態にしてあげればコメント情報が戻せなくなるという訳です。

今回は parser.ParseFile 実行時のモード指定を 0 にしていたので各ノードにコメント情報が存在していませんでしたが、モードに parser.ParseComments を含めて Commentsnil で初期化するとソースコードを戻す際にノードのコメント情報も見るようになるので、今回の例だと一部のコメントは消されず戻ってきます。

https://github.com/golang/go/blob/release-branch.go1.11/src/go/printer/printer.go#L1114

    } else if n, ok := node.(*ast.File); ok {

// use ast.File comments, if any
p.comments = n.Comments
}

// if there are no comments, use node comments
p.useNodeComments = p.comments == nil

パース時に parser.ParseComments を指定して Commentsnil で初期化した ver

func main() {

fset := token.NewFileSet()
f, _ := parser.ParseFile(fset, "example.go", src, parser.ParseComments)
ast.Print(fset, f)

f.Comments = nil

format.Node(os.Stdout, fset, f)
}

実行結果

package p

/*
* sample
*/
func main() {
a := 1
a++
}

なんで一部だけ戻ってきたのかはまだ詳しく見てないのでわかっていないです。。。(わかったら追記します)