Repository: go-swagger/go-swagger (go)

Issue:
How to define properties of an object (that are referenced to definitions) as required ?
## Problem statement

The expected behaviour is that go-swagger defines required fields as pointers. For example,

```
SignupRequest:
    properties:
      email:
        type: string
    required:
      - email
```

will be

```
type SignupRequest struct {
	// email
	// Required: true
	Email *string `json:"email"`
}
```

However, required properties of an object that are referenced to definitions  are not pointers by default. For example:

```
SignupRequest:
    properties:
      email:
        $ref: "#/definitions/Email"
    required:
      - email
definitions:
  Email:
    type: string
```
wille be:

```
type SignupRequest struct {
	// email
	// Required: true
	Email Email `json:"email"`
}
```

Should Email field be of *Email type?

Now to get the email as pointer, we can use `x-nullable`. However the following does not work:

```
SignupRequest:
    properties:
      email:
        $ref: "#/definitions/Email"
        x-nullable: true
    required:
      - email
definitions:
  Email:
    type: string
```

But If we put `x-nullable` in the definition as following, it works, but I do not want to do it in the common definitions!

```
SignupRequest:
    properties:
      email:
        $ref: "#/definitions/Email"
    required:
      - email
definitions:
  Email:
    type: string
    x-nullable: true
```

The other workaround is to use `allOf` as following. Which is weird!

```
SignupRequest:
    properties:
      email:
        allOf:
          - $ref: "#/definitions/Email"
          - x-nullable: true
    required:
      - email
definitions:
  Email:
    type: string
```

## Environment
swagger version: 2.0
go version: go1.14.6


Pull request:
Fixed required aliased type
Whenever a required property is an aliased type, it is rendered with a pointer type (not if interface{}).

* Changed the nullability assessment of aliased types: it now inherits from the nullable decision taken at a lower level (within $ref)
* Fixed ability to override nullability for AllOf, now x-nullable extensions in AllOf are honored

* fixes #2400

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>

Patch:
diff --git a/fixtures/bugs/1487/gen-fixtures.sh b/fixtures/bugs/1487/gen-fixtures.sh
index cae17afa..3612d089 100755
--- a/fixtures/bugs/1487/gen-fixtures.sh
+++ b/fixtures/bugs/1487/gen-fixtures.sh
@@ -1,4 +1,4 @@
-#! /bin/bash 
+#! /bin/bash
 if [[ ${1} == "--clean" ]] ; then
     clean=1
 fi
@@ -65,7 +65,7 @@ for testcase in ${testcases} ; do
     rm -rf ${target}
     mkdir ${target}
     echo "Model generation for ${spec} with opts=${opts}"
-    swagger generate model --skip-validation ${opts} --spec=${spec} --target=${target} --output=${testcase%.*}.log
+    swagger generate model --skip-validation ${opts} --spec=${spec} --target=${target} --log-output=${testcase%.*}.log
     # 1>x.log 2>&1
     #
     if [[ $? != 0 ]] ; then
@@ -92,21 +92,21 @@ for testcase in ${testcases} ; do
             fi
         fi
         echo "${spec}: Build OK"
-        if [[ -n ${clean} ]] ; then 
+        if [[ -n ${clean} ]] ; then
              rm -rf ${target}
         fi
     fi
 done
 done
-if [[ ! -z ${failures} ]] ; then 
+if [[ ! -z ${failures} ]] ; then
     echo ${failures}|tr ' ' '\n'
 else
     echo "No failures"
 fi
 exit
 # Non reg codegen
-# NOTE(fredbi): 
-# - azure: invalid spec 
+# NOTE(fredbi):
+# - azure: invalid spec
 # - bitbucket: model does not compile
 # - issue72: invalid spec
 # - todolist.discriminator: ok now
@@ -117,7 +117,7 @@ for testcase in ${testcases} ; do
     target=./gen-${testcase%.*}
     if [[ -f ../../codegen/${testcase} ]] ; then
       spec=../../codegen/${testcase}
-    else 
+    else
       spec=${testcase}
     fi
     serverName="nrcodegen"
@@ -125,7 +125,7 @@ for testcase in ${testcases} ; do
     mkdir ${target}
     echo "Server generation for ${spec}"
     swagger generate server --skip-validation --spec ${spec} --target ${target} --name=${serverName} 1>${testcase%.*}.log 2>&1
-    #--output=${testcase%.*}.log
+    #--log-output=${testcase%.*}.log
     if [[ $? != 0 ]] ; then
         echo "Generation failed for ${spec}"
         exit 1
@@ -137,7 +137,7 @@ for testcase in ${testcases} ; do
         exit 1
     fi
     echo "${spec}: Build OK"
-    if [[ -n ${clean} ]] ; then 
+    if [[ -n ${clean} ]] ; then
         rm -rf ${target}
     fi
 done
@@ -162,7 +162,7 @@ for testcase in ${testcases} ; do
         exit 1
     fi
     echo "${spec}: Build OK"
-    if [[ -n ${clean} ]] ; then 
+    if [[ -n ${clean} ]] ; then
         rm -rf ${target}
     fi
 done
diff --git a/fixtures/bugs/2364/.gitignore b/fixtures/bugs/2364/.gitignore
new file mode 100644
index 00000000..1e16265b
--- /dev/null
+++ b/fixtures/bugs/2364/.gitignore
@@ -0,0 +1,3 @@
+gen-*
+!gen-fixtures.sh
+*.log
diff --git a/fixtures/bugs/2364/fixture-2364.yaml b/fixtures/bugs/2364/fixture-2364.yaml
index 83670da4..bd32d58e 100644
--- a/fixtures/bugs/2364/fixture-2364.yaml
+++ b/fixtures/bugs/2364/fixture-2364.yaml
@@ -31,6 +31,14 @@ definitions:
           type: array
           items:
             $ref: "#/definitions/ItemBundleSectionResponse"
+        nullableSections:
+            type: array
+            items:
+              $ref: "#/definitions/NullableItemBundleSectionResponse"
+        otherSections:
+            type: array
+            items:
+              $ref: "#/definitions/OtherItemBundleSectionResponse"
 
   BundleType:
     type: string
@@ -63,3 +71,28 @@ definitions:
             items:
               $ref: "#/definitions/BundleItemResponse"
     x-nullable: false
+
+  NullableItemBundleSectionResponse:
+    allOf:
+      - $ref: "#/definitions/BundleSectionResponse"
+      - type: object
+        x-omitempty: false
+        properties:
+          items:
+            description: Array of bundle items inside a section of the bundle
+            type: array
+            items:
+              $ref: "#/definitions/BundleItemResponse"
+    x-nullable: true
+
+  OtherItemBundleSectionResponse:
+    allOf:
+      - $ref: "#/definitions/BundleSectionResponse"
+      - type: object
+        x-omitempty: false
+        properties:
+          items:
+            description: Array of bundle items inside a section of the bundle
+            type: array
+            items:
+              $ref: "#/definitions/BundleItemResponse"
diff --git a/fixtures/bugs/2364/gen-fixtures.sh b/fixtures/bugs/2364/gen-fixtures.sh
new file mode 100755
index 00000000..4946857f
--- /dev/null
+++ b/fixtures/bugs/2364/gen-fixtures.sh
@@ -0,0 +1,74 @@
+#! /bin/bash
+if [[ ${1} == "--clean" ]] ; then
+    clean=1
+fi
+continueOnError=
+# A small utility to build fixture servers
+# Fixtures with models only
+testcases="fixture-2364.yaml"
+for opts in  "" ; do
+for testcase in ${testcases} ; do
+    grep -q discriminator ${testcase}
+    discriminated=$?
+    if [[ ${discriminated} -eq 0 && ${opts} == "--with-expand" ]] ; then
+        echo "Skipped ${testcase} with ${opts}: discriminator not supported with ${opts}"
+        continue
+    fi
+    if [[ ${testcase} == "../1479/fixture-1479-part.yaml" && ${opts} == "--with-expand" ]] ; then
+        echo "Skipped ${testcase} with ${opts}: known issue with enum in anonymous allOf not validated. See you next PR"
+        continue
+    fi
+
+    spec=${testcase}
+    testcase=`basename ${testcase}`
+    if [[ -z ${opts} ]]; then
+        target=./gen-${testcase%.*}-flatten
+    else
+        target=./gen-${testcase%.*}-expand
+    fi
+    serverName="codegensrv"
+    rm -rf ${target}
+    mkdir ${target}
+    echo "Model generation for ${spec} with opts=${opts}"
+    serverName="nrcodegen"
+    swagger generate server --skip-validation ${opts} --spec ${spec} --target ${target} --name=${serverName} 1>${testcase%.*}.log 2>&1
+    # 1>x.log 2>&1
+    #
+    if [[ $? != 0 ]] ; then
+        echo "Generation failed for ${spec}"
+        if [[ ! -z ${continueOnError} ]] ; then
+            failures="${failures} codegen:${spec}"
+            continue
+        else
+            exit 1
+        fi
+    fi
+    echo "${spec}: Generation OK"
+    if [[ ! -d ${target}/models ]] ; then
+        echo "No model in this spec! Continue building server"
+    fi
+    if [[ -d ${target}/cmd/${serverName}"-server" ]] ; then
+        (cd ${target}/cmd/${serverName}"-server"; go build)
+        #(cd ${target}/models; go build)
+        if [[ $? != 0 ]] ; then
+            echo "Build failed for ${spec}"
+            if [[ ! -z ${continueOnError} ]] ; then
+                failures="${failures} build:${spec}"
+                continue
+            else
+                exit 1
+            fi
+        fi
+        echo "${spec}: Build OK"
+        if [[ -n ${clean} ]] ; then
+             rm -rf ${target}
+        fi
+    fi
+done
+done
+if [[ ! -z ${failures} ]] ; then
+    echo ${failures}|tr ' ' '\n'
+else
+    echo "No failures"
+fi
+exit
diff --git a/fixtures/bugs/2381/fixture-2381.yaml b/fixtures/bugs/2381/fixture-2381.yaml
index 0e4ecbd2..ef258a46 100644
--- a/fixtures/bugs/2381/fixture-2381.yaml
+++ b/fixtures/bugs/2381/fixture-2381.yaml
@@ -28,6 +28,9 @@ definitions:
       - required_string
       - required_referenced_object
       - required_referenced_string
+      - required_referenced_struct
+      - required_referenced_array
+      - required_referenced_map
     properties:
       required_string:
         type: string
@@ -35,7 +38,29 @@ definitions:
         $ref: '#/definitions/my_object_ref'
       required_referenced_string:
         $ref: '#/definitions/my_string_ref'
-  my_object_ref:
+      required_referenced_struct:
+        $ref: '#/definitions/my_struct_ref'
+      required_referenced_array:
+        $ref: '#/definitions/my_array_ref'
+      required_referenced_map:
+        $ref: '#/definitions/my_map_ref'
+  my_object_ref:  # <- interface
     type: object
-  my_string_ref:
+  my_string_ref:  # <- aliased type
     type: string
+  my_struct_ref:  # <- struct
+    type: object
+    properties:
+      x:
+        type: integer
+  my_array_ref:  # <- slice
+    type: array
+    items:
+      type: object
+  my_map_ref:  # <- map
+    type: object
+    additionalProperties:
+      type: object
+      properties:
+        'y':
+          type: integer
diff --git a/fixtures/bugs/2400/.gitignore b/fixtures/bugs/2400/.gitignore
new file mode 100644
index 00000000..1e16265b
--- /dev/null
+++ b/fixtures/bugs/2400/.gitignore
@@ -0,0 +1,3 @@
+gen-*
+!gen-fixtures.sh
+*.log
diff --git a/fixtures/bugs/2400/fixture-2400.yaml b/fixtures/bugs/2400/fixture-2400.yaml
new file mode 100644
index 00000000..57617f25
--- /dev/null
+++ b/fixtures/bugs/2400/fixture-2400.yaml
@@ -0,0 +1,42 @@
+---
+  swagger: "2.0"
+  info:
+    title: "required aliased primitive"
+    version: "0.0.1"
+    description: "repro issue 2400"
+    license:
+      name: "Apache 2.0"
+      url: "http://www.apache.org/licenses/LICENSE-2.0.html"
+
+  definitions:
+    SignupRequest:
+      properties:
+        email:
+          type: string
+      required:
+        - email
+
+    Email:
+      type: string
+
+    SignupRequest2:
+      properties:
+        email:
+          $ref: "#/definitions/Email"
+      required:
+        - email
+  paths:
+    /getRecords:
+      get:
+        operationId: getRecords
+        parameters:
+          - name: records
+            in: body
+            required: true
+            schema:
+              $ref: '#/definitions/SignupRequest2'
+        responses:
+          200:
+            description: "OK"
+            schema:
+              $ref: '#/definitions/SignupRequest2'
diff --git a/fixtures/bugs/2400/gen-fixtures.sh b/fixtures/bugs/2400/gen-fixtures.sh
new file mode 100755
index 00000000..c945faf5
--- /dev/null
+++ b/fixtures/bugs/2400/gen-fixtures.sh
@@ -0,0 +1,76 @@
+#! /bin/bash
+if [[ ${1} == "--clean" ]] ; then
+    clean=1
+fi
+continueOnError=
+# A small utility to build fixture servers
+# Fixtures with models only
+testcases="fixture-2400.yaml"
+#testcases="${testcases} ../1487/fixture-844-variations.yaml"
+#testcases="${testcases} ../1487/fixture-itching.yaml"
+for opts in  "" "--with-expand" ; do
+for testcase in ${testcases} ; do
+    grep -q discriminator ${testcase}
+    discriminated=$?
+    if [[ ${discriminated} -eq 0 && ${opts} == "--with-expand" ]] ; then
+        echo "Skipped ${testcase} with ${opts}: discriminator not supported with ${opts}"
+        continue
+    fi
+    if [[ ${testcase} == "../1479/fixture-1479-part.yaml" && ${opts} == "--with-expand" ]] ; then
+        echo "Skipped ${testcase} with ${opts}: known issue with enum in anonymous allOf not validated. See you next PR"
+        continue
+    fi
+
+    spec=${testcase}
+    testcase=`basename ${testcase}`
+    if [[ -z ${opts} ]]; then
+        target=./gen-${testcase%.*}-flatten
+    else
+        target=./gen-${testcase%.*}-expand
+    fi
+    serverName="codegensrv"
+    rm -rf ${target}
+    mkdir ${target}
+    echo "Model generation for ${spec} with opts=${opts}"
+    serverName="nrcodegen"
+    swagger generate server --skip-validation ${opts} --spec ${spec} --target ${target} --name=${serverName} 1>${testcase%.*}.log 2>&1
+    # 1>x.log 2>&1
+    #
+    if [[ $? != 0 ]] ; then
+        echo "Generation failed for ${spec}"
+        if [[ ! -z ${continueOnError} ]] ; then
+            failures="${failures} codegen:${spec}"
+            continue
+        else
+            exit 1
+        fi
+    fi
+    echo "${spec}: Generation OK"
+    if [[ ! -d ${target}/models ]] ; then
+        echo "No model in this spec! Continue building server"
+    fi
+    if [[ -d ${target}/cmd/${serverName}"-server" ]] ; then
+        (cd ${target}/cmd/${serverName}"-server"; go build)
+        #(cd ${target}/models; go build)
+        if [[ $? != 0 ]] ; then
+            echo "Build failed for ${spec}"
+            if [[ ! -z ${continueOnError} ]] ; then
+                failures="${failures} build:${spec}"
+                continue
+            else
+                exit 1
+            fi
+        fi
+        echo "${spec}: Build OK"
+        if [[ -n ${clean} ]] ; then
+             rm -rf ${target}
+        fi
+    fi
+done
+done
+if [[ ! -z ${failures} ]] ; then
+    echo ${failures}|tr ' ' '\n'
+else
+    echo "No failures"
+fi
+exit
diff --git a/generator/model.go b/generator/model.go
index 25705e75..73e56b25 100644
--- a/generator/model.go
+++ b/generator/model.go
@@ -754,7 +754,7 @@ func (sg *schemaGenContext) buildProperties() error {
 
 	for k, v := range sg.Schema.Properties {
 		debugLogAsJSON("building property %s[%q] (tup: %t) (BaseType: %t)",
-			sg.Name, k, sg.IsTuple, sg.GenSchema.IsBaseType, sg.Schema)
+			sg.Name, k, sg.IsTuple, sg.GenSchema.IsBaseType, v)
 		debugLog("property %s[%q] (tup: %t) HasValidations: %t)",
 			sg.Name, k, sg.IsTuple, sg.GenSchema.HasValidations)
 
@@ -1016,7 +1016,12 @@ func (sg *schemaGenContext) buildAllOf() error {
 		log.Printf("warning: cannot generate serializable allOf with conflicting array definitions in %s", sg.Container)
 	}
 
-	sg.GenSchema.IsNullable = true
+	// AllOf types are always considered nullable, except when an extension says otherwise
+	if override, ok := sg.TypeResolver.isNullableOverride(&sg.Schema); ok {
+		sg.GenSchema.IsNullable = override
+	} else {
+		sg.GenSchema.IsNullable = true
+	}
 
 	// prevent IsAliased to bubble up (e.g. when a single branch is itself aliased)
 	sg.GenSchema.IsAliased = sg.GenSchema.IsAliased && len(sg.GenSchema.AllOf) < 2
@@ -1452,7 +1457,7 @@ func (sg *schemaGenContext) buildArray() error {
 	// items from maps of aliased or nullable type remain required
 
 	// NOTE(fredbi): since this is reset below, this Required = true serves the obscure purpose
-	// of indirectly lifting validations from the slice. This is carried on differently now.
+	// of indirectly lifting validations from the slice. This is carried out differently now.
 	// elProp.Required = true
 
 	if err := elProp.makeGenSchema(); err != nil {
@@ -1849,6 +1854,21 @@ func goName(sch *spec.Schema, orig string) string {
 	return orig
 }
 
+func (sg *schemaGenContext) derefMapElement(outer *GenSchema, sch *GenSchema, elem *GenSchema) {
+	derefType := strings.TrimPrefix(elem.GoType, "*")
+
+	if outer.IsAliased {
+		nesting := strings.TrimSuffix(strings.TrimSuffix(outer.AliasedType, elem.GoType), "*")
+		outer.AliasedType = nesting + derefType
+		outer.GoType = derefType
+	} else {
+		nesting := strings.TrimSuffix(strings.TrimSuffix(outer.GoType, elem.GoType), "*")
+		outer.GoType = nesting + derefType
+	}
+
+	elem.GoType = derefType
+}
+
 func (sg *schemaGenContext) checkNeedsPointer(outer *GenSchema, sch *GenSchema, elem *GenSchema) {
 	derefType := strings.TrimPrefix(elem.GoType, "*")
 	switch {
@@ -1890,6 +1910,16 @@ func (sg *schemaGenContext) buildMapOfNullable(sch *GenSchema) {
 						sg.checkNeedsPointer(outer, sch, it)
 					} else if it.IsMap {
 						sg.buildMapOfNullable(it)
+					} else if !it.IsPrimitive && !it.IsArray && it.IsComplexObject && it.IsNullable {
+						// structs in map are not rendered as pointer by default
+						// unless some x-nullable overrides says so
+						_, forced := it.Extensions[xNullable]
+						if !forced {
+							_, forced = it.Extensions[xIsNullable]
+						}
+						if !forced {
+							sg.derefMapElement(outer, sch, it)
+						}
 					}
 					it = it.Items
 				}
diff --git a/generator/types.go b/generator/types.go
index f939cf5f..978aaa06 100644
--- a/generator/types.go
+++ b/generator/types.go
@@ -241,6 +241,7 @@ func (t *typeResolver) resolveSchemaRef(schema *spec.Schema, isRequired bool) (r
 		return
 	}
 	debugLog("resolving ref (anon: %t, req: %t) %s", false, isRequired, schema.Ref.String())
+
 	returns = true
 	var ref *spec.Schema
 	var er error
@@ -268,7 +269,7 @@ func (t *typeResolver) resolveSchemaRef(schema *spec.Schema, isRequired bool) (r
 	}
 	result.HasDiscriminator = res.HasDiscriminator
 	result.IsBaseType = result.HasDiscriminator
-	result.IsNullable = t.isNullable(ref)
+	result.IsNullable = result.IsNullable || t.isNullable(ref) // this has to be overriden for slices and maps
 	result.IsEnumCI = false
 	return
 }
@@ -334,8 +335,18 @@ func (t *typeResolver) resolveFormat(schema *spec.Schema, isAnonymous bool, isRe
 // - it is an object with properties
 // - it is a composed object (allOf)
 //
-// The interpretation of Required as a mean to make a type nullable is carried on elsewhere.
+// The interpretation of Required as a mean to make a type nullable is carried out elsewhere.
 func (t *typeResolver) isNullable(schema *spec.Schema) bool {
+
+	if nullable, ok := t.isNullableOverride(schema); ok {
+		return nullable
+	}
+
+	return len(schema.Properties) > 0 || len(schema.AllOf) > 0
+}
+
+// isNullableOverride determines a nullable flag forced by an extension
+func (t *typeResolver) isNullableOverride(schema *spec.Schema) (bool, bool) {
 	check := func(extension string) (bool, bool) {
 		v, found := schema.Extensions[extension]
 		nullable, cast := v.(bool)
@@ -343,12 +354,14 @@ func (t *typeResolver) isNullable(schema *spec.Schema) bool {
 	}
 
 	if nullable, ok := check(xIsNullable); ok {
-		return nullable
+		return nullable, ok
 	}
+
 	if nullable, ok := check(xNullable); ok {
-		return nullable
+		return nullable, ok
 	}
-	return len(schema.Properties) > 0 || len(schema.AllOf) > 0
+
+	return false, false
 }
 
 func (t *typeResolver) firstType(schema *spec.Schema) string {
@@ -397,9 +410,30 @@ func (t *typeResolver) resolveArray(schema *spec.Schema, isAnonymous, isRequired
 		err = er
 		return
 	}
-	// override the general nullability rule from ResolveSchema():
-	// only complex items are nullable (when not discriminated, not forced by x-nullable)
-	rt.IsNullable = t.isNullable(schema.Items.Schema) && !rt.HasDiscriminator
+
+	// Override the general nullability rule from ResolveSchema() in array elements:
+	// - only complex items are nullable (when not discriminated, not forced by x-nullable)
+	// - arrays of allOf have non nullable elements when not forced by x-nullable
+	elem := schema.Items.Schema
+	if elem.Ref.String() != "" {
+		// drill into $ref to figure out whether we want the element type to nullable or not
+		resolved, erf := spec.ResolveRef(t.Doc.Spec(), &elem.Ref)
+		if erf != nil {
+			debugLog("error resolving ref %s: %v", schema.Ref.String(), erf)
+		}
+		elem = resolved
+	}
+
+	debugLogAsJSON("resolved item for %s", rt.GoType, elem)
+	if nullable, ok := t.isNullableOverride(elem); ok {
+		debugLog("found nullable override in element %s: %t", rt.GoType, nullable)
+		rt.IsNullable = nullable
+	} else {
+		// this differs from isNullable for elements with AllOf
+		debugLog("no nullable override in element %s: Properties: %t, HasDiscriminator: %t", rt.GoType, len(elem.Properties) > 0, rt.HasDiscriminator)
+		rt.IsNullable = len(elem.Properties) > 0 && !rt.HasDiscriminator
+	}
+
 	result.GoType = "[]" + rt.GoType
 	if rt.IsNullable && !strings.HasPrefix(rt.GoType, "*") {
 		result.GoType = "[]*" + rt.GoType
@@ -457,7 +491,12 @@ func (t *typeResolver) resolveObject(schema *spec.Schema, isAnonymous bool) (res
 				isNullable = true
 			}
 		}
-		result.IsNullable = isNullable
+		if override, ok := t.isNullableOverride(schema); ok {
+			// prioritize x-nullable extensions
+			result.IsNullable = override
+		} else {
+			result.IsNullable = isNullable
+		}
 		result.SwaggerType = object
 		return
 	}


Test patch:
diff --git a/generator/moreschemavalidation_fixtures_test.go b/generator/moreschemavalidation_fixtures_test.go
index 6fdb07b1..625bb492 100644
--- a/generator/moreschemavalidation_fixtures_test.go
+++ b/generator/moreschemavalidation_fixtures_test.go
@@ -14,22 +14,48 @@
 
 package generator
 
+func initFixture2400() {
+	f := newModelFixture("../fixtures/bugs/2400/fixture-2400.yaml", "required aliased primitive")
+	flattenRun := f.AddRun(false).WithMinimalFlatten(true)
+
+	flattenRun.AddExpectations("signup_request.go", []string{
+		`type SignupRequest struct {`,
+		`Email *string`,
+	}, todo, noLines, noLines)
+	flattenRun.AddExpectations("signup_request2.go", []string{
+		`type SignupRequest2 struct {`,
+		`Email *Email`,
+	}, todo, noLines, noLines)
+}
+
 func initFixture2381() {
 	f := newModelFixture("../fixtures/bugs/2381/fixture-2381.yaml", "required $ref primitive")
 	flattenRun := f.AddRun(false).WithMinimalFlatten(true)
 	expandRun := f.AddRun(true)
 
 	flattenRun.AddExpectations("my_object.go", []string{
+		`RequiredReferencedObject MyObjectRef`,  // this is an interface{}
+		`RequiredReferencedString *MyStringRef`, // alias to primitive
+		`RequiredString *string`,                // unaliased primitive
+		`RequiredReferencedArray MyArrayRef`,    // no need to use a pointer
+		`RequiredReferencedMap MyMapRef`,        // no need to use a pointer
+		`RequiredReferencedStruct *MyStructRef`, // pointer to struct
 		`func (m *MyObject) validateRequiredReferencedObject(formats strfmt.Registry) error {`,
 		`	if m.RequiredReferencedObject == nil {`,
 		`		return errors.Required("required_referenced_object", "body", nil)`,
 		`func (m *MyObject) validateRequiredReferencedString(formats strfmt.Registry) error {`,
-		`	if err := validate.Required("required_referenced_string", "body", MyStringRef(m.RequiredReferencedString)); err != nil {`,
+		`	if err := validate.Required("required_referenced_string", "body", m.RequiredReferencedString); err != nil {`,
 		`	if err := m.RequiredReferencedString.Validate(formats); err != nil {`,
 		`		if ve, ok := err.(*errors.Validation); ok {`,
 		`			return ve.ValidateName("required_referenced_string")`,
 		`func (m *MyObject) validateRequiredString(formats strfmt.Registry) error {`,
 		`	if err := validate.Required("required_string", "body", m.RequiredString); err != nil {`,
+		`func (m *MyObject) validateRequiredReferencedStruct(formats strfmt.Registry) error {`,
+		`if err := validate.Required("required_referenced_struct", "body", m.RequiredReferencedStruct); err != nil {`,
+		`func (m *MyObject) validateRequiredReferencedArray(formats strfmt.Registry) error {`,
+		`if err := validate.Required("required_referenced_array", "body", m.RequiredReferencedArray); err != nil {`,
+		`func (m *MyObject) validateRequiredReferencedMap(formats strfmt.Registry) error {`,
+		`if err := validate.Required("required_referenced_map", "body", m.RequiredReferencedMap); err != nil {`,
 	}, todo, noLines, noLines)
 
 	expandRun.AddExpectations("my_object.go", []string{
@@ -11281,6 +11307,8 @@ func initFixture2364() {
 		`type BundleAttributesResponse struct {`,
 		`Items []BundleItemResponse`,
 		`Sections []ItemBundleSectionResponse`,
+		`NullableSections []*NullableItemBundleSectionResponse`,
+		`OtherSections []*OtherItemBundleSectionResponse`,
 		`Type BundleType`,
 	},
 		// not expected
diff --git a/generator/moreschemavalidation_test.go b/generator/moreschemavalidation_test.go
index 51c3b4b8..a833b67f 100644
--- a/generator/moreschemavalidation_test.go
+++ b/generator/moreschemavalidation_test.go
@@ -228,6 +228,9 @@ func initModelFixtures() {
 
 	// required $ref primitive
 	initFixture2381()
+
+	// required aliased primitive
+	initFixture2400()
 }
 
 /* Template initTxxx() to prepare and load a fixture:
diff --git a/generator/pointer_test.go b/generator/pointer_test.go
index 01b83846..4285945e 100644
--- a/generator/pointer_test.go
+++ b/generator/pointer_test.go
@@ -27,7 +27,7 @@ func TestTypeResolver_NestedAliasedSlice(t *testing.T) {
 	rt, err := tr.ResolveSchema(&schema, false, false)
 	require.NoError(t, err)
 
-	assert.Equal(t, "[][][]models.StatixItems0", rt.AliasedType)
+	assert.Equal(t, "[][][]*models.StatixItems0", rt.AliasedType)
 }
 
 func TestTypeResolver_PointerLifting(t *testing.T) {
diff --git a/generator/typeresolver_test.go b/generator/typeresolver_test.go
index fb7b53e2..9f0557ce 100644
--- a/generator/typeresolver_test.go
+++ b/generator/typeresolver_test.go
@@ -237,7 +237,9 @@ func TestTypeResolver_Refs(t *testing.T) {
 		require.NoError(t, err)
 
 		assert.True(t, rt.IsArray)
-		assert.Equal(t, "[]"+val.Expected, rt.GoType)
+		// now this behavior has moved down to the type resolver:
+		// * it used to be hidden to the type resolver, but rendered like that eventually
+		assert.Equal(t, "[]*"+val.Expected, rt.GoType)
 	}
 	// for named objects
 	// referenced objects
@@ -263,7 +265,9 @@ func TestTypeResolver_Refs(t *testing.T) {
 		require.NoError(t, err)
 
 		assert.True(t, rt.IsArray)
-		assert.Equal(t, "[]"+val.Expected, rt.GoType)
+		// now this behavior has moved down to the type resolver:
+		// * it used to be hidden to the type resolver, but rendered like that eventually
+		assert.Equal(t, "[]*"+val.Expected, rt.GoType)
 	}
 }
 
@@ -397,7 +401,7 @@ func TestTypeResolver_Notables(t *testing.T) {
 	assert.True(t, rest.IsArray)
 	assert.False(t, rest.IsAnonymous)
 	assert.False(t, rest.IsNullable)
-	assert.Equal(t, "[]models.Notable", rest.GoType)
+	assert.Equal(t, "[]*models.Notable", rest.GoType)
 }
 
 func specResolver(t testing.TB, path string) (*loads.Document, *typeResolver, error) {
@@ -646,7 +650,7 @@ func TestTypeResolver_ExistingModel(t *testing.T) {
 	assert.False(t, rest.IsAnonymous)
 	assert.False(t, rest.IsComplexObject)
 	assert.False(t, rest.IsCustomFormatter)
-	assert.Equal(t, "[]jwk.Key", rest.GoType)
+	assert.Equal(t, "[]*jwk.Key", rest.GoType)
 	assert.Equal(t, "", rest.Pkg)
 	assert.Equal(t, "", rest.PkgAlias)
 }