Repository: getkin/kin-openapi (Go)

Issue:
Nil `Info` in `openapi.T` causes panic when marshalled
I discovered a panic through a test where the `Info` was missing. I understand it's a required field but I would have expected an error from the loader, not a panic when marshalling.

This is roughly how I discovered it:
```go
loader := &openapi3.Loader{}
doc, err := loader.LoadFromData([]byte("{}"))
require.NoError(t, err) // Doesn't error

// This panics on v0.125.0 but not v0.124.0
yaml.Marshal(doc)
```

This is of course simplified. In my actual code I have some transformations going on between that and it loads from a file. If all you want to do is reproduce the panic you can do `yaml.Marshal(&openapi3.Loader{})`. Interestingly `yaml.Marshal(openapi3.Loader{})` still doesn't panic.

Here's the relevant stacktrace, trimmed down a little:
```ts
panic({0x16ba440?, 0xc00005a3d0?})
	/usr/local/go/src/runtime/panic.go:770 +0x132
gopkg.in/yaml%2ev3.handleErr(0xc000149c50)
	gopkg.in/yaml.v3@v3.0.1/yaml.go:294 +0x6d
panic({0x16ba440?, 0xc00005a3d0?})
	/usr/local/go/src/runtime/panic.go:770 +0x132
github.com/getkin/kin-openapi/openapi3.(*Info).MarshalYAML(0x1688540?)
	<autogenerated>:1 +0x8c
gopkg.in/yaml%2ev3.(*encoder).marshal(0xc0004d4c08, {0x0, 0x0}, {0x1688540?, 0xc00005a3c0?, 0x98?})
	gopkg.in/yaml.v3@v3.0.1/encode.go:140 +0x897
gopkg.in/yaml%2ev3.(*encoder).marshal.(*encoder).mapv.func1()
	gopkg.in/yaml.v3@v3.0.1/encode.go:192 +0xe5
gopkg.in/yaml%2ev3.(*encoder).mappingv(0xc0004d4c08, {0x0?, 0x0}, 0xc0001499b0)
	gopkg.in/yaml.v3@v3.0.1/encode.go:265 +0x14a
gopkg.in/yaml%2ev3.(*encoder).mapv(...)
	gopkg.in/yaml.v3@v3.0.1/encode.go:187
gopkg.in/yaml%2ev3.(*encoder).marshal(0xc0004d4c08, {0x0, 0x0}, {0x16a60c0?, 0xc000382ab0?, 0x1?})
	gopkg.in/yaml.v3@v3.0.1/encode.go:164 +0x7d4
gopkg.in/yaml%2ev3.(*encoder).marshal(0xc0004d4c08, {0x0, 0x0}, {0x1872d00?, 0xc000404240?, 0xc000149bd8?})
	gopkg.in/yaml.v3@v3.0.1/encode.go:148 +0x926
gopkg.in/yaml%2ev3.(*encoder).marshalDoc(0xc0004d4c08, {0x0, 0x0}, {0x1872d00?, 0xc000404240?, 0x0?})
	gopkg.in/yaml.v3@v3.0.1/encode.go:105 +0x12e
gopkg.in/yaml%2ev3.Marshal({0x1872d00, 0xc000404240})
	gopkg.in/yaml.v3@v3.0.1/yaml.go:222 +0x326
[my test]
	main_test.go:85 +0x7f


Discussion: I looked into this, and it seems that the yaml package that you are using is to blame:

```
gopkg.in/yaml%2ev3.(*encoder).marshal(0x14000120c08, {0x0, 0x0}, {0x1010ce540?, 0x140001138d0?, 0x98?})
	/Users/anatoliirugalev/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/encode.go:140 +0x704
```

Specifically, this code:

```go
	case Marshaler:
		v, err := value.MarshalYAML()
		if err != nil {
			fail(err)
		}
```

It assumes that `value` will be a pointer, but it's not guaranteed. Here's the issue in `yaml.v3`: https://github.com/go-yaml/yaml/issues/747

I don't think there's anything to be done in `kin-openapi`. I would suggest you to switch to a different library, as `yaml.v3` seems abandoned. For example: https://pkg.go.dev/github.com/goccy/go-yaml




Fix (patch):
diff --git a/.github/docs/openapi3.txt b/.github/docs/openapi3.txt
index e0e4c70c..3a7caf2c 100644
--- a/.github/docs/openapi3.txt
+++ b/.github/docs/openapi3.txt
@@ -645,7 +645,7 @@ type Info struct {
 func (info Info) MarshalJSON() ([]byte, error)
     MarshalJSON returns the JSON encoding of Info.
 
-func (info Info) MarshalYAML() (any, error)
+func (info *Info) MarshalYAML() (any, error)
     MarshalYAML returns the YAML encoding of Info.
 
 func (info *Info) UnmarshalJSON(data []byte) error
diff --git a/maps.sh b/maps.sh
index b23c3601..9cfd0ffd 100755
--- a/maps.sh
+++ b/maps.sh
@@ -155,9 +155,16 @@ EOF
 
 
 maplike_UnMarsh() {
+	if [[ "$type" != '*'* ]]; then
+		echo "TODO: impl non-pointer receiver YAML Marshaler"
+		exit 2
+	fi
 	cat <<EOF >>"$maplike"
 // MarshalYAML returns the YAML encoding of ${type#'*'}.
 func (${name} ${type}) MarshalYAML() (any, error) {
+	if ${name} == nil {
+		return nil, nil
+	}
 	m := make(map[string]any, ${name}.Len()+len(${name}.Extensions))
 	for k, v := range ${name}.Extensions {
 		m[k] = v
diff --git a/openapi3/info.go b/openapi3/info.go
index 7326bcc0..e2468285 100644
--- a/openapi3/info.go
+++ b/openapi3/info.go
@@ -29,7 +29,10 @@ func (info Info) MarshalJSON() ([]byte, error) {
 }
 
 // MarshalYAML returns the YAML encoding of Info.
-func (info Info) MarshalYAML() (any, error) {
+func (info *Info) MarshalYAML() (any, error) {
+	if info == nil {
+		return nil, nil
+	}
 	m := make(map[string]any, 6+len(info.Extensions))
 	for k, v := range info.Extensions {
 		m[k] = v
diff --git a/openapi3/loader.go b/openapi3/loader.go
index 567d7394..4f2766a0 100644
--- a/openapi3/loader.go
+++ b/openapi3/loader.go
@@ -1140,8 +1140,7 @@ func (loader *Loader) resolvePathItemRef(doc *T, pathItem *PathItem, documentPat
 			*pathItem = p
 		} else {
 			var resolved PathItem
-			doc, documentPath, err = loader.resolveComponent(doc, ref, documentPath, &resolved)
-			if err != nil {
+			if doc, documentPath, err = loader.resolveComponent(doc, ref, documentPath, &resolved); err != nil {
 				if err == errMUSTPathItem {
 					return nil
 				}
diff --git a/openapi3/maplike.go b/openapi3/maplike.go
index 2085b7e2..7b8045c6 100644
--- a/openapi3/maplike.go
+++ b/openapi3/maplike.go
@@ -78,6 +78,9 @@ func (responses Responses) JSONLookup(token string) (any, error) {
 
 // MarshalYAML returns the YAML encoding of Responses.
 func (responses *Responses) MarshalYAML() (any, error) {
+	if responses == nil {
+		return nil, nil
+	}
 	m := make(map[string]any, responses.Len()+len(responses.Extensions))
 	for k, v := range responses.Extensions {
 		m[k] = v
@@ -206,6 +209,9 @@ func (callback Callback) JSONLookup(token string) (any, error) {
 
 // MarshalYAML returns the YAML encoding of Callback.
 func (callback *Callback) MarshalYAML() (any, error) {
+	if callback == nil {
+		return nil, nil
+	}
 	m := make(map[string]any, callback.Len()+len(callback.Extensions))
 	for k, v := range callback.Extensions {
 		m[k] = v
@@ -334,6 +340,9 @@ func (paths Paths) JSONLookup(token string) (any, error) {
 
 // MarshalYAML returns the YAML encoding of Paths.
 func (paths *Paths) MarshalYAML() (any, error) {
+	if paths == nil {
+		return nil, nil
+	}
 	m := make(map[string]any, paths.Len()+len(paths.Extensions))
 	for k, v := range paths.Extensions {
 		m[k] = v
diff --git a/openapi3/openapi3.go b/openapi3/openapi3.go
index f8228012..ef1592e8 100644
--- a/openapi3/openapi3.go
+++ b/openapi3/openapi3.go
@@ -66,6 +66,9 @@ func (doc *T) MarshalJSON() ([]byte, error) {
 
 // MarshalYAML returns the YAML encoding of T.
 func (doc *T) MarshalYAML() (any, error) {
+	if doc == nil {
+		return nil, nil
+	}
 	m := make(map[string]any, 4+len(doc.Extensions))
 	for k, v := range doc.Extensions {
 		m[k] = v