[インデックス 13634] ファイルの概要
このコミットは、Go言語の標準ライブラリである text/scanner
パッケージにおける、不正な16進数および8進数のリテラルに対するエラー報告の改善を目的としたバグ修正です。具体的には、0x
の後に続くべき16進数の桁がない場合や、8進数リテラルに不正な数字(8や9)が含まれる場合に、より正確なエラーを報告するようにスキャナーの挙動が修正されています。
コミット
commit ace14d01bda6f8384dea8ab7307e657d80471054
Author: Robert Griesemer <gri@golang.org>
Date: Wed Aug 15 11:09:34 2012 -0700
text/scanner: report illegal hexadecimal numbers (bug fix)
R=r
CC=golang-dev
https://golang.org/cl/6450136
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ace14d01bda6f8384dea8ab7307e657d80471054
元コミット内容
text/scanner: report illegal hexadecimal numbers (bug fix)
このコミットメッセージは、text/scanner
パッケージが不正な16進数リテラルを正しく報告するように修正されたバグ修正であることを示しています。
変更の背景
Go言語のコンパイラやツールチェーンにおいて、ソースコードを解析する際には字句解析(Lexical Analysis)というプロセスが不可欠です。このプロセスは、ソースコードの文字列ストリームをトークン(意味のある最小単位)のストリームに変換します。text/scanner
パッケージは、このような字句解析の汎用的な機能を提供します。
以前の text/scanner
パッケージの実装では、16進数リテラル(0x
または 0X
で始まる数値)の後に有効な16進数の桁が続かない場合、または8進数リテラル(0
で始まる数値)に8や9といった不正な数字が含まれる場合に、適切なエラーを報告しない、あるいは誤ったトークンとして解釈してしまうバグが存在していました。
例えば、0x
のように16進数プレフィックスの後に何も続かない場合や、0xg
のように不正な文字が続く場合、あるいは 01238
のように8進数として解釈されるべき数値に 8
が含まれる場合などです。これらのケースは、Go言語の仕様に則った数値リテラルとしては不正であり、コンパイラはエラーを報告する必要があります。
このコミットは、これらの不正な数値リテラルを text/scanner
が正しく識別し、適切なエラーメッセージを生成するように修正することで、コンパイラの堅牢性とユーザーへのフィードバックの質を向上させることを目的としています。
前提知識の解説
字句解析 (Lexical Analysis) とスキャナー (Scanner)
字句解析は、コンパイラの最初のフェーズであり、ソースコードをトークンのシーケンスに変換します。この処理を行うプログラムを「字句解析器」または「スキャナー(Scanner)」と呼びます。スキャナーは、空白文字やコメントを無視し、キーワード、識別子、演算子、数値リテラル、文字列リテラルなどを識別します。
Go言語の数値リテラル
Go言語では、整数リテラルは以下の形式で記述できます。
- 10進数:
123
,0
など。 - 8進数:
0
で始まり、0
から7
までの数字が続く。例:0755
,0123
。Go 1.13以降では0o
または0O
プレフィックスも導入されましたが、このコミットが作成された2012年時点では0
プレフィックスのみでした。 - 16進数:
0x
または0X
で始まり、0
から9
、a
からf
、A
からF
までの数字が続く。例:0xdeadbeef
,0xFF
。
Go言語の仕様では、これらのリテラルが正しくない形式で記述された場合(例: 8進数に8や9が含まれる、16進数プレフィックスの後に桁がないなど)は、コンパイルエラーとなるべきです。
text/scanner
パッケージ
text/scanner
パッケージは、Go言語のソースコードだけでなく、一般的なテキストの字句解析にも使用できる汎用的なスキャナーを提供します。このパッケージは、入力ストリームから文字を読み込み、それをトークンに変換する機能を持っています。Scanner
型は、Scan()
メソッドを通じて次のトークンを読み込み、その種類と値を返します。エラーが発生した場合は、Error
メソッドを通じて報告されます。
scanNumber
関数
text/scanner
パッケージ内の scanNumber
関数は、数値リテラルを解析する責任を負っています。この関数は、現在の文字が数字であると判断された場合に呼び出され、その後の文字を読み進めて、数値リテラルの種類(整数、浮動小数点数)と値を決定します。このコミットの変更は、特に16進数と8進数の解析ロジックに焦点を当てています。
技術的詳細
このコミットの技術的詳細は、text/scanner
パッケージの scanNumber
関数における数値リテラルの解析ロジックの改善にあります。
16進数リテラルの解析改善
変更前は、0x
または 0X
のプレフィックスを検出した後、続く文字が16進数の桁である限り読み進めるループがありました。しかし、このループが一度も実行されなかった場合(つまり、0x
の直後に有効な16進数の桁がなかった場合)、スキャナーはエラーを報告せず、不正な状態になる可能性がありました。
この修正では、hasMantissa
という新しいブール変数が導入されました。
ch == 'x' || ch == 'X'
の条件で16進数と判断された後、hasMantissa
をfalse
で初期化します。- 16進数の桁を読み進める
for digitVal(ch) < 16
ループ内で、実際に桁が読み込まれた場合にhasMantissa = true
と設定します。 - ループ終了後、
!hasMantissa
であれば、つまり0x
の後に有効な16進数の桁が一つもなかった場合に、s.error("illegal hexadecimal number")
を呼び出してエラーを報告します。
これにより、0x
や 0xg
のような不正な16進数リテラルが正しく「不正な16進数」として扱われるようになりました。
8進数リテラルの解析改善
変更前は、8進数リテラル(0
で始まる数値)の解析において、seenDecimalDigit
という変数が使用されていました。これは、0
から 7
以外の数字(つまり 8
や 9
)が検出されたかどうかを追跡するためのものでした。しかし、変数名が seenDecimalDigit
となっており、8
や 9
が検出されたことを示すにはやや曖昧でした。
この修正では、seenDecimalDigit
を has8or9
にリネームしました。
for isDecimal(ch)
ループ内で、ch > '7'
の条件(つまり8
または9
が検出された場合)でhas8or9 = true
と設定します。- 数値の解析が完了し、それが8進数として扱われるべきであると判断された後(浮動小数点数ではない場合)、
if has8or9
の条件で、8
または9
が含まれていた場合にs.error("illegal octal number")
を呼び出してエラーを報告します。
このリネームとロジックの明確化により、8進数リテラルに不正な数字が含まれるケース(例: 01238
)がより直感的に検出され、適切なエラーメッセージが生成されるようになりました。
これらの変更は、text/scanner
がGo言語の数値リテラル仕様に厳密に準拠し、より堅牢な字句解析を提供するために不可欠です。
コアとなるコードの変更箇所
変更は src/pkg/text/scanner/scanner.go
と src/pkg/text/scanner/scanner_test.go
の2ファイルにわたります。
src/pkg/text/scanner/scanner.go
--- a/src/pkg/text/scanner/scanner.go
+++ b/src/pkg/text/scanner/scanner.go
@@ -389,15 +389,20 @@ func (s *Scanner) scanNumber(ch rune) (rune, rune) {
if ch == 'x' || ch == 'X' {
// hexadecimal int
ch = s.next()
+ hasMantissa := false
for digitVal(ch) < 16 {
ch = s.next()
+ hasMantissa = true
+ }
+ if !hasMantissa {
+ s.error("illegal hexadecimal number")
}
} else {
// octal int or float
- seenDecimalDigit := false
+ has8or9 := false
for isDecimal(ch) {
if ch > '7' {
- seenDecimalDigit = true
+ has8or9 = true
}
ch = s.next()
}
@@ -408,7 +413,7 @@ func (s *Scanner) scanNumber(ch rune) (rune, rune) {
return Float, ch
}
// octal int
- if seenDecimalDigit {
+ if has8or9 {
s.error("illegal octal number")
}
}
src/pkg/text/scanner/scanner_test.go
--- a/src/pkg/text/scanner/scanner_test.go
+++ b/src/pkg/text/scanner/scanner_test.go
@@ -446,6 +446,9 @@ func TestError(t *testing.T) {
testError(t, `"\`"`, "1:3", "illegal char escape", String)
testError(t, `01238`, "1:6", "illegal octal number", Int)
+ testError(t, `01238123`, "1:9", "illegal octal number", Int)
+ testError(t, `0x`, "1:3", "illegal hexadecimal number", Int)
+ testError(t, `0xg`, "1:3", "illegal hexadecimal number", Int)
testError(t, `'aa'`, "1:4", "illegal char literal", Char)
testError(t, `'`, "1:2", "literal not terminated", Char)
コアとなるコードの解説
src/pkg/text/scanner/scanner.go
の変更点
-
16進数解析ロジックの追加:
ch = s.next()
の直後にhasMantissa := false
が追加されました。これは、16進数の桁が少なくとも1つ読み込まれたかどうかを追跡するためのフラグです。for digitVal(ch) < 16
ループ内で、ch = s.next()
の後にhasMantissa = true
が追加されました。これにより、有効な16進数の桁が読み込まれるたびにフラグがtrue
に設定されます。- ループの直後に
if !hasMantissa { s.error("illegal hexadecimal number") }
が追加されました。これは、0x
または0X
のプレフィックスの後に有効な16進数の桁が一つも続かなかった場合に、"illegal hexadecimal number"
というエラーを報告します。
-
8進数解析ロジックの変数名変更と明確化:
seenDecimalDigit := false
がhas8or9 := false
に変更されました。これは、8進数リテラル中に8
または9
が含まれているかどうかをより明確に示します。if ch > '7'
の条件内でseenDecimalDigit = true
がhas8or9 = true
に変更されました。- 最終的な8進数エラーチェック
if seenDecimalDigit
がif has8or9
に変更されました。
これらの変更により、scanNumber
関数は、Go言語の数値リテラル仕様に違反する不正な16進数および8進数リテラルをより正確に識別し、適切なエラーメッセージを生成できるようになりました。
src/pkg/text/scanner/scanner_test.go
の変更点
テストファイルには、新しいエラーケースを検証するための3つの testError
コールが追加されました。
testError(t,
01238123, "1:9", "illegal octal number", Int)
: 8進数リテラル中に8
が含まれるケース。testError(t,
0x, "1:3", "illegal hexadecimal number", Int)
: 16進数プレフィックスの後に何も続かないケース。testError(t,
0xg, "1:3", "illegal hexadecimal number", Int)
: 16進数プレフィックスの後に不正な文字g
が続くケース。
これらのテストケースの追加は、修正が意図通りに機能し、以前は検出されなかった不正な数値リテラルが正しくエラーとして報告されることを保証します。
関連リンク
- Go言語の仕様 - 数値リテラル: https://go.dev/ref/spec#Numeric_literals
text/scanner
パッケージのドキュメント: https://pkg.go.dev/text/scanner
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード(特に
text/scanner
パッケージ) - Go言語のコミット履歴
- Go言語のIssueトラッカー (関連するバグ報告がある場合)
- Go言語のコードレビューシステム (Gerrit) のCL (Change List) ページ: https://golang.org/cl/6450136 (現在は
go.dev/cl/6450136
にリダイレクトされます)