validateRelocatedUserTypes enforces that relocated user types (those with `struct:pkg:path`) only depend on other declared user types with an explicit generation location. Without this constraint, generated code would need to reference service-local user types across packages, which is not safe (it
()
| 228 | // wrappers) are exempt because they are materialized alongside their owning |
| 229 | // types. |
| 230 | func (r *RootExpr) validateRelocatedUserTypes() *eval.ValidationErrors { |
| 231 | var verr eval.ValidationErrors |
| 232 | declared := make(map[string]struct{}, len(r.Types)) |
| 233 | for _, ut := range r.Types { |
| 234 | declared[ut.ID()] = struct{}{} |
| 235 | } |
| 236 | for _, ut := range r.Types { |
| 237 | pkgPath, ok := ut.Attribute().Meta.Last("struct:pkg:path") |
| 238 | if !ok || pkgPath == "" { |
| 239 | continue |
| 240 | } |
| 241 | seen := make(map[string]struct{}) |
| 242 | r.walkUserTypeDependencies(ut, seen, "", func(dep UserType, path string) { |
| 243 | if dep.ID() == ut.ID() { |
| 244 | return |
| 245 | } |
| 246 | if _, ok := declared[dep.ID()]; !ok { |
| 247 | // Generated/derived user types (e.g. union branch wrappers) are |
| 248 | // materialized alongside their owning types and do not require an |
| 249 | // explicit struct:pkg:path. |
| 250 | return |
| 251 | } |
| 252 | if _, ok := dep.Attribute().Meta.Last("struct:pkg:path"); ok { |
| 253 | return |
| 254 | } |
| 255 | msg := fmt.Sprintf( |
| 256 | `type %q is generated in package %q (struct:pkg:path) but depends on %q with no explicit struct:pkg:path`, |
| 257 | ut.Name(), |
| 258 | pkgPath, |
| 259 | dep.Name(), |
| 260 | ) |
| 261 | if path != "" { |
| 262 | msg = fmt.Sprintf("%s (referenced via %s)", msg, path) |
| 263 | } |
| 264 | msg = fmt.Sprintf( |
| 265 | `%s; fix: add Meta("struct:pkg:path", %q) to %q (or move it to an explicitly located shared type)`, |
| 266 | msg, |
| 267 | pkgPath, |
| 268 | dep.Name(), |
| 269 | ) |
| 270 | verr.Add(dep, "%s", msg) |
| 271 | }) |
| 272 | } |
| 273 | return &verr |
| 274 | } |
| 275 | |
| 276 | // walkUserTypeDependencies traverses the attribute graph reachable from root and |
| 277 | // invokes visit for each encountered user type. |