Repository: goadesign/goa (go)

Issue:
ResultType redeclared in generated files if used more than once as Method -> Result using different views
If I use the same `ResultType` in more than one `Method` -> `Result` sing different `View`s , the `goa gen` command generates a `views/view.go` declaring the type twice.

Naturally, if I try to build the project, the command fails:
```
# calc/gen/objects/views
gen\objects\views\view.go:25:6: ObjectMedia redeclared in this block
        previous declaration at gen\objects\views\view.go:17:6
gen\objects\views\view.go:91:6: ValidateObjectMedia redeclared in this block
        previous declaration at gen\objects\views\view.go:77:52
```

I have observed this for version `3.2.5`, I was previously using version `3.2.0` and this issue was not present back then.

This might be related to this other reported bug, maybe they are both caused by the same issue:
https://github.com/goadesign/goa/issues/2681

Here I have reproduced the bug in a simple project:
https://github.com/rozagerardo/samples/tree/go/goa/bugs/redeclared-types

Here I'm using the `ResultType` in two different `Method`s:
https://github.com/rozagerardo/samples/blob/go/goa/bugs/redeclared-types/calc/design/design.go#L117

And this is the generated `view.go` file, declaring the type twice:
https://github.com/rozagerardo/samples/blob/go/goa/bugs/redeclared-types/calc/gen/objects/views/view.go#L16

Pull request description:
Fix duplicate type generation
When design mixes implicit and explicit views in result definitions.
Each method needs its own result type to generate proper view rendering
code based on the Meta attribute for explicit views but the types
themselves only need to be generated once as they define all the fields.

Fix #2723 

Patch:
diff --git a/codegen/service/service_data.go b/codegen/service/service_data.go
index 7d927832..b55cc98d 100644
--- a/codegen/service/service_data.go
+++ b/codegen/service/service_data.go
@@ -598,7 +598,16 @@ func (d ServicesData) analyze(service *expr.ServiceExpr) *Data {
 					projected := seenProj[rt.ID()]
 					projAtt := &expr.AttributeExpr{Type: projected.Type}
 					vrt := buildViewedResultType(e.Result, projAtt, viewspkg, scope, viewScope)
-					viewedRTs = append(viewedRTs, vrt)
+					found := false
+					for _, rt := range viewedRTs {
+						if rt.Type.ID() == vrt.Type.ID() {
+							found = true
+							break
+						}
+					}
+					if !found {
+						viewedRTs = append(viewedRTs, vrt)
+					}
 					m.ViewedResult = vrt
 					seenViewed[vrt.Name+"::"+view] = vrt
 				}


Test patch:
diff --git a/codegen/service/service_test.go b/codegen/service/service_test.go
index 6ecf3053..e40fd66b 100644
--- a/codegen/service/service_test.go
+++ b/codegen/service/service_test.go
@@ -25,6 +25,7 @@ func TestService(t *testing.T) {
 		{"no-payload-result", testdata.EmptyPayloadMethodDSL, testdata.EmptyPayloadMethod},
 		{"payload-result-with-default", testdata.WithDefaultDSL, testdata.WithDefault},
 		{"result-with-multiple-views", testdata.MultipleMethodsResultMultipleViewsDSL, testdata.MultipleMethodsResultMultipleViews},
+		{"result-with-explicit-and-default-views", testdata.WithExplicitAndDefaultViewsDSL, testdata.WithExplicitAndDefaultViews},
 		{"result-collection-multiple-views", testdata.ResultCollectionMultipleViewsMethodDSL, testdata.ResultCollectionMultipleViewsMethod},
 		{"result-with-other-result", testdata.ResultWithOtherResultMethodDSL, testdata.ResultWithOtherResultMethod},
 		{"result-with-result-collection", testdata.ResultWithResultCollectionMethodDSL, testdata.ResultWithResultCollectionMethod},
diff --git a/codegen/service/testdata/service_code.go b/codegen/service/testdata/service_code.go
index 5951e05d..961fb7ce 100644
--- a/codegen/service/testdata/service_code.go
+++ b/codegen/service/testdata/service_code.go
@@ -539,6 +539,106 @@ func newSingleViewView(res *SingleView) *multiplemethodsresultmultipleviewsviews
 }
 `
 
+const WithExplicitAndDefaultViews = `
+// Service is the WithExplicitAndDefaultViews service interface.
+type Service interface {
+	// A implements A.
+	// The "view" return value must have one of the following views
+	//	- "default"
+	//	- "tiny"
+	A(context.Context) (res *MultipleViews, view string, err error)
+	// A implements A.
+	AEndpoint(context.Context) (res *MultipleViews, 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 = "WithExplicitAndDefaultViews"
+
+// 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", "A"}
+
+// MultipleViews is the result type of the WithExplicitAndDefaultViews service
+// A method.
+type MultipleViews struct {
+	A string
+	B int
+}
+
+// NewMultipleViews initializes result type MultipleViews from viewed result
+// type MultipleViews.
+func NewMultipleViews(vres *withexplicitanddefaultviewsviews.MultipleViews) *MultipleViews {
+	var res *MultipleViews
+	switch vres.View {
+	case "default", "":
+		res = newMultipleViews(vres.Projected)
+	case "tiny":
+		res = newMultipleViewsTiny(vres.Projected)
+	}
+	return res
+}
+
+// NewViewedMultipleViews initializes viewed result type MultipleViews from
+// result type MultipleViews using the given view.
+func NewViewedMultipleViews(res *MultipleViews, view string) *withexplicitanddefaultviewsviews.MultipleViews {
+	var vres *withexplicitanddefaultviewsviews.MultipleViews
+	switch view {
+	case "default", "":
+		p := newMultipleViewsView(res)
+		vres = &withexplicitanddefaultviewsviews.MultipleViews{Projected: p, View: "default"}
+	case "tiny":
+		p := newMultipleViewsViewTiny(res)
+		vres = &withexplicitanddefaultviewsviews.MultipleViews{Projected: p, View: "tiny"}
+	}
+	return vres
+}
+
+// newMultipleViews converts projected type MultipleViews to service type
+// MultipleViews.
+func newMultipleViews(vres *withexplicitanddefaultviewsviews.MultipleViewsView) *MultipleViews {
+	res := &MultipleViews{}
+	if vres.A != nil {
+		res.A = *vres.A
+	}
+	if vres.B != nil {
+		res.B = *vres.B
+	}
+	return res
+}
+
+// newMultipleViewsTiny converts projected type MultipleViews to service type
+// MultipleViews.
+func newMultipleViewsTiny(vres *withexplicitanddefaultviewsviews.MultipleViewsView) *MultipleViews {
+	res := &MultipleViews{}
+	if vres.A != nil {
+		res.A = *vres.A
+	}
+	return res
+}
+
+// newMultipleViewsView projects result type MultipleViews to projected type
+// MultipleViewsView using the "default" view.
+func newMultipleViewsView(res *MultipleViews) *withexplicitanddefaultviewsviews.MultipleViewsView {
+	vres := &withexplicitanddefaultviewsviews.MultipleViewsView{
+		A: &res.A,
+		B: &res.B,
+	}
+	return vres
+}
+
+// newMultipleViewsViewTiny projects result type MultipleViews to projected
+// type MultipleViewsView using the "tiny" view.
+func newMultipleViewsViewTiny(res *MultipleViews) *withexplicitanddefaultviewsviews.MultipleViewsView {
+	vres := &withexplicitanddefaultviewsviews.MultipleViewsView{
+		A: &res.A,
+	}
+	return vres
+}
+`
+
 const ResultCollectionMultipleViewsMethod = `
 // Service is the ResultCollectionMultipleViewsMethod service interface.
 type Service interface {
diff --git a/codegen/service/testdata/service_dsls.go b/codegen/service/testdata/service_dsls.go
index 3aa9dd2a..b61c128d 100644
--- a/codegen/service/testdata/service_dsls.go
+++ b/codegen/service/testdata/service_dsls.go
@@ -201,6 +201,34 @@ var MultipleMethodsResultMultipleViewsDSL = func() {
 	})
 }
 
+var WithExplicitAndDefaultViewsDSL = func() {
+	var RTWithViews = ResultType("application/vnd.result.multiple.views", func() {
+		TypeName("MultipleViews")
+		Attributes(func() {
+			Attribute("a", String)
+			Attribute("b", Int)
+			Required("a", "b")
+		})
+		View("default", func() {
+			Attribute("a")
+			Attribute("b")
+		})
+		View("tiny", func() {
+			Attribute("a")
+		})
+	})
+	Service("WithExplicitAndDefaultViews", func() {
+		Method("A", func() {
+			Result(RTWithViews)
+		})
+		Method("A", func() {
+			Result(RTWithViews, func() {
+				View("tiny")
+			})
+		})
+	})
+}
+
 var ResultCollectionMultipleViewsMethodDSL = func() {
 	var RTWithViews = ResultType("application/vnd.result.multiple.views", func() {
 		TypeName("MultipleViews")