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

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

このコミットは、Go言語のコンパイラ(gc)におけるエラーメッセージの改善に関するものです。具体的には、不正なインポートパスが検出された際に表示されるエラーメッセージに、問題となっているインポートパスの全文を含めるように変更されています。これにより、開発者はどのインポートパスがエラーの原因となっているのかをより迅速かつ正確に特定できるようになります。

コミット

commit daacba518425fe2dcbdfd89ff43f8ab11cdabea8
Author: Rob Pike <r@golang.org>
Date:   Tue Mar 13 15:35:08 2012 +1100

    gc: include full text of import path in error message
    
    R=golang-dev, dsymonds
    CC=golang-dev
    https://golang.org/cl/5783091

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

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

元コミット内容

gc: include full text of import path in error message

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/5783091

変更の背景

Go言語のコンパイラgcは、ソースコードをコンパイルする際に、import文で指定されたパッケージのパスがGoの仕様に準拠しているかを検証します。これには、パスが有効なUTF-8シーケンスであるか、制御文字を含んでいないか、バックスラッシュではなくスラッシュを使用しているか、スペース文字を含んでいないか、予約済みの特殊文字を含んでいないか、といった多岐にわたるチェックが含まれます。

このコミットが導入される前は、これらのチェックのいずれかに失敗した場合、コンパイラは一般的なエラーメッセージ(例:「import path contains invalid UTF-8 sequence」)を出力していました。しかし、このメッセージだけでは、どのインポートパスが問題を引き起こしているのか、特に複数のimport文が存在する場合や、長いパスの場合には特定が困難でした。開発者はエラーの原因を特定するために、手動でコードを検査したり、デバッグ情報を追加したりする必要があり、開発効率が低下していました。

この問題に対処するため、エラーメッセージに具体的なインポートパスを含めることで、エラーの診断と解決を容易にすることが目的とされました。これにより、開発者はエラーメッセージを見ただけで、どのインポートパスを修正すべきかを即座に理解できるようになります。

前提知識の解説

  • Go言語のインポートパス: Go言語では、パッケージをインポートする際に、そのパッケージのソースコードがどこにあるかを示すパスを指定します。これは通常、Goモジュールのパスや、標準ライブラリのパッケージ名などです。例えば、"fmt"は標準ライブラリのフォーマットパッケージを、"github.com/user/repo/pkg"はGitHub上のリポジトリにあるパッケージを指します。インポートパスには特定の命名規則と文字の制約があります。
  • Goコンパイラ (gc): gcはGo言語の公式コンパイラであり、Goのソースコードを機械語に変換する役割を担っています。Goツールチェーンの一部として提供され、go buildコマンドなどで内部的に利用されます。
  • src/cmd/gc/subr.c: このファイルは、Goコンパイラgcのソースコードの一部であり、主にサブルーチンやユーティリティ関数が定義されています。isbadimport関数のように、コンパイル時に様々なチェックを行うためのヘルパー関数が含まれています。C言語で記述されています。
  • yyerror: yyerrorは、コンパイラやパーサーの文脈でよく使われるエラー報告関数です。通常、構文解析中にエラーが検出された際に呼び出され、エラーメッセージを標準エラー出力に出力します。C言語のprintf関数のように、フォーマット文字列と可変引数を受け取ることができ、動的にメッセージを生成できます。
  • UTF-8: Unicode文字を符号化するための可変長文字コードです。Go言語のソースコードや文字列はUTF-8で扱われることが多く、インポートパスもUTF-8でエンコードされている必要があります。
  • 制御文字: ASCIIコードの0x00から0x1F、および0x7Fに割り当てられている、表示されない特殊な文字です。これらは通常、テキストのフォーマットやデバイス制御に使用され、インポートパスには含まれるべきではありません。
  • Rune: Go言語におけるUnicodeコードポイントを表す型です。C言語の文脈では、wchar_tintでUnicode文字を扱うことがあります。chartorune関数は、UTF-8バイト列からRuneを読み取るために使用されます。

技術的詳細

この変更は、Goコンパイラのsrc/cmd/gc/subr.cファイル内のisbadimport関数に焦点を当てています。isbadimport関数は、与えられたインポートパス(Strlit *path)がGoのインポートパスの規則に違反していないかを検証する役割を担っています。

具体的には、以下の種類の不正なインポートパスをチェックしています。

  1. 無効なUTF-8シーケンス: chartorune関数を使用してパスをRuneにデコードする際に、無効なUTF-8バイト列が検出された場合。
  2. 制御文字の含有: デコードされたRuneが0x20(スペース)未満または0x7f(DEL)である場合。
  3. バックスラッシュの含有: \(バックスラッシュ)文字が含まれている場合。Goのインポートパスは常に/(スラッシュ)を使用する必要があります。
  4. スペース文字の含有: isspacerune関数によってスペース文字が検出された場合。
  5. 無効な特殊文字の含有: "!\"#$%&'()*,:;<=>?[]^{|}~"`のいずれかの文字が含まれている場合。

変更前は、これらのチェックのいずれかが失敗すると、yyerror関数が固定のエラーメッセージ文字列を引数として呼び出されていました。例えば、無効なUTF-8シーケンスの場合、「yyerror("import path contains invalid UTF-8 sequence");」のように呼び出されていました。

このコミットでは、yyerror関数の呼び出し方を変更し、エラーメッセージに加えて、問題のインポートパスの文字列(path->s)を可変引数として渡すように修正されています。yyerrorprintfスタイルのフォーマット文字列をサポートしているため、"%s"フォーマット指定子を使用してインポートパスをメッセージに埋め込むことができます。

例えば、無効なUTF-8シーケンスのエラーメッセージは、変更前が「import path contains invalid UTF-8 sequence」であったのに対し、変更後は「import path contains invalid UTF-8 sequence: "problematic/path"」のようになります。これにより、エラーが発生した具体的なインポートパスがメッセージに表示され、デバッグが大幅に容易になります。

この修正は、コンパイラのエラー報告の質を向上させ、開発者のデバッグ体験を改善するという点で、Go言語の開発プロセスにおける重要な改善点の一つです。

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

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

--- 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) {
-\t\t\tyyerror("import path contains invalid UTF-8 sequence");
+\t\t\tyyerror("import path contains invalid UTF-8 sequence: \"%s\"", path->s);
 			return 1;
 		}
 		if(r < 0x20 || r == 0x7f) {
-\t\t\tyyerror("import path contains control character");
+\t\t\tyyerror("import path contains control character: \"%s\"", path->s);
 			return 1;
 		}
 		if(r == '\\') {
-\t\t\tyyerror("import path contains backslash; use slash");
+\t\t\tyyerror("import path contains backslash; use slash: \"%s\"", path->s);
 			return 1;
 		}
 		if(isspacerune(r)) {
-\t\t\tyyerror("import path contains space character");
+\t\t\tyyerror("import path contains space character: \"%s\"", path->s);
 			return 1;
 		}
 		if(utfrune("!\"#$%&'()*,:;<=>?[]^`{|}~", r)) {
-\t\t\tyyerror("import path contains invalid character '%C'", r);
+\t\t\tyyerror("import path contains invalid character '%C': \"%s\"", r, path->s);
 			return 1;
 		}
 	}

コアとなるコードの解説

上記の差分を見ると、各yyerror呼び出しにおいて、以下の変更が加えられていることがわかります。

  1. フォーマット文字列の追加: エラーメッセージ文字列の末尾に「: \"%s\"」が追加されています。これは、yyerrorprintfスタイルのフォーマット文字列を解釈することを示しています。
  2. インポートパスの引数追加: path->syyerrorの追加引数として渡されています。pathStrlit構造体へのポインタであり、sはその構造体内の文字列データ(インポートパスの実際の文字列)を指します。これにより、問題のインポートパスの文字列が%sのプレースホルダーに挿入されます。
  3. 特殊文字エラーの修正: utfruneによる特殊文字チェックのエラーメッセージでは、既存の'%C'(問題の文字を表示)に加えて、インポートパス全体も表示されるように': \"%s\"', r, path->sと引数が追加されています。

これらの変更により、コンパイラが出力するエラーメッセージは、単にエラーの種類を伝えるだけでなく、どの具体的なインポートパスがそのエラーを引き起こしたのかを明示するようになります。これは、特に複雑なプロジェクトや、多数のインポートを持つファイルにおいて、デバッグの労力を大幅に削減する効果があります。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード(src/cmd/gc/subr.c
  • Go言語のコンパイラ設計に関する一般的な情報
  • C言語におけるprintfスタイルのフォーマット文字列
  • UnicodeとUTF-8の基本
  • Go言語のインポートパスの規則に関する情報