@@ -3,6 +3,7 @@ package dotc
33package transform
44
55import core .*
6+ import Annotations .Annotation
67import Symbols .* , Types .* , Contexts .* , Flags .* , Decorators .* , reporting .*
78import util .SrcPos
89import 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
0 commit comments