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

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

このコミットは、Goコンパイラ(gc)のエラーメッセージの書式設定に関する改善です。具体的には、不正なインポートパスに関するエラーメッセージにおいて、インポートパスが引用符で囲まれて表示されるように変更されました。これにより、エラーメッセージの可読性が向上し、ユーザーが問題のあるパスをより明確に識別できるようになります。

コミット

commit cc99d8ad0bb8a2299a72dcf123d615335ea09964
Author: Russ Cox <rsc@golang.org>
Date:   Tue Mar 13 09:33:54 2012 -0400

    gc: use quoted string format in import error
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/5794077

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

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

元コミット内容

gc: use quoted string format in import error

R=ken2
CC=golang-dev
https://golang.org/cl/5794077

変更の背景

Goコンパイラは、ソースコードのコンパイル中に様々なエラーを検出してユーザーに報告します。その中でも、import文で指定されるパッケージのパスが不正である場合、コンパイラはエラーメッセージを出力します。このコミット以前は、これらのエラーメッセージにおいて、問題のあるインポートパスがそのままの文字列として表示されていました。

しかし、インポートパスにスペースや特殊文字が含まれている場合、エラーメッセージ内でパスの開始と終了が不明瞭になることがありました。例えば、import path contains space character: my path/to/pkg のようなメッセージでは、my path/to/pkg が一つのパスとして認識しにくい場合があります。このような曖昧さを解消し、ユーザーがエラーの原因となっている正確な文字列を視覚的に把握しやすくするために、インポートパスを引用符で囲んで表示するよう変更する必要がありました。

この変更は、コンパイラが出力するエラーメッセージの品質とユーザーエクスペリエンスを向上させることを目的としています。

前提知識の解説

Goコンパイラ (gc)

gcは、Go言語の公式コンパイラであり、Goプログラムを機械語に変換する役割を担っています。Go言語のツールチェインの中核をなすコンポーネントの一つです。src/cmd/gcディレクトリ以下にそのソースコードが格納されており、コンパイルの各段階(字句解析、構文解析、型チェック、最適化、コード生成など)を処理します。

インポートパス

Go言語では、他のパッケージの機能を利用するためにimport文を使用します。import文に続く文字列は「インポートパス」と呼ばれ、Goモジュールや標準ライブラリ内のパッケージの場所を識別します。例えば、"fmt"は標準ライブラリのfmtパッケージを、"github.com/user/repo/mypkg"はGitHub上の特定のパッケージを指します。インポートパスは、Goのビルドシステムが依存関係を解決し、必要なコードを見つけるために不可欠です。

yyerror関数

yyerrorは、YaccやBisonといったパーサジェネレータによって生成されるC言語のコードでよく見られるエラー報告関数です。通常、構文解析中にエラーが検出された際に呼び出され、エラーメッセージを標準エラー出力(stderr)に表示します。この関数は、printf関数と同様に、フォーマット文字列と可変個の引数を受け取ることが一般的です。Goコンパイラの内部でも、同様の目的でエラー報告のために使用されています。

Strlit

Strlitは、Goコンパイラの内部で文字列リテラルを表現するために使用されるデータ構造であると推測されます。コンパイラは、ソースコード中の文字列定数やパスなどの文字列情報を、このStrlitのような内部表現に変換して扱います。この構造体は、文字列の実際のデータ(sフィールドなど)だけでなく、文字列の長さやエンコーディングに関する情報など、他のメタデータも保持している可能性があります。

フォーマット指定子 (%s, %Z)

C言語のprintf関数やそれに類する関数では、フォーマット文字列内の「フォーマット指定子」を使用して、対応する引数をどのように解釈し、文字列として出力するかを指示します。

  • %s: 標準的なC文字列(char*)をそのまま出力するための指定子です。
  • %Z: このコミットで導入された%Zは、標準Cライブラリには存在しないカスタムのフォーマット指定子です。これは、Goコンパイラのyyerror関数またはその下層の書式設定ロジックが、Strlit型(またはそのポインタ)を引数として受け取り、それを自動的に引用符で囲んだ文字列として整形して出力するための、Goコンパイラ独自の拡張であると考えられます。

技術的詳細

このコミットの核心は、Goコンパイラのsrc/cmd/gc/subr.cファイル内のisbadimport関数におけるエラー報告の改善です。isbadimport関数は、与えられたインポートパスがGoの仕様に準拠しているか(例えば、無効なUTF-8シーケンス、制御文字、バックスラッシュ、スペース、不正な文字が含まれていないか)を検証します。

変更前は、yyerror関数を呼び出す際に、path->sStrlit構造体の文字列データ部分)を%sフォーマット指定子で直接出力していました。これは、パスがそのままの形でエラーメッセージに埋め込まれることを意味します。

変更後、yyerrorの呼び出しは、フォーマット指定子を%sから%Zに変更し、引数もpath->sからpathStrlit型へのポインタ)に直接変更されました。この変更は、以下のメカニズムに基づいています。

  1. カスタムフォーマット指定子 %Z の導入: Goコンパイラの内部的なエラー報告システム(yyerrorの背後にある実装)が拡張され、新しいフォーマット指定子%Zを認識するようになりました。
  2. Strlit型の直接処理: %Z指定子に対応する引数としてStrlit型(またはそのポインタ)が渡されると、コンパイラの書式設定ロジックがそのStrlitオブジェクトから文字列データを抽出し、自動的に二重引用符(")で囲んで出力するようになりました。

このアプローチにより、isbadimport関数内でエラーを報告する際に、開発者が明示的に引用符を追加する手間が省かれ、かつ一貫した引用符付きのパス表示が保証されます。例えば、不正なパスが"my path"であった場合、以前はmy pathと表示されていたものが、変更後は"my path"と表示されるようになり、パスの境界が明確になります。これは、特にパスに空白文字や特殊文字が含まれる場合に、ユーザーがエラーメッセージを解釈する上で非常に役立ちます。

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

変更は、src/cmd/gc/subr.cファイル内のisbadimport関数に集中しています。具体的には、yyerror関数の呼び出しが5箇所変更されています。

--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -3624,23 +3624,23 @@ isbadimport(Strlit *path)
 	while(*s) {
 		s += chartorune(&r, s);
 		if(r == Runeerror) {
-			yyerror("import path contains invalid UTF-8 sequence: \"%s\"", path->s);
+			yyerror("import path contains invalid UTF-8 sequence: \"%Z\"", path);
 			return 1;
 		}
 		if(r < 0x20 || r == 0x7f) {
-			yyerror("import path contains control character: \"%s\"", path->s);
+			yyerror("import path contains control character: \"%Z\"", path);
 			return 1;
 		}
 		if(r == '\\') {
-			yyerror("import path contains backslash; use slash: \"%s\"", path->s);
+			yyerror("import path contains backslash; use slash: \"%Z\"", path);
 			return 1;
 		}
 		if(isspacerune(r)) {
-			yyerror("import path contains space character: \"%s\"", path->s);
+			yyerror("import path contains space character: \"%Z\"", path);
 			return 1;
 		}
 		if(utfrune("!\"#$%&'()*,:;<=>?[]^`{|}", r)) {
-			yyerror("import path contains invalid character '%C': \"%s\"", r, path->s);
+			yyerror("import path contains invalid character '%C': \"%Z\"", r, path);
 			return 1;
 		}
 	}

コアとなるコードの解説

上記の差分は、isbadimport関数内の5つのyyerror呼び出しにおいて、以下の2つの変更が行われていることを示しています。

  1. フォーマット文字列の変更:

    • 変更前: "...: \"%s\""
    • 変更後: "...: \"%Z\"" %s%Zに置き換えられています。これは、yyerror関数が文字列を処理する方法が変更されたことを示唆しています。
  2. 引数の変更:

    • 変更前: path->s
    • 変更後: path Strlit構造体のsフィールド(おそらく文字列データへのポインタ)を直接渡す代わりに、Strlit構造体全体(またはそのポインタ)をyyerrorに渡すようになっています。

これらの変更は、yyerror関数(またはその内部の書式設定ロジック)が、%Zフォーマット指定子を受け取った際に、Strlit型の引数を自動的に引用符で囲んで出力するように実装されたことを強く示唆しています。これにより、エラーメッセージに表示されるインポートパスが常に明確に引用符で囲まれ、可読性が向上します。

例えば、import path contains invalid UTF-8 sequence: "%s" の行では、以前はpath->sが直接文字列として展開されていましたが、変更後はpathオブジェクトが%Zによって処理され、自動的に引用符で囲まれた形式で出力されます。

関連リンク

  • Go Code Review (CL) 5794077: https://golang.org/cl/5794077 このコミットの元となったGoのコードレビューページです。詳細な議論や変更の経緯が確認できます。

参考にした情報源リンク

  • Go言語の公式ドキュメント (Go言語のインポートパス、コンパイラに関する一般的な情報)
  • C言語のprintfフォーマット指定子に関する一般的な情報
  • Yacc/Bisonなどのパーサジェネレータにおけるエラー報告の慣習に関する一般的な情報