Help us understand the problem. What is going on with this article?

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++
}

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

siman
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away