ast.Objectが非推奨になった挙動の対処
tparagenという、GoのASTを解析してテスト並行化ラベルを付与する自作のツールをgo1.23にアップデートしたところ、staticcheck
にて下記のエラーが発生した。
ast.Object has been deprecated since Go 1.22 and an alternative has been available since Go 1.0: The relationship between Idents and Objects cannot be correctly computed without type information. For example, the expression T{K: 0} may denote a struct, map, slice, or array literal, depending on the type of T. If T is a struct, then K refers to a field of T, whereas for the other types it refers to a value in the environment
実際のログ
さて、どこで発生したかというと、ソースコードで構文木の識別子を扱うために、ast.Ident
型を使用しているところ。
Obj
フィールドはast.Object
型になっている。
&ast.Ident{
NamePos: pos,
Name: varName,
Obj: &ast.Object{
Name: varName,
Type: testMethodPackageType,
},
ソースコード:https://github.com/sho-hata/tparagen/blob/2b8b881eafacda0e881806d195579dfa96f83a7e/process.go#L405
go1.22のリリースノートを読みにいくと、非推奨になった旨の情報が記載されていた。
In general, identifiers cannot be accurately resolved without type information. For example, the expression T{K: 0} may denote a struct, map, slice, or array literal, depending on the type of T. If T is a struct, then K refers to a field of T, whereas for the other types it refers to a value in the environment.
型情報なしではIdentifiers(識別子)が定まらない。
T{K: 0}
のような構文が、T がどの型であるかによって全く異なる意味を持つため、型情報がないと正しく解釈できない。
ast.Object
型にはType
フィールドがあるものの、型情報が入っていない。
type Object struct {
Kind ObjKind
Name string // declared name
Decl any // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, AssignStmt, Scope; or nil
Data any // object-specific data; or nil
Type any // placeholder for type information; may be nil
}
型が明示されていない場合、コンパイラは K が何を指すのか(フィールドなのか、環境中の値なのか)を正確に判断できず、
以下のような式があった場合、mapなのか構造体なのか、はたまたスライス、配列なのかわからないだろうというもの。
T{K:0}
構造体パターン
hoge := struct{ K int }{K: 0}
この場合 T は「K というフィールドを持つ構造体」であり、K: 0 はフィールド K に 0 を代入していることになる。
mapパターン
var T map[string]int = map[string]int{"K": 0}
この場合、T{"K": 0} という式は、構造体ではなくマップリテラルとして解釈される。T がマップ型のため、"K" はキーとして解釈され、0はキー"K"に対応した値として解釈される。
ast.Object
型のコードを読みにいくと、非推奨としてマークされていた。
解決
今回のケースにおける解決策:ast.Object
を使用しない。
&ast.Ident{
NamePos: pos,
Name: varName,
- Obj: &ast.Object{
- Name: varName,
- Type: testMethodPackageType,
- },
+ Obj: nil,
},
今回のケース(ast.Ident
を作ってパースしたソースコードを改造するケース)においては上記の対応ができた。
その他のケースについては、ast.Object
型のコメントを参考にするとよい。https://pkg.go.dev/go/ast#Object
New programs should set the [parser.SkipObjectResolution] parser flag to disable syntactic object resolution (which also saves CPU and memory), and instead use the type checker go/types if object resolution is desired. See the Defs, Uses, and Implicits fields of the [types.Info] struct for details.