go/parser: add deprecated func ResolveFile(\\*File)
要約
概要
go/parser パッケージに、レガシーな識別子解決(object resolution)を後から実行できる非推奨関数 ResolveFile(*ast.File) を追加するプロポーザル。SkipObjectResolution モードでパースしたファイルに対して、必要なときだけ識別子解決を適用できるようにする。
ステータス変更
active → likely_accept
2026年6月25日の週次プロポーザルレビュー会議で、@aclements を含むコアチームが「上記の議論に基づき likely accept」と判断した。実装CL(go.dev/cl/794420)も既に提出されており、最終コメント期間(Final Comment Period)へ移行している。
技術的背景
現状の問題点
go/parser は従来、パース時に識別子解決(ast.Ident.Obj への参照設定)を同時に行っていた。この処理はパース時間の約18〜20%を占める重い処理であるため、#46485 で SkipObjectResolution モードフラグが追加され、不要な場合はスキップできるようになった。
しかし問題が発生した。gopls など高性能ツールは SkipObjectResolution を常時有効にしてパースキャッシュを構築するが、一部のアナライザ(go/analysis フレームワーク経由)は依然として ast.Ident.Obj を要求する。そのため gopls は、パース後に識別子解決を遅延適用する独自メソッド parsego.File.Resolve() を自前で実装しなければならなかった。これはコードの重複であり、標準ライブラリの内部アルゴリズムとの整合性リスクを生む。
さらに #79614 では、go/doc.NewFromFiles が内部的に SkipObjectResolution でパースするようになった結果、ast.Ident.Obj を利用するユーザーツールが壊れるという互換性問題も報告されている。
提案された解決策
go/parser パッケージに以下の非推奨関数を公開する:
package parser
// ResolveFile applies the parser's legacy ast.Ident-to-ast.Object resolution to the
// specified file's syntax tree. This is the same operation that is skipped on files
// parsed with the SkipObjectResolution mode flag. It is idempotent.
//
// Deprecated: ast.Object should not be used in new designs; use go/types instead.
// This function is provided to ease migration in applications that have disabled
// legacy object resolution by default but still need it in some circumstances.
func ResolveFile(*ast.File)
この関数は冪等性(何度呼んでも同じ結果)を保証するため、内部で sync.Once を用いた遅延実行を行う。
これによって何ができるようになるか
SkipObjectResolution でパフォーマンス最適化をしつつ、特定の場面でのみ識別子解決が必要なアプリケーションが、標準ライブラリの公式APIで対応できるようになる。
コード例
// Before: gopls が独自実装していたアプローチ(コードの重複)
// golang.org/x/tools/gopls/internal/cache/parsego にて独自の Resolve() メソッドを実装
func (pgf *File) Resolve() {
pgf.once.Do(func() {
// parser の内部アルゴリズムを手動で再実装...
})
}
// After: 標準APIを利用した遅延識別子解決
import (
"go/ast"
"go/parser"
"go/token"
)
fset := token.NewFileSet()
// SkipObjectResolution で高速パース
f, _ := parser.ParseFile(fset, "example.go", src, parser.SkipObjectResolution)
// 必要なときだけ識別子解決を適用(冪等)
parser.ResolveFile(f) // ast.Ident.Obj が設定される
議論のハイライト
- 非推奨としての公開が意図的な設計: 関数名が
ResolveFileであり、godoc コメントにDeprecated:が明記される。これは「後方互換性のためにやむを得ず提供するが、新規コードでは使わないでほしい」という明確なシグナル。go/typesへの移行を促すための橋渡し関数という位置付け。 - 冪等性の保証に
sync.Onceが必要:ast.Fileにsync.Onceフィールドを追加することで、複数回呼び出されても安全かつ効率的に動作する。これはAPIの変更ではなく内部構造の変更を伴う。 goplsのコード重複解消:golang.org/x/tools/goplsが独自に維持してきたparsego.File.Resolve()の実装を標準ライブラリに移管でき、整合性リスクを排除できる。- 互換性問題のセーフネット:
go/docなど標準ライブラリ内でSkipObjectResolutionが広がった結果、ast.Ident.Objに依存するユーザーコードが壊れるケースが報告されており(#79614)、ResolveFileはその移行期間における安全網として機能する。 - 関連する
#45104の未実現タスク: #45104 で識別子解決を独立APIとして公開する可能性は議論されていたが未実装のままだった。今回のプロポーザルはその実現にあたる。
関連リンク
- Proposal Issue github.com/golang/go
- Review Comment proposal review meeting
- Review Minutes
- 関連Issue: go/ast: formally deprecate Object #52463
- Proposal Issue
- 関連Issue: go/parser: add a SkipObjectResolution mode flag #46485
- 関連Issue: go/doc: NewFromFiles no longer does cross-file legacy object resolution #79614
- 関連Issue: go/parser: isolate object resolution from parsing #45104