Skip to content

Commit f2ba6f4

Browse files
committed
Check deprecation of inline methods
We must check these constraint just before inlining as later on there on the call might completely disappear. We do the same as we did for experimental definition checks. Fixes #19913
1 parent 162b543 commit f2ba6f4

File tree

6 files changed

+75
-48
lines changed

6 files changed

+75
-48
lines changed

Diff for: compiler/src/dotty/tools/dotc/inlines/Inlines.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ object Inlines:
100100
if ctx.isAfterTyper then
101101
// During typer we wait with cross version checks until PostTyper, in order
102102
// not to provoke cyclic references. See i16116 for a test case.
103-
CrossVersionChecks.checkExperimentalRef(tree.symbol, tree.srcPos)
103+
CrossVersionChecks.checkRef(tree.symbol, tree.srcPos)
104104

105105
if tree.symbol.isConstructor then return tree // error already reported for the inline constructor definition
106106

Diff for: compiler/src/dotty/tools/dotc/transform/PostTyper.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
369369
}
370370
case tree @ Inlined(call, bindings, expansion) if !tree.inlinedFromOuterScope =>
371371
val pos = call.sourcePos
372-
CrossVersionChecks.checkExperimentalRef(call.symbol, pos)
372+
CrossVersionChecks.checkRef(call.symbol, pos)
373373
withMode(Mode.NoInline)(transform(call))
374374
val callTrace = Inlines.inlineCallTrace(call.symbol, pos)(using ctx.withSource(pos.source))
375375
cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(using inlineContext(tree)))

Diff for: compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala

+50-45
Original file line numberDiff line numberDiff line change
@@ -24,54 +24,13 @@ class CrossVersionChecks extends MiniPhase:
2424
// warnings after the first, but I think it'd be better if we didn't have to
2525
// arbitrarily choose one as more important than the other.
2626
private def checkUndesiredProperties(sym: Symbol, pos: SrcPos)(using Context): Unit =
27-
checkDeprecated(sym, pos)
28-
checkExperimentalRef(sym, pos)
27+
checkRef(sym, pos)
2928

3029
val xMigrationValue = ctx.settings.Xmigration.value
3130
if xMigrationValue != NoScalaVersion then
3231
checkMigration(sym, pos, xMigrationValue)
3332
end checkUndesiredProperties
3433

35-
/**Skip warnings for synthetic members of case classes during declaration and
36-
* scan the chain of outer declaring scopes from the current context
37-
* a deprecation warning will be skipped if one the following holds
38-
* for a given declaring scope:
39-
* - the symbol associated with the scope is also deprecated.
40-
* - if and only if `sym` is an enum case, the scope is either
41-
* a module that declares `sym`, or the companion class of the
42-
* module that declares `sym`.
43-
*/
44-
def skipWarning(sym: Symbol)(using Context): Boolean =
45-
46-
/** is the owner an enum or its companion and also the owner of sym */
47-
def isEnumOwner(owner: Symbol)(using Context) =
48-
// pre: sym is an enumcase
49-
if owner.isEnumClass then owner.companionClass eq sym.owner
50-
else if owner.is(ModuleClass) && owner.companionClass.isEnumClass then owner eq sym.owner
51-
else false
52-
53-
def isDeprecatedOrEnum(owner: Symbol)(using Context) =
54-
// pre: sym is an enumcase
55-
owner.isDeprecated || isEnumOwner(owner)
56-
57-
(ctx.owner.is(Synthetic) && sym.is(CaseClass))
58-
|| ctx.owner.ownersIterator.exists(if sym.isEnumCase then isDeprecatedOrEnum else _.isDeprecated)
59-
end skipWarning
60-
61-
62-
/** If @deprecated is present, and the point of reference is not enclosed
63-
* in either a deprecated member or a scala bridge method, issue a warning.
64-
*/
65-
private def checkDeprecated(sym: Symbol, pos: SrcPos)(using Context): Unit =
66-
67-
// Also check for deprecation of the companion class for synthetic methods
68-
val toCheck = sym :: (if sym.isAllOf(SyntheticMethod) then sym.owner.companionClass :: Nil else Nil)
69-
for sym <- toCheck; annot <- sym.getAnnotation(defn.DeprecatedAnnot) do
70-
if !skipWarning(sym) then
71-
val msg = annot.argumentConstant(0).map(": " + _.stringValue).getOrElse("")
72-
val since = annot.argumentConstant(1).map(" since " + _.stringValue).getOrElse("")
73-
report.deprecationWarning(em"${sym.showLocated} is deprecated${since}${msg}", pos)
74-
7534
private def checkExperimentalAnnots(sym: Symbol)(using Context): Unit =
7635
if sym.exists && !sym.isInExperimentalScope then
7736
for annot <- sym.annotations if annot.symbol.isExperimental do
@@ -160,11 +119,11 @@ class CrossVersionChecks extends MiniPhase:
160119
tpe.foreachPart {
161120
case TypeRef(_, sym: Symbol) =>
162121
if tree.span.isSourceDerived then
163-
checkDeprecated(sym, tree.srcPos)
122+
checkDeprecatedRef(sym, tree.srcPos)
164123
checkExperimentalRef(sym, tree.srcPos)
165124
case TermRef(_, sym: Symbol) =>
166125
if tree.span.isSourceDerived then
167-
checkDeprecated(sym, tree.srcPos)
126+
checkDeprecatedRef(sym, tree.srcPos)
168127
checkExperimentalRef(sym, tree.srcPos)
169128
case _ =>
170129
}
@@ -186,9 +145,55 @@ object CrossVersionChecks:
186145
val name: String = "crossVersionChecks"
187146
val description: String = "check issues related to deprecated and experimental"
188147

148+
/** Check that a reference to an experimental definition with symbol `sym` meets cross-version constraints
149+
* for `@deprecated` and `@experimental`.
150+
*/
151+
def checkRef(sym: Symbol, pos: SrcPos)(using Context): Unit =
152+
checkDeprecatedRef(sym, pos)
153+
checkExperimentalRef(sym, pos)
154+
189155
/** Check that a reference to an experimental definition with symbol `sym` is only
190156
* used in an experimental scope
191157
*/
192-
def checkExperimentalRef(sym: Symbol, pos: SrcPos)(using Context): Unit =
158+
private[CrossVersionChecks] def checkExperimentalRef(sym: Symbol, pos: SrcPos)(using Context): Unit =
193159
if sym.isExperimental && !ctx.owner.isInExperimentalScope then
194160
Feature.checkExperimentalDef(sym, pos)
161+
162+
/** If @deprecated is present, and the point of reference is not enclosed
163+
* in either a deprecated member or a scala bridge method, issue a warning.
164+
*/
165+
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`.
183+
*/
184+
private def skipWarning(sym: Symbol)(using Context): Boolean =
185+
186+
/** is the owner an enum or its companion and also the owner of sym */
187+
def isEnumOwner(owner: Symbol)(using Context) =
188+
// pre: sym is an enumcase
189+
if owner.isEnumClass then owner.companionClass eq sym.owner
190+
else if owner.is(ModuleClass) && owner.companionClass.isEnumClass then owner eq sym.owner
191+
else false
192+
193+
def isDeprecatedOrEnum(owner: Symbol)(using Context) =
194+
// pre: sym is an enumcase
195+
owner.isDeprecated || isEnumOwner(owner)
196+
197+
(ctx.owner.is(Synthetic) && sym.is(CaseClass))
198+
|| ctx.owner.ownersIterator.exists(if sym.isEnumCase then isDeprecatedOrEnum else _.isDeprecated)
199+
end skipWarning

Diff for: scaladoc-testcases/src/tests/hugetype.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tests.hugetype
33
import compiletime._
44
import compiletime.ops.int._
55
import scala.annotation.experimental
6+
import scala.annotation.nowarn
67

78
/**
89
* a particular group of people or things that share similar characteristics and form a smaller division of a larger set:
@@ -39,7 +40,7 @@ trait XD extends E:
3940
*/
4041
@experimental
4142
@deprecated
42-
protected override final implicit transparent inline abstract infix def same[A](a: A): A = a
43+
protected override final implicit transparent inline abstract infix def same[A](a: A): A = a: @nowarn
4344

4445
trait Parent[X, A[_], B[_, _]]
4546

Diff for: tests/warn/i19913.check

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-- Deprecation Warning: tests/warn/i19913.scala:13:25 ------------------------------------------------------------------
2+
13 | Deprecated.inlineMethod() // warn
3+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
4+
| method inlineMethod in object Deprecated is deprecated: test
5+
-- Deprecation Warning: tests/warn/i19913.scala:12:13 ------------------------------------------------------------------
6+
12 | Deprecated.method() // warn
7+
| ^^^^^^^^^^^^^^^^^
8+
| method method in object Deprecated is deprecated: test

Diff for: tests/warn/i19913.scala

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//> using options -deprecation
2+
3+
object Deprecated:
4+
5+
@deprecated("test")
6+
def method() = ???
7+
8+
@deprecated("test")
9+
inline def inlineMethod() = ???
10+
11+
object Test:
12+
Deprecated.method() // warn
13+
Deprecated.inlineMethod() // warn

0 commit comments

Comments
 (0)