Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: scala 2 intersection type erasure #355

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ trait ImporterTest extends AnyFunSuite with ImporterHarness with ParallelTestExe
test("vfile")(assertImportsOk("vfile", pedantic = true))
test("punchcard")(assertImportsOk("punchcard", pedantic = true))
test("recharts")(assertImportsOk("recharts", pedantic = true))
test("firebase")(assertImportsOk("firebase", pedantic = true))

test("material-ui-slinky")(
assertImportsOk("material-ui", pedantic = true, flavour = Slinky),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,8 @@ class Erasure(scalaVersion: Versions.Scala) {
// we don't really use scala arrays, so let's just go with a too broad erasure for that
if (targs.exists(_.typeName === QualifiedName.Array)) QualifiedName.Any
else {
// The erased type for A | B is the erased least upper bound of the erased types of A and B. Quoting from the documentation of TypeErasure#erasedLub
def go(scope: TreeScope, current: TypeRef): IArray[QualifiedName] =
scope
.lookup(simplify(scope, current))
.collectFirst {
// this is an ST specific hack. the printer still adds `StObject` parent for native parents :/
case (x: ClassTree, _) if x.parents.isEmpty && x.isNative =>
go(scope, TypeRef(QualifiedName.StObject)) :+ x.codePath
case (x: ClassTree, newScope) =>
x.parents.flatMap(p => go(newScope, p)) :+ x.codePath
}
.getOrElse(Empty)

val erasedParentLattices: IArray[IArray[QualifiedName]] =
targs.map(t => go(scope, t))
targs.map(t => typeLattice(scope, t))

erasedParentLattices
.reduce(_.intersect(_))
Expand Down Expand Up @@ -98,25 +85,23 @@ class Erasure(scalaVersion: Versions.Scala) {
case tr @ (TypeRef.String | TypeRef.Boolean | TypeRef.Double) => tr.typeName
}

val isAbstract = tpe.targs.collectFirst {
case tpe if scope.isAbstract(tpe) => QualifiedName.Any
}

isPrimitive.orElse(isAbstract).getOrElse {
simplify(scope, tpe.targs.head) match {
case QualifiedName.JsAny if tpe.targs.length > 1 =>
simplify(scope, tpe.targs(1)) match {
case QualifiedName.Any => QualifiedName.JsAny
case other => other
isPrimitive
.getOrElse {
val erasedParentLattices: IArray[IArray[QualifiedName]] =
tpe.targs.map(t => typeLattice(scope, t))

erasedParentLattices
.foldLeft(Empty: IArray[QualifiedName]) {
case (nonEmpty, lattice) =>
val latticeSet = lattice.toSet
nonEmpty.filterNot(latticeSet) match {
case Empty => lattice
case other => other
}
}
case QualifiedName.JsObject if tpe.targs.length > 1 =>
simplify(scope, tpe.targs(1)) match {
case QualifiedName.Any => QualifiedName.JsObject
case other => other
}
case other => other
.headOption
.getOrElse(QualifiedName.Any)
}
}

// if this is a type parameter
case QualifiedName(IArray.exactlyOne(head)) if scope.tparams.contains(head) =>
Expand All @@ -136,6 +121,19 @@ class Erasure(scalaVersion: Versions.Scala) {
}
.getOrElse(other)
}

// The erased type for A | B is the erased least upper bound of the erased types of A and B. Quoting from the documentation of TypeErasure#erasedLub
private def typeLattice(scope: TreeScope, current: TypeRef): IArray[QualifiedName] =
scope
.lookup(simplify(scope, current))
.collectFirst {
// this is an ST specific hack. the printer still adds `StObject` parent for native parents :/
case (x: ClassTree, _) if x.parents.isEmpty && x.isNative =>
typeLattice(scope, TypeRef(QualifiedName.StObject)) :+ x.codePath
case (x: ClassTree, newScope) =>
x.parents.flatMap(p => typeLattice(newScope, p)) :+ x.codePath
}
.getOrElse(Empty)
}

final case class MethodErasure(name: Name, params: IArray[QualifiedName], ret: QualifiedName)
Expand Down
10 changes: 10 additions & 0 deletions tests/firebase/check-3/f/firebase/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
organization := "org.scalablytyped"
name := "firebase"
version := "0.0-unknown-540ecc"
scalaVersion := "3.0.1"
enablePlugins(ScalaJSPlugin)
libraryDependencies ++= Seq(
"com.olvind" %%% "scalablytyped-runtime" % "2.4.2")
publishArtifact in packageDoc := false
scalacOptions ++= List("-encoding", "utf-8", "-feature", "-language:implicitConversions", "-language:higherKinds", "-language:existentials", "-no-indent")
licenses += ("MIT", url("http://opensource.org/licenses/MIT"))
1 change: 1 addition & 0 deletions tests/firebase/check-3/f/firebase/project/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version=1.5.5
1 change: 1 addition & 0 deletions tests/firebase/check-3/f/firebase/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
addSbtPlugin("org.scala-js" %% "sbt-scalajs" % "1.7.0")
15 changes: 15 additions & 0 deletions tests/firebase/check-3/f/firebase/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

# Scala.js typings for firebase




## Note
This library has been generated from typescript code from first party type definitions.

Provided with :purple_heart: from [ScalablyTyped](https://github.com/oyvindberg/ScalablyTyped)

## Usage
See [the main readme](../../readme.md) for instructions.


Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package typings.firebase

import org.scalablytyped.runtime.StObject
import scala.scalajs.js
import scala.scalajs.js.annotation.{JSGlobalScope, JSGlobal, JSImport, JSName, JSBracketAccess}

@js.native
trait FirebaseTest[T]
extends StObject
with Foo {

def name(p: js.Any & T): Unit = js.native
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package typings.firebase

import org.scalablytyped.runtime.StObject
import scala.scalajs.js
import scala.scalajs.js.annotation.{JSGlobalScope, JSGlobal, JSImport, JSName, JSBracketAccess}

trait Foo extends StObject {

def name(p: js.Any): Unit
}
object Foo {

inline def apply(name: js.Any => Unit): Foo = {
val __obj = js.Dynamic.literal(name = js.Any.fromFunction1(name))
__obj.asInstanceOf[Foo]
}

extension [Self <: Foo](x: Self) {

inline def setName(value: js.Any => Unit): Self = StObject.set(x, "name", js.Any.fromFunction1(value))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package typings.firebase

import org.scalablytyped.runtime.StObject
import scala.scalajs.js
import scala.scalajs.js.annotation.{JSGlobalScope, JSGlobal, JSImport, JSName, JSBracketAccess}

/* This can be used to `require` the library as a side effect.
If it is a global library this will make scalajs-bundler include it */
@JSImport("firebase", JSImport.Namespace)
@js.native
object firebaseRequire extends StObject
10 changes: 10 additions & 0 deletions tests/firebase/in/firebase/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
interface Foo {
name(p: any): void
}

interface FirebaseTest<T> extends Foo {

name(p: any & T): void
name(p: T & any): void

}
Empty file added tests/firebase/in/stdlib.d.ts
Empty file.