Skip to content

Commit 4d8d90b

Browse files
authored
Add zero test for ir package (#645)
This PR adds a zero test for the `ir` package, similar to the one for the `ast` package to ensure that getters/accessors on zero values return zero values and do not panic. It also fixes panics on zero values.
1 parent db46c1b commit 4d8d90b

File tree

7 files changed

+270
-27
lines changed

7 files changed

+270
-27
lines changed

experimental/ir/export_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,8 @@ func GetImports(f *File) *Imports {
3636
}
3737

3838
func (s Symbol) RawData() arena.Untyped {
39+
if s.IsZero() {
40+
return arena.Nil()
41+
}
3942
return s.Raw().data
4043
}

experimental/ir/ir_file.go

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ func (f *File) InternedPath() intern.ID {
148148
// google/protobuf/descriptor.proto, which is given special treatment in
149149
// the language.
150150
func (f *File) IsDescriptorProto() bool {
151+
if f == nil {
152+
return false
153+
}
151154
return f.InternedPath() == f.session.builtins.DescriptorFile
152155
}
153156

@@ -176,15 +179,23 @@ func (f *File) InternedPackage() intern.ID {
176179

177180
// Imports returns an indexer over the imports declared in this file.
178181
func (f *File) Imports() seq.Indexer[Import] {
179-
return f.imports.Directs()
182+
var imp imports
183+
if f != nil {
184+
imp = f.imports
185+
}
186+
return imp.Directs()
180187
}
181188

182189
// TransitiveImports returns an indexer over the transitive imports for this
183190
// file.
184191
//
185192
// This function does not report whether those imports are weak or not.
186193
func (f *File) TransitiveImports() seq.Indexer[Import] {
187-
return f.imports.Transitive()
194+
var imp imports
195+
if f != nil {
196+
imp = f.imports
197+
}
198+
return imp.Transitive()
188199
}
189200

190201
// ImportFor returns import metadata for a given file, if this file imports it.
@@ -199,8 +210,12 @@ func (f *File) ImportFor(that *File) Import {
199210

200211
// Types returns the top level types of this file.
201212
func (f *File) Types() seq.Indexer[Type] {
213+
var types []id.ID[Type]
214+
if f != nil {
215+
types = f.types[:f.topLevelTypesEnd]
216+
}
202217
return seq.NewFixedSlice(
203-
f.types[:f.topLevelTypesEnd],
218+
types,
204219
func(_ int, p id.ID[Type]) Type {
205220
return id.Wrap(f, p)
206221
},
@@ -209,8 +224,12 @@ func (f *File) Types() seq.Indexer[Type] {
209224

210225
// AllTypes returns all types defined in this file.
211226
func (f *File) AllTypes() seq.Indexer[Type] {
227+
var types []id.ID[Type]
228+
if f != nil {
229+
types = f.types
230+
}
212231
return seq.NewFixedSlice(
213-
f.types,
232+
types,
214233
func(_ int, p id.ID[Type]) Type {
215234
return id.Wrap(f, p)
216235
},
@@ -220,8 +239,12 @@ func (f *File) AllTypes() seq.Indexer[Type] {
220239
// Extensions returns the top level extensions defined in this file (i.e.,
221240
// the contents of any top-level `extends` blocks).
222241
func (f *File) Extensions() seq.Indexer[Member] {
242+
var slice []id.ID[Member]
243+
if f != nil {
244+
slice = f.extns[:f.topLevelExtnsEnd]
245+
}
223246
return seq.NewFixedSlice(
224-
f.extns[:f.topLevelExtnsEnd],
247+
slice,
225248
func(_ int, p id.ID[Member]) Member {
226249
return id.Wrap(f, p)
227250
},
@@ -230,8 +253,12 @@ func (f *File) Extensions() seq.Indexer[Member] {
230253

231254
// AllExtensions returns all extensions defined in this file.
232255
func (f *File) AllExtensions() seq.Indexer[Member] {
256+
var extns []id.ID[Member]
257+
if f != nil {
258+
extns = f.extns
259+
}
233260
return seq.NewFixedSlice(
234-
f.extns,
261+
extns,
235262
func(_ int, p id.ID[Member]) Member {
236263
return id.Wrap(f, p)
237264
},
@@ -240,8 +267,12 @@ func (f *File) AllExtensions() seq.Indexer[Member] {
240267

241268
// Extends returns the top level extend blocks in this file.
242269
func (f *File) Extends() seq.Indexer[Extend] {
270+
var slice []id.ID[Extend]
271+
if f != nil {
272+
slice = f.extends[:f.topLevelExtendsEnd]
273+
}
243274
return seq.NewFixedSlice(
244-
f.extends[:f.topLevelExtendsEnd],
275+
slice,
245276
func(_ int, p id.ID[Extend]) Extend {
246277
return id.Wrap(f, p)
247278
},
@@ -250,8 +281,12 @@ func (f *File) Extends() seq.Indexer[Extend] {
250281

251282
// AllExtends returns all extend blocks in this file.
252283
func (f *File) AllExtends() seq.Indexer[Extend] {
284+
var extends []id.ID[Extend]
285+
if f != nil {
286+
extends = f.extends
287+
}
253288
return seq.NewFixedSlice(
254-
f.extends,
289+
extends,
255290
func(_ int, p id.ID[Extend]) Extend {
256291
return id.Wrap(f, p)
257292
},
@@ -261,17 +296,25 @@ func (f *File) AllExtends() seq.Indexer[Extend] {
261296
// AllMembers returns all fields defined in this file, including extensions
262297
// and enum values.
263298
func (f *File) AllMembers() iter.Seq[Member] {
299+
var raw iter.Seq[*rawMember]
300+
if f != nil {
301+
raw = f.arenas.members.Values()
302+
}
264303
i := 0
265-
return iterx.Map(f.arenas.members.Values(), func(raw *rawMember) Member {
304+
return iterx.Map(raw, func(raw *rawMember) Member {
266305
i++
267306
return id.WrapRaw(f, id.ID[Member](i), raw)
268307
})
269308
}
270309

271310
// Services returns all services defined in this file.
272311
func (f *File) Services() seq.Indexer[Service] {
312+
var services []id.ID[Service]
313+
if f != nil {
314+
services = f.services
315+
}
273316
return seq.NewFixedSlice(
274-
f.services,
317+
services,
275318
func(_ int, p id.ID[Service]) Service {
276319
return id.Wrap(f, p)
277320
},
@@ -280,11 +323,18 @@ func (f *File) Services() seq.Indexer[Service] {
280323

281324
// Options returns the top level options applied to this file.
282325
func (f *File) Options() MessageValue {
283-
return id.Wrap(f, f.options).AsMessage()
326+
var options id.ID[Value]
327+
if f != nil {
328+
options = f.options
329+
}
330+
return id.Wrap(f, options).AsMessage()
284331
}
285332

286333
// FeatureSet returns the Editions features associated with this file.
287334
func (f *File) FeatureSet() FeatureSet {
335+
if f == nil {
336+
return FeatureSet{}
337+
}
288338
return id.Wrap(f, f.features)
289339
}
290340

@@ -308,8 +358,12 @@ func (f *File) Deprecated() Value {
308358
// imported by the file. The symbols are returned in an arbitrary but fixed
309359
// order.
310360
func (f *File) Symbols() seq.Indexer[Symbol] {
361+
var symbols []Ref[Symbol]
362+
if f != nil {
363+
symbols = f.imported
364+
}
311365
return seq.NewFixedSlice(
312-
f.imported,
366+
symbols,
313367
func(_ int, r Ref[Symbol]) Symbol {
314368
return GetRef(f, r)
315369
},

experimental/ir/ir_imports.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,21 @@ func (i *imports) MarkUsed(file *File) {
193193

194194
// DescriptorProto returns the file for descriptor.proto.
195195
func (i *imports) DescriptorProto() *File {
196+
if i == nil {
197+
return nil
198+
}
196199
imported, _ := slicesx.Last(i.files)
197200
return imported.file
198201
}
199202

200203
// Directs returns an indexer over the Directs imports.
201204
func (i *imports) Directs() seq.Indexer[Import] {
205+
var slice []imported
206+
if i != nil {
207+
slice = i.files[:i.importEnd]
208+
}
202209
return seq.NewFixedSlice(
203-
i.files[:i.importEnd],
210+
slice,
204211
func(j int, imported imported) Import {
205212
n := uint32(j)
206213
public := n < i.publicEnd
@@ -223,8 +230,12 @@ func (i *imports) Directs() seq.Indexer[Import] {
223230
//
224231
// This function does not report whether those imports are weak or used.
225232
func (i *imports) Transitive() seq.Indexer[Import] {
233+
var slice []imported
234+
if i != nil {
235+
slice = i.files[:max(0, len(i.files)-1)] // Exclude the implicit descriptor.proto
236+
}
226237
return seq.NewFixedSlice(
227-
i.files[:max(0, len(i.files)-1)], // Exclude the implicit descriptor.proto.
238+
slice,
228239
func(j int, imported imported) Import {
229240
n := uint32(j)
230241
return Import{

experimental/ir/ir_member.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,10 @@ func (m Member) Oneof() Oneof {
336336

337337
// Options returns the options applied to this member.
338338
func (m Member) Options() MessageValue {
339+
if m.IsZero() {
340+
return MessageValue{}
341+
}
342+
339343
return id.Wrap(m.Context(), m.Raw().options).AsMessage()
340344
}
341345

@@ -587,12 +591,13 @@ func (o Oneof) Index() int {
587591

588592
// Members returns this oneof's member fields.
589593
func (o Oneof) Members() seq.Indexer[Member] {
590-
return seq.NewFixedSlice(
591-
o.Raw().members,
592-
func(_ int, p id.ID[Member]) Member {
593-
return id.Wrap(o.Context(), p)
594-
},
595-
)
594+
var members []id.ID[Member]
595+
if !o.IsZero() {
596+
members = o.Raw().members
597+
}
598+
return seq.NewFixedSlice(members, func(_ int, p id.ID[Member]) Member {
599+
return id.Wrap(o.Context(), p)
600+
})
596601
}
597602

598603
// Parent returns the type that this oneof is declared within,.
@@ -715,6 +720,9 @@ type rawReservedName struct {
715720

716721
// AST returns the expression that this name was evaluated from, if known.
717722
func (r ReservedName) AST() ast.ExprAny {
723+
if r.IsZero() {
724+
return ast.ExprAny{}
725+
}
718726
return r.raw.ast
719727
}
720728

experimental/ir/ir_type.go

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ func (t Type) AllowsAlias() bool {
196196
// IsAny returns whether this is the type google.protobuf.Any, which gets special
197197
// treatment in the language.
198198
func (t Type) IsAny() bool {
199-
return t.InternedFullName() == t.Context().session.builtins.AnyPath
199+
return !t.IsZero() && t.InternedFullName() == t.Context().session.builtins.AnyPath
200200
}
201201

202202
// Predeclared returns the predeclared type that this Type corresponds to, if any.
@@ -417,7 +417,10 @@ func (t Type) Extensions() seq.Indexer[Member] {
417417
//
418418
// This does not include reserved field names; see [Type.ReservedNames].
419419
func (t Type) AllRanges() seq.Indexer[ReservedRange] {
420-
slice := t.Raw().ranges
420+
var slice []id.ID[ReservedRange]
421+
if !t.IsZero() {
422+
slice = t.Raw().ranges
423+
}
421424
return seq.NewFixedSlice(slice, func(_ int, p id.ID[ReservedRange]) ReservedRange {
422425
return id.Wrap(t.Context(), p)
423426
})
@@ -427,24 +430,34 @@ func (t Type) AllRanges() seq.Indexer[ReservedRange] {
427430
//
428431
// This does not include reserved field names; see [Type.ReservedNames].
429432
func (t Type) ReservedRanges() seq.Indexer[ReservedRange] {
430-
slice := t.Raw().ranges[:t.Raw().rangesExtnStart]
433+
var slice []id.ID[ReservedRange]
434+
if !t.IsZero() {
435+
slice = t.Raw().ranges[:t.Raw().rangesExtnStart]
436+
}
431437
return seq.NewFixedSlice(slice, func(_ int, p id.ID[ReservedRange]) ReservedRange {
432438
return id.Wrap(t.Context(), p)
433439
})
434440
}
435441

436442
// ExtensionRanges returns the extension ranges declared in this type.
437443
func (t Type) ExtensionRanges() seq.Indexer[ReservedRange] {
438-
slice := t.Raw().ranges[t.Raw().rangesExtnStart:]
444+
var slice []id.ID[ReservedRange]
445+
if !t.IsZero() {
446+
slice = t.Raw().ranges[t.Raw().rangesExtnStart:]
447+
}
439448
return seq.NewFixedSlice(slice, func(_ int, p id.ID[ReservedRange]) ReservedRange {
440449
return id.Wrap(t.Context(), p)
441450
})
442451
}
443452

444453
// ReservedNames returns the reserved named declared in this type.
445454
func (t Type) ReservedNames() seq.Indexer[ReservedName] {
455+
var slice []rawReservedName
456+
if !t.IsZero() {
457+
slice = t.Raw().reservedNames
458+
}
446459
return seq.NewFixedSlice(
447-
t.Raw().reservedNames,
460+
slice,
448461
func(i int, _ rawReservedName) ReservedName {
449462
return ReservedName{id.WrapContext(t.Context()), &t.Raw().reservedNames[i]}
450463
},
@@ -453,8 +466,12 @@ func (t Type) ReservedNames() seq.Indexer[ReservedName] {
453466

454467
// Oneofs returns the options applied to this type.
455468
func (t Type) Oneofs() seq.Indexer[Oneof] {
469+
var oneofs []id.ID[Oneof]
470+
if !t.IsZero() {
471+
oneofs = t.Raw().oneofs
472+
}
456473
return seq.NewFixedSlice(
457-
t.Raw().oneofs,
474+
oneofs,
458475
func(_ int, p id.ID[Oneof]) Oneof {
459476
return id.Wrap(t.Context(), p)
460477
},
@@ -463,8 +480,12 @@ func (t Type) Oneofs() seq.Indexer[Oneof] {
463480

464481
// Extends returns the options applied to this type.
465482
func (t Type) Extends() seq.Indexer[Extend] {
483+
var extends []id.ID[Extend]
484+
if !t.IsZero() {
485+
extends = t.Raw().extends
486+
}
466487
return seq.NewFixedSlice(
467-
t.Raw().extends,
488+
extends,
468489
func(_ int, p id.ID[Extend]) Extend {
469490
return id.Wrap(t.Context(), p)
470491
},
@@ -473,6 +494,9 @@ func (t Type) Extends() seq.Indexer[Extend] {
473494

474495
// Options returns the options applied to this type.
475496
func (t Type) Options() MessageValue {
497+
if t.IsZero() {
498+
return MessageValue{}
499+
}
476500
return id.Wrap(t.Context(), t.Raw().options).AsMessage()
477501
}
478502

experimental/ir/ir_value.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,13 @@ func (e Element) AST() ast.ExprAny {
571571
// this element, e.g.
572572
//
573573
// key := e.Value().MessageKeys().At(e.ValueNodeIndex())
574+
//
575+
// If the element is empty, this returns -1.
574576
func (e Element) ValueNodeIndex() int {
577+
if e.IsZero() {
578+
return -1
579+
}
580+
575581
// We do O(log n) work here, because this function doesn't get called except
576582
// for diagnostics.
577583

0 commit comments

Comments
 (0)