Skip to content

Commit cb86624

Browse files
Reflect deprecation in bytecode
1 parent a162b7b commit cb86624

File tree

7 files changed

+70
-40
lines changed

7 files changed

+70
-40
lines changed

compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -298,10 +298,9 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes {
298298
*/
299299
final def javaFlags(sym: Symbol): Int = {
300300

301-
302301
val privateFlag = sym.is(Private) || (sym.isPrimaryConstructor && sym.owner.isTopLevelModuleClass)
303302

304-
val finalFlag = sym.is(Final) && !toDenot(sym).isClassConstructor && !(sym.is(Mutable)) && !(sym.enclosingClass.is(Trait))
303+
val finalFlag = sym.is(Final) && !toDenot(sym).isClassConstructor && !sym.is(Mutable) && !sym.enclosingClass.is(Trait)
305304

306305
import asm.Opcodes._
307306
GenBCodeOps.mkFlags(
@@ -318,23 +317,25 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes {
318317
// Mixin forwarders are bridges and can be final, but final bridges confuse some frameworks
319318
!sym.is(Bridge))
320319
ACC_FINAL else 0,
320+
321321
if (sym.isStaticMember) ACC_STATIC else 0,
322322
if (sym.is(Bridge)) ACC_BRIDGE | ACC_SYNTHETIC else 0,
323323
if (sym.is(Artifact)) ACC_SYNTHETIC else 0,
324324
if (sym.isClass && !sym.isInterface) ACC_SUPER else 0,
325325
if (sym.isAllOf(JavaEnumTrait)) ACC_ENUM else 0,
326326
if (sym.is(JavaVarargs)) ACC_VARARGS else 0,
327327
if (sym.is(Synchronized)) ACC_SYNCHRONIZED else 0,
328-
if (false /*sym.isDeprecated*/) asm.Opcodes.ACC_DEPRECATED else 0, // TODO: add an isDeprecated method in SymUtils
329-
if (sym.is(Enum)) asm.Opcodes.ACC_ENUM else 0
328+
if (sym.isDeprecated) ACC_DEPRECATED else 0,
329+
if (sym.is(Enum)) ACC_ENUM else 0
330330
)
331331
}
332332

333333
def javaFieldFlags(sym: Symbol) = {
334+
import asm.Opcodes._
334335
javaFlags(sym) | GenBCodeOps.mkFlags(
335-
if (sym hasAnnotation TransientAttr) asm.Opcodes.ACC_TRANSIENT else 0,
336-
if (sym hasAnnotation VolatileAttr) asm.Opcodes.ACC_VOLATILE else 0,
337-
if (sym.is(Mutable)) 0 else asm.Opcodes.ACC_FINAL
336+
if (sym.hasAnnotation(TransientAttr)) ACC_TRANSIENT else 0,
337+
if (sym.hasAnnotation(VolatileAttr)) ACC_VOLATILE else 0,
338+
if (sym.is(Mutable)) 0 else ACC_FINAL
338339
)
339340
}
340341
}

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,6 @@ object Annotations {
211211
*/
212212
implicit class AnnotInfo(val sym: Symbol) extends AnyVal {
213213

214-
def isDeprecated(using Context): Boolean =
215-
sym.hasAnnotation(defn.DeprecatedAnnot)
216-
217214
def deprecationMessage(using Context): Option[String] =
218215
for {
219216
annot <- sym.getAnnotation(defn.DeprecatedAnnot)

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,22 +1925,21 @@ object messages {
19251925
def explain = "A sealed class or trait can only be extended in the same file as its declaration"
19261926
}
19271927

1928-
class SymbolHasUnparsableVersionNumber(symbol: Symbol, migrationMessage: => String)(using Context)
1928+
class SymbolHasUnparsableVersionNumber(symbol: Symbol, migrationVersion: String)(using Context)
19291929
extends SyntaxMsg(SymbolHasUnparsableVersionNumberID) {
1930-
def msg = em"${symbol.showLocated} has an unparsable version number: $migrationMessage"
1930+
def msg = em"${symbol.showLocated} has an unparsable version number: $migrationVersion"
19311931
def explain =
1932-
em"""$migrationMessage
1933-
|
1934-
|The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics
1932+
em"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics
19351933
|between versions and the ${hl("-Xmigration")} settings is used to warn about constructs
19361934
|whose behavior may have changed since version change."""
19371935
}
19381936

19391937
class SymbolChangedSemanticsInVersion(
19401938
symbol: Symbol,
1941-
migrationVersion: ScalaVersion
1939+
migrationVersion: ScalaVersion,
1940+
migrationMessage: String
19421941
)(using Context) extends SyntaxMsg(SymbolChangedSemanticsInVersionID) {
1943-
def msg = em"${symbol.showLocated} has changed semantics in version $migrationVersion"
1942+
def msg = em"${symbol.showLocated} has changed semantics in version $migrationVersion: $migrationMessage"
19441943
def explain = {
19451944
em"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics
19461945
|between versions and the ${hl("-Xmigration")} settings is used to warn about constructs

compiler/src/dotty/tools/dotc/transform/SymUtils.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,10 @@ class SymUtils(val self: Symbol) extends AnyVal {
223223
self.is(ModuleClass) && self.sourceModule.is(Extension) && !self.sourceModule.isExtensionMethod
224224

225225
def isScalaStatic(using Context): Boolean =
226-
self.hasAnnotation(ctx.definitions.ScalaStaticAnnot)
226+
self.hasAnnotation(defn.ScalaStaticAnnot)
227+
228+
def isDeprecated(using Context): Boolean =
229+
self.hasAnnotation(defn.DeprecatedAnnot)
227230

228231
/** Is symbol assumed or declared as an infix symbol? */
229232
def isDeclaredInfix(using Context): Boolean =

compiler/src/dotty/tools/dotc/typer/RefChecks.scala

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,16 @@ import util.Spans._
1111
import util.{Store, SourcePosition}
1212
import scala.collection.{ mutable, immutable }
1313
import ast._
14-
import Trees._
1514
import MegaPhase._
1615
import config.Printers.{checks, noPrinter}
17-
import scala.util.Failure
18-
import config.NoScalaVersion
16+
import scala.util.{Try, Failure, Success}
17+
import config.{ScalaVersion, NoScalaVersion}
1918
import Decorators._
2019
import typer.ErrorReporting._
2120
import config.Feature.warnOnMigration
2221

2322
object RefChecks {
24-
import tpd._
23+
import tpd.{Tree, MemberDef}
2524
import reporting.messages._
2625

2726
val name: String = "refchecks"
@@ -817,24 +816,39 @@ object RefChecks {
817816
// I assume that's a consequence of some code trying to avoid noise by suppressing
818817
// warnings after the first, but I think it'd be better if we didn't have to
819818
// arbitrarily choose one as more important than the other.
820-
private def checkUndesiredProperties(sym: Symbol, pos: SourcePosition)(using Context): Unit = {
821-
// If symbol is deprecated, and the point of reference is not enclosed
822-
// in either a deprecated member or a scala bridge method, issue a warning.
823-
if (sym.isDeprecated && !ctx.owner.ownersIterator.exists(_.isDeprecated))
824-
ctx.deprecationWarning("%s is deprecated%s".format(
825-
sym.showLocated, sym.deprecationMessage map (": " + _) getOrElse ""), pos)
826-
// Similar to deprecation: check if the symbol is marked with @migration
827-
// indicating it has changed semantics between versions.
819+
private def checkUndesiredProperties(sym: Symbol, pos: SourcePosition)(using Context): Unit =
820+
checkDeprecated(sym, pos)
821+
828822
val xMigrationValue = ctx.settings.Xmigration.value
829-
if (sym.hasAnnotation(defn.MigrationAnnot) && xMigrationValue != NoScalaVersion)
830-
sym.migrationVersion.get match {
831-
case scala.util.Success(symVersion) if xMigrationValue < symVersion=>
832-
ctx.warning(SymbolChangedSemanticsInVersion(sym, symVersion), pos)
823+
if xMigrationValue != NoScalaVersion then
824+
checkMigration(sym, pos, xMigrationValue)
825+
826+
827+
/** If @deprecated is present, and the point of reference is not enclosed
828+
* in either a deprecated member or a scala bridge method, issue a warning.
829+
*/
830+
private def checkDeprecated(sym: Symbol, pos: SourcePosition)(using Context): Unit =
831+
for
832+
annot <- sym.getAnnotation(defn.DeprecatedAnnot)
833+
if !ctx.owner.ownersIterator.exists(_.isDeprecated)
834+
do
835+
val msg = annot.argumentConstant(0).map(": " + _.stringValue).getOrElse("")
836+
val since = annot.argumentConstant(1).map(" since " + _.stringValue).getOrElse("")
837+
ctx.deprecationWarning(s"${sym.showLocated} is deprecated${since}${msg}", pos)
838+
839+
/** If @migration is present (indicating that the symbol has changed semantics between versions),
840+
* emit a warning.
841+
*/
842+
private def checkMigration(sym: Symbol, pos: SourcePosition, xMigrationValue: ScalaVersion)(using Context): Unit =
843+
for annot <- sym.getAnnotation(defn.MigrationAnnot) do
844+
val migrationVersion = ScalaVersion.parse(annot.argumentConstant(1).get.stringValue)
845+
migrationVersion match
846+
case Success(symVersion) if xMigrationValue < symVersion =>
847+
val msg = annot.argumentConstant(0).get.stringValue
848+
ctx.warning(SymbolChangedSemanticsInVersion(sym, symVersion, msg), pos)
833849
case Failure(ex) =>
834-
ctx.warning(SymbolHasUnparsableVersionNumber(sym, ex.getMessage()), pos)
850+
ctx.warning(SymbolHasUnparsableVersionNumber(sym, ex.getMessage), pos)
835851
case _ =>
836-
}
837-
}
838852

839853
/** Check that a deprecated val or def does not override a
840854
* concrete, non-deprecated method. If it does, then

compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,7 @@ class TestBCode extends DottyBytecodeTest {
938938
|
939939
|}
940940
""".stripMargin
941-
checkBCode(List(code)) { dir =>
941+
checkBCode(code) { dir =>
942942
val c = loadClassNode(dir.lookupName("C.class", directory = false).input)
943943

944944
assertInvoke(getMethod(c, "f1"), "[Ljava/lang/String;", "clone") // array descriptor as receiver
@@ -947,6 +947,23 @@ class TestBCode extends DottyBytecodeTest {
947947
assertInvoke(getMethod(c, "f4"), "java/lang/Object", "toString")
948948
}
949949
}
950+
951+
@Test
952+
def deprecation(): Unit = {
953+
val code =
954+
"""@deprecated
955+
|class Test {
956+
| @deprecated("do not use this function!")
957+
| def f(): Unit = ()
958+
|}
959+
""".stripMargin
960+
961+
checkBCode(code) { dir =>
962+
val c = loadClassNode(dir.lookupName("Test.class", directory = false).input)
963+
assert((c.access & Opcodes.ACC_DEPRECATED) != 0)
964+
assert((getMethod(c, "f").access & Opcodes.ACC_DEPRECATED) != 0)
965+
}
966+
}
950967
}
951968

952969
object invocationReceiversTestCode {

language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import scala.io.Codec
1919
import dotc._
2020
import ast.{Trees, tpd, untpd}
2121
import core._, core.Decorators._
22-
import Annotations.AnnotInfo
2322
import Comments._, Constants._, Contexts._, Flags._, Names._, NameOps._, Symbols._, SymDenotations._, Trees._, Types._
2423
import classpath.ClassPathEntries
2524
import reporting._
@@ -856,7 +855,7 @@ object DottyLanguageServer {
856855
item.setDocumentation(hoverContent(None, documentation))
857856
}
858857

859-
item.setDeprecated(completion.symbols.forall(_.isDeprecated))
858+
item.setDeprecated(completion.symbols.forall(_.hasAnnotation(defn.DeprecatedAnnot)))
860859
completion.symbols.headOption.foreach(s => item.setKind(completionItemKind(s)))
861860
item
862861
}

0 commit comments

Comments
 (0)