Repository: goadesign/goa (go)

Issue:
Errors in Generated Types.go file
In my goa design file I have the following type definition:
```
var Workspace = Type("workspace", func() {
	Attribute("id", String, "Workspace ID")
	Attribute("name", String)
	Attribute("description", String)
	Attribute("destination_ids", ArrayOf(String))
	Attribute("logo_url", String)
})
```
and in the same file, I use it as such:
```
	Method("update", func() {
		Description("Update an existing workspace information")
		Payload(Workspace, func() {
			Required("id")
		})

		HTTP(func() {
			PATCH("/{id}")

			Response("workspace_not_found", StatusNotFound)
		})
	})
```
The goa gen command runs successfully without any error output.
Upon trying to compile/run my app, I get the following error in my `gen/http/workspaces/server/types.go` file:
`cannot use id (variable of type string) as type *string in assignment`
Pointed at the following block of generated code:
```
// NewUpdateWorkspace builds a workspaces service update endpoint payload.
func NewUpdateWorkspace(body *UpdateRequestBody, id string) *workspaces.Workspace {
	v := &workspaces.Workspace{
		Name:        body.Name,
		Description: body.Description,
		LogoURL:     body.LogoURL,
	}
	if body.DestinationIds != nil {
		v.DestinationIds = make([]string, len(body.DestinationIds))
		for i, val := range body.DestinationIds {
			v.DestinationIds[i] = val
		}
	}
	v.ID = id

	return v
}
```
where the second to last line `v.ID = id` is the one throwing the error.

Upon quick inspection, I find that the generated `workspaces.Workspace` type above has the `ID` field as a `*string`, but as you can see, the incoming parameter on this method is a non-pointer `string`. 

If I alter my design code above to remove the `Required("id")`line from the method Payload and put it inside the type definition...the error doesn't occur. Similarly if I have the required line in BOTH the Payload definition and the Type definition...no error.

I can see in the [FAQ page](https://goa.design/learn/faq/) the reasons why generated fields are sometimes pointers and sometimes not, however the mismatch allowed to result in the generated code is what I believe is an issue. 

So that leads me to the following questions:

1. If I'm not supposed to do the above, why doesn't the goa gen command give me an error to let me know rather than generating non-functioning code?
2. If I'm not supposed to do the above, then why is there an example just like it in the dsl documentation? If you look at the `Payload()` [examples](https://pkg.go.dev/goa.design/goa/dsl#Payload), the last example "divide", clearly is defining making attributes required on a user defined Type from the Payload method (same as me). 

Upon seeing that example in the dsl docs, it makes sense to me that it would allow that, because you may want to take the same Type in the Payload for several methods, but you only need to make a certain field in it required for some but not all. Otherwise, you'd have to have duplicates of the Type (either inlined or separately defined) with different Required calls. 

I'm really loving goa so far and I'm hoping we can find a solution to this!

Thanks in advance for your help!

Pull request:
Properly support inline Require
in Payload and Result DSLs. The code generator needs
to produce a different type since the corresponding
fields may not be pointers.

Fix #3120

Patch:
diff --git a/dsl/payload.go b/dsl/payload.go
index 7509be80..c7fa108e 100644
--- a/dsl/payload.go
+++ b/dsl/payload.go
@@ -18,57 +18,56 @@ import (
 //
 // The valid usage for Payload are thus:
 //
-//    Payload(Type)
+//	Payload(Type)
 //
-//    Payload(func())
+//	Payload(func())
 //
-//    Payload(Type, "description")
+//	Payload(Type, "description")
 //
-//    Payload(Type, func())
+//	Payload(Type, func())
 //
-//    Payload(Type, "description", func())
+//	Payload(Type, "description", func())
 //
 // Examples:
 //
-//    Method("upper", func() {
-//        // Use primitive type.
-//        Payload(String)
-//    })
-//
-//    Method("upper", func() {
-//        // Use primitive type.and description
-//        Payload(String, "string to convert to uppercase")
-//    })
-//
-//    Method("upper", func() {
-//        // Use primitive type, description and validations
-//        Payload(String, "string to convert to uppercase", func() {
-//            Pattern("^[a-z]")
-//        })
-//    })
-//
-//    Method("add", func() {
-//        // Define payload data structure inline
-//        Payload(func() {
-//            Description("Left and right operands to add")
-//            Attribute("left", Int32, "Left operand")
-//            Attribute("right", Int32, "Left operand")
-//            Required("left", "right")
-//        })
-//    })
-//
-//    Method("add", func() {
-//        // Define payload type by reference to user type
-//        Payload(Operands)
-//    })
-//
-//    Method("divide", func() {
-//        // Specify additional required attributes on user type.
-//        Payload(Operands, func() {
-//            Required("left", "right")
-//        })
-//    })
-//
+//	Method("upper", func() {
+//	    // Use primitive type.
+//	    Payload(String)
+//	})
+//
+//	Method("upper", func() {
+//	    // Use primitive type.and description
+//	    Payload(String, "string to convert to uppercase")
+//	})
+//
+//	Method("upper", func() {
+//	    // Use primitive type, description and validations
+//	    Payload(String, "string to convert to uppercase", func() {
+//	        Pattern("^[a-z]")
+//	    })
+//	})
+//
+//	Method("add", func() {
+//	    // Define payload data structure inline
+//	    Payload(func() {
+//	        Description("Left and right operands to add")
+//	        Attribute("left", Int32, "Left operand")
+//	        Attribute("right", Int32, "Left operand")
+//	        Required("left", "right")
+//	    })
+//	})
+//
+//	Method("add", func() {
+//	    // Define payload type by reference to user type
+//	    Payload(Operands)
+//	})
+//
+//	Method("divide", func() {
+//	    // Specify additional required attributes on user type.
+//	    Payload(Operands, func() {
+//	        Required("left", "right")
+//	    })
+//	})
 func Payload(val interface{}, args ...interface{}) {
 	if len(args) > 2 {
 		eval.ReportError("too many arguments")
@@ -78,7 +77,7 @@ func Payload(val interface{}, args ...interface{}) {
 		eval.IncompatibleDSL()
 		return
 	}
-	e.Payload = methodDSL("Payload", val, args...)
+	e.Payload = methodDSL(e, "Payload", val, args...)
 }
 
 // StreamingPayload defines a method that accepts a stream of instances of the
@@ -90,40 +89,39 @@ func Payload(val interface{}, args ...interface{}) {
 //
 // Examples:
 //
-//    // Method payload is the JWT token and the method streaming payload is a
-//    // stream of strings.
-//    Method("upper", func() {
-//        Payload(func() {
-//            Token("token", String, func() {
-//                Description("JWT used for authentication")
-//            })
-//        })
-//        StreamingPayload(String)
-//    })
-//
-//    // Method streaming payload is a stream of string with validation set
-//    // on each
-//    Method("upper"), func() {
-//        StreamingPayload(String, "string to convert to uppercase", func() {
-//            Pattern("^[a-z]")
-//        })
-//    }
-//
-//    // Method payload is a stream of objects defined inline
-//    Method("add", func() {
-//        StreamingPayload(func() {
-//            Description("Left and right operands to add")
-//            Attribute("left", Int32, "Left operand")
-//            Attribute("right", Int32, "Left operand")
-//            Required("left", "right")
-//        })
-//    })
-//
-//    // Method payload is a stream of user type
-//    Method("add", func() {
-//        StreamingPayload(Operands)
-//    })
-//
+//	// Method payload is the JWT token and the method streaming payload is a
+//	// stream of strings.
+//	Method("upper", func() {
+//	    Payload(func() {
+//	        Token("token", String, func() {
+//	            Description("JWT used for authentication")
+//	        })
+//	    })
+//	    StreamingPayload(String)
+//	})
+//
+//	// Method streaming payload is a stream of string with validation set
+//	// on each
+//	Method("upper"), func() {
+//	    StreamingPayload(String, "string to convert to uppercase", func() {
+//	        Pattern("^[a-z]")
+//	    })
+//	}
+//
+//	// Method payload is a stream of objects defined inline
+//	Method("add", func() {
+//	    StreamingPayload(func() {
+//	        Description("Left and right operands to add")
+//	        Attribute("left", Int32, "Left operand")
+//	        Attribute("right", Int32, "Left operand")
+//	        Required("left", "right")
+//	    })
+//	})
+//
+//	// Method payload is a stream of user type
+//	Method("add", func() {
+//	    StreamingPayload(Operands)
+//	})
 func StreamingPayload(val interface{}, args ...interface{}) {
 	if len(args) > 2 {
 		eval.ReportError("too many arguments")
@@ -133,7 +131,7 @@ func StreamingPayload(val interface{}, args ...interface{}) {
 		eval.IncompatibleDSL()
 		return
 	}
-	e.StreamingPayload = methodDSL("StreamingPayload", val, args...)
+	e.StreamingPayload = methodDSL(e, "StreamingPayload", val, args...)
 	if e.Stream == expr.ServerStreamKind {
 		e.Stream = expr.BidirectionalStreamKind
 	} else {
@@ -141,7 +139,7 @@ func StreamingPayload(val interface{}, args ...interface{}) {
 	}
 }
 
-func methodDSL(suffix string, p interface{}, args ...interface{}) *expr.AttributeExpr {
+func methodDSL(m *expr.MethodExpr, suffix string, p interface{}, args ...interface{}) *expr.AttributeExpr {
 	var (
 		att *expr.AttributeExpr
 		fn  func()
@@ -155,7 +153,20 @@ func methodDSL(suffix string, p interface{}, args ...interface{}) *expr.Attribut
 			// Do not duplicate type if it is not customized
 			return &expr.AttributeExpr{Type: actual}
 		}
-		att = &expr.AttributeExpr{Type: expr.Dup(actual)}
+		dupped := expr.Dup(actual)
+		att = &expr.AttributeExpr{Type: dupped}
+		if f, ok := args[len(args)-1].(func()); ok {
+			eval.Execute(f, att)
+			if att.Validation != nil && len(att.Validation.Required) > 0 {
+				// If the DSL modifies the type attributes "requiredness"
+				// then rename the type to avoid collisions.
+				if renamer, ok := dupped.(interface {
+					Rename(string)
+				}); ok {
+					renamer.Rename(actual.Name() + "_" + m.Name + "_" + suffix)
+				}
+			}
+		}
 	case expr.DataType:
 		att = &expr.AttributeExpr{Type: actual}
 	default:
diff --git a/dsl/result.go b/dsl/result.go
index 829a3358..02cc4c9e 100644
--- a/dsl/result.go
+++ b/dsl/result.go
@@ -20,57 +20,56 @@ import (
 //
 // The valid syntax for Result is thus:
 //
-//    Result(Type)
+//	Result(Type)
 //
-//    Result(func())
+//	Result(func())
 //
-//    Result(Type, "description")
+//	Result(Type, "description")
 //
-//    Result(Type, func())
+//	Result(Type, func())
 //
-//    Result(Type, "description", func())
+//	Result(Type, "description", func())
 //
 // Examples:
 //
-//    // Define result using primitive type
-//    Method("add", func() {
-//        Result(Int32)
-//    })
-//
-//    // Define result using primitive type and description
-//    Method("add", func() {
-//        Result(Int32, "Resulting sum")
-//    })
-//
-//    // Define result using primitive type, description and validations.
-//    Method("add", func() {
-//        Result(Int32, "Resulting sum", func() {
-//            Minimum(0)
-//        })
-//    })
-//
-//    // Define result using object defined inline
-//    Method("add", func() {
-//        Result(func() {
-//            Description("Result defines a single field which is the sum.")
-//            Attribute("value", Int32, "Resulting sum")
-//            Required("value")
-//        })
-//    })
-//
-//    // Define result type using user type
-//    Method("add", func() {
-//        Result(Sum)
-//    })
-//
-//    // Specify view and required attributes on result type
-//    Method("add", func() {
-//        Result(Sum, func() {
-//            View("default")
-//            Required("value")
-//        })
-//    })
-//
+//	// Define result using primitive type
+//	Method("add", func() {
+//	    Result(Int32)
+//	})
+//
+//	// Define result using primitive type and description
+//	Method("add", func() {
+//	    Result(Int32, "Resulting sum")
+//	})
+//
+//	// Define result using primitive type, description and validations.
+//	Method("add", func() {
+//	    Result(Int32, "Resulting sum", func() {
+//	        Minimum(0)
+//	    })
+//	})
+//
+//	// Define result using object defined inline
+//	Method("add", func() {
+//	    Result(func() {
+//	        Description("Result defines a single field which is the sum.")
+//	        Attribute("value", Int32, "Resulting sum")
+//	        Required("value")
+//	    })
+//	})
+//
+//	// Define result type using user type
+//	Method("add", func() {
+//	    Result(Sum)
+//	})
+//
+//	// Specify view and required attributes on result type
+//	Method("add", func() {
+//	    Result(Sum, func() {
+//	        View("default")
+//	        Required("value")
+//	    })
+//	})
 func Result(val interface{}, args ...interface{}) {
 	if len(args) > 2 {
 		eval.ReportError("too many arguments")
@@ -81,7 +80,7 @@ func Result(val interface{}, args ...interface{}) {
 		eval.IncompatibleDSL()
 		return
 	}
-	e.Result = methodDSL("Result", val, args...)
+	e.Result = methodDSL(e, "Result", val, args...)
 }
 
 // StreamingResult defines a method that streams instances of the given type.
@@ -92,44 +91,43 @@ func Result(val interface{}, args ...interface{}) {
 //
 // Examples:
 //
-//    // Method result is a stream of integers
-//    Method("add", func() {
-//        StreamingResult(Int32)
-//    })
-//
-//    Method("add", func() {
-//        StreamingResult(Int32, "Resulting sum")
-//    })
-//
-//    // Method result is a stream of integers with validation set on each
-//    Method("add", func() {
-//        StreamingResult(Int32, "Resulting sum", func() {
-//            Minimum(0)
-//        })
-//    })
-//
-//    // Method result is a stream of objects defined inline
-//    Method("add", func() {
-//        StreamingResult(func() {
-//            Description("Result defines a single field which is the sum.")
-//            Attribute("value", Int32, "Resulting sum")
-//            Required("value")
-//        })
-//    })
-//
-//    // Method result is a stream of user type
-//    Method("add", func() {
-//        StreamingResult(Sum)
-//    })
-//
-//    // Method result is a stream of result type with a view
-//    Method("add", func() {
-//        StreamingResult(Sum, func() {
-//            View("default")
-//            Required("value")
-//        })
-//    })
-//
+//	// Method result is a stream of integers
+//	Method("add", func() {
+//	    StreamingResult(Int32)
+//	})
+//
+//	Method("add", func() {
+//	    StreamingResult(Int32, "Resulting sum")
+//	})
+//
+//	// Method result is a stream of integers with validation set on each
+//	Method("add", func() {
+//	    StreamingResult(Int32, "Resulting sum", func() {
+//	        Minimum(0)
+//	    })
+//	})
+//
+//	// Method result is a stream of objects defined inline
+//	Method("add", func() {
+//	    StreamingResult(func() {
+//	        Description("Result defines a single field which is the sum.")
+//	        Attribute("value", Int32, "Resulting sum")
+//	        Required("value")
+//	    })
+//	})
+//
+//	// Method result is a stream of user type
+//	Method("add", func() {
+//	    StreamingResult(Sum)
+//	})
+//
+//	// Method result is a stream of result type with a view
+//	Method("add", func() {
+//	    StreamingResult(Sum, func() {
+//	        View("default")
+//	        Required("value")
+//	    })
+//	})
 func StreamingResult(val interface{}, args ...interface{}) {
 	if len(args) > 2 {
 		eval.ReportError("too many arguments")
@@ -140,7 +138,7 @@ func StreamingResult(val interface{}, args ...interface{}) {
 		eval.IncompatibleDSL()
 		return
 	}
-	e.Result = methodDSL("Result", val, args...)
+	e.Result = methodDSL(e, "Result", val, args...)
 	if e.Stream == expr.ClientStreamKind {
 		e.Stream = expr.BidirectionalStreamKind
 	} else {


Test patch:
diff --git a/codegen/service/service_test.go b/codegen/service/service_test.go
index 75bb2b35..1b107fd6 100644
--- a/codegen/service/service_test.go
+++ b/codegen/service/service_test.go
@@ -34,6 +34,7 @@ func TestService(t *testing.T) {
 		{"result-with-result-collection", testdata.ResultWithResultCollectionMethodDSL, testdata.ResultWithResultCollectionMethod},
 		{"result-with-dashed-mime-type", testdata.ResultWithDashedMimeTypeMethodDSL, testdata.ResultWithDashedMimeTypeMethod},
 		{"result-with-one-of-type", testdata.ResultWithOneOfTypeMethodDSL, testdata.ResultWithOneOfTypeMethod},
+		{"result-with-inline-validation", testdata.ResultWithInlineValidationDSL, testdata.ResultWithInlineValidation},
 		{"service-level-error", testdata.ServiceErrorDSL, testdata.ServiceError},
 		{"custom-errors", testdata.CustomErrorsDSL, testdata.CustomErrors},
 		{"custom-errors-custom-field", testdata.CustomErrorsCustomFieldDSL, testdata.CustomErrorsCustomField},
diff --git a/codegen/service/testdata/service_code.go b/codegen/service/testdata/service_code.go
index e0d9da73..e3ab82a6 100644
--- a/codegen/service/testdata/service_code.go
+++ b/codegen/service/testdata/service_code.go
@@ -1492,6 +1492,74 @@ func transformItemToResultwithoneoftypeviewsItemView(v *Item) *resultwithoneofty
 }
 `
 
+const ResultWithInlineValidation = `
+// Service is the ResultWithInlineValidation service interface.
+type Service interface {
+	// A implements A.
+	A(context.Context) (res *ResultInlineValidation, err error)
+	// B implements B.
+	B(context.Context) (res *ResultInlineValidationBResult, err error)
+}
+
+// ServiceName is the name of the service as defined in the design. This is the
+// same value that is set in the endpoint request contexts under the ServiceKey
+// key.
+const ServiceName = "ResultWithInlineValidation"
+
+// MethodNames lists the service method names as defined in the design. These
+// are the same values that are set in the endpoint request contexts under the
+// MethodKey key.
+var MethodNames = [2]string{"A", "B"}
+
+// ResultInlineValidation is the result type of the ResultWithInlineValidation
+// service A method.
+type ResultInlineValidation struct {
+	A *string
+	B *int
+}
+
+// ResultInlineValidationBResult is the result type of the
+// ResultWithInlineValidation service B method.
+type ResultInlineValidationBResult struct {
+	A string
+	B *int
+}
+
+// NewResultInlineValidation initializes result type ResultInlineValidation
+// from viewed result type ResultInlineValidation.
+func NewResultInlineValidation(vres *resultwithinlinevalidationviews.ResultInlineValidation) *ResultInlineValidation {
+	return newResultInlineValidation(vres.Projected)
+}
+
+// NewViewedResultInlineValidation initializes viewed result type
+// ResultInlineValidation from result type ResultInlineValidation using the
+// given view.
+func NewViewedResultInlineValidation(res *ResultInlineValidation, view string) *resultwithinlinevalidationviews.ResultInlineValidation {
+	p := newResultInlineValidationView(res)
+	return &resultwithinlinevalidationviews.ResultInlineValidation{Projected: p, View: "default"}
+}
+
+// newResultInlineValidation converts projected type ResultInlineValidation to
+// service type ResultInlineValidation.
+func newResultInlineValidation(vres *resultwithinlinevalidationviews.ResultInlineValidationView) *ResultInlineValidation {
+	res := &ResultInlineValidation{
+		A: vres.A,
+		B: vres.B,
+	}
+	return res
+}
+
+// newResultInlineValidationView projects result type ResultInlineValidation to
+// projected type ResultInlineValidationView using the "default" view.
+func newResultInlineValidationView(res *ResultInlineValidation) *resultwithinlinevalidationviews.ResultInlineValidationView {
+	vres := &resultwithinlinevalidationviews.ResultInlineValidationView{
+		A: res.A,
+		B: res.B,
+	}
+	return vres
+}
+`
+
 const ForceGenerateType = `
 // Service is the ForceGenerateType service interface.
 type Service interface {
diff --git a/codegen/service/testdata/service_dsls.go b/codegen/service/testdata/service_dsls.go
index 3488ab6c..5fd9b06b 100644
--- a/codegen/service/testdata/service_dsls.go
+++ b/codegen/service/testdata/service_dsls.go
@@ -454,6 +454,25 @@ var ResultWithOneOfTypeMethodDSL = func() {
 	})
 }
 
+var ResultWithInlineValidationDSL = func() {
+	var RT = ResultType("application/vnd.result.inline.validation", func() {
+		Attributes(func() {
+			Attribute("a", String)
+			Attribute("b", Int)
+		})
+	})
+	Service("ResultWithInlineValidation", func() {
+		Method("A", func() {
+			Result(RT)
+		})
+		Method("B", func() {
+			Result(RT, func() {
+				Required("a")
+			})
+		})
+	})
+}
+
 var ForceGenerateTypeDSL = func() {
 	var _ = Type("ForcedType", func() {
 		Attribute("a", String)