Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 14247] ファイルの概要

このコミットは、Go言語のコードフォーマッタであるgofmtと、その内部で使用されるgo/printerパッケージにおけるコメント内の末尾の空白文字のトリミングに関する変更を導入しています。具体的には、コメントの整形時に不要な末尾の空白が削除されるようになり、これによりコードの見た目の一貫性が向上します。

コミット

commit db2b6ed854122bc987e0cb8778a9c77b481614ec
Author: Robert Griesemer <gri@golang.org>
Date:   Tue Oct 30 13:09:47 2012 -0700

    go/printer, gofmt: trim trailing whitespace in comments
    
    Also: updated go fix testcases to pass tests.
    
    Fixes #4310.
    
    R=r
    CC=golang-dev
    https://golang.org/cl/6810055

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/db2b6ed854122bc987e0cb8778a9c77b481614ec

元コミット内容

go/printer, gofmt: trim trailing whitespace in comments

このコミットは、go/printerパッケージとgofmtツールにおいて、コメント内の末尾の空白文字をトリミングする変更を実装しています。また、go fixのテストケースがこの変更に対応するように更新されています。この変更はIssue #4310を修正するものです。

変更の背景

Go言語の公式フォーマッタであるgofmtは、Goコードのスタイルを統一し、可読性を高める上で非常に重要なツールです。gofmtは、コードのインデント、スペース、改行などを自動的に調整しますが、コメント内の空白文字の扱いは、これまで一貫性が保たれていない場合がありました。特に、コメントの行末に不要な空白文字が残っていると、コードの見た目が乱れるだけでなく、バージョン管理システムでの差分表示(diff)が不必要に大きくなる原因にもなります。

このコミットの背景には、おそらくユーザーからのフィードバックや、開発者自身がコードベースのクリーンさを追求する中で、コメント内の末尾の空白が問題として認識されたことがあります。Issue #4310がこの問題の具体的な報告であったと考えられます。末尾の空白は、エディタの設定や手動での入力ミスによって容易に発生し、コードの品質を低下させる要因となります。この変更は、gofmtがより厳密にコードスタイルを強制し、Goコードベース全体の品質と一貫性を向上させることを目的としています。

前提知識の解説

  • gofmt: Go言語のソースコードを自動的にフォーマットするツールです。Go言語の標準的なコーディングスタイルを強制し、コードの可読性と一貫性を高めます。開発者がスタイルガイドを意識することなく、常に統一されたコードを書けるようにするために不可欠なツールです。
  • go/printerパッケージ: gofmtの基盤となるパッケージの一つで、Goの抽象構文木(AST)を整形してGoソースコードとして出力する機能を提供します。このパッケージが、コメントを含むコードの整形ロジックを担っています。
  • 抽象構文木(AST: Abstract Syntax Tree): プログラムのソースコードを解析して得られる、その構造を木構造で表現したものです。コンパイラやリンタ、フォーマッタなどのツールは、ソースコードをASTに変換し、そのASTを操作することで様々な処理を行います。go/printerはASTを受け取り、整形されたコードを出力します。
  • 末尾の空白(Trailing Whitespace): 行の末尾に存在するスペースやタブなどの空白文字のことです。プログラミングにおいては、通常は意味を持たず、コードの見た目を損ねたり、差分管理を複雑にしたりする原因となることがあります。
  • go fix: Go言語の古いAPIや構文を新しいものに自動的に修正するツールです。Go言語のバージョンアップに伴うコードの移行を支援します。このコミットでは、gofmtの変更によって影響を受ける可能性のあるgo fixのテストケースが更新されています。

技術的詳細

このコミットの主要な変更は、src/pkg/go/printer/printer.goファイルに集中しています。具体的には、コメントの出力処理において、行末の空白を削除するロジックが追加されました。

  1. unicodeパッケージの導入: 新たにunicodeパッケージがインポートされています。これは、Unicodeのプロパティに基づいて文字を分類するための機能を提供し、特にunicode.IsSpace関数が空白文字の判定に利用されます。
  2. trimRight関数の追加: strings.TrimRightFunc関数とunicode.IsSpaceを組み合わせて、文字列の末尾から空白文字をトリミングするtrimRightヘルパー関数が追加されました。
    // trimRight returns s with trailing whitespace removed.
    func trimRight(s string) string {
    	return strings.TrimRightFunc(s, unicode.IsSpace)
    }
    
    この関数は、Goの標準ライブラリであるstringsパッケージのTrimRightFuncを使用しています。TrimRightFuncは、指定された関数(ここではunicode.IsSpace)がtrueを返す文字を文字列の右端から削除します。unicode.IsSpaceは、Unicodeの空白文字(スペース、タブ、改行など)を判定します。
  3. writeComment関数でのtrimRightの適用: go/printerパッケージのwriteComment関数は、コメントを整形して出力する役割を担っています。この関数内で、コメントの各行を出力する直前に、新しく追加されたtrimRight関数が適用されるようになりました。
    • //スタイルのコメントの場合:
      // shortcut common case of //-style comments
      if text[1] == '/' {
      	p.writeString(pos, trimRight(text), true)
      	return
      }
      
      コメント全体(text)に対してtrimRightが適用されます。
    • /* ... */スタイルのコメントの場合:
      if len(line) > 0 {
      	p.writeString(pos, trimRight(line), true)
      }
      
      コメントを構成する各行(line)に対してtrimRightが適用されます。

この変更により、go/printerがコメントを出力する際に、自動的に行末の不要な空白が除去されるようになり、結果としてgofmtによって整形されたコードのコメントがよりクリーンになります。

また、src/cmd/fix/testdata/以下の複数のテストファイル(reflect.encoder.go.in, reflect.encoder.go.out, reflect.export.go.in, reflect.export.go.out, reflect.print.go.in, reflect.print.go.out)が更新されています。これは、go fixがこれらのテストケースを処理する際に、コメントの末尾の空白がトリミングされることを考慮して、期待される出力(.outファイル)が修正されたことを示しています。これにより、go fixのテストが新しいgofmtの挙動と整合性が取れるようになります。

さらに、src/pkg/go/printer/testdata/comments.goldensrc/pkg/go/printer/testdata/comments.inputも更新されています。これらはgo/printerのテストデータであり、コメントの整形に関する期待される挙動を定義しています。特にcomments.inputには末尾に空白を持つコメントが追加され、comments.goldenにはそれらがトリミングされた状態が記述されており、この変更が正しく機能していることを検証しています。

コアとなるコードの変更箇所

src/pkg/go/printer/printer.go

--- a/src/pkg/go/printer/printer.go
+++ b/src/pkg/go/printer/printer.go
@@ -14,6 +14,7 @@ import (
 	"strconv"
 	"strings"
 	"text/tabwriter"
+	"unicode"
 )
 
 const (
@@ -405,6 +406,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
 // Split comment text into lines
 // (using strings.Split(text, "\n") is significantly slower for
 // this specific purpose, as measured with: go test -bench=Print)
+//
 func split(text string) []string {
 	// count lines (comment text never ends in a newline)
 	n := 1
@@ -432,6 +434,7 @@ func split(text string) []string {
 
 // Returns true if s contains only white space
 // (only tabs and blanks can appear in the printer's context).
+//
 func isBlank(s string) bool {
 	for i := 0; i < len(s); i++ {
 		if s[i] > ' ' {
@@ -441,6 +444,7 @@ func isBlank(s string) bool {
 	return true
 }
 
+// commonPrefix returns the common prefix of a and b.
 func commonPrefix(a, b string) string {
 	i := 0
 	for i < len(a) && i < len(b) && a[i] == b[i] && (a[i] <= ' ' || a[i] == '*') {
@@ -449,6 +453,11 @@ func commonPrefix(a, b string) string {
 	return a[0:i]
 }
 
+// trimRight returns s with trailing whitespace removed.
+func trimRight(s string) string {
+	return strings.TrimRightFunc(s, unicode.IsSpace)
+}
+
 // stripCommonPrefix removes a common prefix from /*-style comment lines (unless no
 // comment line is indented, all but the first line have some form of space prefix).\n // The prefix is computed using heuristics such that is is likely that the comment
 //
@@ -611,7 +620,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
 
 	// shortcut common case of //-style comments
 	if text[1] == '/' {
-		p.writeString(pos, text, true)
+		p.writeString(pos, trimRight(text), true)
 		return
 	}
 
@@ -641,7 +650,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
 			pos = p.pos
 		}
 		if len(line) > 0 {
-			p.writeString(pos, line, true)
+			p.writeString(pos, trimRight(line), true)
 		}
 	}
 }
@@ -1159,7 +1168,7 @@ func (p *trimmer) Write(data []byte) (n int, err error) {
 // ----------------------------------------------------------------------------
 // Public interface
 
-// A Mode value is a set of flags (or 0). They control printing. 
+// A Mode value is a set of flags (or 0). They control printing.
 type Mode uint
 
 const (

src/pkg/go/printer/testdata/comments.golden および src/pkg/go/printer/testdata/comments.input

これらのファイルは、go/printerのテストデータであり、コメントの整形に関する期待される挙動を定義しています。特に、末尾に空白を持つコメントが追加され、それがトリミングされた結果がcomments.goldenに反映されています。

コアとなるコードの解説

このコミットの核心は、src/pkg/go/printer/printer.goに追加されたtrimRight関数と、それがwriteComment関数内でどのように利用されているかです。

  1. import "unicode": Goの標準ライブラリであるunicodeパッケージがインポートされています。これは、Unicode文字のプロパティ(例えば、ある文字が空白文字であるかどうか)を判定するための関数を提供します。

  2. func trimRight(s string) string: この新しいヘルパー関数は、与えられた文字列sの末尾からすべての空白文字を削除します。 strings.TrimRightFunc(s, unicode.IsSpace)という呼び出しがその役割を果たします。

    • strings.TrimRightFunc: stringsパッケージの関数で、第二引数に渡された関数がtrueを返す文字を、文字列の右端から連続して削除します。
    • unicode.IsSpace: unicodeパッケージの関数で、引数に渡されたルーン(Unicodeコードポイント)が空白文字である場合にtrueを返します。これにより、通常のスペースやタブだけでなく、Unicodeで定義されている様々な空白文字(例えば、ノーブレークスペースなど)も正確に識別し、削除することができます。
  3. func (p *printer) writeComment(comment *ast.Comment)内でのtrimRightの適用: writeComment関数は、GoのASTからコメントノードを受け取り、それを整形して出力する主要なロジックを含んでいます。

    • //スタイルのコメント: if text[1] == '/'の条件で識別される単一行コメントの場合、コメントのテキスト全体(text変数)に対してtrimRight(text)が適用されます。これにより、コメント行全体の末尾の空白が削除されます。
    • /* ... */スタイルのコメント: 複数行にわたるブロックコメントの場合、コメントは行ごとに処理されます。if len(line) > 0のブロック内で、各コメント行(line変数)に対してtrimRight(line)が適用されます。これにより、ブロックコメント内の各行の末尾の空白が個別に削除されます。

この変更により、go/printerはコメントを出力する際に、自動的に行末の不要な空白を検出し、それらを削除するようになりました。これは、gofmtがGoコードを整形する際の「厳密さ」を向上させ、コードベース全体の一貫性とクリーンさを保つ上で重要な改善です。

関連リンク

参考にした情報源リンク