diff --git a/project/MimaFilters.scala b/project/MimaFilters.scala
index cd2cd01e208..9f75e4349ad 100644
--- a/project/MimaFilters.scala
+++ b/project/MimaFilters.scala
@@ -39,6 +39,8 @@ object MimaFilters extends AutoPlugin {
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.concurrent.impl.FutureConvertersImpl#P.accept"),
ProblemFilters.exclude[IncompatibleMethTypeProblem]("scala.concurrent.impl.FutureConvertersImpl#P.andThen"),
+ // PR 10406
+ ProblemFilters.exclude[DirectMissingMethodProblem]("scala.reflect.runtime.JavaUniverse#PerRunReporting.deprecationWarning"),
)
override val buildSettings = Seq(
diff --git a/src/compiler/scala/reflect/macros/contexts/Parsers.scala b/src/compiler/scala/reflect/macros/contexts/Parsers.scala
index 7ac893d1378..2dfa8322503 100644
--- a/src/compiler/scala/reflect/macros/contexts/Parsers.scala
+++ b/src/compiler/scala/reflect/macros/contexts/Parsers.scala
@@ -32,7 +32,7 @@ trait Parsers {
})
val tree = gen.mkTreeOrBlock(parser.parseStatsOrPackages())
sreporter.infos.foreach {
- case Info(pos, msg, Reporter.ERROR) => throw ParseException(pos, msg)
+ case Info(pos, msg, Reporter.ERROR, _) => throw ParseException(pos, msg)
case _ =>
}
tree
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala
index fe971938438..156d5e177b4 100644
--- a/src/compiler/scala/tools/nsc/CompilationUnits.scala
+++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala
@@ -47,6 +47,9 @@ trait CompilationUnits { global: Global =>
def freshTermName(prefix: String = nme.FRESH_TERM_NAME_PREFIX) = global.freshTermName(prefix)
def freshTypeName(prefix: String) = global.freshTypeName(prefix)
+ def sourceAt(pos: Position): String =
+ if (pos.start < pos.end) new String(source.content.slice(pos.start, pos.end)) else ""
+
/** the content of the compilation unit in tree form */
var body: Tree = EmptyTree
diff --git a/src/compiler/scala/tools/nsc/Parsing.scala b/src/compiler/scala/tools/nsc/Parsing.scala
index 7d48e27678d..b78f8feab1f 100644
--- a/src/compiler/scala/tools/nsc/Parsing.scala
+++ b/src/compiler/scala/tools/nsc/Parsing.scala
@@ -14,6 +14,7 @@ package scala
package tools.nsc
import scala.reflect.internal.Positions
+import scala.reflect.internal.util.CodeAction
/** Similar to Reporting: gather global functionality specific to parsing.
*/
@@ -35,8 +36,8 @@ trait Parsing { self : Positions with Reporting =>
}
def incompleteHandled = incompleteHandler != null
- def incompleteInputError(pos: Position, msg: String): Unit =
+ def incompleteInputError(pos: Position, msg: String, actions: List[CodeAction] = Nil): Unit =
if (incompleteHandled) incompleteHandler(pos, msg)
- else reporter.error(pos, msg)
+ else reporter.error(pos, msg, actions)
}
}
diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala
index f5a96415803..7c0b33efc5c 100644
--- a/src/compiler/scala/tools/nsc/Reporting.scala
+++ b/src/compiler/scala/tools/nsc/Reporting.scala
@@ -19,7 +19,7 @@ import scala.annotation.nowarn
import scala.collection.mutable
import scala.reflect.internal
import scala.reflect.internal.util.StringOps.countElementsAsString
-import scala.reflect.internal.util.{NoSourceFile, Position, SourceFile}
+import scala.reflect.internal.util.{CodeAction, NoSourceFile, Position, SourceFile}
import scala.tools.nsc.Reporting.Version.{NonParseableVersion, ParseableVersion}
import scala.tools.nsc.Reporting._
import scala.tools.nsc.settings.NoScalaVersion
@@ -99,7 +99,7 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio
source <- suppressions.keysIterator.toList
sups <- suppressions.remove(source)
sup <- sups.reverse
- } if (!sup.used && !sup.synthetic) issueWarning(Message.Plain(sup.annotPos, "@nowarn annotation does not suppress any warnings", WarningCategory.UnusedNowarn, ""))
+ } if (!sup.used && !sup.synthetic) issueWarning(Message.Plain(sup.annotPos, "@nowarn annotation does not suppress any warnings", WarningCategory.UnusedNowarn, "", Nil))
}
def reportSuspendedMessages(unit: CompilationUnit): Unit = {
@@ -120,16 +120,16 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio
private def issueWarning(warning: Message): Unit = {
def verbose = warning match {
- case Message.Deprecation(_, msg, site, origin, version) => s"[${warning.category.name} @ $site | origin=$origin | version=${version.filterString}] $msg"
- case Message.Origin(_, msg, category, site, origin @ _) => s"[${category.name} @ $site] $msg" // origin text is obvious at caret
- case Message.Plain(_, msg, category, site) => s"[${category.name} @ $site] $msg"
+ case Message.Deprecation(_, msg, site, origin, version, _) => s"[${warning.category.name} @ $site | origin=$origin | version=${version.filterString}] $msg"
+ case Message.Origin(_, msg, category, site, origin @ _, _) => s"[${category.name} @ $site] $msg" // origin text is obvious at caret
+ case Message.Plain(_, msg, category, site, _) => s"[${category.name} @ $site] $msg"
}
wconf.action(warning) match {
- case Action.Error => reporter.error(warning.pos, warning.msg)
- case Action.Warning => reporter.warning(warning.pos, warning.msg)
- case Action.WarningVerbose => reporter.warning(warning.pos, verbose)
- case Action.Info => reporter.echo(warning.pos, warning.msg)
- case Action.InfoVerbose => reporter.echo(warning.pos, verbose)
+ case Action.Error => reporter.error(warning.pos, warning.msg, warning.actions)
+ case Action.Warning => reporter.warning(warning.pos, warning.msg, warning.actions)
+ case Action.WarningVerbose => reporter.warning(warning.pos, verbose, warning.actions)
+ case Action.Info => reporter.echo(warning.pos, warning.msg, warning.actions)
+ case Action.InfoVerbose => reporter.echo(warning.pos, verbose, warning.actions)
case a @ (Action.WarningSummary | Action.InfoSummary) =>
val m = summaryMap(a, warning.category.summaryCategory)
if (!m.contains(warning.pos)) m.addOne((warning.pos, warning))
@@ -206,9 +206,12 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio
impl(sym)
} else ""
- override def deprecationWarning(pos: Position, msg: String, since: String, site: String, origin: String): Unit =
- issueIfNotSuppressed(Message.Deprecation(pos, msg, site, origin, Version.fromString(since)))
+ override def deprecationWarning(pos: Position, msg: String, since: String, site: String, origin: String, actions: List[CodeAction] = Nil): Unit =
+ issueIfNotSuppressed(Message.Deprecation(pos, msg, site, origin, Version.fromString(since), actions))
+ // multiple overloads cannot use default args
+ def deprecationWarning(pos: Position, origin: Symbol, site: Symbol, msg: String, since: String, actions: List[CodeAction]): Unit =
+ deprecationWarning(pos, msg, since, siteName(site), siteName(origin), actions)
def deprecationWarning(pos: Position, origin: Symbol, site: Symbol, msg: String, since: String): Unit =
deprecationWarning(pos, msg, since, siteName(site), siteName(origin))
@@ -268,17 +271,19 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio
} else warning(pos, msg, featureCategory(featureTrait.nameString), site)
}
- // Used in the optimizer where we don't have no symbols, the site string is created from the class internal name and method name.
+ // Used in the optimizer where we don't have symbols, the site string is created from the class internal name and method name.
+ def warning(pos: Position, msg: String, category: WarningCategory, site: String, actions: List[CodeAction]): Unit =
+ issueIfNotSuppressed(Message.Plain(pos, msg, category, site, actions))
def warning(pos: Position, msg: String, category: WarningCategory, site: String): Unit =
- issueIfNotSuppressed(Message.Plain(pos, msg, category, site))
+ warning(pos, msg, category, site, Nil)
// Preferred over the overload above whenever a site symbol is available
- def warning(pos: Position, msg: String, category: WarningCategory, site: Symbol): Unit =
- warning(pos, msg, category, siteName(site))
+ def warning(pos: Position, msg: String, category: WarningCategory, site: Symbol, actions: List[CodeAction] = Nil): Unit =
+ warning(pos, msg, category, siteName(site), actions)
// Provide an origin for the warning.
def warning(pos: Position, msg: String, category: WarningCategory, site: Symbol, origin: String): Unit =
- issueIfNotSuppressed(Message.Origin(pos, msg, category, siteName(site), origin))
+ issueIfNotSuppressed(Message.Origin(pos, msg, category, siteName(site), origin, actions = Nil))
// used by Global.deprecationWarnings, which is used by sbt
def deprecationWarnings: List[(Position, String)] = summaryMap(Action.WarningSummary, WarningCategory.Deprecation).toList.map(p => (p._1, p._2.msg))
@@ -320,17 +325,18 @@ object Reporting {
def msg: String
def category: WarningCategory
def site: String // sym.FullName of the location where the warning is positioned, may be empty
+ def actions: List[CodeAction]
}
object Message {
// an ordinary Message has a `category` for filtering and the `site` where it was issued
- final case class Plain(pos: Position, msg: String, category: WarningCategory, site: String) extends Message
+ final case class Plain(pos: Position, msg: String, category: WarningCategory, site: String, actions: List[CodeAction]) extends Message
// a Plain message with an `origin` which should not be empty. For example, the origin of an unused import is the fully-qualified selection
- final case class Origin(pos: Position, msg: String, category: WarningCategory, site: String, origin: String) extends Message
+ final case class Origin(pos: Position, msg: String, category: WarningCategory, site: String, origin: String, actions: List[CodeAction]) extends Message
// `site` and `origin` may be empty
- final case class Deprecation(pos: Position, msg: String, site: String, origin: String, since: Version) extends Message {
+ final case class Deprecation(pos: Position, msg: String, site: String, origin: String, since: Version, actions: List[CodeAction]) extends Message {
def category: WarningCategory = WarningCategory.Deprecation
}
}
@@ -538,7 +544,7 @@ object Reporting {
final case class DeprecatedSince(comp: Int, version: ParseableVersion) extends MessageFilter {
def matches(message: Message): Boolean = message match {
- case Message.Deprecation(_, _, _, _, mv: ParseableVersion) =>
+ case Message.Deprecation(_, _, _, _, mv: ParseableVersion, _) =>
if (comp == -1) mv.smaller(version)
else if (comp == 0) mv.same(version)
else mv.greater(version)
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index 5b97c782096..554a173d277 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -19,7 +19,14 @@ package ast.parser
import scala.annotation.tailrec
import scala.collection.mutable, mutable.ListBuffer
import scala.reflect.internal.{ModifierFlags => Flags, Precedence}
-import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Position, SourceFile}
+import scala.reflect.internal.util.{
+ CodeAction,
+ FreshNameCreator,
+ ListOfNil,
+ Position,
+ SourceFile,
+ TextEdit,
+}
import Tokens._
import scala.tools.nsc.Reporting.WarningCategory
@@ -48,7 +55,7 @@ trait ParsersCommon extends ScannersCommon {
*/
abstract class ParserCommon {
val in: ScannerCommon
- def deprecationWarning(off: Offset, msg: String, since: String): Unit
+ def deprecationWarning(off: Offset, msg: String, since: String, actions: List[CodeAction] = Nil): Unit
def accept(token: Token): Int
/** Methods inParensOrError and similar take a second argument which, should
@@ -175,11 +182,11 @@ self =>
def unit = global.currentUnit
// suppress warnings; silent abort on errors
- def warning(offset: Offset, msg: String, category: WarningCategory): Unit = ()
- def deprecationWarning(offset: Offset, msg: String, since: String): Unit = ()
+ def warning(offset: Offset, msg: String, category: WarningCategory, actions: List[CodeAction]): Unit = ()
+ def deprecationWarning(offset: Offset, msg: String, since: String, actions: List[CodeAction]): Unit = ()
- def syntaxError(offset: Offset, msg: String): Unit = throw new MalformedInput(offset, msg)
- def incompleteInputError(msg: String): Unit = throw new MalformedInput(source.content.length - 1, msg)
+ def syntaxError(offset: Offset, msg: String, actions: List[CodeAction]): Unit = throw new MalformedInput(offset, msg)
+ def incompleteInputError(msg: String, actions: List[CodeAction]): Unit = throw new MalformedInput(source.content.length - 1, msg)
object symbXMLBuilder extends SymbolicXMLBuilder(this, preserveWS = true) { // DEBUG choices
val global: self.global.type = self.global
@@ -225,12 +232,12 @@ self =>
override def newScanner() = new UnitScanner(unit, patches)
- override def warning(offset: Offset, msg: String, category: WarningCategory): Unit =
- runReporting.warning(o2p(offset), msg, category, site = "")
+ override def warning(offset: Offset, msg: String, category: WarningCategory, actions: List[CodeAction]): Unit =
+ runReporting.warning(o2p(offset), msg, category, site = "", actions)
- override def deprecationWarning(offset: Offset, msg: String, since: String): Unit =
+ override def deprecationWarning(offset: Offset, msg: String, since: String, actions: List[CodeAction]): Unit =
// we cannot provide a `site` in the parser, there's no context telling us where we are
- runReporting.deprecationWarning(o2p(offset), msg, since, site = "", origin = "")
+ runReporting.deprecationWarning(o2p(offset), msg, since, site = "", origin = "", actions)
private var smartParsing = false
@inline private def withSmartParsing[T](body: => T): T = {
@@ -241,20 +248,20 @@ self =>
}
def withPatches(patches: List[BracePatch]): UnitParser = new UnitParser(unit, patches)
- val syntaxErrors = new ListBuffer[(Int, String)]
+ val syntaxErrors = new ListBuffer[(Int, String, List[CodeAction])]
def showSyntaxErrors() =
- for ((offset, msg) <- syntaxErrors)
- reporter.error(o2p(offset), msg)
+ for ((offset, msg, actions) <- syntaxErrors)
+ reporter.error(o2p(offset), msg, actions)
- override def syntaxError(offset: Offset, msg: String): Unit = {
- if (smartParsing) syntaxErrors += ((offset, msg))
- else reporter.error(o2p(offset), msg)
+ override def syntaxError(offset: Offset, msg: String, actions: List[CodeAction]): Unit = {
+ if (smartParsing) syntaxErrors += ((offset, msg, actions))
+ else reporter.error(o2p(offset), msg, actions)
}
- override def incompleteInputError(msg: String): Unit = {
+ override def incompleteInputError(msg: String, actions: List[CodeAction]): Unit = {
val offset = source.content.length - 1
- if (smartParsing) syntaxErrors += ((offset, msg))
- else currentRun.parsing.incompleteInputError(o2p(offset), msg)
+ if (smartParsing) syntaxErrors += ((offset, msg, actions))
+ else currentRun.parsing.incompleteInputError(o2p(offset), msg, actions)
}
/** parse unit. If there are unbalanced braces,
@@ -576,50 +583,61 @@ self =>
in.nextToken()
}
}
- def warning(offset: Offset, msg: String, category: WarningCategory): Unit
- def incompleteInputError(msg: String): Unit
- def syntaxError(offset: Offset, msg: String): Unit
- private def syntaxError(pos: Position, msg: String, skipIt: Boolean): Unit =
- syntaxError(pos pointOrElse in.offset, msg, skipIt)
- def syntaxError(msg: String, skipIt: Boolean): Unit =
- syntaxError(in.offset, msg, skipIt)
+ def warning(offset: Offset, msg: String, category: WarningCategory, actions: List[CodeAction] = Nil): Unit
+
+ def incompleteInputError(msg: String, actions: List[CodeAction] = Nil): Unit
+
+ def syntaxError(offset: Offset, msg: String, actions: List[CodeAction] = Nil): Unit
+
+ private def syntaxError(pos: Position, msg: String, skipIt: Boolean): Unit = syntaxError(pos, msg, skipIt, Nil)
+ private def syntaxError(pos: Position, msg: String, skipIt: Boolean, actions: List[CodeAction]): Unit =
+ syntaxError(pos pointOrElse in.offset, msg, skipIt, actions)
- def syntaxError(offset: Offset, msg: String, skipIt: Boolean): Unit = {
+ def syntaxError(msg: String, skipIt: Boolean): Unit = syntaxError(msg, skipIt, Nil)
+ def syntaxError(msg: String, skipIt: Boolean, actions: List[CodeAction]): Unit =
+ syntaxError(in.offset, msg, skipIt, actions)
+
+ def syntaxError(offset: Offset, msg: String, skipIt: Boolean): Unit = syntaxError(offset, msg, skipIt, Nil)
+ def syntaxError(offset: Offset, msg: String, skipIt: Boolean, actions: List[CodeAction]): Unit = {
if (offset > lastErrorOffset) {
- syntaxError(offset, msg)
+ syntaxError(offset, msg, actions)
lastErrorOffset = in.offset // no more errors on this token.
}
if (skipIt)
skip(UNDEF)
}
- def warning(msg: String, category: WarningCategory): Unit = warning(in.offset, msg, category)
+ def warning(msg: String, category: WarningCategory): Unit = warning(in.offset, msg, category, Nil)
+ def warning(msg: String, category: WarningCategory, actions: List[CodeAction]): Unit = warning(in.offset, msg, category, actions)
- def syntaxErrorOrIncomplete(msg: String, skipIt: Boolean): Unit = {
+ def syntaxErrorOrIncomplete(msg: String, skipIt: Boolean, actions: List[CodeAction] = Nil): Unit = {
if (in.token == EOF)
- incompleteInputError(msg)
+ incompleteInputError(msg, actions)
else
- syntaxError(in.offset, msg, skipIt)
+ syntaxError(in.offset, msg, skipIt, actions)
}
- def syntaxErrorOrIncompleteAnd[T](msg: String, skipIt: Boolean)(and: T): T = {
- syntaxErrorOrIncomplete(msg, skipIt)
+ def syntaxErrorOrIncompleteAnd[T](msg: String, skipIt: Boolean, actions: List[CodeAction] = Nil)(and: T): T = {
+ syntaxErrorOrIncomplete(msg, skipIt, actions)
and
}
// warn under -Xsource:3
- def migrationWarning(offset: Offset, msg: String, since: String): Unit =
- if (currentRun.isScala3) warning(offset, msg, WarningCategory.Scala3Migration)
+ def migrationWarning(offset: Offset, msg: String, since: String, actions: List[CodeAction] = Nil): Unit =
+ if (currentRun.isScala3)
+ warning(offset, msg, WarningCategory.Scala3Migration, actions)
// warn under -Xsource:3, otherwise deprecation
- def hardMigrationWarning(offset: Offset, msg: String, since: String): Unit =
- if (currentRun.isScala3) warning(offset, msg, WarningCategory.Scala3Migration)
- else deprecationWarning(offset, msg, since)
+ def hardMigrationWarning(offset: Offset, msg: String, since: String, actions: List[CodeAction] = Nil): Unit =
+ if (currentRun.isScala3) warning(offset, msg, WarningCategory.Scala3Migration, actions)
+ else deprecationWarning(offset, msg, since, actions)
// deprecation or migration under -Xsource:3, with different messages
+ def hardMigrationWarning(offset: Offset, depr: => String, migr: => String, since: String, actions: List[CodeAction]): Unit =
+ if (currentRun.isScala3) warning(offset, migr, WarningCategory.Scala3Migration, actions)
+ else deprecationWarning(offset, depr, since, actions)
def hardMigrationWarning(offset: Offset, depr: => String, migr: => String, since: String): Unit =
- if (currentRun.isScala3) warning(offset, migr, WarningCategory.Scala3Migration)
- else deprecationWarning(offset, depr, since)
+ hardMigrationWarning(offset, depr, migr, since, Nil)
def expectedMsgTemplate(exp: String, fnd: String) = s"$exp expected but $fnd found."
def expectedMsg(token: Token): String =
@@ -797,7 +815,11 @@ self =>
val msg = "parentheses are required around the parameter of a lambda"
val wrn = sm"""|$msg
|Use '-Wconf:msg=lambda-parens:s' to silence this warning."""
- migrationWarning(tree.pos.point, wrn, "2.13.11")
+ def actions =
+ if (tree.pos.isRange)
+ List(CodeAction("lambda parameter", Some(msg), List(TextEdit(tree.pos, s"(${unit.sourceAt(tree.pos)})"))))
+ else Nil
+ migrationWarning(tree.pos.point, wrn, "2.13.11", actions)
List(convertToParam(tree))
case _ => List(convertToParam(tree))
}
@@ -2064,6 +2086,7 @@ self =>
in.skipCASE()
val hasVal = in.token == VAL
+ val valOffset = in.offset
if (hasVal)
in.nextToken()
@@ -2072,12 +2095,17 @@ self =>
val hasEq = in.token == EQUALS
if (hasVal) {
+ def actions = {
+ val pos = r2p(valOffset, valOffset, valOffset + 4)
+ if (unit.sourceAt(pos) != "val ") Nil else
+ List(CodeAction("val in for comprehension", None, List(TextEdit(pos, ""))))
+ }
def msg(what: String, instead: String): String = s"`val` keyword in for comprehension is $what: $instead"
if (hasEq) {
val without = "instead, bind the value without `val`"
- hardMigrationWarning(in.offset, msg("deprecated", without), msg("unsupported", without), "2.10.0")
+ hardMigrationWarning(in.offset, msg("deprecated", without), msg("unsupported", without), "2.10.0", actions)
}
- else syntaxError(in.offset, msg("unsupported", "just remove `val`"))
+ else syntaxError(in.offset, msg("unsupported", "just remove `val`"), actions)
}
if (hasEq && eqOK && !hasCase) in.nextToken()
@@ -2978,16 +3006,18 @@ self =>
var restype = fromWithinReturnType(typedOpt())
def msg(what: String, instead: String) =
s"procedure syntax is $what: instead, add `$instead` to explicitly declare `$name`'s return type"
+ def declActions = List(CodeAction("procedure syntax (decl)", None, List(TextEdit(o2p(in.lastOffset), ": Unit"))))
+ def defnActions = List(CodeAction("procedure syntax (defn)", None, List(TextEdit(o2p(in.lastOffset), ": Unit ="))))
val rhs =
if (isStatSep || in.token == RBRACE) {
if (restype.isEmpty) {
- hardMigrationWarning(in.lastOffset, msg("deprecated", ": Unit"), msg("unsupported", ": Unit"), "2.13.0")
+ hardMigrationWarning(in.lastOffset, msg("deprecated", ": Unit"), msg("unsupported", ": Unit"), "2.13.0", declActions)
restype = scalaUnitConstr
}
newmods |= Flags.DEFERRED
EmptyTree
} else if (restype.isEmpty && in.token == LBRACE) {
- hardMigrationWarning(in.offset, msg("deprecated", ": Unit ="), msg("unsupported", ": Unit ="), "2.13.0")
+ hardMigrationWarning(in.offset, msg("deprecated", ": Unit ="), msg("unsupported", ": Unit ="), "2.13.0", defnActions)
restype = scalaUnitConstr
blockExpr()
} else {
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index 51d1c0a4be1..3a7d180416c 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -74,7 +74,7 @@ trait ScannersCommon {
def error(off: Offset, msg: String): Unit
def incompleteInputError(off: Offset, msg: String): Unit
def warning(off: Offset, msg: String, category: WarningCategory): Unit
- def deprecationWarning(off: Offset, msg: String, since: String): Unit
+ def deprecationWarning(off: Offset, msg: String, since: String, actions: List[CodeAction] = Nil): Unit
// advance past COMMA NEWLINE RBRACE (to whichever token is the matching close bracket)
def skipTrailingComma(right: Token): Boolean = false
@@ -1402,7 +1402,8 @@ trait Scanners extends ScannersCommon {
/** generate an error at the current token offset */
def syntaxError(msg: String): Unit = syntaxError(offset, msg)
- def deprecationWarning(msg: String, since: String): Unit = deprecationWarning(offset, msg, since)
+ def deprecationWarning(msg: String, since: String): Unit = deprecationWarning(msg, since, Nil)
+ def deprecationWarning(msg: String, since: String, actions: List[CodeAction]): Unit = deprecationWarning(offset, msg, since, actions)
/** signal an error where the input ended in the middle of a token */
def incompleteInputError(msg: String): Unit = {
@@ -1576,7 +1577,7 @@ trait Scanners extends ScannersCommon {
// suppress warnings, throw exception on errors
def warning(off: Offset, msg: String, category: WarningCategory): Unit = ()
- def deprecationWarning(off: Offset, msg: String, since: String): Unit = ()
+ def deprecationWarning(off: Offset, msg: String, since: String, actions: List[CodeAction]): Unit = ()
def error(off: Offset, msg: String): Unit = throw new MalformedInput(off, msg)
def incompleteInputError(off: Offset, msg: String): Unit = throw new MalformedInput(off, msg)
}
@@ -1586,10 +1587,14 @@ trait Scanners extends ScannersCommon {
class UnitScanner(val unit: CompilationUnit, patches: List[BracePatch]) extends SourceFileScanner(unit.source) {
def this(unit: CompilationUnit) = this(unit, List())
- override def warning(off: Offset, msg: String, category: WarningCategory): Unit = runReporting.warning(unit.position(off), msg, category, site = "")
- override def deprecationWarning(off: Offset, msg: String, since: String) = runReporting.deprecationWarning(unit.position(off), msg, since, site = "", origin = "")
- override def error(off: Offset, msg: String) = reporter.error(unit.position(off), msg)
- override def incompleteInputError(off: Offset, msg: String) = currentRun.parsing.incompleteInputError(unit.position(off), msg)
+ override def warning(off: Offset, msg: String, category: WarningCategory): Unit =
+ runReporting.warning(unit.position(off), msg, category, site = "")
+ override def deprecationWarning(off: Offset, msg: String, since: String, actions: List[CodeAction]) =
+ runReporting.deprecationWarning(unit.position(off), msg, since, site = "", origin = "", actions)
+ override def error(off: Offset, msg: String) =
+ reporter.error(unit.position(off), msg)
+ override def incompleteInputError(off: Offset, msg: String) =
+ currentRun.parsing.incompleteInputError(unit.position(off), msg)
private var bracePatches: List[BracePatch] = patches
diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
index ffe71142df0..f54ce5feca9 100644
--- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
+++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
@@ -22,7 +22,7 @@ import scala.annotation._
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.language.implicitConversions
-import scala.reflect.internal.util.{ListOfNil, Position}
+import scala.reflect.internal.util.{CodeAction, ListOfNil, Position}
import scala.tools.nsc.Reporting.WarningCategory
import scala.util.chaining._
@@ -38,7 +38,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
def freshName(prefix: String): Name = freshTermName(prefix)
def freshTermName(prefix: String): TermName = unit.freshTermName(prefix)
def freshTypeName(prefix: String): TypeName = unit.freshTypeName(prefix)
- def deprecationWarning(off: Int, msg: String, since: String) = runReporting.deprecationWarning(off, msg, since, site = "", origin = "")
+ def deprecationWarning(off: Int, msg: String, since: String, actions: List[CodeAction]) = runReporting.deprecationWarning(off, msg, since, site = "", origin = "", actions)
implicit def i2p(offset : Int) : Position = Position.offset(unit.source, offset)
def warning(pos : Int, msg : String) : Unit = runReporting.warning(pos, msg, WarningCategory.JavaSource, site = "")
def syntaxError(pos: Int, msg: String) : Unit = reporter.error(pos, msg)
diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala
index 0733376c029..8e80da83af3 100644
--- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala
+++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala
@@ -1031,7 +1031,7 @@ trait JavaScanners extends ast.parser.ScannersCommon {
def error(pos: Int, msg: String) = reporter.error(pos, msg)
def incompleteInputError(pos: Int, msg: String) = currentRun.parsing.incompleteInputError(pos, msg)
def warning(pos: Int, msg: String, category: WarningCategory) = runReporting.warning(pos, msg, category, site = "")
- def deprecationWarning(pos: Int, msg: String, since: String) = runReporting.deprecationWarning(pos, msg, since, site = "", origin = "")
+ def deprecationWarning(pos: Int, msg: String, since: String, actions: List[CodeAction]) = runReporting.deprecationWarning(pos, msg, since, site = "", origin = "", actions)
implicit def g2p(pos: Int): Position = Position.offset(unit.source, pos)
}
}
diff --git a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala
index 9dafc2426d7..ce0bd318638 100644
--- a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala
@@ -15,14 +15,14 @@ package tools.nsc
package reporters
import java.io.{BufferedReader, PrintWriter}
-import scala.reflect.internal.util.Position
+import scala.reflect.internal.util.{CodeAction, Position}
/** This class implements a Reporter that displays messages on a text console. */
class ConsoleReporter(val settings: Settings, val reader: BufferedReader, val writer: PrintWriter, val echoWriter: PrintWriter) extends FilteringReporter with PrintReporter {
def this(settings: Settings) = this(settings, Console.in, new PrintWriter(Console.err, true), new PrintWriter(Console.out, true))
def this(settings: Settings, reader: BufferedReader, writer: PrintWriter) = this(settings, reader, writer, writer)
- def doReport(pos: Position, msg: String, severity: Severity): Unit = display(pos, msg, severity)
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit = display(pos, msg, severity)
override def finish(): Unit = {
import reflect.internal.util.StringOps.countElementsAsString
diff --git a/src/compiler/scala/tools/nsc/reporters/ForwardingReporter.scala b/src/compiler/scala/tools/nsc/reporters/ForwardingReporter.scala
index bca541105a5..61a7751c737 100644
--- a/src/compiler/scala/tools/nsc/reporters/ForwardingReporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/ForwardingReporter.scala
@@ -12,7 +12,7 @@
package scala.tools.nsc.reporters
import scala.reflect.internal.settings.MutableSettings
-import scala.reflect.internal.util.Position
+import scala.reflect.internal.util.{CodeAction, Position}
import scala.tools.nsc.Settings
@@ -20,7 +20,7 @@ import scala.tools.nsc.Settings
* customize error reporting.
* {{{
* val myReporter = new ForwardingReporter(global.reporter) {
- * override def doReport(pos: Position, msg: String, severity: Severity): Unit = { ... }
+ * override def doReport(pos: Position, msg: String, severity: Severity, actions: List[Action]): Unit = { ... }
* }
* global.reporter = myReporter
* }}}
@@ -28,7 +28,8 @@ import scala.tools.nsc.Settings
class ForwardingReporter(delegate: FilteringReporter) extends FilteringReporter {
def settings: Settings = delegate.settings
- def doReport(pos: Position, msg: String, severity: Severity): Unit = delegate.doReport(pos, msg, severity)
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit =
+ delegate.doReport(pos, msg, severity, actions)
override def filter(pos: Position, msg: String, severity: Severity): Int = delegate.filter(pos, msg, severity)
@@ -56,7 +57,8 @@ class ForwardingReporter(delegate: FilteringReporter) extends FilteringReporter
* maxerrs and do position filtering.
*/
class MakeFilteringForwardingReporter(delegate: Reporter, val settings: Settings) extends FilteringReporter {
- def doReport(pos: Position, msg: String, severity: Severity): Unit = delegate.nonProtectedInfo0(pos, msg, severity)
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit =
+ delegate.doReport(pos, msg, severity, actions)
override def increment(severity: Severity): Unit = delegate.increment(severity)
diff --git a/src/compiler/scala/tools/nsc/reporters/NoReporter.scala b/src/compiler/scala/tools/nsc/reporters/NoReporter.scala
index b59a0444d13..95bec3701ef 100644
--- a/src/compiler/scala/tools/nsc/reporters/NoReporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/NoReporter.scala
@@ -12,11 +12,11 @@
package scala.tools.nsc.reporters
-import scala.reflect.internal.util.Position
+import scala.reflect.internal.util.{CodeAction, Position}
import scala.tools.nsc.Settings
/** A reporter that ignores reports.
*/
class NoReporter(val settings: Settings) extends FilteringReporter {
- def doReport(pos: Position, msg: String, severity: Severity): Unit = ()
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit = ()
}
diff --git a/src/compiler/scala/tools/nsc/reporters/Reporter.scala b/src/compiler/scala/tools/nsc/reporters/Reporter.scala
index 5dde7649bbe..9510d878026 100644
--- a/src/compiler/scala/tools/nsc/reporters/Reporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/Reporter.scala
@@ -13,7 +13,7 @@
package scala.tools.nsc
package reporters
-import scala.annotation.unused
+import scala.annotation.{nowarn, unused}
import scala.collection.mutable
import scala.reflect.internal
import scala.reflect.internal.util.{Position, ScalaClassLoader}
@@ -27,10 +27,6 @@ abstract class Reporter extends internal.Reporter {
@deprecated("Use echo, as internal.Reporter does not support unforced info", since="2.13.0")
final def info(pos: Position, msg: String, @unused force: Boolean): Unit = info0(pos, msg, INFO, force = true)
- // allow calling info0 in MakeFilteringForwardingReporter
- private[reporters] final def nonProtectedInfo0(pos: Position, msg: String, severity: Severity): Unit =
- info0(pos, msg, severity, force = true)
-
// overridden by sbt, IDE -- should not be in the reporting interface
// (IDE receives comments from ScaladocAnalyzer using this hook method)
// TODO: IDE should override a hook method in the parser instead
@@ -97,11 +93,19 @@ object Reporter {
abstract class FilteringReporter extends Reporter {
def settings: Settings
+ @deprecatedOverriding("override the `doReport` overload (defined in reflect.internal.Reporter) instead", "2.13.12")
+ @deprecated("use the `doReport` overload instead", "2.13.12")
+ def doReport(pos: Position, msg: String, severity: Severity): Unit = doReport(pos, msg, severity, Nil)
+
// this should be the abstract method all the way up in reflect.internal.Reporter, but sbt compat
- def doReport(pos: Position, msg: String, severity: Severity): Unit
+ // the abstract override is commented-out to maintain binary compatibility for FilteringReporter subclasses
+ // override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit
- @deprecatedOverriding("override doReport instead", "2.13.1") // overridden in scalameta for example
- protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = doReport(pos, msg, severity)
+ @deprecatedOverriding("override `doReport` instead", "2.13.1") // overridden in scalameta for example
+ @nowarn("cat=deprecation")
+ protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit =
+ // call the deprecated overload to support existing FilteringReporter subclasses (they override that overload)
+ doReport(pos, msg, severity)
private lazy val positions = mutable.Map[Position, Severity]() withDefaultValue INFO
private lazy val messages = mutable.Map[Position, List[String]]() withDefaultValue Nil
@@ -118,8 +122,8 @@ abstract class FilteringReporter extends Reporter {
}
// Invoked when an error or warning is filtered by position.
@inline def suppress = {
- if (settings.prompt.value) doReport(pos, msg, severity)
- else if (settings.isDebug) doReport(pos, s"[ suppressed ] $msg", severity)
+ if (settings.prompt.value) doReport(pos, msg, severity, Nil)
+ else if (settings.isDebug) doReport(pos, s"[ suppressed ] $msg", severity, Nil)
Suppress
}
if (!duplicateOk(pos, severity, msg)) suppress else if (!maxOk) Count else Display
diff --git a/src/compiler/scala/tools/nsc/reporters/StoreReporter.scala b/src/compiler/scala/tools/nsc/reporters/StoreReporter.scala
index f5e5c6b414f..07d3545e3c2 100644
--- a/src/compiler/scala/tools/nsc/reporters/StoreReporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/StoreReporter.scala
@@ -16,7 +16,7 @@ package reporters
import scala.annotation.unchecked.uncheckedStable
import scala.collection.mutable
import scala.reflect.internal.Reporter.Severity
-import scala.reflect.internal.util.Position
+import scala.reflect.internal.util.{CodeAction, Position}
/** This class implements a Reporter that stores its reports in the set `infos`. */
class StoreReporter(val settings: Settings) extends FilteringReporter {
@@ -31,8 +31,10 @@ class StoreReporter(val settings: Settings) extends FilteringReporter {
val infos = new mutable.LinkedHashSet[StoreReporter.Info]
- def doReport(pos: Position, msg: String, severity: Severity): Unit =
- infos += StoreReporter.Info(pos, msg, severity)
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit = {
+ val info = StoreReporter.Info(pos, msg, severity, actions)
+ infos += info
+ }
override def reset(): Unit = {
super.reset()
@@ -40,7 +42,7 @@ class StoreReporter(val settings: Settings) extends FilteringReporter {
}
}
object StoreReporter {
- case class Info(pos: Position, msg: String, severity: Severity) {
- override def toString: String = s"pos: $pos $msg $severity"
+ case class Info(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]) {
+ override def toString: String = s"pos: $pos $msg $severity${if (actions.isEmpty) "" else " " + actions}"
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index 8d7a2581407..c6051f7a41a 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -15,7 +15,6 @@ package typechecker
import scala.reflect.internal.util.StringOps.{countAsString, countElementsAsString}
import java.lang.System.{lineSeparator => EOL}
-
import scala.PartialFunction.cond
import scala.annotation.tailrec
import scala.reflect.runtime.ReflectionUtils
@@ -24,7 +23,7 @@ import scala.util.control.{ControlThrowable, NonFatal}
import scala.tools.nsc.Reporting.WarningCategory
import scala.tools.nsc.util.stackTraceString
import scala.reflect.io.NoAbstractFile
-import scala.reflect.internal.util.NoSourceFile
+import scala.reflect.internal.util.{CodeAction, NoSourceFile}
trait ContextErrors extends splain.SplainErrors {
self: Analyzer =>
@@ -32,10 +31,17 @@ trait ContextErrors extends splain.SplainErrors {
import global._
import definitions._
+ final case class ContextWarning(pos: Position, msg: String, cat: WarningCategory, sym: Symbol, actions: List[CodeAction])
+
sealed abstract class AbsTypeError {
def errPos: Position
def errMsg: String
override def toString() = "[Type error at:" + errPos + "] " + errMsg
+
+ // To include code actions in type errors, add a field to the corresponding case class
+ // override val actions: List[CodeAction] = Nil
+ // See TypeErrorWrapper for example
+ def actions: List[CodeAction] = Nil
}
abstract class AbsAmbiguousTypeError extends AbsTypeError
@@ -65,7 +71,7 @@ trait ContextErrors extends splain.SplainErrors {
def errPos = underlyingSym.pos
}
- case class TypeErrorWrapper(ex: TypeError)
+ case class TypeErrorWrapper(ex: TypeError, override val actions: List[CodeAction] = Nil)
extends AbsTypeError {
def errMsg = ex.msg
def errPos = ex.pos
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index b76149843e6..a7936511be5 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -14,8 +14,8 @@ package scala.tools.nsc
package typechecker
import scala.annotation.tailrec
-import scala.collection.{immutable, mutable}
-import scala.reflect.internal.util.{ReusableInstance, shortClassOfInstance, ListOfNil, SomeOfNil}
+import scala.collection.mutable
+import scala.reflect.internal.util.{CodeAction, ReusableInstance, shortClassOfInstance, ListOfNil, SomeOfNil}
import scala.tools.nsc.Reporting.WarningCategory
import scala.util.chaining._
@@ -812,15 +812,25 @@ trait Contexts { self: Analyzer =>
//
/** Issue/buffer/throw the given type error according to the current mode for error reporting. */
- private[typechecker] def issue(err: AbsTypeError) = reporter.issue(err)(this)
+ private[typechecker] def issue(err: AbsTypeError) = reporter.issue(err)(this)
+
/** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */
private[typechecker] def issueAmbiguousError(err: AbsAmbiguousTypeError) = reporter.issueAmbiguousError(err)(this)
+
/** Issue/throw the given error message according to the current mode for error reporting. */
- def error(pos: Position, msg: String) = reporter.errorAndDumpIfDebug(fixPosition(pos), msg)
+ def error(pos: Position, msg: String, actions: List[CodeAction] = Nil) =
+ reporter.errorAndDumpIfDebug(fixPosition(pos), msg, actions)
+
/** Issue/throw the given error message according to the current mode for error reporting. */
- def warning(pos: Position, msg: String, category: WarningCategory) = reporter.warning(fixPosition(pos), msg, category, owner)
- def warning(pos: Position, msg: String, category: WarningCategory, site: Symbol) = reporter.warning(fixPosition(pos), msg, category, site)
- def echo(pos: Position, msg: String) = reporter.echo(fixPosition(pos), msg)
+ def warning(pos: Position, msg: String, category: WarningCategory, actions: List[CodeAction] = Nil): Unit =
+ reporter.warning(fixPosition(pos), msg, category, owner, actions)
+ def warning(pos: Position, msg: String, category: WarningCategory, site: Symbol, actions: List[CodeAction]): Unit =
+ reporter.warning(fixPosition(pos), msg, category, site, actions)
+ def warning(pos: Position, msg: String, category: WarningCategory, site: Symbol): Unit =
+ warning(pos, msg, category, site, Nil)
+
+ def echo(pos: Position, msg: String) = reporter.echo(fixPosition(pos), msg)
+
def fixPosition(pos: Position): Position = pos match {
case NoPosition => nextEnclosing(_.tree.pos != NoPosition).tree.pos
case _ => pos
@@ -828,9 +838,8 @@ trait Contexts { self: Analyzer =>
// TODO: buffer deprecations under silent (route through ContextReporter, store in BufferingReporter)
- def deprecationWarning(pos: Position, sym: Symbol, msg: String, since: String): Unit =
- runReporting.deprecationWarning(fixPosition(pos), sym, owner, msg, since)
-
+ def deprecationWarning(pos: Position, sym: Symbol, msg: String, since: String, actions: List[CodeAction] = Nil): Unit =
+ runReporting.deprecationWarning(fixPosition(pos), sym, owner, msg, since, actions)
def deprecationWarning(pos: Position, sym: Symbol): Unit =
runReporting.deprecationWarning(fixPosition(pos), sym, owner)
@@ -1712,24 +1721,19 @@ trait Contexts { self: Analyzer =>
*
* To handle nested contexts, reporters share buffers. TODO: only buffer in BufferingReporter, emit immediately in ImmediateReporter
*/
- abstract class ContextReporter(private[this] var _errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, private[this] var _warningBuffer: mutable.LinkedHashSet[(Position, String, WarningCategory, Symbol)] = null) {
- type Error = AbsTypeError
- type Warning = (Position, String, WarningCategory, Symbol)
-
- def issue(err: AbsTypeError)(implicit context: Context): Unit = errorAndDumpIfDebug(context.fixPosition(err.errPos), addDiagString(err.errMsg))
+ abstract class ContextReporter(private[this] var _errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, private[this] var _warningBuffer: mutable.LinkedHashSet[ContextWarning] = null) {
+ def issue(err: AbsTypeError)(implicit context: Context): Unit = errorAndDumpIfDebug(context.fixPosition(err.errPos), addDiagString(err.errMsg), err.actions)
def echo(msg: String): Unit = echo(NoPosition, msg)
+ def echo(pos: Position, msg: String): Unit = reporter.echo(pos, msg)
- def echo(pos: Position, msg: String): Unit =
- reporter.echo(pos, msg)
-
- def warning(pos: Position, msg: String, category: WarningCategory, site: Symbol): Unit =
- runReporting.warning(pos, msg, category, site)
+ def warning(pos: Position, msg: String, category: WarningCategory, site: Symbol, actions: List[CodeAction] = Nil): Unit =
+ runReporting.warning(pos, msg, category, site, actions)
- def error(pos: Position, msg: String): Unit
+ def error(pos: Position, msg: String, actions: List[CodeAction]): Unit
- final def errorAndDumpIfDebug(pos: Position, msg: String): Unit = {
- error(pos, msg)
+ final def errorAndDumpIfDebug(pos: Position, msg: String, actions: List[CodeAction]): Unit = {
+ error(pos, msg, actions)
if (settings.VdebugTypeError.value) {
Thread.dumpStack()
}
@@ -1748,7 +1752,7 @@ trait Contexts { self: Analyzer =>
* - else, let this context reporter decide
*/
final def issueAmbiguousError(err: AbsAmbiguousTypeError)(implicit context: Context): Unit =
- if (context.ambiguousErrors) reporter.error(context.fixPosition(err.errPos), addDiagString(err.errMsg)) // force reporting... see TODO above
+ if (context.ambiguousErrors) reporter.error(context.fixPosition(err.errPos), addDiagString(err.errMsg), err.actions) // force reporting... see TODO above
else handleSuppressedAmbiguous(err)
@inline final def withFreshErrorBuffer[T](expr: => T): T = {
@@ -1766,7 +1770,7 @@ trait Contexts { self: Analyzer =>
if (target.isBuffering) {
target ++= errors
} else {
- errors.foreach(e => target.errorAndDumpIfDebug(e.errPos, e.errMsg))
+ errors.foreach(e => target.errorAndDumpIfDebug(e.errPos, e.errMsg, e.actions))
}
// TODO: is clearAllErrors necessary? (no tests failed when dropping it)
// NOTE: even though `this ne target`, it may still be that `target.errorBuffer eq _errorBuffer`,
@@ -1826,19 +1830,19 @@ trait Contexts { self: Analyzer =>
final def emitWarnings() = if (_warningBuffer != null) {
_warningBuffer foreach {
- case (pos, msg, category, site) => runReporting.warning(pos, msg, category, site)
+ case ContextWarning(pos, msg, category, site, actions) => runReporting.warning(pos, msg, category, site, actions)
}
_warningBuffer = null
}
// [JZ] Contexts, pre- the scala/bug#7345 refactor, avoided allocating the buffers until needed. This
// is replicated here out of conservatism.
- private def newBuffer[A] = mutable.LinkedHashSet.empty[A] // Important to use LinkedHS for stable results.
- final protected def errorBuffer = { if (_errorBuffer == null) _errorBuffer = newBuffer; _errorBuffer }
+ private def newBuffer[A] = mutable.LinkedHashSet.empty[A] // Important to use LinkedHS for stable results.
+ final protected def errorBuffer = { if (_errorBuffer == null) _errorBuffer = newBuffer; _errorBuffer }
final protected def warningBuffer = { if (_warningBuffer == null) _warningBuffer = newBuffer; _warningBuffer }
- final def errors: immutable.Seq[Error] = errorBuffer.toVector
- final def warnings: immutable.Seq[Warning] = warningBuffer.toVector
+ final def errors: Seq[AbsTypeError] = errorBuffer.toVector
+ final def warnings: Seq[ContextWarning] = warningBuffer.toVector
final def firstError: Option[AbsTypeError] = errorBuffer.headOption
// TODO: remove ++= and clearAll* entirely in favor of more high-level combinators like withFreshErrorBuffer
@@ -1850,22 +1854,22 @@ trait Contexts { self: Analyzer =>
final def clearAllErrors(): Unit = { _errorBuffer = null }
}
- private[typechecker] class ImmediateReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[(Position, String, WarningCategory, Symbol)] = null) extends ContextReporter(_errorBuffer, _warningBuffer) {
+ private[typechecker] class ImmediateReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[ContextWarning] = null) extends ContextReporter(_errorBuffer, _warningBuffer) {
override def makeBuffering: ContextReporter = new BufferingReporter(errorBuffer, warningBuffer)
- def error(pos: Position, msg: String): Unit = reporter.error(pos, msg)
+ def error(pos: Position, msg: String, actions: List[CodeAction]): Unit = reporter.error(pos, msg, actions)
}
- private[typechecker] class BufferingReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[(Position, String, WarningCategory, Symbol)] = null) extends ContextReporter(_errorBuffer, _warningBuffer) {
+ private[typechecker] class BufferingReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[ContextWarning] = null) extends ContextReporter(_errorBuffer, _warningBuffer) {
override def isBuffering = true
override def issue(err: AbsTypeError)(implicit context: Context): Unit = errorBuffer += err
// this used to throw new TypeError(pos, msg) -- buffering lets us report more errors (test/files/neg/macro-basic-mamdmi)
// the old throwing behavior was relied on by diagnostics in manifestOfType
- def error(pos: Position, msg: String): Unit = errorBuffer += TypeErrorWrapper(new TypeError(pos, msg))
+ def error(pos: Position, msg: String, actions: List[CodeAction]): Unit = errorBuffer += TypeErrorWrapper(new TypeError(pos, msg), actions)
- override def warning(pos: Position, msg: String, category: WarningCategory, site: Symbol): Unit =
- warningBuffer += ((pos, msg, category, site))
+ override def warning(pos: Position, msg: String, category: WarningCategory, site: Symbol, actions: List[CodeAction]): Unit =
+ warningBuffer += ContextWarning(pos, msg, category, site, actions)
override protected def handleSuppressedAmbiguous(err: AbsAmbiguousTypeError): Unit = errorBuffer += err
@@ -1879,12 +1883,12 @@ trait Contexts { self: Analyzer =>
*/
private[typechecker] class ThrowingReporter extends ContextReporter {
override def isThrowing = true
- def error(pos: Position, msg: String): Unit = throw new TypeError(pos, msg)
+ def error(pos: Position, msg: String, actions: List[CodeAction]): Unit = throw new TypeError(pos, msg)
}
/** Used during a run of [[scala.tools.nsc.typechecker.TreeCheckers]]? */
private[typechecker] class CheckingReporter extends ContextReporter {
- def error(pos: Position, msg: String): Unit = onTreeCheckerError(pos, msg)
+ def error(pos: Position, msg: String, actions: List[CodeAction]): Unit = onTreeCheckerError(pos, msg)
}
class ImportInfo(val tree: Import, val depth: Int, val isRootImport: Boolean) {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index d897a8349ff..ea95a6c0660 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -17,7 +17,14 @@ package typechecker
import scala.annotation.{tailrec, unused}
import scala.collection.mutable, mutable.ListBuffer
import scala.reflect.internal.{Chars, TypesStats}
-import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Statistics, StringContextStripMarginOps}
+import scala.reflect.internal.util.{
+ CodeAction,
+ FreshNameCreator,
+ ListOfNil,
+ Statistics,
+ StringContextStripMarginOps,
+ TextEdit,
+}
import scala.tools.nsc.Reporting.{MessageFilter, Suppression, WConf, WarningCategory}
import scala.util.chaining._
import symtab.Flags._
@@ -85,7 +92,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case s : SilentTypeError => f(s.reportableErrors)
}
}
- class SilentTypeError private(val errors: List[AbsTypeError], val warnings: List[(Position, String, WarningCategory, Symbol)]) extends SilentResult[Nothing] {
+ class SilentTypeError private(val errors: List[AbsTypeError], val warnings: List[ContextWarning]) extends SilentResult[Nothing] {
override def isEmpty = true
def err: AbsTypeError = errors.head
def reportableErrors = errors match {
@@ -97,7 +104,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
object SilentTypeError {
def apply(errors: AbsTypeError*): SilentTypeError = apply(errors.toList, Nil)
- def apply(errors: List[AbsTypeError], warnings: List[(Position, String, WarningCategory, Symbol)]): SilentTypeError = new SilentTypeError(errors, warnings)
+ def apply(errors: List[AbsTypeError], warnings: List[ContextWarning]): SilentTypeError = new SilentTypeError(errors, warnings)
// todo: this extracts only one error, should be a separate extractor.
def unapply(error: SilentTypeError): Option[AbsTypeError] = error.errors.headOption
}
@@ -964,9 +971,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// This means an accessor that overrides a Java-defined method gets a MethodType instead of a NullaryMethodType, which breaks lots of assumptions about accessors)
def checkCanAutoApply(): Boolean = {
if (!isPastTyper && !matchNullaryLoosely) {
- context.deprecationWarning(tree.pos, NoSymbol, s"Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method ${meth.decodedName},\n" +
- s"or remove the empty argument list from its definition (Java-defined methods are exempt).\n"+
- s"In Scala 3, an unapplied method like this will be eta-expanded into a function.", "2.13.3")
+ val description =
+ s"""Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method ${meth.decodedName},
+ |or remove the empty argument list from its definition (Java-defined methods are exempt).
+ |In Scala 3, an unapplied method like this will be eta-expanded into a function.""".stripMargin
+ val actions = if (tree.pos.isRange)
+ List(CodeAction("auto-application of empty-paren methods", Some(description),
+ List(TextEdit(tree.pos.focusEnd, "()"))))
+ else Nil
+ context.deprecationWarning(tree.pos, NoSymbol, description, "2.13.3", actions)
}
true
}
@@ -4991,7 +5004,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def tryTypedApply(fun: Tree, args: List[Tree]): Tree = {
val start = if (settings.areStatisticsEnabled) statistics.startTimer(failedApplyNanos) else null
- def onError(typeErrors: Seq[AbsTypeError], warnings: Seq[(Position, String, WarningCategory, Symbol)]): Tree = {
+ def onError(typeErrors: Seq[AbsTypeError], warnings: Seq[ContextWarning]): Tree = {
if (settings.areStatisticsEnabled) statistics.stopTimer(failedApplyNanos, start)
// If the problem is with raw types, convert to existentials and try again.
@@ -5059,7 +5072,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
else err
typeErrors.foreach(err => context.issue(adjust(err)))
- warnings.foreach { case (p, m, c, s) => context.warning(p, m, c, s) }
+ warnings.foreach { case ContextWarning(p, m, c, s, as) => context.warning(p, m, c, s, as) }
setError(treeCopy.Apply(tree, fun, args))
}
@@ -5081,7 +5094,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def reportError(error: SilentTypeError): Tree = {
error.reportableErrors.foreach(context.issue)
- error.warnings.foreach { case (p, m, c, s) => context.warning(p, m, c, s) }
+ error.warnings.foreach { case ContextWarning(p, m, c, s, as) => context.warning(p, m, c, s, as) }
args.foreach(typed(_, mode, ErrorType))
setError(tree)
}
diff --git a/src/compiler/scala/tools/reflect/package.scala b/src/compiler/scala/tools/reflect/package.scala
index 3f592e3ae57..a9ee6324a38 100644
--- a/src/compiler/scala/tools/reflect/package.scala
+++ b/src/compiler/scala/tools/reflect/package.scala
@@ -15,7 +15,7 @@ package scala.tools
import scala.language.implicitConversions
import scala.reflect.api.JavaUniverse
import scala.reflect.internal.Reporter
-import scala.reflect.internal.util.Position
+import scala.reflect.internal.util.{CodeAction, Position}
import scala.tools.nsc.Settings
import scala.tools.nsc.reporters.{ConsoleReporter, FilteringReporter}
@@ -88,7 +88,7 @@ package object reflect {
val NSC_WARNING = Reporter.WARNING
val NSC_ERROR = Reporter.ERROR
- def doReport(pos: Position, msg: String, nscSeverity: NscSeverity): Unit =
+ override def doReport(pos: Position, msg: String, nscSeverity: NscSeverity, actions: List[CodeAction]): Unit =
frontEnd.log(pos, msg, (nscSeverity: @unchecked) match {
case NSC_INFO => API_INFO
case NSC_WARNING => API_WARNING
diff --git a/src/interactive/scala/tools/nsc/interactive/InteractiveReporter.scala b/src/interactive/scala/tools/nsc/interactive/InteractiveReporter.scala
index de93a2974f9..1bbc308cd56 100644
--- a/src/interactive/scala/tools/nsc/interactive/InteractiveReporter.scala
+++ b/src/interactive/scala/tools/nsc/interactive/InteractiveReporter.scala
@@ -14,10 +14,10 @@ package scala.tools.nsc
package interactive
import scala.collection.mutable.ArrayBuffer
-import scala.reflect.internal.util.Position
+import scala.reflect.internal.util.{CodeAction, Position}
import scala.tools.nsc.reporters.FilteringReporter
-case class Problem(pos: Position, msg: String, severityLevel: Int)
+case class Problem(pos: Position, msg: String, severityLevel: Int, actions: List[CodeAction])
abstract class InteractiveReporter extends FilteringReporter {
@@ -27,7 +27,7 @@ abstract class InteractiveReporter extends FilteringReporter {
val otherProblems = new ArrayBuffer[Problem]
- override def doReport(pos: Position, msg: String, severity: Severity): Unit = try {
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit = try {
val problems =
if (compiler eq null) {
otherProblems
@@ -44,7 +44,7 @@ abstract class InteractiveReporter extends FilteringReporter {
compiler.debugLog("[no position] :" + msg)
otherProblems
}
- problems += Problem(pos, msg, severity.id)
+ problems += Problem(pos, msg, severity.id, actions)
} catch {
case ex: UnsupportedOperationException =>
}
diff --git a/src/partest/scala/tools/partest/nest/DirectCompiler.scala b/src/partest/scala/tools/partest/nest/DirectCompiler.scala
index dd647837454..9d1a379693d 100644
--- a/src/partest/scala/tools/partest/nest/DirectCompiler.scala
+++ b/src/partest/scala/tools/partest/nest/DirectCompiler.scala
@@ -14,9 +14,8 @@ package scala.tools.partest
package nest
import java.io.{BufferedReader, FileWriter, PrintWriter}
-
import scala.collection.mutable.ListBuffer
-import scala.reflect.internal.util.{NoPosition, Position, ScalaClassLoader}
+import scala.reflect.internal.util.{CodeAction, NoPosition, Position, ScalaClassLoader}
import scala.reflect.io.AbstractFile
import scala.tools.nsc.reporters.{ConsoleReporter, Reporter}
import scala.tools.nsc.{CompilerCommand, Global, Settings}
@@ -30,7 +29,7 @@ object ExtConsoleReporter {
}
}
class PlainReporter(settings: Settings, reader: BufferedReader, writer: PrintWriter, echo: PrintWriter) extends ConsoleReporter(settings, reader, writer, echo) {
- override def doReport(pos: Position, msg: String, severity: Severity): Unit = writer.println(s"[$severity] [$pos]: $msg")
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit = writer.println(s"[$severity] [$pos]: $msg")
}
class TestSettings(cp: String, error: String => Unit) extends Settings(error) {
diff --git a/src/reflect/scala/reflect/internal/Reporting.scala b/src/reflect/scala/reflect/internal/Reporting.scala
index 0d97927be93..ebb31e712bc 100644
--- a/src/reflect/scala/reflect/internal/Reporting.scala
+++ b/src/reflect/scala/reflect/internal/Reporting.scala
@@ -15,6 +15,7 @@ package reflect
package internal
import scala.annotation.unchecked.uncheckedStable
+import scala.reflect.internal.util.CodeAction
import settings.MutableSettings
/** Provides delegates to the reporter doing the actual work.
@@ -35,7 +36,7 @@ trait Reporting { self : Positions =>
type PerRunReporting <: PerRunReportingBase
protected def PerRunReporting: PerRunReporting
abstract class PerRunReportingBase {
- def deprecationWarning(pos: Position, msg: String, since: String, site: String, origin: String): Unit
+ def deprecationWarning(pos: Position, msg: String, since: String, site: String, origin: String, actions: List[CodeAction] = Nil): Unit
/** Have we already supplemented the error message of a compiler crash? */
private[this] var supplementedError = false
@@ -96,24 +97,33 @@ abstract class Reporter {
@uncheckedStable final def WARNING: Severity = Reporter.WARNING
@uncheckedStable final def ERROR: Severity = Reporter.ERROR
- // TODO: rename to `doReport`, remove the `force` parameter.
+ // TODO: rename to `doReport`, remove the `force` parameter (but sbt compat).
// Note: `force` is ignored. It used to mean: if `!force`, the reporter may skip INFO messages.
// If `force`, INFO messages were always printed. Now, INFO messages are always printed.
+ @deprecatedOverriding("extend scala.tools.nsc.reporters.FilteringReporter, and override doReport instead", "2.13.12")
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit
+ def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit =
+ info0(pos, msg, severity, force = false)
+
/** @return Reporter.Display, or override for Count, Suppress
*/
def filter(pos: Position, msg: String, severity: Severity): Int = Reporter.Display
- final def echo(msg: String): Unit = echo(util.NoPosition, msg)
- final def echo(pos: Position, msg: String): Unit = if (filter(pos, msg, INFO) == 0) info0(pos, msg, INFO, force = true)
- final def warning(pos: Position, msg: String): Unit = filteredInfo(pos, msg, WARNING)
- final def error(pos: Position, msg: String): Unit = filteredInfo(pos, msg, ERROR)
+ final def echo(msg: String): Unit = echo(util.NoPosition, msg)
+ final def echo(pos: Position, msg: String, actions: List[CodeAction] = Nil): Unit =
+ if (filter(pos, msg, INFO) == 0) doReport(pos, msg, INFO, actions)
+
+ final def warning(pos: Position, msg: String, actions: List[CodeAction] = Nil): Unit =
+ filteredInfo(pos, msg, WARNING, actions)
+
+ final def error(pos: Position, msg: String, actions: List[CodeAction] = Nil): Unit =
+ filteredInfo(pos, msg, ERROR, actions)
- private def filteredInfo(pos: Position, msg: String, severity: Severity): Unit = {
+ private def filteredInfo(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit = {
val f = filter(pos, msg, severity)
if (f <= 1) increment(severity)
- if (f == 0) info0(pos, msg, severity, force = false)
+ if (f == 0) doReport(pos, msg, severity, actions)
}
def increment(severity: Severity): Unit = severity match {
diff --git a/src/reflect/scala/reflect/internal/util/CodeAction.scala b/src/reflect/scala/reflect/internal/util/CodeAction.scala
new file mode 100644
index 00000000000..b8525ebbff2
--- /dev/null
+++ b/src/reflect/scala/reflect/internal/util/CodeAction.scala
@@ -0,0 +1,39 @@
+/*
+ * Scala (https://www.scala-lang.org)
+ *
+ * Copyright EPFL and Lightbend, Inc.
+ *
+ * Licensed under Apache License 2.0
+ * (http://www.apache.org/licenses/LICENSE-2.0).
+ *
+ * See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ */
+
+package scala
+package reflect
+package internal
+package util
+
+/**
+ * EXPERIMENTAL
+ *
+ * CodeAction is used to communicate code edit suggestion to tooling in
+ * a structured manner.
+ *
+ * @see `CodeAction`
+ *
+ * @groupname Common Commonly used methods
+ * @group ReflectionAPI
+ */
+case class CodeAction(title: String, description: Option[String], edits: List[TextEdit])
+
+/**
+ * EXPERIMENTAL
+ *
+ *
+ * @groupname Common Commonly used methods
+ * @group ReflectionAPI
+ */
+case class TextEdit(position: Position, newText: String)
diff --git a/src/reflect/scala/reflect/runtime/JavaUniverse.scala b/src/reflect/scala/reflect/runtime/JavaUniverse.scala
index 6d17d6be3fd..7f9dd853d14 100644
--- a/src/reflect/scala/reflect/runtime/JavaUniverse.scala
+++ b/src/reflect/scala/reflect/runtime/JavaUniverse.scala
@@ -19,7 +19,7 @@ import scala.reflect.internal.{SomePhase, TreeInfo}
import scala.reflect.internal.{SymbolTable => InternalSymbolTable}
import scala.reflect.runtime.{SymbolTable => RuntimeSymbolTable}
import scala.reflect.api.{TypeCreator, Universe}
-import scala.reflect.internal.util.Statistics
+import scala.reflect.internal.util.{CodeAction, Statistics}
/** An implementation of [[scala.reflect.api.Universe]] for runtime reflection using JVM classloaders.
*
@@ -39,13 +39,15 @@ class JavaUniverse extends InternalSymbolTable with JavaUniverseForce with Refle
// TODO: why put output under isLogging? Calls to inform are already conditional on debug/verbose/...
import scala.reflect.internal.Reporter
override def reporter: Reporter = new Reporter {
+ @nowarn("msg=overriding method info0")
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = log(msg)
}
// minimal Run to get Reporting wired
def currentRun = new RunReporting {}
class PerRunReporting extends PerRunReportingBase {
- def deprecationWarning(pos: Position, msg: String, since: String, site: String, origin: String): Unit = reporter.warning(pos, msg)
+ def deprecationWarning(pos: Position, msg: String, since: String, site: String, origin: String, actions: List[CodeAction]): Unit =
+ reporter.warning(pos, msg)
}
protected def PerRunReporting = new PerRunReporting
diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/shell/Reporter.scala b/src/repl-frontend/scala/tools/nsc/interpreter/shell/Reporter.scala
index 538ed284c8e..e8069b64271 100644
--- a/src/repl-frontend/scala/tools/nsc/interpreter/shell/Reporter.scala
+++ b/src/repl-frontend/scala/tools/nsc/interpreter/shell/Reporter.scala
@@ -13,9 +13,8 @@
package scala.tools.nsc.interpreter.shell
import java.io.PrintWriter
-
import scala.reflect.internal
-import scala.reflect.internal.util.{NoSourceFile, Position, StringOps}
+import scala.reflect.internal.util.{CodeAction, NoSourceFile, Position, StringOps}
import scala.tools.nsc.interpreter.{Naming, ReplReporter, ReplRequest}
import scala.tools.nsc.reporters.{FilteringReporter, Reporter}
import scala.tools.nsc.{ConsoleWriter, NewLinePrintWriter, Settings}
@@ -150,7 +149,7 @@ class ReplReporterImpl(val config: ShellConfig, val settings: Settings = new Set
case internal.Reporter.INFO => RESET
}
- def doReport(pos: Position, msg: String, severity: Severity): Unit = withoutTruncating {
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit = withoutTruncating {
val prefix =
if (colorOk) severityColor(severity) + clabel(severity) + RESET
else clabel(severity)
diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/shell/Scripted.scala b/src/repl-frontend/scala/tools/nsc/interpreter/shell/Scripted.scala
index 5587c8333c9..93d33873570 100644
--- a/src/repl-frontend/scala/tools/nsc/interpreter/shell/Scripted.scala
+++ b/src/repl-frontend/scala/tools/nsc/interpreter/shell/Scripted.scala
@@ -14,12 +14,10 @@ package scala.tools.nsc.interpreter.shell
import java.io.{Closeable, OutputStream, PrintWriter, Reader}
import java.util.Arrays.asList
-
import javax.script._
-
import scala.beans.BeanProperty
import scala.jdk.CollectionConverters._
-import scala.reflect.internal.util.Position
+import scala.reflect.internal.util.{CodeAction, Position}
import scala.tools.nsc.Settings
import scala.tools.nsc.interpreter.Results.Incomplete
import scala.tools.nsc.interpreter.{ImportContextPreamble, ScriptedInterpreter, ScriptedRepl}
@@ -317,7 +315,7 @@ private class SaveFirstErrorReporter(settings: Settings, out: PrintWriter) exten
private var _firstError: Option[(Position, String)] = None
def firstError = _firstError
- override def doReport(pos: Position, msg: String, severity: Severity): Unit =
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit =
if (severity == ERROR && _firstError.isEmpty) _firstError = Some((pos, msg))
override def reset() = { super.reset(); _firstError = None }
diff --git a/test/files/presentation/t12308.check b/test/files/presentation/t12308.check
index 80792e4a7f2..51b1da10bf8 100644
--- a/test/files/presentation/t12308.check
+++ b/test/files/presentation/t12308.check
@@ -1,11 +1,11 @@
reload: Foo.scala
askLoadedTyped 1
-Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1)
+Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1,List())
askLoadedTyped 2
-Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1)
+Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1,List())
reload: Foo.scala
askLoadedTyped 3
-Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1)
+Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1,List())
targeted 1
askType at Foo.scala(2,37)
@@ -25,7 +25,7 @@ askType at Foo.scala(4,37)
[response] askTypeAt (4,37)
1
================================================================================
-Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1)
+Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1,List())
reload: Foo.scala
targeted 2 - doesn't handle nowarn correctly
@@ -46,5 +46,5 @@ askType at Foo.scala(4,37)
[response] askTypeAt (4,37)
1
================================================================================
-Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1)
-Problem(RangePosition(t12308/src/Foo.scala, 109, 109, 114),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1)
+Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1,List())
+Problem(RangePosition(t12308/src/Foo.scala, 109, 109, 114),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1,List())
diff --git a/test/files/run/maxerrs.scala b/test/files/run/maxerrs.scala
index fa2768ec668..4bb2f8f3baf 100644
--- a/test/files/run/maxerrs.scala
+++ b/test/files/run/maxerrs.scala
@@ -1,7 +1,8 @@
import scala.tools.partest._
import scala.tools.nsc.Settings
-import scala.tools.nsc.reporters.Reporter
+import scala.tools.nsc.reporters.FilteringReporter
+import scala.reflect.internal.util.CodeAction
/** Test that compiler enforces maxerrs when given a plain Reporter. */
object Test extends DirectTest {
@@ -14,8 +15,9 @@ object Test extends DirectTest {
}
""".trim
+ var store0: UnfilteredStoreReporter = _
// a reporter that ignores all limits
- lazy val store = new UnfilteredStoreReporter
+ def store = store0
final val limit = 3
@@ -28,17 +30,25 @@ object Test extends DirectTest {
s.maxerrs.value = limit
s
}
- override def reporter(s: Settings) = store
+ override def reporter(s: Settings) =
+ if (store0 ne null) store0
+ else {
+ store0 = new UnfilteredStoreReporter(s)
+ store0
+ }
}
-class UnfilteredStoreReporter extends Reporter {
+class UnfilteredStoreReporter(s: Settings) extends FilteringReporter {
import scala.tools.nsc.reporters.StoreReporter._
import scala.collection.mutable
import scala.reflect.internal.util.Position
val infos = new mutable.LinkedHashSet[Info]
- override def info0(pos: Position, msg: String, severity: Severity, force: Boolean) = infos += Info(pos, msg, severity)
+ override def settings: Settings = s
+
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit =
+ infos += Info(pos, msg, severity, actions)
override def reset(): Unit = {
super.reset()
diff --git a/test/junit/scala/tools/nsc/async/AnnotationDrivenAsyncTest.scala b/test/junit/scala/tools/nsc/async/AnnotationDrivenAsyncTest.scala
index 905a90ba6f2..e98e70f1f8d 100644
--- a/test/junit/scala/tools/nsc/async/AnnotationDrivenAsyncTest.scala
+++ b/test/junit/scala/tools/nsc/async/AnnotationDrivenAsyncTest.scala
@@ -11,7 +11,7 @@ import org.junit.{Assert, Ignore, Test}
import scala.annotation.{StaticAnnotation, nowarn, unused}
import scala.collection.mutable
import scala.concurrent.duration.Duration
-import scala.reflect.internal.util.Position
+import scala.reflect.internal.util.{CodeAction, Position}
import scala.reflect.internal.util.ScalaClassLoader.URLClassLoader
import scala.tools.nsc.backend.jvm.AsmUtils
import scala.tools.nsc.plugins.{Plugin, PluginComponent}
@@ -515,9 +515,9 @@ class AnnotationDrivenAsyncTest {
val out = createTempDir()
val reporter = new StoreReporter(new Settings) {
- override def doReport(pos: Position, msg: String, severity: Severity): Unit =
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit =
if (severity == INFO) println(msg)
- else super.doReport(pos, msg, severity)
+ else super.doReport(pos, msg, severity, actions)
}
val settings = new Settings(println(_))
settings.async.value = true
diff --git a/test/junit/scala/tools/nsc/reporters/AbstractCodeActionTest.scala b/test/junit/scala/tools/nsc/reporters/AbstractCodeActionTest.scala
new file mode 100644
index 00000000000..3211c766433
--- /dev/null
+++ b/test/junit/scala/tools/nsc/reporters/AbstractCodeActionTest.scala
@@ -0,0 +1,97 @@
+package scala.tools.nsc.reporters
+
+import org.junit.Test
+import org.junit.Assert._
+
+import scala.reflect.internal.util.CodeAction
+import scala.tools.testkit.BytecodeTesting
+
+abstract class AbstractCodeActionTest extends BytecodeTesting {
+ override def compilerArgs: String = "-Ystop-after:typer -Yvalidate-pos:typer -Yrangepos -deprecation -Xsource:3"
+ protected def reporter = compiler.global.reporter.asInstanceOf[StoreReporter]
+
+ @Test
+ def testProcedureSyntaxDecl(): Unit =
+ assertCodeSuggestion(
+ """trait Test {
+ | def foo
+ | def bar;
+ | def pub { print() }
+ | def club{ print() }
+ | def saloon = { print() }
+ |}""".stripMargin,
+ """trait Test {
+ | def foo: Unit
+ | def bar: Unit;
+ | def pub: Unit = { print() }
+ | def club: Unit ={ print() }
+ | def saloon = { print() }
+ |}""".stripMargin,
+ )
+
+ @Test
+ def testGreatParenInsert(): Unit = {
+ assertCodeSuggestion(
+ """trait Test {
+ | def foo = {
+ | println
+ | Predef.println
+ | toString + this.toString
+ | }
+ | def bar: Unit = Predef println()
+ |}
+ """.stripMargin,
+ """trait Test {
+ | def foo = {
+ | println()
+ | Predef.println()
+ | toString + this.toString
+ | }
+ | def bar: Unit = Predef println()
+ |}
+ """.stripMargin,
+ )
+ }
+
+ @Test
+ def testValInFor(): Unit =
+ assertCodeSuggestion(
+ """trait Test {
+ | def foo: Unit = {
+ | for {
+ | val x <- 1 to 5
+ | val y = x
+ | } yield x+y
+ | }
+ |}
+ """.stripMargin,
+ """trait Test {
+ | def foo: Unit = {
+ | for {
+ | x <- 1 to 5
+ | y = x
+ | } yield x+y
+ | }
+ |}
+ """.stripMargin,
+ )
+
+ def assertCodeSuggestion(original: String, expected: String): Unit = {
+ val run = compiler.newRun()
+ run.compileSources(compiler.global.newSourceFile(original) :: Nil)
+ val actions = reporter.infos.flatMap(_.actions).toList
+ val newCode = applyChanges(original, actions)
+ assertEquals(s"\n$newCode", expected, newCode)
+ }
+
+ def applyChanges(code: String, as: List[CodeAction]): String = {
+ var offset = 0
+ var res = code.toVector
+ for (change <- as.flatMap(_.edits).sortBy(_.position.start)) {
+ // not the most efficient but it's just for tests
+ res = res.take(change.position.start + offset) ++ change.newText.toVector ++ res.drop(change.position.end + offset)
+ offset = offset - (change.position.end - change.position.start) + change.newText.length
+ }
+ new String(res.toArray)
+ }
+}
diff --git a/test/junit/scala/tools/nsc/reporters/CodeActionTest.scala b/test/junit/scala/tools/nsc/reporters/CodeActionTest.scala
new file mode 100644
index 00000000000..6aaad689c9a
--- /dev/null
+++ b/test/junit/scala/tools/nsc/reporters/CodeActionTest.scala
@@ -0,0 +1,9 @@
+package scala.tools.nsc.reporters
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(classOf[JUnit4])
+class CodeActionTest extends AbstractCodeActionTest {
+ override def compilerArgs: String = "-Ystop-after:typer -Yvalidate-pos:typer -Yrangepos -deprecation"
+}
diff --git a/test/junit/scala/tools/nsc/reporters/CodeActionXsource3Test.scala b/test/junit/scala/tools/nsc/reporters/CodeActionXsource3Test.scala
new file mode 100644
index 00000000000..718bb2f1489
--- /dev/null
+++ b/test/junit/scala/tools/nsc/reporters/CodeActionXsource3Test.scala
@@ -0,0 +1,39 @@
+package scala.tools.nsc.reporters
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(classOf[JUnit4])
+class CodeActionXsource3Test extends AbstractCodeActionTest {
+ override def compilerArgs: String = "-Ystop-after:typer -Yvalidate-pos:typer -Yrangepos -deprecation -Xsource:3"
+
+ @Test
+ def testLambdaParameterList(): Unit =
+ assertCodeSuggestion(
+ """trait Test {
+ | def foo: Any = {
+ | x: Int => x * 2
+ | }
+ | def bar: Any = {
+ | x: Int=> x * 2
+ | }
+ | def tavern: Any = { x: Int =>
+ | x * 2
+ | }
+ |}
+ """.stripMargin,
+ """trait Test {
+ | def foo: Any = {
+ | (x: Int) => x * 2
+ | }
+ | def bar: Any = {
+ | (x: Int)=> x * 2
+ | }
+ | def tavern: Any = { (x: Int) =>
+ | x * 2
+ | }
+ |}
+ """.stripMargin,
+ )
+}
diff --git a/test/junit/scala/tools/nsc/reporters/ConsoleReporterTest.scala b/test/junit/scala/tools/nsc/reporters/ConsoleReporterTest.scala
index 13948f678e2..72bc58726fb 100644
--- a/test/junit/scala/tools/nsc/reporters/ConsoleReporterTest.scala
+++ b/test/junit/scala/tools/nsc/reporters/ConsoleReporterTest.scala
@@ -88,8 +88,8 @@ class ConsoleReporterTest {
reporter.settings.maxwarns.value = 1
// counting happens in .error/.warning, doReport doesn't count
- testHelper(msg = "Testing display", severity = "warning: ")(reporter.doReport(_, "Testing display", reporter.WARNING))
- testHelper(msg = "Testing display", severity = "error: ")(reporter.doReport(_, "Testing display", reporter.ERROR))
+ testHelper(msg = "Testing display", severity = "warning: ")(reporter.doReport(_, "Testing display", reporter.WARNING, Nil))
+ testHelper(msg = "Testing display", severity = "error: ")(reporter.doReport(_, "Testing display", reporter.ERROR, Nil))
testHelper(msg = "Testing display")(reporter.echo(_, "Testing display"))
testHelper(msg = "Testing display", severity = "warning: ")(reporter.warning(_, "Testing display"))
@@ -100,8 +100,8 @@ class ConsoleReporterTest {
testHelper(msg = "")(reporter.error(_, "Test maxwarns"))
// the filter happens in .error/.warning, doReport always reports
- testHelper(posWithSource, msg = "Testing display", severity = "warning: ")(reporter.doReport(_, "Testing display", reporter.WARNING))
- testHelper(posWithSource, msg = "Testing display", severity = "error: ")(reporter.doReport(_, "Testing display", reporter.ERROR))
+ testHelper(posWithSource, msg = "Testing display", severity = "warning: ")(reporter.doReport(_, "Testing display", reporter.WARNING, Nil))
+ testHelper(posWithSource, msg = "Testing display", severity = "error: ")(reporter.doReport(_, "Testing display", reporter.ERROR, Nil))
reporter.reset()
@@ -178,7 +178,8 @@ class ConsoleReporterTest {
new FilteringReporter {
def settings: Settings = conf
- def doReport(pos: Position, msg: String, severity: Severity): Unit = reporter.doReport(pos, msg, severity)
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit =
+ reporter.doReport(pos, msg, severity, actions)
}
}
@@ -213,7 +214,7 @@ class ConsoleReporterTest {
def filteredInfoTest(): Unit = {
val reporter = new FilteringReporter {
val settings: Settings = new Settings
- def doReport(pos: Position, msg: String, severity: Severity): Unit = ()
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit = ()
}
// test obsolete API, make sure it doesn't throw
reporter.info(NoPosition, "goodbye, cruel world", force = false)
@@ -224,7 +225,8 @@ class ConsoleReporterTest {
val reporter = createConsoleReporter("r", writerOut)
val adapted = new FilteringReporter {
def settings: Settings = reporter.settings
- def doReport(pos: Position, msg: String, severity: Severity): Unit = reporter.doReport(pos, msg, severity)
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit =
+ reporter.doReport(pos, msg, severity, actions)
}
// pass one message
diff --git a/test/junit/scala/tools/nsc/reporters/PositionFilterTest.scala b/test/junit/scala/tools/nsc/reporters/PositionFilterTest.scala
index a02c83b8e55..1eaaaa501d8 100644
--- a/test/junit/scala/tools/nsc/reporters/PositionFilterTest.scala
+++ b/test/junit/scala/tools/nsc/reporters/PositionFilterTest.scala
@@ -19,7 +19,8 @@ class PositionFilterTest {
def createFilter: FilteringReporter = new FilteringReporter {
def settings: Settings = store.settings
- def doReport(pos: Position, msg: String, severity: Severity): Unit = store.doReport(pos, msg, severity)
+ override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit =
+ store.doReport(pos, msg, severity, actions)
}
@Test
diff --git a/test/junit/scala/tools/nsc/reporters/WConfTest.scala b/test/junit/scala/tools/nsc/reporters/WConfTest.scala
index f83a005457f..3fba15a03ed 100644
--- a/test/junit/scala/tools/nsc/reporters/WConfTest.scala
+++ b/test/junit/scala/tools/nsc/reporters/WConfTest.scala
@@ -338,7 +338,8 @@ class WConfTest extends BytecodeTesting {
}, Array().toIndexedSeq), 0),
msg = "",
WarningCategory.Other,
- site = "")
+ site = "",
+ actions = Nil)
val aTest = Reporting.WConf.parseFilter("src=a/.*Test.scala", rootDir = "").getOrElse(null)
assertTrue(aTest.matches(m("/a/FooTest.scala")))
diff --git a/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala b/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala
index 71eec93127f..d54287e2c4e 100644
--- a/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala
+++ b/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala
@@ -1,9 +1,10 @@
package scala.tools.nsc
package symtab
+import scala.annotation.nowarn
import scala.reflect.ClassTag
import scala.reflect.internal.{NoPhase, Phase, Reporter, SomePhase}
-import scala.reflect.internal.util.Statistics
+import scala.reflect.internal.util.{CodeAction, Statistics}
import scala.tools.util.PathResolver
import util.ClassPath
import io.AbstractFile
@@ -85,13 +86,14 @@ class SymbolTableForUnitTesting extends SymbolTable {
// Members declared in scala.reflect.internal.Reporting
def reporter = new Reporter {
+ @nowarn("msg=overriding method info0")
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = println(msg)
}
// minimal Run to get Reporting wired
def currentRun = new RunReporting {}
class PerRunReporting extends PerRunReportingBase {
- def deprecationWarning(pos: Position, msg: String, since: String, site: String, origin: String): Unit = reporter.warning(pos, msg)
+ def deprecationWarning(pos: Position, msg: String, since: String, site: String, origin: String, actions: List[CodeAction]): Unit = reporter.warning(pos, msg)
}
protected def PerRunReporting = new PerRunReporting