Skip to content

Commit

Permalink
Use full names in macro to avoid collision - fixes #3407 (#3413)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan-klass authored Dec 20, 2023
1 parent 0aff30b commit 3d66e7b
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 33 deletions.
44 changes: 17 additions & 27 deletions core/src/main/scala-2/sttp/tapir/internal/OneOfMacro.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,31 +45,26 @@ private[tapir] object OneOfMacro {

val schemaForE =
q"""{
import _root_.sttp.tapir.internal._
import _root_.sttp.tapir.Schema
import _root_.sttp.tapir.Schema._
import _root_.sttp.tapir.SchemaType._
import _root_.scala.collection.immutable.{List, Map}
val mappingAsList = List(..$mapping)
val mappingAsMap: Map[$weakTypeV, Schema[_]] = mappingAsList.toMap
val mappingAsList = _root_.scala.collection.immutable.List(..$mapping)
val mappingAsMap: _root_.scala.collection.immutable.Map[$weakTypeV, _root_.sttp.tapir.Schema[_]] = mappingAsList.toMap
val discriminatorName = _root_.sttp.tapir.FieldName($name, $conf.toEncodedName($name))
// cannot use .collect because of a bug in ScalaJS (Trying to access the this of another class ... during phase: jscode)
val discriminatorMapping = mappingAsMap.toList.flatMap {
case (k, Schema(_, Some(fname), _, _, _, _, _, _, _, _, _)) => List($asString.apply(k) -> SRef(fname))
case _ => Nil
case (k, _root_.sttp.tapir.Schema(_, _root_.scala.Some(fname), _, _, _, _, _, _, _, _, _)) => _root_.scala.collection.immutable.List($asString.apply(k) -> _root_.sttp.tapir.SchemaType.SRef(fname))
case _ => _root_.scala.Nil
}
.toMap
val sname = SName(${weakTypeE.typeSymbol.fullName},${extractTypeArguments(c)(weakTypeE)})
val sname = _root_.sttp.tapir.Schema.SName(${weakTypeE.typeSymbol.fullName},${extractTypeArguments(c)(weakTypeE)})
// cast needed because of Scala 2.12
val subtypes = mappingAsList.map(_._2)
Schema((SCoproduct[$weakTypeE](subtypes, None) { e =>
_root_.sttp.tapir.Schema((_root_.sttp.tapir.SchemaType.SCoproduct[$weakTypeE](subtypes, _root_.scala.None) { e =>
val ee: $weakTypeV = $extractor(e)
mappingAsMap.get(ee).map(m => SchemaWithValue(m.asInstanceOf[Schema[Any]], e))
mappingAsMap.get(ee).map(m => _root_.sttp.tapir.SchemaType.SchemaWithValue(m.asInstanceOf[_root_.sttp.tapir.Schema[Any]], e))
}).addDiscriminatorField(
discriminatorName, $discriminatorSchema, discriminatorMapping
), Some(sname))
), _root_.scala.Some(sname))
}"""

Debug.logGeneratedCode(c)(weakTypeE.typeSymbol.fullName, schemaForE)
Expand All @@ -88,32 +83,27 @@ private[tapir] object OneOfMacro {
val subclasses = symbol.knownDirectSubclasses.toList.sortBy(_.name.encodedName.toString)

val subclassesSchemas = subclasses.map(subclass =>
q"${subclass.name.encodedName.toString} -> Schema.wrapWithSingleFieldProduct(implicitly[Schema[${subclass.asType}]])($conf)"
q"${subclass.name.encodedName.toString} -> _root_.sttp.tapir.Schema.wrapWithSingleFieldProduct(_root_.scala.Predef.implicitly[_root_.sttp.tapir.Schema[${subclass.asType}]])($conf)"
)

val subclassesSchemaCases = subclasses.map { subclass =>
cq"""v: ${subclass.asType} => Some(SchemaWithValue(subclassNameToSchemaMap(${subclass.name.encodedName.toString}).asInstanceOf[Schema[Any]], v))"""
cq"""v: ${subclass.asType} => _root_.scala.Some(_root_.sttp.tapir.SchemaType.SchemaWithValue(subclassNameToSchemaMap(${subclass.name.encodedName.toString}).asInstanceOf[_root_.sttp.tapir.Schema[Any]], v))"""
}

val schemaForE = q"""{
import _root_.sttp.tapir.Schema
import _root_.sttp.tapir.Schema._
import _root_.sttp.tapir.SchemaType._
import _root_.scala.collection.immutable.{List, Map}
val subclassNameToSchema: _root_.scala.collection.immutable.List[(String, _root_.sttp.tapir.Schema[_])] = _root_.scala.collection.immutable.List($subclassesSchemas: _*)
val subclassNameToSchemaMap: _root_.scala.collection.immutable.Map[String, _root_.sttp.tapir.Schema[_]] = subclassNameToSchema.toMap
val subclassNameToSchema: List[(String, Schema[_])] = List($subclassesSchemas: _*)
val subclassNameToSchemaMap: Map[String, Schema[_]] = subclassNameToSchema.toMap
val sname = SName(${weakTypeE.typeSymbol.fullName},${extractTypeArguments(c)(weakTypeE)})
val sname = _root_.sttp.tapir.Schema.SName(${weakTypeE.typeSymbol.fullName},${extractTypeArguments(c)(weakTypeE)})
// cast needed because of Scala 2.12
val subtypes = subclassNameToSchema.map(_._2)
Schema(
schemaType = SCoproduct[$weakTypeE](subtypes, None) { e =>
_root_.sttp.tapir.Schema(
schemaType = _root_.sttp.tapir.SchemaType.SCoproduct[$weakTypeE](subtypes, _root_.scala.None) { e =>
e match {
case ..$subclassesSchemaCases
}
},
name = Some(sname)
},
name = _root_.scala.Some(sname)
)
}"""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private[tapir] object SchemaAnnotationsMacro {
val validateEach = annotations.collect { case ann if ann.tree.tpe <:< ValidateEachAnn => firstArg(ann) }

c.Expr[SchemaAnnotations[T]](
q"""_root_.sttp.tapir.SchemaAnnotations.apply($description, $encodedExample, $default, $format, $deprecated, $hidden, $encodedName, List(..$validate), List(..$validateEach))"""
q"""_root_.sttp.tapir.SchemaAnnotations.apply($description, $encodedExample, $default, $format, $deprecated, $hidden, $encodedName, _root_.scala.List(..$validate), _root_.scala.List(..$validateEach))"""
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ private[tapir] object SchemaMapMacro {
c: blackbox.Context
)(schemaForV: c.Expr[Schema[V]]): c.Expr[Schema[Map[String, V]]] = {
import c.universe._
generateSchemaForMap[String, V](c)(c.Expr[String => String](q"""identity"""))(schemaForV)
generateSchemaForMap[String, V](c)(c.Expr[String => String](q"""_root_.scala.Predef.identity"""))(schemaForV)
}

/* Extract name and generic type parameters of map value type for object info creation */
Expand All @@ -34,8 +34,8 @@ private[tapir] object SchemaMapMacro {
q"""{
val s = $schemaForV
_root_.sttp.tapir.Schema(
_root_.sttp.tapir.SchemaType.SOpenProduct(Nil, s)(_.map { case (k, v) => ($keyToString(k), v) }),
Some(_root_.sttp.tapir.Schema.SName("Map", $genericTypeParameters))
_root_.sttp.tapir.SchemaType.SOpenProduct(_root_.scala.Nil, s)(_.map { case (k, v) => ($keyToString(k), v) }),
_root_.scala.Some(_root_.sttp.tapir.Schema.SName("Map", $genericTypeParameters))
)
}"""
Debug.logGeneratedCode(c)(weakTypeV.typeSymbol.fullName, schemaForMap)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ private[tapir] object ValidatorEnumerationMacro {
} else {
val instances = subclasses.map(x => Ident(x.asInstanceOf[scala.reflect.internal.Symbols#Symbol].sourceModule.asInstanceOf[Symbol]))
val validatorEnum =
q"_root_.sttp.tapir.Validator.Enumeration($instances, None, Some(_root_.sttp.tapir.Schema.SName(${symbol.fullName})))"
q"_root_.sttp.tapir.Validator.Enumeration($instances, _root_.scala.None, _root_.scala.Some(_root_.sttp.tapir.Schema.SName(${symbol.fullName})))"
Debug.logGeneratedCode(c)(t.typeSymbol.fullName, validatorEnum)
c.Expr[Validator.Enumeration[E]](validatorEnum)
}
Expand All @@ -54,7 +54,7 @@ private[tapir] object ValidatorEnumerationMacro {
case Nil => c.abort(c.enclosingPosition, s"Invalid enum name: ${weakTypeT.toString}")
}

q"_root_.sttp.tapir.Validator.enumeration($enumeration.values.toList, v => Option(v), Some(sttp.tapir.Schema.SName(${enumNameComponents
q"_root_.sttp.tapir.Validator.enumeration($enumeration.values.toList, v => _root_.scala.Option(v), _root_.scala.Some(sttp.tapir.Schema.SName(${enumNameComponents
.mkString(".")})))"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,11 @@ class SchemaMacroNamespaceTest extends AnyFlatSpec with Matchers {
import sttp.tapir.Codec
Codec.derivedEnumeration[String, MyProduct](MyProduct.fromString, _.toString)
}

it should "compile macro-generated code for shadowed _root_.scala names" in {

assert(sttp.tapir.testdata.BuiltinTypenameCollisionEnum.schema.name.nonEmpty)
assert(sttp.tapir.testdata.BuiltinTypenameCollisionCaseClass.schema.name.nonEmpty)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package sttp.tapir.testdata

sealed abstract class BuiltinTypenameCollisionCaseClass

object BuiltinTypenameCollisionCaseClass {

case class Option(a: String) extends BuiltinTypenameCollisionCaseClass
case class Some(a: Int) extends BuiltinTypenameCollisionCaseClass
case class None(b: Boolean) extends BuiltinTypenameCollisionCaseClass
case class List(c: String) extends BuiltinTypenameCollisionCaseClass
case class Nil(b: String) extends BuiltinTypenameCollisionCaseClass
case class Map(d: Int) extends BuiltinTypenameCollisionCaseClass
// TODO: magnolia issue - https://github.com/softwaremill/magnolia/pull/504
// case class Array(a: Boolean)
case class Either(e: String) extends BuiltinTypenameCollisionCaseClass
case class Left(x: Double) extends BuiltinTypenameCollisionCaseClass
case class Right(y: Double) extends BuiltinTypenameCollisionCaseClass
case class Unit() extends BuiltinTypenameCollisionCaseClass
case class implicitly(a: Int) extends BuiltinTypenameCollisionCaseClass
case class identity(b: Boolean) extends BuiltinTypenameCollisionCaseClass

import sttp.tapir.generic.auto._

val schema: sttp.tapir.Schema[BuiltinTypenameCollisionCaseClass] =
sttp.tapir.Schema.oneOfWrapped[BuiltinTypenameCollisionCaseClass]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package sttp.tapir.testdata

/** @see [[https://github.com/softwaremill/tapir/issues/3407 Github Issue #3407]] */
sealed abstract class BuiltinTypenameCollisionEnum

object BuiltinTypenameCollisionEnum {
case object Option extends BuiltinTypenameCollisionEnum
case object Some extends BuiltinTypenameCollisionEnum
case object None extends BuiltinTypenameCollisionEnum
case object List extends BuiltinTypenameCollisionEnum
case object Nil extends BuiltinTypenameCollisionEnum
case object Map extends BuiltinTypenameCollisionEnum
case object Array extends BuiltinTypenameCollisionEnum
case object Either extends BuiltinTypenameCollisionEnum
case object Left extends BuiltinTypenameCollisionEnum
case object Right extends BuiltinTypenameCollisionEnum
case object Unit extends BuiltinTypenameCollisionEnum

case object implicitly extends BuiltinTypenameCollisionEnum
case object identity extends BuiltinTypenameCollisionEnum

val schema: sttp.tapir.Schema[BuiltinTypenameCollisionEnum] =
sttp.tapir.Schema.derivedEnumeration[BuiltinTypenameCollisionEnum].defaultStringBased
}

0 comments on commit 3d66e7b

Please sign in to comment.