Skip to content

Commit c4959a1

Browse files
Backport "Deprecation of case class elements" to LTS (#21065)
Backports #17911 to the LTS branch. PR submitted by the release tooling. [skip ci]
2 parents 9c05b26 + 107e258 commit c4959a1

File tree

6 files changed

+68
-23
lines changed

6 files changed

+68
-23
lines changed

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

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dotc
33
package transform
44

55
import core.*
6+
import Annotations.Annotation
67
import Symbols.*, Types.*, Contexts.*, Flags.*, Decorators.*, reporting.*
78
import util.SrcPos
89
import config.{ScalaVersion, NoScalaVersion, Feature, ScalaRelease}
@@ -161,29 +162,42 @@ object CrossVersionChecks:
161162

162163
/** If @deprecated is present, and the point of reference is not enclosed
163164
* in either a deprecated member or a scala bridge method, issue a warning.
165+
*
166+
* Also check for deprecation of the companion class for synthetic methods in the companion module.
164167
*/
165168
private[CrossVersionChecks] def checkDeprecatedRef(sym: Symbol, pos: SrcPos)(using Context): Unit =
166-
167-
// Also check for deprecation of the companion class for synthetic methods
168-
val toCheck = sym :: (if sym.isAllOf(SyntheticMethod) then sym.owner.companionClass :: Nil else Nil)
169-
for sym <- toCheck; annot <- sym.getAnnotation(defn.DeprecatedAnnot) do
170-
if !skipWarning(sym) then
171-
val msg = annot.argumentConstant(0).map(": " + _.stringValue).getOrElse("")
172-
val since = annot.argumentConstant(1).map(" since " + _.stringValue).getOrElse("")
173-
report.deprecationWarning(em"${sym.showLocated} is deprecated${since}${msg}", pos)
174-
175-
/** Skip warnings for synthetic members of case classes during declaration and
176-
* scan the chain of outer declaring scopes from the current context
177-
* a deprecation warning will be skipped if one the following holds
178-
* for a given declaring scope:
179-
* - the symbol associated with the scope is also deprecated.
180-
* - if and only if `sym` is an enum case, the scope is either
181-
* a module that declares `sym`, or the companion class of the
182-
* module that declares `sym`.
169+
def maybeWarn(annotee: Symbol, annot: Annotation) = if !skipWarning(sym) then
170+
val message = annot.argumentConstantString(0).filter(!_.isEmpty).map(": " + _).getOrElse("")
171+
val since = annot.argumentConstantString(1).filter(!_.isEmpty).map(" since " + _).getOrElse("")
172+
report.deprecationWarning(em"${annotee.showLocated} is deprecated${since}${message}", pos)
173+
sym.getAnnotation(defn.DeprecatedAnnot) match
174+
case Some(annot) => maybeWarn(sym, annot)
175+
case _ =>
176+
if sym.isAllOf(SyntheticMethod) then
177+
val companion = sym.owner.companionClass
178+
if companion.is(CaseClass) then companion.getAnnotation(defn.DeprecatedAnnot).foreach(maybeWarn(companion, _))
179+
180+
/** Decide whether the deprecation of `sym` should be ignored in this context.
181+
*
182+
* The warning is skipped if any symbol in the context owner chain is deprecated,
183+
* that is, an enclosing scope is associated with a deprecated symbol.
184+
*
185+
* Further exclusions are needed for enums and case classes,
186+
* since they typically need to refer to deprecated members
187+
* even if the enclosing enum or case class is not deprecated.
188+
*
189+
* If and only if `sym` is an enum case, the warning is skipped
190+
* if an enclosing scope is either a module that declares `sym`,
191+
* or the companion class of the module that declares `sym`.
192+
*
193+
* For a deprecated case class or case class element,
194+
* the warning is skipped for synthetic sites where the enclosing
195+
* class (or its companion) is either the deprecated case class
196+
* or the case class of the deprecated element.
183197
*/
184198
private def skipWarning(sym: Symbol)(using Context): Boolean =
185199

186-
/** is the owner an enum or its companion and also the owner of sym */
200+
// is the owner an enum or its companion and also the owner of sym
187201
def isEnumOwner(owner: Symbol)(using Context) =
188202
// pre: sym is an enumcase
189203
if owner.isEnumClass then owner.companionClass eq sym.owner
@@ -194,6 +208,19 @@ object CrossVersionChecks:
194208
// pre: sym is an enumcase
195209
owner.isDeprecated || isEnumOwner(owner)
196210

197-
(ctx.owner.is(Synthetic) && sym.is(CaseClass))
198-
|| ctx.owner.ownersIterator.exists(if sym.isEnumCase then isDeprecatedOrEnum else _.isDeprecated)
211+
def siteIsEnclosedByDeprecatedElement =
212+
ctx.owner.ownersIterator.exists:
213+
if sym.isEnumCase then isDeprecatedOrEnum else _.isDeprecated
214+
215+
def siteIsSyntheticCaseClassMember =
216+
val owner = ctx.owner
217+
def symIsCaseOrMember =
218+
val enclosing = owner.enclosingClass
219+
val companion = enclosing.companionClass
220+
// deprecated sym is either enclosing case class or a sibling member
221+
def checkSym(k: Symbol) = sym == k || sym.owner == k
222+
(enclosing.is(CaseClass) || companion.is(CaseClass)) && (checkSym(enclosing) || checkSym(companion))
223+
owner.is(Synthetic) && symIsCaseOrMember
224+
225+
siteIsSyntheticCaseClassMember || siteIsEnclosedByDeprecatedElement
199226
end skipWarning

compiler/test-resources/repl/reset-command

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ scala> def f(thread: Thread) = thread.stop()
1212
-- Deprecation Warning: --------------------------------------------------------
1313
1 | def f(thread: Thread) = thread.stop()
1414
| ^^^^^^^^^^^
15-
|method stop in class Thread is deprecated since : see corresponding Javadoc for more information.
15+
|method stop in class Thread is deprecated: see corresponding Javadoc for more information.
1616
def f(thread: Thread): Unit
1717

1818
scala> def resetNoArgsStillWorks = 1

compiler/test-resources/repl/settings-command

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ scala> def f(thread: Thread) = thread.stop()
1111
-- Deprecation Warning: --------------------------------------------------------
1212
1 | def f(thread: Thread) = thread.stop()
1313
| ^^^^^^^^^^^
14-
|method stop in class Thread is deprecated since : see corresponding Javadoc for more information.
14+
|method stop in class Thread is deprecated: see corresponding Javadoc for more information.
1515
def f(thread: Thread): Unit
1616

1717
scala>

tests/pos/i11022.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
//> using options -Werror -deprecation
22
@deprecated("no CaseClass")
33
case class CaseClass(rgb: Int)
4+
5+
case class K(@deprecated("don't use k, ok?","0.1") k: Int)

tests/warn/i11022.check

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
-- Deprecation Warning: tests/warn/i11022.scala:19:22 ------------------------------------------------------------------
2+
19 | def usage(k: K) = k.k // warn
3+
| ^^^
4+
| value k in class K is deprecated since 0.1: don't use k, ok?
15
-- Deprecation Warning: tests/warn/i11022.scala:10:7 -------------------------------------------------------------------
26
10 |val a: CaseClass = CaseClass(42) // warn: deprecated type // warn: deprecated apply method
37
| ^^^^^^^^^
@@ -18,3 +22,7 @@
1822
12 |val c: Unit = CaseClass(42).magic() // warn: deprecated apply method
1923
| ^^^^^^^^^
2024
| class CaseClass is deprecated: no CaseClass
25+
-- Deprecation Warning: tests/warn/i11022.scala:14:4 -------------------------------------------------------------------
26+
14 |val CaseClass(rgb) = b // warn
27+
| ^^^^^^^^^
28+
| class CaseClass is deprecated: no CaseClass

tests/warn/i11022.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,12 @@ object CaseClass:
1010
val a: CaseClass = CaseClass(42) // warn: deprecated type // warn: deprecated apply method
1111
val b: CaseClass = new CaseClass(42) // warn: deprecated type // warn: deprecated class
1212
val c: Unit = CaseClass(42).magic() // warn: deprecated apply method
13-
val d: Unit = CaseClass.notDeprecated() // compiles
13+
val d: Unit = CaseClass.notDeprecated() // compiles
14+
val CaseClass(rgb) = b // warn
15+
16+
case class K(@deprecated("don't use k, ok?","0.1") k: Int)
17+
18+
object K:
19+
def usage(k: K) = k.k // warn
20+
21+
val s: String = CaseClass.toString

0 commit comments

Comments
 (0)