[インデックス 11715] ファイルの概要
このコミットは、Go言語の標準ライブラリの一部であるgo/scanner
パッケージのエラー処理APIを、よりGoらしい(idiomatic Go)スタイルにクリーンアップし、改善することを目的としています。具体的には、ErrorVector
型を廃止し、既存のErrorList
型に統合するとともに、スキャナーのエラーハンドラーをインターフェースからシンプルな関数型に変更しています。これにより、APIの使いやすさと整合性が向上しています。
コミット
commit d08dd8bec1e976ccd278403addac6ecfa349f2bf
Author: Robert Griesemer <gri@golang.org>
Date: Wed Feb 8 11:41:32 2012 -0800
go/scanner: clean up error interface
Issue 2856 asks for a rename of a few methods to a
more idiomatic Go style. This is a very early API
that evolved organically throughout the years.
Together with the fact that ErrorVectors were embedded
in other data structures (e.g. Parser), just renaming
methods (e.g. GetError -> Error) would lead to undesired
behavior (e.g., Parser would act like an Error). Instead,
cleaned up API a bit more:
- removed ErrorVector in favor of ErrorList (already
present)
- simplified Scanner.Init by making the error handler a
function instead of requiring an ErrorHandler implementation
- adjusted helper functions accordingly
- updated Go 1 doc
Fixes #2856.
R=rsc
CC=golang-dev
https://golang.org/cl/5624047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d08dd8bec1e976ccd278403addac6ecfa349f2bf
元コミット内容
go/scanner
: エラーインターフェースのクリーンアップ
Issue 2856は、いくつかのメソッドをよりGoらしいスタイルにリネームすることを求めています。これは長年にわたって有機的に進化してきた非常に初期のAPIです。ErrorVector
が他のデータ構造(例:Parser
)に埋め込まれていたという事実と相まって、単にメソッド名を変更するだけでは(例:GetError
をError
に)望ましくない動作(例:Parser
がError
のように振る舞う)につながる可能性がありました。代わりに、APIをもう少しクリーンアップしました:
- 既存の
ErrorList
を優先してErrorVector
を削除しました。 - エラーハンドラーを
ErrorHandler
インターフェースの実装を要求する代わりに、関数にすることでScanner.Init
を簡素化しました。 - ヘルパー関数をそれに応じて調整しました。
- Go 1のドキュメントを更新しました。
Fixes #2856.
R=rsc CC=golang-dev https://golang.org/cl/5624047
変更の背景
このコミットの背景には、Go言語の初期のAPI設計と、その後の言語の成熟に伴う「Goらしい」コーディングスタイルの確立があります。
-
Issue 2856の存在: コミットメッセージに明記されているように、GoのIssue 2856がこの変更の直接的なトリガーとなっています。このIssueは、
go/scanner
パッケージ内のいくつかのメソッド名がGoの慣用的な命名規則に沿っていないことを指摘し、リネームを提案していました。特に、エラー取得メソッドがGetError
のようなプレフィックスを持つことが、Goの慣習(例えば、エラーを返す関数は通常Error
という名前のメソッドを持つ)に反すると考えられました。 -
APIの有機的な進化:
go/scanner
パッケージのAPIは、Go言語の初期段階から「有機的に」進化してきたと述べられています。これは、初期の設計が必ずしも長期的な視点やGoの慣用的なスタイルを完全に考慮していなかった可能性を示唆しています。時間が経つにつれて、より良い設計パターンや慣習が明らかになり、それらに合わせて既存のAPIを調整する必要が生じました。 -
ErrorVector
の埋め込みによる問題: コミットメッセージの核心的な問題提起は、ErrorVector
型がParser
のような他のデータ構造に埋め込まれていた点です。Goでは、構造体にインターフェースを埋め込むことで、そのインターフェースのメソッドを構造体自身が持つかのように振る舞わせることができます(Goの埋め込みフィールドのメカニズム)。もしErrorVector
がError
というメソッドを持っていた場合、Parser
構造体も自動的にError
メソッドを持つことになり、Parser
がerror
インターフェースを満たしてしまうという意図しない副作用が生じる可能性がありました。これは、Parser
がエラーオブジェクトとして扱われるべきではない文脈で、誤ってエラーとして扱われるリスクをはらんでいました。この問題を回避しつつ、APIをよりクリーンにするために、単なるリネーム以上の根本的な変更が必要と判断されました。
これらの背景から、このコミットは単なる表面的なリネームに留まらず、Goの設計原則と慣用的なスタイルに沿った、より堅牢で理解しやすいエラー処理メカニズムを構築するための重要なステップであったと言えます。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念とgo/scanner
、go/parser
パッケージに関する知識が必要です。
1. Go言語のエラーハンドリング
Go言語では、エラーは通常、組み込みのerror
インターフェースを実装する値として扱われます。
type error interface {
Error() string
}
慣例として、関数は通常、最後の戻り値としてerror
型の値を返します。エラーがない場合はnil
を返します。
2. go/token
パッケージ
go/token
パッケージは、Goソースコードの字句解析(スキャン)や構文解析(パース)において、ソースコード上の位置情報を管理するための基本的な型を提供します。
token.Pos
: ソースコード内のバイトオフセットを表す型です。token.Position
:token.Pos
に加えて、ファイル名、行番号、列番号を含む、より人間が読みやすい位置情報を提供します。token.FileSet
: 複数のソースファイルをまとめて管理し、token.Pos
からtoken.Position
への変換を可能にするための構造体です。
3. go/scanner
パッケージ
go/scanner
パッケージは、Goソースコードをトークン(識別子、キーワード、演算子など)に分割する字句解析器(スキャナー)を提供します。
scanner.Scanner
: 字句解析を行う主要な構造体です。Init
メソッドで初期化され、Scan
メソッドで次のトークンを読み取ります。- エラーハンドリングの仕組み(変更前):
scanner.ErrorHandler
インターフェース: 変更前は、スキャナーがエラーを報告するためのインターフェースでした。
スキャナーの利用者はこのインターフェースを実装し、type ErrorHandler interface { Error(pos token.Position, msg string) }
Scanner.Init
メソッドに渡すことで、カスタムのエラー処理ロジックを提供できました。scanner.ErrorVector
構造体:ErrorHandler
インターフェースを実装し、発生したエラーを内部のスライス([]*Error
)に収集する具体的な型でした。これは、複数のエラーをまとめて管理するために使用されました。type ErrorVector struct { errors []*Error } func (h *ErrorVector) Error(pos token.Position, msg string) { ... } func (h *ErrorVector) GetError(mode int) error { ... }
ErrorVector
は、GetError
メソッドを通じて収集したエラーのリストをerror
型として返す機能を持っていました。
scanner.ErrorList
型: 変更前から存在していた型で、[]*Error
のエイリアスであり、エラーのリストを表します。sort.Interface
を実装しており、エラーを位置でソートする機能を持っていました。
4. go/parser
パッケージ
go/parser
パッケージは、go/scanner
パッケージを利用してGoソースコードを抽象構文木(AST)に構文解析する機能を提供します。
parser.parser
構造体: 構文解析を行う主要な構造体です。内部でscanner.Scanner
を持ち、字句解析の結果を利用して構文解析を行います。- エラーハンドリングとの連携:
parser.parser
は、内部にscanner.ErrorVector
を埋め込むことで、スキャナーやパーサー自身が生成するエラーを収集していました。
5. Goの埋め込みフィールド (Embedded Fields)
Go言語では、構造体の中に型を宣言なしで埋め込むことができます。これにより、埋め込まれた型のメソッドが、その構造体のメソッドであるかのように振る舞います。
type MyError struct {
Msg string
}
func (e MyError) Error() string { return e.Msg }
type MyStruct struct {
MyError // MyErrorを埋め込む
Value int
}
func main() {
s := MyStruct{MyError: MyError{Msg: "something went wrong"}, Value: 10}
fmt.Println(s.Error()) // MyStructがMyErrorのError()メソッドを直接呼び出せる
}
この機能はコードの再利用性を高めますが、今回のコミットの背景で述べられているように、意図しないインターフェースの実装を引き起こす可能性もあります。
これらの前提知識を理解することで、コミットがなぜ行われたのか、そしてその変更がGoのエコシステム全体にどのような影響を与えるのかを深く把握することができます。
技術的詳細
このコミットの技術的詳細は、主にgo/scanner
パッケージのエラー処理メカニズムの再設計に集約されます。
1. ErrorVector
の廃止とErrorList
への統合
- 変更前:
scanner.ErrorVector
は、ErrorHandler
インターフェースを実装し、エラーを収集するための独立した構造体でした。これは、GetErrorList
やGetError
といったメソッドを通じて、収集したエラーのリストを返す機能を持っていました。 - 変更後:
ErrorVector
型は完全に削除されました。その機能は、既存のscanner.ErrorList
型に統合されました。ErrorList
は元々[]*Error
のエイリアスでしたが、このコミットにより、エラーを追加するAdd
メソッド、リセットするReset
メソッド、ソートするSort
メソッド、重複を削除するRemoveMultiples
メソッド、そしてerror
インターフェースを満たすErr
メソッドが追加されました。- これにより、
ErrorList
自体がエラーの収集と管理の責任を持つようになり、ErrorVector
という中間層が不要になりました。 ErrorList
がerror
インターフェースを満たすError()
メソッドを持つことで、ErrorList
のインスタンスを直接error
型の変数に代入できるようになりました。
2. ErrorHandler
インターフェースから関数型への変更
- 変更前:
scanner.ErrorHandler
は、エラーを報告するためのインターフェースでした。type ErrorHandler interface { Error(pos token.Position, msg string) }
Scanner.Init
メソッドは、このインターフェースの実装を受け取っていました。 - 変更後:
ErrorHandler
はインターフェースではなく、シンプルな関数型(シグネチャ)になりました。type ErrorHandler func(pos token.Position, msg string)
Scanner.Init
メソッドは、この関数型の引数を受け取るように変更されました。- この変更により、エラーハンドラーを実装するためにわざわざ新しい型を定義する必要がなくなり、匿名関数や既存の関数を直接渡せるようになりました。これはGoの関数型プログラミングの慣習に沿ったものです。
- 例えば、
ErrorList
のAdd
メソッドを直接エラーハンドラーとして利用できるようになりました(func(pos token.Position, msg string) { myErrorList.Add(pos, msg) }
)。
3. parser
パッケージにおけるエラー処理の変更
go/parser
パッケージのparser
構造体は、以前はscanner.ErrorVector
を埋め込んでいました。- このコミットにより、
parser
構造体はscanner.ErrorVector
の代わりにscanner.ErrorList
を直接持つようになりました。// 変更前 type parser struct { file *token.File scanner.ErrorVector // ErrorVectorを埋め込み // ... } // 変更後 type parser struct { file *token.File errors scanner.ErrorList // ErrorListを直接持つ // ... }
- これにより、
parser
がErrorVector
のメソッド(例:Error()
)を継承してしまい、parser
自身がerror
インターフェースを満たしてしまうという、コミットメッセージで述べられていた「望ましくない動作」が回避されました。 - エラーの追加は、
p.Error(p.file.Position(pos), msg)
からp.errors.Add(p.file.Position(pos), msg)
のように、明示的にErrorList
のAdd
メソッドを呼び出す形に変更されました。 - 最終的なエラーの取得も、
p.errors()
からp.errors.Err()
に変更され、ErrorList
が持つErr()
メソッドが利用されるようになりました。
4. ドキュメントの更新
doc/go1.html
とdoc/go1.tmpl
が更新され、go/scanner
パッケージのエラーハンドリングの変更(ErrorHandler
が関数になったこと、ErrorVector
がErrorList
に置き換えられたこと)が反映されました。これは、Go 1のリリースに向けたAPIの安定化と明確化の一環です。
これらの変更は、Goのエラー処理の設計原則、特に「エラーは値である」という考え方をより強く反映し、APIの整合性と使いやすさを向上させるものです。また、埋め込みフィールドの利用における潜在的な落とし穴を回避する良い例とも言えます。
コアとなるコードの変更箇所
このコミットにおける主要なコード変更は、以下のファイルに集中しています。
-
src/pkg/go/scanner/errors.go
:ErrorHandler
インターフェースとErrorVector
構造体の定義が完全に削除されました。ErrorList
型に、エラーを追加するAdd
、リセットするReset
、ソートするSort
、重複を削除するRemoveMultiples
、そしてerror
インターフェースを満たすErr
メソッドが追加されました。Error
構造体のError()
メソッドのレシーバーがポインタから値に変更されました (*Error
からError
)。
-
src/pkg/go/scanner/scanner.go
:ErrorHandler
がインターフェースから関数型type ErrorHandler func(pos token.Position, msg string)
に変更されました。Scanner.Init
メソッドのerr
引数の型がErrorHandler
インターフェースから新しい関数型に変更されました。s.error
メソッド内でエラーハンドラーを呼び出す部分が、s.err.Error(...)
からs.err(...)
に変更されました。
-
src/pkg/go/parser/parser.go
:parser
構造体のフィールドscanner.ErrorVector
がerrors scanner.ErrorList
に変更されました。parser.init
メソッド内でscanner.Scanner
を初期化する際に、エラーハンドラーとしてp.errors.Add
をラップした匿名関数が渡されるようになりました。parser.errors()
メソッドが削除され、代わりにParseFile
内でp.errors.Err()
が直接呼び出されるようになりました。- エラーを報告する
p.error
メソッド内で、p.Error(...)
の代わりにp.errors.Add(...)
が呼び出されるようになりました。 p.ErrorCount()
の代わりにp.errors.Len()
が使用されるようになりました。
-
src/pkg/go/parser/interface.go
:ParseFile
関数内で、エラーのソートと重複削除のロジックがp.errors()
の呼び出しから、p.errors.RemoveMultiples()
またはp.errors.Sort()
とp.errors.Err()
の直接呼び出しに変更されました。
-
src/pkg/exp/types/check.go
:checker
構造体のフィールドscanner.ErrorVector
がerrors scanner.ErrorList
に変更されました。- エラーを報告する
errorf
メソッド内で、c.Error(...)
の代わりにc.errors.Add(...)
が呼び出されるようになりました。 - 最終的なエラーの取得が
c.GetError(...)
からc.errors.Err()
に変更されました。
-
src/pkg/go/ast/resolve.go
:pkgBuilder
構造体のフィールドscanner.ErrorVector
がerrors scanner.ErrorList
に変更されました。- エラーを報告する
error
メソッド内で、p.Error(...)
の代わりにp.errors.Add(...)
が呼び出されるようになりました。 - 最終的なエラーの取得が
p.GetError(...)
からp.errors.Err()
に変更されました。
-
doc/go1.html
およびdoc/go1.tmpl
:go/scanner
パッケージのエラーハンドリングに関する説明が更新され、ErrorHandler
が関数になったこと、ErrorVector
がErrorList
に置き換えられたことが明記されました。
これらの変更は、go/scanner
のエラー処理APIの根本的な再構築と、それを利用するgo/parser
、exp/types
、go/ast
といった上位パッケージのコードの適応を示しています。
コアとなるコードの解説
ここでは、上記の「コアとなるコードの変更箇所」で挙げた主要なファイルにおける具体的なコードの変更とその意味を詳しく解説します。
1. src/pkg/go/scanner/errors.go
このファイルは、go/scanner
パッケージのエラー関連の型とメソッドを定義しています。
変更のハイライト:
-
ErrorHandler
インターフェースの削除:--- a/src/pkg/go/scanner/errors.go +++ b/src/pkg/go/scanner/errors.go @@ -11,14 +11,18 @@ import ( "sort" ) -// An implementation of an ErrorHandler may be provided to the Scanner. -// If a syntax error is encountered and a handler was installed, Error -// is called with a position and an error message. The position points -// to the beginning of the offending token. -// -type ErrorHandler interface { - Error(pos token.Position, msg string) -}
ErrorHandler
インターフェースが完全に削除されました。これは、エラーハンドリングのメカニズムがインターフェースベースから関数ベースへと移行したことを意味します。 -
ErrorVector
構造体の削除とErrorList
への機能統合:--- a/src/pkg/go/scanner/errors.go +++ b/src/pkg/go/scanner/errors.go @@ -26,24 +26,38 @@ type ErrorVector struct { // Within ErrorVector, an error is represented by an Error node. The // position Pos, if valid, points to the beginning of the offending // token, and the error condition is described by Msg. +// In an ErrorList, an error is represented by an *Error. +// The position Pos, if valid, points to the beginning of +// the offending token, and the error condition is described +// by Msg. // type Error struct { Pos token.Position Msg string } -func (e *Error) Error() string { +// Error implements the error interface. +func (e Error) Error() string { if e.Pos.Filename != "" || e.Pos.IsValid() { // don't print "<unknown position>" // TODO(gri) reconsider the semantics of Position.IsValid @@ -57,9 +71,19 @@ func (e *Error) Error() string { return e.Msg } -// An ErrorList is a (possibly sorted) list of Errors. +// ErrorList is a list of *Errors. +// The zero value for an ErrorList is an empty ErrorList ready to use. +// type ErrorList []*Error +// Add adds an Error with given position and error message to an ErrorList. +func (p *ErrorList) Add(pos token.Position, msg string) { + *p = append(*p, &Error{pos, msg}) +} + +// Reset resets an ErrorList to no errors. +func (p *ErrorList) Reset() { *p = (*p)[0:0] } + // ErrorList implements the sort Interface. func (p ErrorList) Len() int { return len(p) } func (p ErrorList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } @@ -84,72 +108,47 @@ func (p ErrorList) Less(i, j int) bool { return false } +// Sort sorts an ErrorList. *Error entries are sorted by position, +// other errors are sorted by error message, and before any *Error +// entry. +// +func (p ErrorList) Sort() { + sort.Sort(p) +} + +// RemoveMultiples sorts an ErrorList and removes all but the first error per line. +func (p *ErrorList) RemoveMultiples() { + sort.Sort(p) + var last token.Position // initial last.Line is != any legal error line + i := 0 + for _, e := range *p { + if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line { + last = e.Pos + (*p)[i] = e + i++ + } + } + (*p) = (*p)[0:i] +} + +// An ErrorList implements the error interface. func (p ErrorList) Error() string { switch len(p) { case 0: - return "unspecified error" + return "no errors" case 1: return p[0].Error() } return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1) } -// These constants control the construction of the ErrorList -// returned by GetErrors. -// -const ( - Raw = iota // leave error list unchanged - Sorted // sort error list by file, line, and column number - NoMultiples // sort error list and leave only the first error per line -) - -// GetErrorList returns the list of errors collected by an ErrorVector. -// The construction of the ErrorList returned is controlled by the mode -// parameter. If there are no errors, the result is nil. -// -func (h *ErrorVector) GetErrorList(mode int) ErrorList { - if len(h.errors) == 0 { - return nil - } - - list := make(ErrorList, len(h.errors)) - copy(list, h.errors) - - if mode >= Sorted { - sort.Sort(list) - } - - if mode >= NoMultiples { - var last token.Position // initial last.Line is != any legal error line - i := 0 - for _, e := range list { - if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line { - last = e.Pos - list[i] = e - i++ - } - } - list = list[0:i] - } - - return list -} - -// GetError is like GetErrorList, but it returns an error instead -// so that a nil result can be assigned to an error variable and -// remains nil. -// -func (h *ErrorVector) GetError(mode int) error { - if len(h.errors) == 0 { +// Err returns an error equivalent to this error list. +// If the list is empty, Err returns nil. +func (p ErrorList) Err() error { + if len(p) == 0 { return nil } - - return h.GetErrorList(mode) -} - -// ErrorVector implements the ErrorHandler interface. -func (h *ErrorVector) Error(pos token.Position, msg string) { - h.errors = append(h.errors, &Error{pos, msg}) +}
ErrorVector
構造体とその関連メソッド(Reset
,ErrorCount
,GetErrorList
,GetError
,Error
)が削除されました。代わりに、ErrorList
型にAdd
,Reset
,Sort
,RemoveMultiples
,Err
メソッドが追加され、エラーの収集、管理、およびerror
インターフェースとしての振る舞いをErrorList
自身が担うようになりました。Error
構造体のError()
メソッドのレシーバーが値型になった点も注目です。
2. src/pkg/go/scanner/scanner.go
このファイルは、Goソースコードをトークンに分割するスキャナーの主要なロジックを含んでいます。
変更のハイライト:
ErrorHandler
の関数型への変更:--- a/src/pkg/go/scanner/scanner.go +++ b/src/pkg/go/scanner/scanner.go @@ -30,6 +30,13 @@ import ( "unicode/utf8" ) +// An ErrorHandler may be provided to Scanner.Init. If a syntax error is +// encountered and a handler was installed, the handler is called with a +// position and an error message. The position points to the beginning of +// the offending token. +// +type ErrorHandler func(pos token.Position, msg string) + // A Scanner holds the scanner's internal state while processing // a given text. It can be allocated as part of another data // structure but must be initialized via Init before use. @@ -103,7 +110,7 @@ const ( // line information which is already present is ignored. Init causes a // panic if the file size does not match the src size. // -// Calls to Scan will use the error handler err if they encounter a +// Calls to Scan will invoke the error handler err if they encounter a // syntax error and err is not nil. Also, for each error encountered, // the Scanner field ErrorCount is incremented by one. The mode parameter // determines how comments are handled. @@ -134,7 +141,7 @@ func (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode func (s *Scanner) error(offs int, msg string) { if s.err != nil { - s.err.Error(s.file.Position(s.file.Pos(offs)), msg) + s.err(s.file.Position(s.file.Pos(offs)), msg) } s.ErrorCount++ }
ErrorHandler
がインターフェースからfunc(pos token.Position, msg string)
という関数型に変わりました。これにより、Scanner.Init
メソッドのerr
引数もこの関数型を受け取るようになり、エラー発生時の呼び出しもs.err.Error(...)
からs.err(...)
という直接的な関数呼び出しに簡素化されました。
3. src/pkg/go/parser/parser.go
このファイルは、GoソースコードをASTに構文解析するパーサーの主要なロジックを含んでいます。
変更のハイライト:
-
parser
構造体の変更:--- a/src/pkg/go/parser/parser.go +++ b/src/pkg/go/parser/parser.go @@ -18,8 +18,8 @@ import ( // The parser structure holds the parser's internal state. type parser struct { - file *token.File - scanner.ErrorVector + file *token.File + errors scanner.ErrorList scanner scanner.Scanner // Tracing/debugging
parser
構造体からscanner.ErrorVector
の埋め込みが削除され、代わりにerrors scanner.ErrorList
というフィールドが明示的に追加されました。これにより、parser
がErrorVector
のメソッドを意図せず継承する問題が解消されました。 -
parser.init
メソッドでのスキャナー初期化:--- a/src/pkg/go/parser/parser.go +++ b/src/pkg/go/parser/parser.go @@ -58,7 +58,8 @@ func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mod if mode&ParseComments != 0 { m = scanner.ScanComments } - p.scanner.Init(p.file, src, p, m) + eh := func(pos token.Position, msg string) { p.errors.Add(pos, msg) } + p.scanner.Init(p.file, src, eh, m) p.mode = mode p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)
Scanner.Init
の呼び出しにおいて、以前はp
(parser
自身がErrorHandler
インターフェースを満たしていた)を渡していましたが、変更後はp.errors.Add
をラップした匿名関数eh
を渡すようになりました。これは、ErrorList
のAdd
メソッドが新しいErrorHandler
関数型のシグネチャに合致するため、直接エラーをp.errors
に追加できるようになったことを示しています。 -
エラー報告メソッドの変更:
--- a/src/pkg/go/parser/parser.go +++ b/src/pkg/go/parser/parser.go @@ -334,7 +327,7 @@ func (p *parser) next() { } func (p *parser) error(pos token.Pos, msg string) { - p.Error(p.file.Position(pos), msg) + p.errors.Add(p.file.Position(pos), msg) }
parser.error
メソッド内で、エラーを報告する際にp.Error(...)
(埋め込まれたErrorVector
のメソッド)を呼び出す代わりに、明示的にp.errors.Add(...)
を呼び出すようになりました。 -
エラーカウントの取得:
--- a/src/pkg/go/parser/parser.go +++ b/src/pkg/go/parser/parser.go @@ -2123,7 +2116,7 @@ func (p *parser) parseFile() *ast.File { // Don't bother parsing the rest if we had errors already. // Likely not a Go source file at all. - if p.ErrorCount() == 0 && p.mode&PackageClauseOnly == 0 { + if p.errors.Len() == 0 && p.mode&PackageClauseOnly == 0 { // import decls for p.tok == token.IMPORT { decls = append(decls, p.parseGenDecl(token.IMPORT, parseImportSpec))\
エラーの数をチェックする際に、
p.ErrorCount()
(ErrorVector
のメソッド)の代わりに、p.errors.Len()
(ErrorList
のメソッド)が使用されるようになりました。
4. src/pkg/go/parser/interface.go
このファイルは、go/parser
パッケージの公開インターフェースを定義しています。
変更のハイライト:
ParseFile
関数のエラー処理ロジック:--- a/src/pkg/go/parser/interface.go +++ b/src/pkg/go/parser/interface.go @@ -80,13 +80,25 @@ const ( // are returned via a scanner.ErrorList which is sorted by file position. // func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (*ast.File, error) { + // get source text, err := readSource(filename, src) if err != nil { return nil, err } + + // parse source var p parser p.init(fset, filename, text, mode) - return p.parseFile(), p.errors() + f := p.parseFile() + + // sort errors + if p.mode&SpuriousErrors == 0 { + p.errors.RemoveMultiples() + } else { + p.errors.Sort() + } + + return f, p.errors.Err() }
ParseFile
関数内で、最終的なエラーを返す部分がp.errors()
からp.errors.Err()
に変更されました。また、エラーのソートや重複削除のロジックがp.errors()
メソッドの内部ではなく、ParseFile
関数内で明示的にp.errors.RemoveMultiples()
やp.errors.Sort()
を呼び出す形になりました。これにより、エラー処理のフローがより明確になりました。
これらの変更は、Goのエラー処理の慣用的なパターンに沿って、APIをよりシンプルで堅牢なものにするための重要なステップでした。特に、ErrorVector
の埋め込みによる意図しないインターフェースの実装という問題を解決し、ErrorList
をエラー収集の中心的な型として確立した点が大きな改善です。
関連リンク
- Go Issue 2856: https://go.dev/issue/2856 (コミットメッセージに記載されているIssue番号)
- Go CL 5624047: https://golang.org/cl/5624047 (Gerrit Code Reviewへのリンク)
参考にした情報源リンク
- Go言語の公式ドキュメント (
go/scanner
,go/parser
,go/token
パッケージ) - Go言語のエラーハンドリングに関する一般的な慣習とベストプラクティス
- Go言語の埋め込みフィールドに関する情報
(注:この解説は、提供されたコミット情報とGo言語に関する一般的な知識に基づいて生成されています。Web検索は、Go言語の特定の概念や慣習に関する詳細な背景情報を補完するために内部的に活用されました。)