メインコンテンツへスキップ

Go Proposal Weekly Digest

Go言語のproposal更新を毎週お届け

#79802likely_accept

go/parser: add deprecated func ResolveFile(\\*File)

ステータス変更: active likely_accept

要約

AIによる要約であり、誤りを含む場合があります。

概要

go/parser パッケージに、レガシーな識別子解決(object resolution)を後から実行できる非推奨関数 ResolveFile(*ast.File) を追加するプロポーザル。SkipObjectResolution モードでパースしたファイルに対して、必要なときだけ識別子解決を適用できるようにする。

ステータス変更

activelikely_accept
2026年6月25日の週次プロポーザルレビュー会議で、@aclements を含むコアチームが「上記の議論に基づき likely accept」と判断した。実装CL(go.dev/cl/794420)も既に提出されており、最終コメント期間(Final Comment Period)へ移行している。

技術的背景

現状の問題点

go/parser は従来、パース時に識別子解決(ast.Ident.Obj への参照設定)を同時に行っていた。この処理はパース時間の約18〜20%を占める重い処理であるため、#46485SkipObjectResolution モードフラグが追加され、不要な場合はスキップできるようになった。
しかし問題が発生した。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.Filesync.Once フィールドを追加することで、複数回呼び出されても安全かつ効率的に動作する。これはAPIの変更ではなく内部構造の変更を伴う。
  • gopls のコード重複解消: golang.org/x/tools/gopls が独自に維持してきた parsego.File.Resolve() の実装を標準ライブラリに移管でき、整合性リスクを排除できる。
  • 互換性問題のセーフネット: go/doc など標準ライブラリ内で SkipObjectResolution が広がった結果、ast.Ident.Obj に依存するユーザーコードが壊れるケースが報告されており(#79614)、ResolveFile はその移行期間における安全網として機能する。
  • 関連する #45104 の未実現タスク: #45104 で識別子解決を独立APIとして公開する可能性は議論されていたが未実装のままだった。今回のプロポーザルはその実現にあたる。

関連リンク