ITADN

OpenAPI.Parameter overrides equals on (name, in) but inherits the case-class hashCode (uses all fields)

#4133Openhaskiindahouse 创建于 2026-05-10
H
haskiindahousecommented
**Describe the bug** `endpoint.openapi.OpenAPI.Parameter` is a case class with a custom `equals` that returns `true` whenever `(name, in)` match — likely for OpenAPI spec deduplication. There's no matching `hashCode` override, so the auto-generated case-class `hashCode` still mixes in every field. Two `Parameter`s that compare as equal frequently get different hash codes, which silently breaks `Set`, `Map`, `distinct`, etc. Source: https://github.com/zio/zio-http/blob/02e1b6b60fc26794cec053d4b0690a64f3c39a9f/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/OpenAPI.scala#L686-L703 ```scala final case class Parameter( name: String, in: String, description: Option[Doc], required: Option[Boolean] = None, deprecated: Option[Boolean] = None, schema: Option[ReferenceOr[JsonSchema]], explode: Option[Boolean] = None, examples: Map[String, ReferenceOr[Example]] = Map.empty, allowReserved: Option[Boolean], style: Option[String], content: Option[(String, MediaType)], ) { override def equals(obj: Any): Boolean = obj match { case p: Parameter if name == p.name && in == p.in => true case _ => false } // no hashCode override } ``` **To Reproduce** ```scala val p1 = Parameter("id", "query", Some(Doc("first")), schema = None, allowReserved = None, style = None, content = None) val p2 = Parameter("id", "query", Some(Doc("second")), schema = None, allowReserved = None, style = None, content = None) p1 == p2 // true p1.hashCode == p2.hashCode // typically false Set(p1, p2).size // 2 — but they were "equal", so Sets/Maps misbehave ``` **Expected behaviour** Add a matching `hashCode`: ```scala override def hashCode(): Int = (name, in).hashCode ``` (Worth a separate look at whether `equals` should really collapse parameters with different schemas to "equal" — but at minimum the contract should hold.) Happy to PR.
0 条评论