From ed8d1af149b4fab72f8305ebb61dba39475e2248 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 29 Nov 2023 11:25:36 +0100 Subject: [PATCH] Refactor migration warnings Now we have a single way to define migration warning versions and a single place to see all the deprecations we still need to address. --- .../tools/dotc/config/MigrationVersion.scala | 42 ++++++++++ .../dotty/tools/dotc/parsing/Parsers.scala | 84 +++++++++---------- .../dotty/tools/dotc/parsing/Scanners.scala | 7 +- compiler/src/dotty/tools/dotc/report.scala | 15 ++-- .../dotc/transform/NonLocalReturns.scala | 6 +- .../src/dotty/tools/dotc/typer/Checking.scala | 21 +++-- .../dotty/tools/dotc/typer/RefChecks.scala | 17 ++-- .../src/dotty/tools/dotc/typer/Typer.scala | 18 ++-- tests/neg/i11567.scala | 2 +- 9 files changed, 122 insertions(+), 90 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/config/MigrationVersion.scala diff --git a/compiler/src/dotty/tools/dotc/config/MigrationVersion.scala b/compiler/src/dotty/tools/dotc/config/MigrationVersion.scala new file mode 100644 index 000000000000..851e34c298db --- /dev/null +++ b/compiler/src/dotty/tools/dotc/config/MigrationVersion.scala @@ -0,0 +1,42 @@ +package dotty.tools +package dotc +package config + +import SourceVersion.* +import Feature.* +import core.Contexts.Context + +class MigrationVersion(val warnFrom: SourceVersion, val errorFrom: SourceVersion): + assert(warnFrom.ordinal <= errorFrom.ordinal) + def needsPatch(using Context): Boolean = + sourceVersion.isMigrating && sourceVersion.isAtLeast(errorFrom) + +object MigrationVersion: + + val Scala2to3 = MigrationVersion(`3.0`, `3.0`) + + val OverrideValParameter = MigrationVersion(`3.0`, future) + + // we tighten for-comprehension without `case` to error in 3.4, + // but we keep pat-defs as warnings for now ("@unchecked"), + // until we propose an alternative way to assert exhaustivity to the typechecker. + val ForComprehensionPatternWithoutCase = MigrationVersion(`3.2`, `3.4`) + val ForComprehensionUncheckedPathDefs = MigrationVersion(`3.2`, future) + + val NonLocalReturns = MigrationVersion(`3.2`, future) + + val AscriptionAfterPattern = MigrationVersion(`3.3`, future) + + val AlphanumericInfix = MigrationVersion(`3.4`, future) + val RemoveThisQualifier = MigrationVersion(`3.4`, future) + val UninitializedVars = MigrationVersion(`3.4`, future) + val VarargSpliceAscription = MigrationVersion(`3.4`, future) + val WildcardType = MigrationVersion(`3.4`, future) + val WithOperator = MigrationVersion(`3.4`, future) + val FunctionUnderscore = MigrationVersion(`3.4`, future) + + val ImportWildcard = MigrationVersion(future, future) + val ImportRename = MigrationVersion(future, future) + val ParameterEnclosedByParenthesis = MigrationVersion(future, future) + +end MigrationVersion diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index f3a2300913ec..ae5f77aa4850 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -33,6 +33,7 @@ import config.Feature import config.Feature.{sourceVersion, migrateTo3, globalOnlyImports} import config.SourceVersion.* import config.SourceVersion +import dotty.tools.dotc.config.MigrationVersion object Parsers { @@ -462,8 +463,8 @@ object Parsers { case t @ Typed(Ident(_), _) => report.errorOrMigrationWarning( em"parentheses are required around the parameter of a lambda${rewriteNotice()}", - in.sourcePos(), from = `3.0`) - if sourceVersion.isMigrating then + in.sourcePos(), MigrationVersion.Scala2to3) + if MigrationVersion.Scala2to3.needsPatch then patch(source, t.span.startPos, "(") patch(source, t.span.endPos, ")") convertToParam(t, mods) :: Nil @@ -1314,8 +1315,8 @@ object Parsers { |or enclose in braces '{$name} if you want a quoted expression. |For now, you can also `import language.deprecated.symbolLiterals` to accept |the idiom, but this possibility might no longer be available in the future.""", - in.sourcePos(), from = `3.0`) - if sourceVersion.isMigrating then + in.sourcePos(), MigrationVersion.Scala2to3) + if MigrationVersion.Scala2to3.needsPatch then patch(source, Span(in.offset, in.offset + 1), "Symbol(\"") patch(source, Span(in.charOffset - 1), "\")") atSpan(in.skipToken()) { SymbolLit(in.strVal) } @@ -1412,8 +1413,8 @@ object Parsers { em"""This opening brace will start a new statement in Scala 3. |It needs to be indented to the right to keep being treated as |an argument to the previous expression.${rewriteNotice()}""", - in.sourcePos(), from = `3.0`) - if sourceVersion.isMigrating then + in.sourcePos(), MigrationVersion.Scala2to3) + if MigrationVersion.Scala2to3.needsPatch then patch(source, Span(in.offset), " ") def possibleTemplateStart(isNew: Boolean = false): Unit = @@ -1776,12 +1777,11 @@ object Parsers { t else val withSpan = Span(withOffset, withOffset + 4) - report.gradualErrorOrMigrationWarning( + report.errorOrMigrationWarning( DeprecatedWithOperator(rewriteNotice(`3.4-migration`)), source.atSpan(withSpan), - warnFrom = `3.4`, - errorFrom = future) - if sourceVersion.isMigrating && sourceVersion.isAtLeast(`3.4-migration`) then + MigrationVersion.WithOperator) + if MigrationVersion.WithOperator.needsPatch then patch(source, withSpan, "&") atSpan(startOffset(t)) { makeAndType(t, withType()) } else t @@ -1882,12 +1882,11 @@ object Parsers { Ident(tpnme.USCOREkw).withSpan(Span(start, in.lastOffset, start)) else if !inTypeMatchPattern then - report.gradualErrorOrMigrationWarning( + report.errorOrMigrationWarning( em"`_` is deprecated for wildcard arguments of types: use `?` instead${rewriteNotice(`3.4-migration`)}", in.sourcePos(), - warnFrom = `3.4`, - errorFrom = future) - if sourceVersion.isMigrating && sourceVersion.isAtLeast(`3.4-migration`) then + MigrationVersion.WildcardType) + if MigrationVersion.WildcardType.needsPatch then patch(source, Span(in.offset, in.offset + 1), "?") end if val start = in.skipToken() @@ -2111,7 +2110,7 @@ object Parsers { else if in.token == VIEWBOUND then report.errorOrMigrationWarning( em"view bounds `<%' are no longer supported, use a context bound `:' instead", - in.sourcePos(), from = `3.0`) + in.sourcePos(), MigrationVersion.Scala2to3) atSpan(in.skipToken()) { Function(Ident(pname) :: Nil, toplevelTyp()) } :: contextBounds(pname) @@ -2260,7 +2259,7 @@ object Parsers { report.errorOrMigrationWarning( em"""`do while ` is no longer supported, |use `while ; do ()` instead.${rewriteNotice()}""", - in.sourcePos(), from = `3.0`) + in.sourcePos(), MigrationVersion.Scala2to3) val start = in.skipToken() atSpan(start) { val body = expr() @@ -2268,7 +2267,7 @@ object Parsers { val whileStart = in.offset accept(WHILE) val cond = expr() - if sourceVersion.isMigrating then + if MigrationVersion.Scala2to3.needsPatch then patch(source, Span(start, start + 2), "while ({") patch(source, Span(whileStart, whileStart + 5), ";") cond match { @@ -2370,18 +2369,17 @@ object Parsers { val isVarargSplice = location.inArgs && followingIsVararg() in.nextToken() if isVarargSplice then - report.gradualErrorOrMigrationWarning( + report.errorOrMigrationWarning( em"The syntax `x: _*` is no longer supported for vararg splices; use `x*` instead${rewriteNotice(`3.4-migration`)}", in.sourcePos(uscoreStart), - warnFrom = `3.4`, - errorFrom = future) - if sourceVersion.isMigrating && sourceVersion.isAtLeast(`3.4-migration`) then + MigrationVersion.VarargSpliceAscription) + if MigrationVersion.VarargSpliceAscription.needsPatch then patch(source, Span(t.span.end, in.lastOffset), "*") else if opStack.nonEmpty then report.errorOrMigrationWarning( em"""`_*` can be used only for last argument of method application. |It is no longer allowed in operands of infix operations.""", - in.sourcePos(uscoreStart), from = `3.0`) + in.sourcePos(uscoreStart), MigrationVersion.Scala2to3) else syntaxError(SeqWildcardPatternPos(), uscoreStart) Typed(t, atSpan(uscoreStart) { Ident(tpnme.WILDCARD_STAR) }) @@ -2448,13 +2446,12 @@ object Parsers { report.errorOrMigrationWarning( em"This syntax is no longer supported; parameter needs to be enclosed in (...)${rewriteNotice(`future-migration`)}", source.atSpan(Span(start, in.lastOffset)), - from = future) + MigrationVersion.ParameterEnclosedByParenthesis) in.nextToken() val t = infixType() - if (sourceVersion == `future-migration`) { + if MigrationVersion.ParameterEnclosedByParenthesis.needsPatch then patch(source, Span(start), "(") patch(source, Span(in.lastOffset), ")") - } t } else TypeTree() @@ -2958,15 +2955,13 @@ object Parsers { if in.isColon then val isVariableOrNumber = isVarPattern(p) || p.isInstanceOf[Number] if !isVariableOrNumber then - report.gradualErrorOrMigrationWarning( + report.errorOrMigrationWarning( em"""Type ascriptions after patterns other than: | * variable pattern, e.g. `case x: String =>` | * number literal pattern, e.g. `case 10.5: Double =>` |are no longer supported. Remove the type ascription or move it to a separate variable pattern.""", in.sourcePos(), - warnFrom = `3.3`, - errorFrom = future - ) + MigrationVersion.AscriptionAfterPattern) in.nextToken() ascription(p, location) else p @@ -3147,13 +3142,12 @@ object Parsers { else mods.withPrivateWithin(ident().toTypeName) } if mods1.is(Local) then - report.gradualErrorOrMigrationWarning( + report.errorOrMigrationWarning( em"""The [this] qualifier will be deprecated in the future; it should be dropped. |See: https://docs.scala-lang.org/scala3/reference/dropped-features/this-qualifier.html${rewriteNotice(`3.4-migration`)}""", in.sourcePos(), - warnFrom = `3.4`, - errorFrom = future) - if sourceVersion.isMigrating && sourceVersion.isAtLeast(`3.4-migration`) then + MigrationVersion.RemoveThisQualifier) + if MigrationVersion.RemoveThisQualifier.needsPatch then patch(source, Span(startOffset, in.lastOffset), "") mods1 } @@ -3542,8 +3536,8 @@ object Parsers { report.errorOrMigrationWarning( em"`_` is no longer supported for a wildcard $exprName; use `*` instead${rewriteNotice(`future-migration`)}", in.sourcePos(), - from = future) - if sourceVersion == `future-migration` then + MigrationVersion.ImportWildcard) + if MigrationVersion.ImportWildcard.needsPatch then patch(source, Span(in.offset, in.offset + 1), "*") ImportSelector(atSpan(in.skipToken()) { Ident(nme.WILDCARD) }) @@ -3562,8 +3556,8 @@ object Parsers { report.errorOrMigrationWarning( em"The $exprName renaming `a => b` is no longer supported ; use `a as b` instead${rewriteNotice(`future-migration`)}", in.sourcePos(), - from = future) - if sourceVersion == `future-migration` then + MigrationVersion.ImportRename) + if MigrationVersion.ImportRename.needsPatch then patch(source, Span(in.offset, in.offset + 2), if testChar(in.offset - 1, ' ') && testChar(in.offset + 2, ' ') then "as" else " as ") @@ -3674,13 +3668,12 @@ object Parsers { subExpr() match case rhs0 @ Ident(name) if placeholderParams.nonEmpty && name == placeholderParams.head.name && !tpt.isEmpty && mods.is(Mutable) && lhs.forall(_.isInstanceOf[Ident]) => - report.gradualErrorOrMigrationWarning( + report.errorOrMigrationWarning( em"""`= _` has been deprecated; use `= uninitialized` instead. |`uninitialized` can be imported with `scala.compiletime.uninitialized`.${rewriteNotice(`3.4-migration`)}""", in.sourcePos(rhsOffset), - warnFrom = `3.4`, - errorFrom = future) - if sourceVersion.isMigrating && sourceVersion.isAtLeast(`3.4-migration`) then + MigrationVersion.UninitializedVars) + if MigrationVersion.UninitializedVars.needsPatch then patch(source, Span(rhsOffset, rhsOffset + 1), "scala.compiletime.uninitialized") placeholderParams = placeholderParams.tail atSpan(rhs0.span) { Ident(nme.WILDCARD) } @@ -3722,9 +3715,10 @@ object Parsers { else ": Unit " // trailing space ensures that `def f()def g()` works. if migrateTo3 then report.errorOrMigrationWarning( - em"Procedure syntax no longer supported; `$toInsert` should be inserted here", - in.sourcePos(), from = `3.0`) - patch(source, Span(in.lastOffset), toInsert) + em"Procedure syntax no longer supported; `$toInsert` should be inserted here${rewriteNotice()}", + in.sourcePos(), MigrationVersion.Scala2to3) + if MigrationVersion.Scala2to3.needsPatch then + patch(source, Span(in.lastOffset), toInsert) true else false @@ -4150,7 +4144,7 @@ object Parsers { if (in.token == LBRACE || in.token == COLONeol) { report.errorOrMigrationWarning( em"`extends` must be followed by at least one parent", - in.sourcePos(), from = `3.0`) + in.sourcePos(), MigrationVersion.Scala2to3) Nil } else constrApps() diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index f1e9e646bf43..ea43706e9fdb 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -19,6 +19,7 @@ import rewrites.Rewrites.patch import config.Feature import config.Feature.{migrateTo3, fewerBracesEnabled} import config.SourceVersion.{`3.0`, `3.0-migration`} +import config.MigrationVersion import reporting.{NoProfile, Profile, Message} import java.util.Objects @@ -257,8 +258,8 @@ object Scanners { report.errorOrMigrationWarning( em"$what is now a keyword, write `$what` instead of $what to keep it as an identifier${rewriteNotice("This", `3.0-migration`)}", sourcePos(), - from = `3.0`) - if sourceVersion.isMigrating then + MigrationVersion.Scala2to3) + if MigrationVersion.Scala2to3.needsPatch then patch(source, Span(offset), "`") patch(source, Span(offset + identifier.length), "`") IDENTIFIER @@ -470,7 +471,7 @@ object Scanners { em"""$what starts with an operator; |it is now treated as a continuation of the $previous, |not as a separate statement.""", - sourcePos(), from = `3.0`) + sourcePos(), MigrationVersion.Scala2to3) true } diff --git a/compiler/src/dotty/tools/dotc/report.scala b/compiler/src/dotty/tools/dotc/report.scala index 142561dcbbee..00b20b94ac87 100644 --- a/compiler/src/dotty/tools/dotc/report.scala +++ b/compiler/src/dotty/tools/dotc/report.scala @@ -9,6 +9,7 @@ import config.SourceVersion import ast.* import config.Feature.sourceVersion import java.lang.System.currentTimeMillis +import dotty.tools.dotc.config.MigrationVersion object report: @@ -80,15 +81,11 @@ object report: if ctx.settings.YdebugError.value then Thread.dumpStack() if ctx.settings.YdebugTypeError.value then ex.printStackTrace() - def errorOrMigrationWarning(msg: Message, pos: SrcPos, from: SourceVersion)(using Context): Unit = - if sourceVersion.isAtLeast(from) then - if sourceVersion.isMigrating && sourceVersion.ordinal <= from.ordinal then - if ctx.settings.rewrite.value.isEmpty then migrationWarning(msg, pos) - else error(msg, pos) - - def gradualErrorOrMigrationWarning(msg: Message, pos: SrcPos, warnFrom: SourceVersion, errorFrom: SourceVersion)(using Context): Unit = - if sourceVersion.isAtLeast(errorFrom) then errorOrMigrationWarning(msg, pos, errorFrom) - else if sourceVersion.isAtLeast(warnFrom) then warning(msg, pos) + def errorOrMigrationWarning(msg: Message, pos: SrcPos, migrationVersion: MigrationVersion)(using Context): Unit = + if sourceVersion.isAtLeast(migrationVersion.errorFrom) then + if !sourceVersion.isMigrating then error(msg, pos) + else if ctx.settings.rewrite.value.isEmpty then migrationWarning(msg, pos) + else if sourceVersion.isAtLeast(migrationVersion.warnFrom) then warning(msg, pos) def restrictionError(msg: Message, pos: SrcPos = NoSourcePosition)(using Context): Unit = error(msg.mapMsg("Implementation restriction: " + _), pos) diff --git a/compiler/src/dotty/tools/dotc/transform/NonLocalReturns.scala b/compiler/src/dotty/tools/dotc/transform/NonLocalReturns.scala index 4bdcc8d9606d..6ff81ab13cf1 100644 --- a/compiler/src/dotty/tools/dotc/transform/NonLocalReturns.scala +++ b/compiler/src/dotty/tools/dotc/transform/NonLocalReturns.scala @@ -7,6 +7,7 @@ import MegaPhase.* import NameKinds.NonLocalReturnKeyName import config.SourceVersion.* import Decorators.em +import dotty.tools.dotc.config.MigrationVersion object NonLocalReturns { import ast.tpd.* @@ -96,11 +97,10 @@ class NonLocalReturns extends MiniPhase { override def transformReturn(tree: Return)(using Context): Tree = if isNonLocalReturn(tree) then - report.gradualErrorOrMigrationWarning( + report.errorOrMigrationWarning( em"Non local returns are no longer supported; use `boundary` and `boundary.break` in `scala.util` instead", tree.srcPos, - warnFrom = `3.2`, - errorFrom = future) + MigrationVersion.NonLocalReturns) nonLocalReturnThrow(tree.expr, tree.from.symbol).withSpan(tree.span) else tree } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 7ed6748eb337..86b3fee5ae7e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -36,6 +36,7 @@ import transform.ValueClasses.underlyingOfValueClass import config.Feature import config.Feature.sourceVersion import config.SourceVersion.* +import config.MigrationVersion import printing.Formatting.hlAsKeyword import cc.isCaptureChecking @@ -131,7 +132,7 @@ object Checking { if tp.isUnreducibleWild then report.errorOrMigrationWarning( showInferred(UnreducibleApplication(tycon), tp, tpt), - tree.srcPos, from = `3.0`) + tree.srcPos, MigrationVersion.Scala2to3) case _ => } def checkValidIfApply(using Context): Unit = @@ -217,7 +218,7 @@ object Checking { val rstatus = realizability(tp) if (rstatus ne Realizable) report.errorOrMigrationWarning( - em"$tp is not a legal $what\nsince it${rstatus.msg}", pos, from = `3.0`) + em"$tp is not a legal $what\nsince it${rstatus.msg}", pos, MigrationVersion.Scala2to3) } /** Given a parent `parent` of a class `cls`, if `parent` is a trait check that @@ -690,7 +691,7 @@ object Checking { } val notPrivate = new NotPrivate val info = notPrivate(sym.info) - notPrivate.errors.foreach(report.errorOrMigrationWarning(_, sym.srcPos, from = `3.0`)) + notPrivate.errors.foreach(report.errorOrMigrationWarning(_, sym.srcPos, MigrationVersion.Scala2to3)) info } @@ -919,18 +920,18 @@ trait Checking { case RefutableExtractor => pat.source.atSpan(pat.span union sel.span) else pat.srcPos def rewriteMsg = Message.rewriteNotice("This patch", `3.2-migration`) - report.gradualErrorOrMigrationWarning( + report.errorOrMigrationWarning( message.append( i"""| | |If $usage is intentional, this can be communicated by $fix, |which $addendum.$rewriteMsg"""), pos, - warnFrom = `3.2`, // we tighten for-comprehension without `case` to error in 3.4, // but we keep pat-defs as warnings for now ("@unchecked"), // until we propose an alternative way to assert exhaustivity to the typechecker. - errorFrom = if isPatDef then `future` else `3.4` + if isPatDef then MigrationVersion.ForComprehensionUncheckedPathDefs + else MigrationVersion.ForComprehensionPatternWithoutCase ) false } @@ -1097,16 +1098,14 @@ trait Checking { else ("method", (n: Name) => s"method syntax .$n(...)") def rewriteMsg = Message.rewriteNotice("The latter", version = `3.4-migration`) - report.gradualErrorOrMigrationWarning( + report.errorOrMigrationWarning( em"""Alphanumeric $kind $name is not declared ${hlAsKeyword("infix")}; it should not be used as infix operator. |Instead, use ${alternative(name)} or backticked identifier `$name`.$rewriteMsg""", tree.op.srcPos, - warnFrom = `3.4`, - errorFrom = future) - if sourceVersion.isMigrating && sourceVersion.isAtLeast(`3.4-migration`) then { + MigrationVersion.AlphanumericInfix) + if MigrationVersion.AlphanumericInfix.needsPatch then patch(Span(tree.op.span.start, tree.op.span.start), "`") patch(Span(tree.op.span.end, tree.op.span.end), "`") - } case _ => } } diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 5e114fa7534b..85781c500753 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -16,7 +16,8 @@ import Decorators.* import OverridingPairs.isOverridingPair import typer.ErrorReporting.* import config.Feature.{warnOnMigration, migrateTo3, sourceVersion} -import config.SourceVersion.{`3.0`, `future`} +import config.SourceVersion.`3.0` +import config.MigrationVersion import config.Printers.refcheck import reporting.* import Constants.Constant @@ -571,12 +572,10 @@ object RefChecks { else overrideError("cannot have a @targetName annotation since external names would be different") else if other.is(ParamAccessor) && !isInheritedAccessor(member, other) then // (1.13) - if sourceVersion.isAtLeast(`future`) then - overrideError(i"cannot override val parameter ${other.showLocated}") - else - report.deprecationWarning( - em"overriding val parameter ${other.showLocated} is deprecated, will be illegal in a future version", - member.srcPos) + report.errorOrMigrationWarning( + em"cannot override val parameter ${other.showLocated}", + member.srcPos, + MigrationVersion.OverrideValParameter) else if !other.isExperimental && member.hasAnnotation(defn.ExperimentalAnnot) then // (1.12) overrideError("may not override non-experimental member") else if other.hasAnnotation(defn.DeprecatedOverridingAnnot) then @@ -831,7 +830,7 @@ object RefChecks { em"""${mbr.showLocated} is not a legal implementation of `$name` in $clazz | its type $mbrType | does not conform to ${mbrd.info}""", - (if (mbr.owner == clazz) mbr else clazz).srcPos, from = `3.0`) + (if (mbr.owner == clazz) mbr else clazz).srcPos, MigrationVersion.Scala2to3) } } } @@ -845,7 +844,7 @@ object RefChecks { for (baseCls <- caseCls.info.baseClasses.tail) if (baseCls.typeParams.exists(_.paramVarianceSign != 0)) for (problem <- variantInheritanceProblems(baseCls, caseCls, "non-variant", "case ")) - report.errorOrMigrationWarning(problem, clazz.srcPos, from = `3.0`) + report.errorOrMigrationWarning(problem, clazz.srcPos, MigrationVersion.Scala2to3) checkNoAbstractMembers() if (abstractErrors.isEmpty) checkNoAbstractDecls(clazz) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 9310a4acd8ae..a1ef6c0b2f25 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -50,6 +50,7 @@ import Nullables.* import NullOpsDecorator.* import cc.CheckCaptures import config.Config +import config.MigrationVersion import scala.annotation.constructorOnly import dotty.tools.dotc.rewrites.Rewrites @@ -445,8 +446,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if !symsMatch && !suppressErrors then report.errorOrMigrationWarning( AmbiguousReference(name, Definition, Inheritance, prevCtx)(using outer), - pos, from = `3.0`) - if sourceVersion.isMigrating then + pos, MigrationVersion.Scala2to3) + if MigrationVersion.Scala2to3.needsPatch then patch(Span(pos.span.start), if prevCtx.owner == refctx.owner.enclosingClass then "this." else s"${prevCtx.owner.name}.this.") @@ -2987,8 +2988,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => val recovered = typed(qual)(using ctx.fresh.setExploreTyperState()) val msg = OnlyFunctionsCanBeFollowedByUnderscore(recovered.tpe.widen, tree) - report.errorOrMigrationWarning(msg, tree.srcPos, from = `3.0`) - if sourceVersion.isMigrating then + report.errorOrMigrationWarning(msg, tree.srcPos, MigrationVersion.Scala2to3) + if MigrationVersion.Scala2to3.needsPatch then // Under -rewrite, patch `x _` to `(() => x)` msg.actions .headOption @@ -3008,13 +3009,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if ((prefix ++ suffix).isEmpty) "simply leave out the trailing ` _`" else s"use `$prefix$suffix` instead" def rewrite = Message.rewriteNotice("This construct", `3.4-migration`) - report.gradualErrorOrMigrationWarning( + report.errorOrMigrationWarning( em"""The syntax ` _` is no longer supported; |you can $remedy$rewrite""", tree.srcPos, - warnFrom = `3.4`, - errorFrom = future) - if sourceVersion.isMigrating && sourceVersion.isAtLeast(`3.4-migration`) then + MigrationVersion.FunctionUnderscore) + if MigrationVersion.FunctionUnderscore.needsPatch then patch(Span(tree.span.start), prefix) patch(Span(qual.span.end, tree.span.end), suffix) @@ -3143,7 +3143,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else "" val namePos = tree.sourcePos.withSpan(tree.nameSpan) report.errorOrMigrationWarning( - em"`?` is not a valid type name$addendum", namePos, from = `3.0`) + em"`?` is not a valid type name$addendum", namePos, MigrationVersion.Scala2to3) if tree.isClassDef then typedClassDef(tree, sym.asClass)(using ctx.localContext(tree, sym)) else diff --git a/tests/neg/i11567.scala b/tests/neg/i11567.scala index a6eed7cf0271..89fc1092c04b 100644 --- a/tests/neg/i11567.scala +++ b/tests/neg/i11567.scala @@ -1,4 +1,4 @@ -import language.`future-migration` +import language.future class Test object Test { def foo[A <% Test](x: A) = x // error