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

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

このコミットは、Go言語の標準ライブラリ go/printer パッケージにおける、ファイル名変更時の挙動に関する不要なコードの削除を目的としています。具体的には、AST(抽象構文木)のノード位置情報に含まれるファイル名が変更された際に、go/printer が内部状態をリセットする処理が削除されました。

コミット

commit c00bda13528c47b604595b17d76cf4b89425f632
Author: Robert Griesemer <gri@golang.org>
Date:   Mon Nov 19 13:23:32 2012 -0800

    go/printer: simply ignore filename changes in position information
    
    There's no good reason to make any printer state adjustments
    simply because the file name in node position information has
    changed. Eliminate the relevant code.
    
    R=r
    CC=golang-dev
    https://golang.org/cl/6856054

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

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

元コミット内容

go/printer: simply ignore filename changes in position information

このコミットは、go/printer が位置情報におけるファイル名の変更を単に無視するように変更します。ノードの位置情報内のファイル名が変更されたという理由だけで、プリンタの状態を調整する正当な理由はないため、関連するコードを削除します。

変更の背景

Go言語の go/printer パッケージは、Goの抽象構文木(AST)を整形してGoのソースコードとして出力する役割を担っています。ASTは、ソースコードの構造を表現するデータ構造であり、各ノードは元のソースコード内の位置情報(ファイル名、行、列)を持っています。

以前の go/printer の実装では、writeString メソッド内で、現在のノードの位置情報 (pos) のファイル名が、直前のノードの位置情報 (p.last) のファイル名と異なる場合に、プリンタの内部状態(p.modep.wsbuf)をリセットする処理が含まれていました。この処理は、ast.MergePackageFiles のようなツールによって異なるファイルのASTがマージされた場合など、複数のファイルから構成されるASTを整形する際に、プリンタの状態を適切に調整することを意図していたと考えられます。

しかし、コミットメッセージにあるように、「ノードの位置情報内のファイル名が変更されたという理由だけで、プリンタの状態を調整する正当な理由はない」という判断が下されました。ファイル名の変更は、コードの構造や整形に直接影響を与えるものではなく、むしろ不必要な状態リセットが、整形結果の不整合やバグ(特にインデントのずれなど)を引き起こす可能性がありました。コミットメッセージで言及されている "issue 4300 (11/16/2012)" は、この問題に関連する可能性がありましたが、Goの公式イシュートラッカーでは該当するイシューは見つかりませんでした。しかし、このコメントが示唆するように、ファイル名変更による状態リセットが、インデントのずれなどの問題を引き起こす可能性があったことが背景にあります。

この変更は、go/printer の堅牢性と予測可能性を高め、特に複雑なAST操作が行われた場合でも、正しい整形結果を保証することを目的としています。

前提知識の解説

  • Go言語の go/ast および go/printer パッケージ:
    • go/ast パッケージは、Goのソースコードを解析して抽象構文木(AST)を構築するための機能を提供します。ASTは、プログラムの構造を木構造で表現したもので、コンパイラやツールがコードを理解・操作するために使用します。
    • go/printer パッケージは、go/ast で構築されたASTをGoのソースコードとして整形(pretty-print)するための機能を提供します。これにより、人間が読みやすい形式でコードを出力したり、コードの自動整形を行ったりすることができます。
  • token.Position:
    • Go言語の go/token パッケージで定義されている token.Position 構造体は、ソースコード内の特定の位置(ファイル名、行番号、列番号、オフセット)を表します。ASTの各ノードは、通常、この token.Position を保持しており、元のソースコードのどの部分に対応するかを示します。
  • ASTのマージ:
    • Goのツールの中には、複数のソースファイルから生成されたASTを一つにマージする機能を持つものがあります(例: ast.MergePackageFiles)。このような操作を行うと、マージされたAST内のノードが、異なる元のファイルからの位置情報を持つ可能性があります。
  • プリンタの内部状態:
    • go/printer は、コードを整形する際に、現在のインデントレベル、行の開始位置、空白文字のバッファなど、様々な内部状態を管理しています。これらの状態は、整形結果の正確性に直接影響します。不適切な状態リセットは、インデントのずれや余分な空白の挿入など、整形結果の品質を低下させる可能性があります。

技術的詳細

このコミットの技術的な核心は、go/printer パッケージの printer 型の writeString メソッドから、ファイル名の変更を検出してプリンタの内部状態をリセットするロジックを削除した点にあります。

削除されたコードブロックは以下の通りです。

		// reset state if the file changed
		// (used when printing merged ASTs of different files
		// e.g., the result of ast.MergePackageFiles)
		if p.last.IsValid() && p.last.Filename != pos.Filename {
			// Note: Do not set p.indent to 0 - this seems to be a bad heuristic.
			//       ASTs may be created by various tools and built from nodes of
			//       different files. An incorrectly constructed AST will likely
			//       not print at all, but a (structurally) correct AST with bad
			//       position information should still print structurally correct.
			//       If p.indent is reset, indentation may be off, and likely lead
			//       to indentation underflow (to detect set: debug = true).
			//       See also issue 4300 (11/16/2012).
			p.mode = 0
			p.wsbuf = p.wsbuf[0:0]
		}

このコードは、以下の条件が満たされた場合に実行されていました。

  1. p.last.IsValid(): 直前の位置情報が有効であること。
  2. p.last.Filename != pos.Filename: 直前のノードのファイル名と現在のノードのファイル名が異なること。

この条件が満たされると、プリンタの p.mode0 にリセットされ、p.wsbuf (空白文字バッファ) がクリアされていました。

コミットメッセージが示唆するように、このロジックは「異なるファイルのASTがマージされた場合」を想定していましたが、実際にはファイル名の変更がプリンタの整形ロジックに影響を与えるべきではありませんでした。ファイル名は、あくまで元のソースコードの位置を示すメタデータであり、コードの構造やインデント、空白の挿入といった整形ルールとは直接関係がありません。

むしろ、この状態リセットは、以下のような問題を引き起こす可能性がありました。

  • 不適切なインデント: コメントにもあるように、p.indent (インデントレベル) はリセットされないものの、p.mode のリセットがインデントの計算に影響を与え、結果的にインデントがずれる可能性がありました。特に、p.mode はプリンタが現在どのような状態(例えば、行の先頭であるか、コメントの途中であるかなど)にあるかを示すフラグであり、これを不適切にリセットすると、後続の整形処理が誤った前提で実行されることになります。
  • 空白文字の不整合: p.wsbuf は、出力すべき空白文字を一時的に保持するバッファです。これをクリアすることで、本来出力されるべき空白が失われたり、逆に不必要な空白が挿入されたりする可能性がありました。

この変更により、go/printer はファイル名の変更を完全に無視し、コードの構造に基づいて一貫した整形処理を行うようになります。これにより、特に複雑なAST操作が行われた場合でも、より堅牢で予測可能な整形結果が得られることが期待されます。

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

変更は src/pkg/go/printer/printer.go ファイルの writeString メソッド内で行われました。

--- a/src/pkg/go/printer/printer.go
+++ b/src/pkg/go/printer/printer.go
@@ -221,21 +221,6 @@ func (p *printer) writeString(pos token.Position, s string, isLit bool) {
 	\t// atLineBegin updates p.pos if there's indentation, but p.pos
 	\t// is the position of s.
 	\tp.pos = pos
-\t\t// reset state if the file changed
-\t\t// (used when printing merged ASTs of different files
-\t\t// e.g., the result of ast.MergePackageFiles)
-\t\tif p.last.IsValid() && p.last.Filename != pos.Filename {\n-\t\t\t// Note: Do not set p.indent to 0 - this seems to be a bad heuristic.\n-\t\t\t//       ASTs may be created by various tools and built from nodes of\n-\t\t\t//       different files. An incorrectly constructed AST will likely\n-\t\t\t//       not print at all, but a (structurally) correct AST with bad\n-\t\t\t//       position information should still print structurally correct.\n-\t\t\t//       If p.indent is reset, indentation may be off, and likely lead\n-\t\t\t//       to indentation underflow (to detect set: debug = true).\n-\t\t\t//       See also issue 4300 (11/16/2012).\n-\t\t\tp.mode = 0\n-\t\t\tp.wsbuf = p.wsbuf[0:0]\n-\t\t}\n \t}\n \n \tif isLit {\n```

具体的には、221行目から235行目までの15行が削除されました。

## コアとなるコードの解説

削除されたコードブロックは、`go/printer` の `printer` 型が持つ `writeString` メソッドの一部でした。このメソッドは、ASTノードから文字列(識別子、リテラルなど)を整形して出力する際に呼び出されます。

削除された `if` ブロックは、以下の役割を担っていました。

1.  **ファイル名変更の検出**: `p.last.Filename != pos.Filename` という条件で、現在のノードの位置情報 (`pos`) のファイル名が、直前に処理されたノードの位置情報 (`p.last`) のファイル名と異なるかどうかをチェックしていました。
2.  **プリンタ状態のリセット**: ファイル名が変更されたと検出された場合、プリンタの内部状態である `p.mode` を `0` に、`p.wsbuf` を空のスライスにリセットしていました。
    *   `p.mode`: プリンタの現在のモードや状態を示すフラグです。これを `0` にリセットすることは、プリンタが「初期状態」に戻ることを意味し、その後の整形ロジックに影響を与えます。
    *   `p.wsbuf`: 出力待ちの空白文字を保持するバッファです。これをクリアすることで、ファイル名変更の境界で空白文字の出力がリセットされます。

コミットメッセージと削除されたコードのコメントが示唆するように、このロジックは「マージされたASTを整形する際に使用される」ことを意図していましたが、実際にはファイル名の変更が整形ロジックに影響を与えるべきではないという結論に至りました。ファイル名はコードの構造とは独立した情報であり、その変更によってプリンタの内部状態をリセットすることは、むしろ整形結果の不整合やバグ(特にインデントのずれなど)を引き起こす可能性がありました。

このコードの削除により、`go/printer` はファイル名の変更を完全に無視し、ASTの構造のみに基づいて整形処理を行うようになります。これにより、よりシンプルで堅牢な整形ロジックが実現され、特に複数のファイルからマージされたASTを整形する際の予測可能性と正確性が向上します。

## 関連リンク

*   **Go言語の公式リポジトリ**: [https://github.com/golang/go](https://github.com/golang/go)
*   **Gerrit Change-Id**: `https://golang.org/cl/6856054` (GoプロジェクトのコードレビューシステムであるGerritの変更リンク)

## 参考にした情報源リンク

*   Go言語のソースコード (`src/pkg/go/printer/printer.go`)
*   Gitコミットメッセージ
*   Go言語の `go/ast` および `go/token` パッケージに関する一般的な知識
*   `golang issue 4300` についてのWeb検索結果(公式イシューとしては見つからず、他のプロジェクトのイシューがヒットしたため、このコミットで言及されているイシューは内部的なものか、あるいは番号が異なる可能性があります。)