OpenAPITools / OpenAPITools/openapi-generator

[REQ] [Go] Mixed pointer/non-pointer usage leads to messy nested types

Open
#8,358 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Enhancement: Feature
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Is your feature request related to a problem? Please describe.

The Go code generator was updated in version 5.0 to support distinction between set/unset fields. While this may be a useful addition in some scenarios, there is a flaw in how Getters for nested types are represented: They act on a pointer, but return a plain object instead of a pointer. This makes it impossible to chain Get*() calls, because Go doesn't allow taking the address of a return value, neither implicitly nor explicitly.

Describe the solution you'd like

Ideally, the concept of "set" and "unset" should be dropped in favor of plain Go data structures, using pointers where a field is optional. When a default value is defined, it should be assigned during deserialization.

As a simpler alternative, getters on complex data types should return a pointer instead of a plain object.

Describe alternatives you've considered

The alternative would be to assign every nested field in a chain to a temporary variable, then call the next getter on this variable. This is awkward and not very idiomatic.

Additional context

Here's an example OpenAPI definition:

openapi: "3.0.1"
components:
  schemas:
    Container:
      type: "object"
      properties:
        nestedfield:
          $ref: "#/components/schemas/Child"
    Child:
      type: "object"
      properties:
        field:
          type: "boolean"

The generated Go code will look similar to this:

type Container struct {
	Nestedfield *Child
}
func (o *Container) GetNestedfield() Child {
	if o == nil || o.Nestedfield == nil {
		var ret Child
		return ret
	}
	return *o.Nestedfield
}
type Child struct {
	Field *bool
}
func (o *Child) GetField() bool {
	if o == nil || o.Field == nil {
		var ret bool
		return ret
	}
	return *o.Field
}

Testing it with a simple, straight-foward example like

func main() {
	var c *Container
	c.GetNestedfield().GetField()
}

Will yield:

./prog.go:24:20: cannot call pointer method on c.GetNestedfield()
./prog.go:24:20: cannot take the address of c.GetNestedfield()

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the issue with the OpenAPI Container and Child schemas and the generated Go types and main example shown here. Compare the nested getter signatures and verify the chosen pointer or set/unset behavior; done means nested Get* calls can be chained without the reported compilation errors while optional fields and defaults remain correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, openapi
Domain
api, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.