Skip to content

Commit 82b955a

Browse files
committed
Support non-hashable Generators in genall
It was previous possible for a generator implementation to become non-hashable by adding non-hashable fields, and since interface values compare by deferencing to their underlying values, this was a problem. This changes it to refer to everything by a *pointer* to a Generator, which just compares the pointer value. Since we're shipping around unique instances anyway, this is fine.
1 parent ebf8b0e commit 82b955a

File tree

3 files changed

+16
-11
lines changed

3 files changed

+16
-11
lines changed

pkg/genall/genall.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ type GenerationContext struct {
9090
}
9191

9292
// WriteYAML writes the given objects out, serialized as YAML, using the
93-
// context's OutputRule.
93+
// context's OutputRule. Objects are written as separate documents, separated
94+
// from each other by `---` (as per the YAML spec).
9495
func (g GenerationContext) WriteYAML(itemPath string, objs ...interface{}) error {
9596
out, err := g.Open(nil, itemPath)
9697
if err != nil {
@@ -161,10 +162,11 @@ func (r *Runtime) Run() bool {
161162
return true
162163
}
163164

164-
for _, gen := range r.Generators {
165+
for i := range r.Generators {
166+
gen := &r.Generators[i] // don't take a reference to the loop variable
165167
ctx := r.GenerationContext // make a shallow copy
166168
ctx.OutputRule = r.OutputRules.ForGenerator(gen)
167-
if err := gen.Generate(&ctx); err != nil {
169+
if err := (*gen).Generate(&ctx); err != nil {
168170
fmt.Fprintln(os.Stderr, err)
169171
}
170172
}

pkg/genall/options.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,14 @@ func FromOptions(optionsRegistry *markers.Registry, options []string) (*Runtime,
108108
func protoFromOptions(optionsRegistry *markers.Registry, options []string) (protoRuntime, error) {
109109
var gens Generators
110110
rules := OutputRules{
111-
ByGenerator: make(map[Generator]OutputRule),
111+
ByGenerator: make(map[*Generator]OutputRule),
112112
}
113113
var paths []string
114114

115115
// collect the generators first, so that we can key the output on the actual
116116
// generator, which matters if there's settings in the gen object and it's not a pointer.
117117
outputByGen := make(map[string]OutputRule)
118-
gensByName := make(map[string]Generator)
118+
gensByName := make(map[string]*Generator)
119119

120120
for _, rawOpt := range options {
121121
if rawOpt[0] != '+' {
@@ -134,7 +134,7 @@ func protoFromOptions(optionsRegistry *markers.Registry, options []string) (prot
134134
switch val := val.(type) {
135135
case Generator:
136136
gens = append(gens, val)
137-
gensByName[defn.Name] = val
137+
gensByName[defn.Name] = &val
138138
case OutputRule:
139139
_, genName := splitOutputRuleOption(defn.Name)
140140
if genName == "" {
@@ -176,7 +176,7 @@ type protoRuntime struct {
176176
Paths []string
177177
Generators Generators
178178
OutputRules OutputRules
179-
GeneratorsByName map[string]Generator
179+
GeneratorsByName map[string]*Generator
180180
}
181181

182182
// splitOutputRuleOption splits a marker name of "output:rule:gen" or "output:rule"

pkg/genall/output.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ func (n nopCloser) Close() error {
3939
// DirectoryPerGenerator produces output rules mapping output to a different subdirectory
4040
// of the given base directory for each generator (with each subdirectory specified as
4141
// the key in the input map).
42-
func DirectoryPerGenerator(base string, generators map[string]Generator) OutputRules {
42+
func DirectoryPerGenerator(base string, generators map[string]*Generator) OutputRules {
4343
rules := OutputRules{
4444
Default: OutputArtifacts{Config: OutputToDirectory(base)},
45-
ByGenerator: make(map[Generator]OutputRule, len(generators)),
45+
ByGenerator: make(map[*Generator]OutputRule, len(generators)),
4646
}
4747

4848
for name, gen := range generators {
@@ -59,12 +59,15 @@ type OutputRules struct {
5959
// Default is the output rule used when no specific per-generator overrides match.
6060
Default OutputRule
6161
// ByGenerator contains specific per-generator overrides.
62-
ByGenerator map[Generator]OutputRule
62+
// NB(directxman12): this is a pointer to avoid issues if a given Generator becomes unhashable
63+
// (interface values compare by "dereferencing" their internal pointer first, whereas pointers
64+
// compare by the actual pointer itself).
65+
ByGenerator map[*Generator]OutputRule
6366
}
6467

6568
// ForGenerator returns the output rule that should be used
6669
// by the given Generator.
67-
func (o OutputRules) ForGenerator(gen Generator) OutputRule {
70+
func (o OutputRules) ForGenerator(gen *Generator) OutputRule {
6871
if forGen, specific := o.ByGenerator[gen]; specific {
6972
return forGen
7073
}

0 commit comments

Comments
 (0)