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

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

このコミットは、Go言語のコンパイラ(gc)において、複合リテラル(composite literals)の構文エラーに関するエラーメッセージを改善することを目的としています。具体的には、複合リテラル内で要素の後にカンマがなく改行された場合に、より分かりやすいエラーメッセージ「need trailing comma before newline in composite literal」を出力するように修正が加えられました。これにより、開発者がGoの複合リテラルの構文規則、特に末尾のカンマの必要性について、より迅速に理解し、デバッグできるようになります。

コミット

  • コミットハッシュ: 8a686792e08f1114fd7819a32d8c2e3c4ddddc14
  • 作者: Ryan Hitchman hitchmanr@gmail.com
  • コミット日時: Mon Apr 2 11:00:55 2012 -0400

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

https://github.com/golang/go/commit/8a686792e08f1114fd7819a32d8c2e3c4ddddc14

元コミット内容

gc: improve error message for composite literals with unexpected newlines

R=golang-dev, r, rsc
CC=golang-dev
https://golang.org/cl/5857045

変更の背景

Go言語の複合リテラルは、構造体、配列、スライス、マップなどのコレクション型を初期化するための強力な構文です。Goの文法規則では、複合リテラルの要素が複数行にわたる場合、各要素の後に末尾のカンマ(trailing comma)を付けることが推奨され、場合によっては必須となります。これは、Goの自動セミコロン挿入(automatic semicolon insertion)のルールと密接に関連しています。

このコミット以前は、複合リテラル内で要素の後にカンマを付けずに改行した場合、コンパイラが出力するエラーメッセージが必ずしも直感的ではありませんでした。例えば、var a = []int{3} のように、要素の後にカンマがなく、かつ閉じブレースの前に改行がある場合、コンパイラは構文エラーを報告しますが、その原因が「末尾のカンマの欠如」であることを明確に示していませんでした。

この曖昧なエラーメッセージは、特にGo言語に不慣れな開発者にとって、デバッグを困難にする可能性がありました。そのため、コンパイラがより具体的で分かりやすいエラーメッセージを提供することで、開発者の生産性を向上させ、Goの構文規則への理解を深めることが、この変更の背景にあります。

前提知識の解説

Go言語の複合リテラル (Composite Literals)

複合リテラルは、Go言語で構造体、配列、スライス、マップなどの複合データ型を初期化するための構文です。

例:

  • 配列/スライス: []int{1, 2, 3}
  • 構造体: struct{x, y int}{x: 10, y: 20}
  • マップ: map[string]int{"apple": 1, "banana": 2}

複合リテラルは、要素をブレース {} で囲んで記述します。要素が複数行にわたる場合、Goの文法では、最後の要素であっても末尾にカンマを付けることが許可されており、推奨されています。これにより、要素の追加や削除が容易になり、バージョン管理システムでの差分が最小限に抑えられます。

Go言語の自動セミコロン挿入 (Automatic Semicolon Insertion)

Go言語のパーサーは、特定の状況で改行の後に自動的にセミコロンを挿入します。このルールは、コードの記述を簡潔にする一方で、特定の構文(特に複合リテラルやifforなどの制御構造)において、開発者が意図しないセミコロンが挿入され、構文エラーを引き起こすことがあります。

基本的なルールとして、Goのパーサーは、トークンが改行で区切られ、かつその改行がセミコロンを挿入するのに適切な場所であると判断した場合に、自動的にセミコロンを挿入します。例えば、識別子、数値リテラル、文字列リテラル、breakcontinuefallthroughreturn++--)]} の後に改行がある場合などです。

複合リテラルにおいて、要素の後にカンマがない状態で改行すると、この自動セミコロン挿入のルールが適用され、意図しないセミコロンが挿入される可能性があります。これにより、コンパイラは構文エラーを報告しますが、そのエラーメッセージが「セミコロンが挿入されたため、次のトークンが予期しない」といった、直接的な原因を示さないものになることがありました。

gc (Go Compiler)

gcは、Go言語の公式コンパイラであり、Goのソースコードを機械語に変換する役割を担っています。コンパイルプロセス中に構文エラーや型エラーなどを検出し、開発者にエラーメッセージを報告します。このコミットは、gcが生成するエラーメッセージの品質を向上させるためのものです。

go.errorsyerr.h

これらはGoコンパイラのソースコードの一部であり、コンパイラが生成するエラーメッセージの定義に関連するファイルです。

  • src/cmd/gc/go.errors: コンパイラのエラーメッセージのパターンと、それに対応するメッセージ文字列を定義するファイルです。これは、Goコンパイラの字句解析器と構文解析器がエラーを検出した際に、どのメッセージを出力するかを決定するために使用されます。
  • src/cmd/gc/yerr.h: エラーコードや関連するシンボルを定義するヘッダーファイルです。go.errorsで定義されたパターンとメッセージが、コンパイラの内部でどのように参照されるかを制御します。

これらのファイルを変更することで、特定の構文パターンに遭遇した際に、コンパイラがより正確で分かりやすいエラーメッセージを出力するようにカスタマイリングできます。

技術的詳細

このコミットの技術的な核心は、Goコンパイラの字句解析器/構文解析器が、複合リテラル内で末尾のカンマがない状態で改行された特定の構文パターンをより正確に識別し、それに対応する適切なエラーメッセージを生成するように拡張された点にあります。

具体的には、src/cmd/gc/go.errors ファイルに新しいエラーパターンが追加されました。このパターンは、var a = []int{3} のように、変数宣言の右辺で複合リテラルが使用され、そのリテラル内で要素の後にカンマがなく、かつ改行がある場合にマッチします。

追加されたパターンは以下の通りです。

% loadsys package imports LVAR LNAME '=' comptype '{' LNAME ';'
"need trailing comma before newline in composite literal",

このパターンは、以下のような構文を検出します。

  • % loadsys package imports: コンパイラの内部的な状態やコンテキストを示すプレフィックス。
  • LVAR LNAME '=': var 変数名 = のような変数宣言の開始。
  • comptype '{': 複合リテラルの型(例: []int)と開始ブレース {
  • LNAME ';': リテラル内の要素(例: 3)の後に、自動セミコロン挿入によって追加されたセミコロン ; が続くパターン。

このパターンにマッチした場合、コンパイラは「need trailing comma before newline in composite literal」というエラーメッセージを出力します。これにより、開発者は、複合リテラルの要素の後にカンマが必要であること、そして改行が意図しないセミコロン挿入を引き起こしていることを明確に理解できます。

また、src/cmd/gc/yerr.h には、この新しいエラーパターンに対応する内部的なエラーコード(435)と、関連するシンボル(;)が追加されました。これは、コンパイラの内部処理でこの特定のエラーを識別し、適切なエラーメッセージと関連付けるために必要です。

最後に、test/syntax/composite.go という新しいテストファイルが追加されました。このテストファイルには、意図的に末尾のカンマを省略した複合リテラルを含むGoコードが記述されており、コンパイラが期待されるエラーメッセージを正確に出力するかどうかを検証します。これにより、このエラーメッセージの改善が正しく機能すること、および将来の変更によって回帰しないことが保証されます。

この変更は、Goコンパイラのエラー報告メカニズムの洗練化の一環であり、開発者体験の向上に貢献します。

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

このコミットによる主要なコード変更は以下の3つのファイルにわたります。

  1. src/cmd/gc/go.errors

    --- a/src/cmd/gc/go.errors
    +++ b/src/cmd/gc/go.errors
    @@ -65,6 +65,9 @@ static struct {
     	% loadsys package imports LVAR LNAME '=' LNAME '{' LNAME ';'
     	"need trailing comma before newline in composite literal",
     	
    +% loadsys package imports LVAR LNAME '=' comptype '{' LNAME ';'
    +"need trailing comma before newline in composite literal",
    +
     	% loadsys package imports LFUNC LNAME '(' ')' '{' LFUNC LNAME
     	"nested func not allowed",
    
    • 新しいエラーパターン % loadsys package imports LVAR LNAME '=' comptype '{' LNAME ';' が追加され、これに「need trailing comma before newline in composite literal」というエラーメッセージが関連付けられました。
  2. src/cmd/gc/yerr.h

    --- a/src/cmd/gc/yerr.h
    +++ b/src/cmd/gc/yerr.h
    @@ -65,6 +65,9 @@ static struct {
     	425, ';',
     	"need trailing comma before newline in composite literal",
     	
    +	435, ';',
    +	"need trailing comma before newline in composite literal",
    +
     	112, LNAME,
     	"nested func not allowed",
    
    • 新しいエラーコード 435 が追加され、セミコロン ; と関連付けられました。これは、go.errors で定義された新しいエラーパターンに対応する内部的な識別子です。
  3. test/syntax/composite.go

    --- /dev/null
    +++ b/test/syntax/composite.go
    @@ -0,0 +1,11 @@
    +// errorcheck
    +
    +// Copyright 2012 The Go Authors.  All rights reserved.
    +// Use of this source code is governed by a BSD-style
    +// license that can be found in the LICENSE file.
    +
    +package main
    +
    +var a = []int{
    +	3 // ERROR "need trailing comma before newline in composite literal"
    +}
    
    • 新しいテストファイルが作成されました。このファイルには、複合リテラル内で要素の後にカンマを付けずに改行したコードが含まれており、コンパイラが期待されるエラーメッセージ「need trailing comma before newline in composite literal」を出力するかどうかを検証します。// ERROR "..." コメントは、errorcheck テストツールがこの行で特定のエラーメッセージが出力されることを期待していることを示します。

コアとなるコードの解説

src/cmd/gc/go.errors の変更

このファイルは、Goコンパイラが構文解析中に遭遇する可能性のあるエラーパターンと、それに対応するエラーメッセージを定義しています。追加された行は、特定の構文エラーをより詳細に捕捉するための新しいルールを導入しています。

% loadsys package imports LVAR LNAME '=' comptype '{' LNAME ';' というパターンは、以下のようなGoコードの構造をターゲットにしています。

var myVar = SomeType{
    element // ここにカンマがない
}

ここで、LVAR LNAME '='var myVar = の部分を、comptype '{'SomeType{ の部分を、LNAMEelement の部分をそれぞれ表します。そして、最も重要なのが ; です。これは、Goの自動セミコロン挿入のルールにより、element の後に改行があることで、コンパイラが自動的にセミコロンを挿入した状態を示しています。

このパターンがマッチした場合、コンパイラは「need trailing comma before newline in composite literal」というメッセージを出力します。これにより、開発者は、複合リテラルの要素の後にカンマがないために、Goの自動セミコロン挿入が働き、それが構文エラーを引き起こしていることを明確に理解できます。以前は、この状況でより一般的な、あるいは誤解を招く可能性のあるエラーメッセージが出力されていたと考えられます。

src/cmd/gc/yerr.h の変更

yerr.h は、go.errors で定義されたエラーメッセージと、コンパイラの内部的なエラーコードやシンボルとのマッピングを管理するヘッダーファイルです。

追加された 435, ';', "need trailing comma before newline in composite literal", の行は、新しいエラーメッセージ「need trailing comma before newline in composite literal」に、内部的なエラーコード 435 を割り当て、このエラーがセミコロン ; のコンテキストで発生することを示しています。このマッピングにより、コンパイラの内部ロジックは、特定の構文解析エラーが発生した際に、正しいエラーメッセージを効率的に参照し、報告することができます。

test/syntax/composite.go の変更

この新しいテストファイルは、コンパイラが期待通りにエラーメッセージを生成するかどうかを検証するためのものです。

var a = []int{
	3 // ERROR "need trailing comma before newline in composite literal"
}

このコードスニペットでは、スライスリテラル []int{} の中に要素 3 がありますが、その後にカンマがなく、改行されています。Goの自動セミコロン挿入のルールにより、3 の後にセミコロンが挿入され、これが構文エラーを引き起こします。

// ERROR "need trailing comma before newline in composite literal" というコメントは、Goのテストフレームワーク errorcheck が使用するディレクティブです。このディレクティブは、コンパイラがこの行で指定された正確なエラーメッセージを出力することを期待していることを示します。もしコンパイラが異なるエラーメッセージを出力したり、エラーを報告しなかったりした場合、テストは失敗します。

このテストの追加により、コンパイラが複合リテラルにおける末尾のカンマの欠如と改行の組み合わせによって発生する特定の構文エラーに対して、改善されたエラーメッセージを正確に生成することが保証されます。これは、コンパイラの堅牢性と開発者へのフィードバックの質を向上させる上で非常に重要です。

関連リンク

  • Go Code Review (CL 5857045): https://golang.org/cl/5857045 このリンクは、このコミットがGoプロジェクトのコードレビューシステム(Gerrit)でどのように議論され、承認されたかを示すものです。通常、ここには変更の動機、実装の詳細、レビューコメントなどが含まれています。

参考にした情報源リンク