Skip to content

Commit 8179687

Browse files
committed
Scala.js: Implement the PrepJSInterop phase, minus exports handling.
The `PrepJSInterop` phase is responsible for: * Performing all kinds of Scala.js-specific compile-time checks, and emitting the appropriate compile errors. * Perform some transformations that are necessary for JavaScript interop, notably generating exports forwarders. This commit ports all the functionality of `PrepJSInterop` from Scala 2, except the following: * Handling of `scala.Enumeration`s: it is unclear whether we still want to support that in the core, or if it should be handled by an optional compiler plugin in the future. * Exports: they will be done later. * Warnings about duplicate fields in `js.Dynamic.literal`: mostly because they are non-essential. The test cases are ported from the Scala.js compiler tests.
1 parent b0f2868 commit 8179687

File tree

102 files changed

+5164
-87
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+5164
-87
lines changed

.appveyor.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ test_script:
1818
# - cmd: sbt test
1919
# - cmd: sbt dotty-bootstrapped/test
2020
- cmd: sbt sjsJUnitTests/test
21+
- cmd: sbt sjsCompilerTests/test

compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import org.scalajs.ir.OriginalName
3636
import org.scalajs.ir.OriginalName.NoOriginalName
3737
import org.scalajs.ir.Trees.OptimizerHints
3838

39+
import dotty.tools.dotc.transform.sjs.JSInteropUtils._
40+
3941
import JSEncoding._
4042
import JSInterop._
4143
import ScopedVar.withScopedVars
@@ -60,6 +62,7 @@ class JSCodeGen()(using genCtx: Context) {
6062
import JSCodeGen._
6163
import tpd._
6264

65+
private val sjsPlatform = dotty.tools.dotc.config.SJSPlatform.sjsPlatform
6366
private val jsdefn = JSDefinitions.jsdefn
6467
private val primitives = new JSPrimitives(genCtx)
6568

@@ -426,14 +429,7 @@ class JSCodeGen()(using genCtx: Context) {
426429
val superClass =
427430
if (sym.is(Trait)) None
428431
else Some(encodeClassNameIdent(sym.superClass))
429-
val jsNativeLoadSpec = {
430-
if (sym.is(Trait)) None
431-
else if (sym.hasAnnotation(jsdefn.JSGlobalScopeAnnot)) None
432-
else {
433-
val path = fullJSNameOf(sym).split('.').toList
434-
Some(js.JSNativeLoadSpec.Global(path.head, path.tail))
435-
}
436-
}
432+
val jsNativeLoadSpec = sjsPlatform.perRunInfo.jsNativeLoadSpecOfOption(sym)
437433

438434
js.ClassDef(
439435
classIdent,
@@ -954,6 +950,30 @@ class JSCodeGen()(using genCtx: Context) {
954950
result
955951
}
956952

953+
private def genExpr(name: JSName)(implicit pos: SourcePosition): js.Tree = name match {
954+
case JSName.Literal(name) => js.StringLiteral(name)
955+
case JSName.Computed(sym) => genComputedJSName(sym)
956+
}
957+
958+
private def genComputedJSName(sym: Symbol)(implicit pos: SourcePosition): js.Tree = {
959+
/* By construction (i.e. restriction in PrepJSInterop), we know that sym
960+
* must be a static method.
961+
* Therefore, at this point, we can invoke it by loading its owner and
962+
* calling it.
963+
*/
964+
def moduleOrGlobalScope = genLoadModuleOrGlobalScope(sym.owner)
965+
def module = genLoadModule(sym.owner)
966+
967+
if (sym.owner.isJSType) {
968+
if (!sym.owner.isNonNativeJSClass || sym.isExposed)
969+
genApplyJSMethodGeneric(sym, moduleOrGlobalScope, args = Nil, isStat = false)
970+
else
971+
genApplyJSClassMethod(module, sym, arguments = Nil)
972+
} else {
973+
genApplyMethod(module, sym, arguments = Nil)
974+
}
975+
}
976+
957977
/** Gen JS code for a tree in expression position (in the IR) or the
958978
* global scope.
959979
*/
@@ -2042,7 +2062,7 @@ class JSCodeGen()(using genCtx: Context) {
20422062
genApplyStatic(sym, genActualArgs(sym, args))
20432063
} else if (isJSType(sym.owner)) {
20442064
//if (!isScalaJSDefinedJSClass(sym.owner) || isExposed(sym))
2045-
genApplyJSMethodGeneric(tree, sym, genExprOrGlobalScope(receiver), genActualJSArgs(sym, args), isStat)
2065+
genApplyJSMethodGeneric(sym, genExprOrGlobalScope(receiver), genActualJSArgs(sym, args), isStat)(tree.sourcePos)
20462066
/*else
20472067
genApplyJSClassMethod(genExpr(receiver), sym, genActualArgs(sym, args))*/
20482068
} else {
@@ -2061,19 +2081,17 @@ class JSCodeGen()(using genCtx: Context) {
20612081
* - Getters and parameterless methods are translated as `JSBracketSelect`
20622082
* - Setters are translated to `Assign` to `JSBracketSelect`
20632083
*/
2064-
private def genApplyJSMethodGeneric(tree: Tree, sym: Symbol,
2084+
private def genApplyJSMethodGeneric(sym: Symbol,
20652085
receiver: MaybeGlobalScope, args: List[js.TreeOrJSSpread], isStat: Boolean,
20662086
jsSuperClassValue: Option[js.Tree] = None)(
2067-
implicit pos: Position): js.Tree = {
2068-
2069-
implicit val pos: SourcePosition = tree.sourcePos
2087+
implicit pos: SourcePosition): js.Tree = {
20702088

20712089
def noSpread = !args.exists(_.isInstanceOf[js.JSSpread])
20722090
val argc = args.size // meaningful only for methods that don't have varargs
20732091

20742092
def requireNotSuper(): Unit = {
20752093
if (jsSuperClassValue.isDefined)
2076-
report.error("Illegal super call in Scala.js-defined JS class", tree.sourcePos)
2094+
report.error("Illegal super call in Scala.js-defined JS class", pos)
20772095
}
20782096

20792097
def requireNotSpread(arg: js.TreeOrJSSpread): js.Tree =
@@ -2102,7 +2120,7 @@ class JSCodeGen()(using genCtx: Context) {
21022120
js.JSFunctionApply(ruleOutGlobalScope(receiver), args)
21032121

21042122
case _ =>
2105-
def jsFunName = js.StringLiteral(jsNameOf(sym))
2123+
def jsFunName = genExpr(jsNameOf(sym))
21062124

21072125
def genSuperReference(propName: js.Tree): js.Tree = {
21082126
jsSuperClassValue.fold[js.Tree] {

compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ final class JSDefinitions()(using Context) {
3636
def JSPackage_constructorOf(using Context) = JSPackage_constructorOfR.symbol
3737
@threadUnsafe lazy val JSPackage_nativeR = ScalaJSJSPackageClass.requiredMethodRef("native")
3838
def JSPackage_native(using Context) = JSPackage_nativeR.symbol
39+
@threadUnsafe lazy val JSPackage_undefinedR = ScalaJSJSPackageClass.requiredMethodRef("undefined")
40+
def JSPackage_undefined(using Context) = JSPackage_undefinedR.symbol
3941

4042
@threadUnsafe lazy val JSNativeAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.native")
4143
def JSNativeAnnot(using Context) = JSNativeAnnotType.symbol.asClass
@@ -63,6 +65,10 @@ final class JSDefinitions()(using Context) {
6365
@threadUnsafe lazy val JavaScriptExceptionType: TypeRef = requiredClassRef("scala.scalajs.js.JavaScriptException")
6466
def JavaScriptExceptionClass(using Context) = JavaScriptExceptionType.symbol.asClass
6567

68+
@threadUnsafe lazy val JSGlobalAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSGlobal")
69+
def JSGlobalAnnot(using Context) = JSGlobalAnnotType.symbol.asClass
70+
@threadUnsafe lazy val JSImportAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSImport")
71+
def JSImportAnnot(using Context) = JSImportAnnotType.symbol.asClass
6672
@threadUnsafe lazy val JSGlobalScopeAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSGlobalScope")
6773
def JSGlobalScopeAnnot(using Context) = JSGlobalScopeAnnotType.symbol.asClass
6874
@threadUnsafe lazy val JSNameAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSName")
@@ -73,21 +79,24 @@ final class JSDefinitions()(using Context) {
7379
def JSBracketAccessAnnot(using Context) = JSBracketAccessAnnotType.symbol.asClass
7480
@threadUnsafe lazy val JSBracketCallAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSBracketCall")
7581
def JSBracketCallAnnot(using Context) = JSBracketCallAnnotType.symbol.asClass
82+
@threadUnsafe lazy val JSExportTopLevelAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSExportTopLevel")
83+
def JSExportTopLevelAnnot(using Context) = JSExportTopLevelAnnotType.symbol.asClass
7684
@threadUnsafe lazy val JSExportAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSExport")
7785
def JSExportAnnot(using Context) = JSExportAnnotType.symbol.asClass
78-
@threadUnsafe lazy val JSExportDescendentObjectsAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSExportDescendentObjects")
79-
def JSExportDescendentObjectsAnnot(using Context) = JSExportDescendentObjectsAnnotType.symbol.asClass
80-
@threadUnsafe lazy val JSExportDescendentClassesAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSExportDescendentClasses")
81-
def JSExportDescendentClassesAnnot(using Context) = JSExportDescendentClassesAnnotType.symbol.asClass
86+
@threadUnsafe lazy val JSExportStaticAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSExportStatic")
87+
def JSExportStaticAnnot(using Context) = JSExportStaticAnnotType.symbol.asClass
8288
@threadUnsafe lazy val JSExportAllAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSExportAll")
8389
def JSExportAllAnnot(using Context) = JSExportAllAnnotType.symbol.asClass
84-
@threadUnsafe lazy val JSExportNamedAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSExportNamed")
85-
def JSExportNamedAnnot(using Context) = JSExportNamedAnnotType.symbol.asClass
86-
@threadUnsafe lazy val RawJSTypeAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.RawJSType")
87-
def RawJSTypeAnnot(using Context) = RawJSTypeAnnotType.symbol.asClass
88-
@threadUnsafe lazy val ExposedJSMemberAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.ExposedJSMember")
90+
@threadUnsafe lazy val JSTypeAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.internal.JSType")
91+
def JSTypeAnnot(using Context) = JSTypeAnnotType.symbol.asClass
92+
@threadUnsafe lazy val JSOptionalAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.internal.JSOptional")
93+
def JSOptionalAnnot(using Context) = JSOptionalAnnotType.symbol.asClass
94+
@threadUnsafe lazy val ExposedJSMemberAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.internal.ExposedJSMember")
8995
def ExposedJSMemberAnnot(using Context) = ExposedJSMemberAnnotType.symbol.asClass
9096

97+
@threadUnsafe lazy val JSImportNamespaceModuleRef = requiredModuleRef("scala.scalajs.js.annotation.JSImport.Namespace")
98+
def JSImportNamespaceModule(using Context) = JSImportNamespaceModuleRef.symbol
99+
91100
@threadUnsafe lazy val JSAnyModuleRef = requiredModuleRef("scala.scalajs.js.Any")
92101
def JSAnyModule(using Context) = JSAnyModuleRef.symbol
93102
@threadUnsafe lazy val JSAny_fromFunctionR = (0 to 22).map(n => JSAnyModule.requiredMethodRef("fromFunction" + n)).toArray

compiler/src/dotty/tools/backend/sjs/JSInterop.scala

Lines changed: 19 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,33 @@ import Symbols._
77
import NameOps._
88
import StdNames._
99
import Phases._
10-
import NameKinds.DefaultGetterName
1110

12-
import JSDefinitions._
11+
import dotty.tools.dotc.transform.sjs.JSInteropUtils._
1312

14-
/** Management of the interoperability with JavaScript. */
13+
/** Management of the interoperability with JavaScript.
14+
*
15+
* This object only contains forwarders for extension methods in
16+
* `transform.sjs.JSInteropUtils`. They are kept to minimize changes in
17+
* `JSCodeGen` in the short term, but it will eventually be removed.
18+
*/
1519
object JSInterop {
1620

1721
/** Is this symbol a JavaScript type? */
18-
def isJSType(sym: Symbol)(using Context): Boolean = {
19-
atPhase(erasurePhase) {
20-
sym.derivesFrom(jsdefn.JSAnyClass) || sym == jsdefn.PseudoUnionClass
21-
}
22-
}
22+
def isJSType(sym: Symbol)(using Context): Boolean =
23+
sym.isJSType
2324

2425
/** Is this symbol a Scala.js-defined JS class, i.e., a non-native JS class? */
2526
def isScalaJSDefinedJSClass(sym: Symbol)(using Context): Boolean =
26-
isJSType(sym) && !sym.hasAnnotation(jsdefn.JSNativeAnnot)
27+
sym.isNonNativeJSClass
2728

2829
/** Should this symbol be translated into a JS getter?
2930
*
3031
* This is true for any parameterless method, i.e., defined without `()`.
3132
* Unlike `SymDenotations.isGetter`, it applies to user-defined methods as
3233
* much as *accessor* methods created for `val`s and `var`s.
3334
*/
34-
def isJSGetter(sym: Symbol)(using Context): Boolean = {
35-
sym.info.firstParamTypes.isEmpty && atPhase(erasurePhase) {
36-
sym.info.isParameterless
37-
}
38-
}
35+
def isJSGetter(sym: Symbol)(using Context): Boolean =
36+
sym.isJSGetter
3937

4038
/** Should this symbol be translated into a JS setter?
4139
*
@@ -44,74 +42,37 @@ object JSInterop {
4442
* much as *accessor* methods created for `var`s.
4543
*/
4644
def isJSSetter(sym: Symbol)(using Context): Boolean =
47-
sym.name.isSetterName && sym.is(Method)
45+
sym.isJSSetter
4846

4947
/** Should this symbol be translated into a JS bracket access?
5048
*
5149
* This is true for methods annotated with `@JSBracketAccess`.
5250
*/
5351
def isJSBracketAccess(sym: Symbol)(using Context): Boolean =
54-
sym.hasAnnotation(jsdefn.JSBracketAccessAnnot)
52+
sym.isJSBracketAccess
5553

5654
/** Should this symbol be translated into a JS bracket call?
5755
*
5856
* This is true for methods annotated with `@JSBracketCall`.
5957
*/
6058
def isJSBracketCall(sym: Symbol)(using Context): Boolean =
61-
sym.hasAnnotation(jsdefn.JSBracketCallAnnot)
59+
sym.isJSBracketCall
6260

6361
/** Is this symbol a default param accessor for a JS method?
6462
*
6563
* For default param accessors of *constructors*, we need to test whether
6664
* the companion *class* of the owner is a JS type; not whether the owner
6765
* is a JS type.
6866
*/
69-
def isJSDefaultParam(sym: Symbol)(using Context): Boolean = {
70-
sym.name.is(DefaultGetterName) && {
71-
val owner = sym.owner
72-
if (owner.is(ModuleClass)) {
73-
val isConstructor = sym.name match {
74-
case DefaultGetterName(methName, _) => methName == nme.CONSTRUCTOR
75-
case _ => false
76-
}
77-
if (isConstructor)
78-
isJSType(owner.linkedClass)
79-
else
80-
isJSType(owner)
81-
} else {
82-
isJSType(owner)
83-
}
84-
}
85-
}
67+
def isJSDefaultParam(sym: Symbol)(using Context): Boolean =
68+
sym.isJSDefaultParam
8669

8770
/** Gets the unqualified JS name of a symbol.
8871
*
8972
* If it is not explicitly specified with an `@JSName` annotation, the
9073
* JS name is inferred from the Scala name.
9174
*/
92-
def jsNameOf(sym: Symbol)(using Context): String = {
93-
sym.getAnnotation(jsdefn.JSNameAnnot).flatMap(_.argumentConstant(0)).fold {
94-
val base = sym.name.unexpandedName.decode.toString.stripSuffix("_=")
95-
if (sym.is(ModuleClass)) base.stripSuffix("$")
96-
else if (!sym.is(Method)) base.stripSuffix(" ")
97-
else base
98-
} { constant =>
99-
constant.stringValue
100-
}
101-
}
102-
103-
/** Gets the fully qualified JS name of a static class of module Symbol.
104-
*
105-
* This is the JS name of the symbol qualified by the fully qualified JS
106-
* name of its original owner if the latter is a native JS object.
107-
*/
108-
def fullJSNameOf(sym: Symbol)(using Context): String = {
109-
assert(sym.isClass, s"fullJSNameOf called for non-class symbol $sym")
110-
sym.getAnnotation(jsdefn.JSFullNameAnnot).flatMap(_.argumentConstant(0)).fold {
111-
jsNameOf(sym)
112-
} { constant =>
113-
constant.stringValue
114-
}
115-
}
75+
def jsNameOf(sym: Symbol)(using Context): JSName =
76+
sym.jsName
11677

11778
}

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class Compiler {
4141
List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks
4242
List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files
4343
List(new PostTyper) :: // Additional checks and cleanups after type checking
44+
List(new sjs.PrepJSInterop) :: // Additional checks and transformations for Scala.js (Scala.js only)
4445
List(new Staging) :: // Check PCP, heal quoted types and expand macros
4546
List(new sbt.ExtractAPI) :: // Sends a representation of the API of classes to sbt via callbacks
4647
List(new SetRootTree) :: // Set the `rootTreeOrProvider` on class symbols

compiler/src/dotty/tools/dotc/config/JavaPlatform.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ class JavaPlatform extends Platform {
6161
(sym derivesFrom BoxedBooleanClass)
6262
}
6363

64+
def shouldReceiveJavaSerializationMethods(sym: ClassSymbol)(using Context): Boolean =
65+
true
66+
6467
def newClassLoader(bin: AbstractFile)(using Context): SymbolLoader =
6568
new ClassfileLoader(bin)
6669
}

compiler/src/dotty/tools/dotc/config/Platform.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ abstract class Platform {
3131
/** The various ways a boxed primitive might materialize at runtime. */
3232
def isMaybeBoxed(sym: ClassSymbol)(using Context): Boolean
3333

34+
/** Is the given class symbol eligible for Java serialization-specific methods? */
35+
def shouldReceiveJavaSerializationMethods(sym: ClassSymbol)(using Context): Boolean
36+
3437
/** Create a new class loader to load class file `bin` */
3538
def newClassLoader(bin: AbstractFile)(using Context): SymbolLoader
3639

@@ -44,4 +47,3 @@ abstract class Platform {
4447
case _ => false
4548
}
4649
}
47-

compiler/src/dotty/tools/dotc/config/SJSPlatform.scala

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,18 @@ package dotty.tools.dotc.config
33
import dotty.tools.dotc.core._
44
import Contexts._
55
import Symbols._
6+
import SymDenotations._
67

78
import dotty.tools.backend.sjs.JSDefinitions
89

10+
import org.scalajs.ir.Trees.JSNativeLoadSpec
11+
12+
object SJSPlatform {
13+
/** The `SJSPlatform` for the current context. */
14+
def sjsPlatform(using Context): SJSPlatform =
15+
ctx.platform.asInstanceOf[SJSPlatform]
16+
}
17+
918
class SJSPlatform()(using Context) extends JavaPlatform {
1019

1120
/** Scala.js-specific definitions. */
@@ -16,4 +25,27 @@ class SJSPlatform()(using Context) extends JavaPlatform {
1625
defn.isFunctionClass(cls)
1726
|| jsDefinitions.isJSFunctionClass(cls)
1827
|| jsDefinitions.isJSThisFunctionClass(cls)
28+
29+
override def shouldReceiveJavaSerializationMethods(sym: ClassSymbol)(using Context): Boolean =
30+
!sym.isSubClass(jsDefinitions.JSAnyClass)
31+
32+
object perRunInfo {
33+
private val jsNativeLoadSpecs = new MutableSymbolMap[JSNativeLoadSpec]
34+
35+
/** Clears all the info at the beginning of a run. */
36+
def clear(): Unit =
37+
jsNativeLoadSpecs.clear()
38+
39+
/** Stores the JS native load spec of a symbol for the current compilation run. */
40+
def storeJSNativeLoadSpec(sym: Symbol, spec: JSNativeLoadSpec): Unit =
41+
jsNativeLoadSpecs(sym) = spec
42+
43+
/** Gets the JS native load spec of a symbol in the current compilation run. */
44+
def jsNativeLoadSpecOf(sym: Symbol): JSNativeLoadSpec =
45+
jsNativeLoadSpecs(sym)
46+
47+
/** Gets the JS native load spec of a symbol in the current compilation run, if it has one. */
48+
def jsNativeLoadSpecOfOption(sym: Symbol): Option[JSNativeLoadSpec] =
49+
jsNativeLoadSpecs.get(sym)
50+
}
1951
}

compiler/src/dotty/tools/dotc/core/Annotations.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ object Annotations {
3333
if (i < args.length) Some(args(i)) else None
3434
}
3535
def argumentConstant(i: Int)(using Context): Option[Constant] =
36-
for (ConstantType(c) <- argument(i) map (_.tpe)) yield c
36+
for (ConstantType(c) <- argument(i) map (_.tpe.widenTermRefExpr.normalized)) yield c
37+
38+
def argumentConstantString(i: Int)(using Context): Option[String] =
39+
for (Constant(s: String) <- argumentConstant(i)) yield s
3740

3841
/** The tree evaluaton is in progress. */
3942
def isEvaluating: Boolean = false

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ object SymDenotations {
259259
final def addAnnotation(annot: Annotation): Unit =
260260
annotations = annot :: myAnnotations
261261

262+
/** Add the given annotation without parameters to the annotations of this denotation */
263+
final def addAnnotation(cls: ClassSymbol)(using Context): Unit =
264+
addAnnotation(Annotation(cls))
265+
262266
/** Remove annotation with given class from this denotation */
263267
final def removeAnnotation(cls: Symbol)(using Context): Unit =
264268
annotations = myAnnotations.filterNot(_ matches cls)

0 commit comments

Comments
 (0)