diff --git a/community-build/src/scala/dotty/communitybuild/projects.scala b/community-build/src/scala/dotty/communitybuild/projects.scala index 1349c3adc3b9..0ba5d32a78a8 100644 --- a/community-build/src/scala/dotty/communitybuild/projects.scala +++ b/community-build/src/scala/dotty/communitybuild/projects.scala @@ -140,7 +140,7 @@ final case class SbtCommunityProject( case Some(ivyHome) => List(s"-Dsbt.ivy.home=$ivyHome") case _ => Nil extraSbtArgs ++ sbtProps ++ List( - "-sbt-version", "1.8.2", + "-sbt-version", "1.9.0", "-Dsbt.supershell=false", s"-Ddotty.communitybuild.dir=$communitybuildDir", s"--addPluginSbtFile=$sbtPluginFilePath" diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 493a6e1cc18e..bc2ac0aa20a9 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3029,8 +3029,7 @@ object Parsers { val mod = atSpan(in.skipToken()) { modOfToken(tok, name) } if mods.isOneOf(mod.flags) then - syntaxError(RepeatedModifier(mod.flags.flagsString), mod.span) - + syntaxError(RepeatedModifier(mod.flags.flagsString, source, mod.span), mod.span) addMod(mods, mod) } diff --git a/compiler/src/dotty/tools/dotc/reporting/CodeAction.scala b/compiler/src/dotty/tools/dotc/reporting/CodeAction.scala new file mode 100644 index 000000000000..b2b18cc00104 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/reporting/CodeAction.scala @@ -0,0 +1,16 @@ +package dotty.tools.dotc.reporting + +import dotty.tools.dotc.rewrites.Rewrites.ActionPatch + +/** A representation of a code action / fix that can be used by tooling to + * apply a fix to their code. + * + * @param title The title of the fix, often showed to a user in their editor. + * @param description An optional description of the fix. + * @param patches The patches that this fix contains. + */ +case class CodeAction( + title: String, + description: Option[String], + patches: List[ActionPatch] +) diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index a1fe6773c1d2..1bfcf5c2f9dd 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -10,7 +10,6 @@ import printing.Formatting.hl import config.SourceVersion import scala.language.unsafeNulls - import scala.annotation.threadUnsafe /** ## Tips for error message generation @@ -384,6 +383,11 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => */ def showAlways = false + /** A list of actions attached to this message to address the issue this + * message represents. + */ + def actions(using Context): List[CodeAction] = List.empty + override def toString = msg } diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 104304c3409c..a536e10fb959 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -29,6 +29,11 @@ import transform.SymUtils._ import scala.util.matching.Regex import java.util.regex.Matcher.quoteReplacement import cc.CaptureSet.IdentityCaptRefMap +import dotty.tools.dotc.rewrites.Rewrites.ActionPatch +import dotty.tools.dotc.util.Spans.Span +import dotty.tools.dotc.util.SourcePosition +import scala.jdk.CollectionConverters.* +import dotty.tools.dotc.util.SourceFile /** Messages * ======== @@ -493,7 +498,7 @@ extends SyntaxMsg(ObjectMayNotHaveSelfTypeID) { } } -class RepeatedModifier(modifier: String)(implicit ctx:Context) +class RepeatedModifier(modifier: String, source: SourceFile, span: Span)(implicit ctx:Context) extends SyntaxMsg(RepeatedModifierID) { def msg(using Context) = i"""Repeated modifier $modifier""" @@ -512,6 +517,17 @@ extends SyntaxMsg(RepeatedModifierID) { | |""" } + + override def actions(using Context) = + import scala.language.unsafeNulls + List( + CodeAction(title = s"""Remove repeated modifier: "$modifier"""", + description = None, + patches = List( + ActionPatch(SourcePosition(source, span), "") + ) + ) + ) } class InterpolatedStringError()(implicit ctx:Context) @@ -1846,15 +1862,28 @@ class FailureToEliminateExistential(tp: Type, tp1: Type, tp2: Type, boundSyms: L |are only approximated in a best-effort way.""" } -class OnlyFunctionsCanBeFollowedByUnderscore(tp: Type)(using Context) +class OnlyFunctionsCanBeFollowedByUnderscore(tp: Type, tree: untpd.PostfixOp)(using Context) extends SyntaxMsg(OnlyFunctionsCanBeFollowedByUnderscoreID) { def msg(using Context) = i"Only function types can be followed by ${hl("_")} but the current expression has type $tp" def explain(using Context) = i"""The syntax ${hl("x _")} is no longer supported if ${hl("x")} is not a function. |To convert to a function value, you need to explicitly write ${hl("() => x")}""" + + override def actions(using Context) = + import scala.language.unsafeNulls + val untpd.PostfixOp(qual, Ident(nme.WILDCARD)) = tree: @unchecked + List( + CodeAction(title = "Rewrite to function value", + description = None, + patches = List( + ActionPatch(SourcePosition(tree.source, Span(tree.span.start)), "(() => "), + ActionPatch(SourcePosition(tree.source, Span(qual.span.end, tree.span.end)), ")") + ) + ) + ) } -class MissingEmptyArgumentList(method: String)(using Context) +class MissingEmptyArgumentList(method: String, tree: tpd.Tree)(using Context) extends SyntaxMsg(MissingEmptyArgumentListID) { def msg(using Context) = i"$method must be called with ${hl("()")} argument" def explain(using Context) = { @@ -1869,6 +1898,17 @@ class MissingEmptyArgumentList(method: String)(using Context) |In Dotty, this idiom is an error. The application syntax has to follow exactly the parameter syntax. |Excluded from this rule are methods that are defined in Java or that override methods defined in Java.""" } + + override def actions(using Context) = + import scala.language.unsafeNulls + List( + CodeAction(title = "Insert ()", + description = None, + patches = List( + ActionPatch(SourcePosition(tree.source, tree.span.endPos), "()"), + ) + ) + ) } class DuplicateNamedTypeParameter(name: Name)(using Context) diff --git a/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala b/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala index f2dfac88d464..5bea0fb66ed0 100644 --- a/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala +++ b/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala @@ -7,9 +7,11 @@ import core.Contexts._ import collection.mutable import scala.annotation.tailrec import dotty.tools.dotc.reporting.Reporter +import dotty.tools.dotc.util.SourcePosition; import java.io.OutputStreamWriter import java.nio.charset.StandardCharsets.UTF_8 +import dotty.tools.dotc.reporting.CodeAction /** Handles rewriting of Scala2 files to Dotty */ object Rewrites { @@ -19,6 +21,16 @@ object Rewrites { def delta = replacement.length - (span.end - span.start) } + /** A special type of Patch that instead of just a span, contains the + * full SourcePosition. This is useful when being used by + * [[dotty.tools.dotc.reporting.CodeAction]] or if the patch doesn't + * belong to the same file that the actual issue it's addressing is in. + * + * @param srcPos The SourcePosition of the patch. + * @param replacement The Replacement that should go in that position. + */ + case class ActionPatch(srcPos: SourcePosition, replacement: String) + private class Patches(source: SourceFile) { private[Rewrites] val pbuf = new mutable.ListBuffer[Patch]() @@ -88,6 +100,14 @@ object Rewrites { report.echo(s"[patched file ${source.file.path}]") rewrites.patched(source).writeBack() } + + /** Given a CodeAction take the patches and apply them. + * + * @param action The CodeAction containing the patches + */ + def applyAction(action: CodeAction)(using Context): Unit = + action.patches.foreach: actionPatch => + patch(actionPatch.srcPos.span, actionPatch.replacement) } /** A completely encapsulated class representing rewrite state, used diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 126d109889e1..25cbfdfec600 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -55,7 +55,7 @@ object ErrorReporting { val meth = err.exprStr(methPart(tree)) val info = if tree.symbol.exists then tree.symbol.info else mt if isCallableWithSingleEmptyArgumentList(info) then - report.error(MissingEmptyArgumentList(meth), tree.srcPos) + report.error(MissingEmptyArgumentList(meth, tree), tree.srcPos) else report.error(MissingArgumentList(meth, tree.symbol), tree.srcPos) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 74be1dee9a9b..76b7b6add57c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -54,6 +54,7 @@ import cc.CheckCaptures import config.Config import scala.annotation.constructorOnly +import dotty.tools.dotc.rewrites.Rewrites object Typer { @@ -2877,11 +2878,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case closure(_, _, _) => case _ => val recovered = typed(qual)(using ctx.fresh.setExploreTyperState()) - report.errorOrMigrationWarning(OnlyFunctionsCanBeFollowedByUnderscore(recovered.tpe.widen), tree.srcPos, from = `3.0`) + val msg = OnlyFunctionsCanBeFollowedByUnderscore(recovered.tpe.widen, tree) + report.errorOrMigrationWarning(msg, tree.srcPos, from = `3.0`) if (migrateTo3) { // Under -rewrite, patch `x _` to `(() => x)` - patch(Span(tree.span.start), "(() => ") - patch(Span(qual.span.end, tree.span.end), ")") + msg.actions + .headOption + .foreach(Rewrites.applyAction) return typed(untpd.Function(Nil, qual), pt) } } @@ -3878,10 +3881,17 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def adaptNoArgsUnappliedMethod(wtp: MethodType, functionExpected: Boolean, arity: Int): Tree = { /** Is reference to this symbol `f` automatically expanded to `f()`? */ def isAutoApplied(sym: Symbol): Boolean = + lazy val msg = MissingEmptyArgumentList(sym.show, tree) + sym.isConstructor || sym.matchNullaryLoosely - || Feature.warnOnMigration(MissingEmptyArgumentList(sym.show), tree.srcPos, version = `3.0`) - && { patch(tree.span.endPos, "()"); true } + || Feature.warnOnMigration(msg, tree.srcPos, version = `3.0`) + && { + msg.actions + .headOption + .foreach(Rewrites.applyAction) + true + } /** If this is a selection prototype of the form `.apply(...): R`, return the nested * function prototype `(...)R`. Otherwise `pt`. diff --git a/compiler/test/dotty/tools/dotc/reporting/CodeActionTest.scala b/compiler/test/dotty/tools/dotc/reporting/CodeActionTest.scala new file mode 100644 index 000000000000..1740542b46b0 --- /dev/null +++ b/compiler/test/dotty/tools/dotc/reporting/CodeActionTest.scala @@ -0,0 +1,91 @@ +package dotty.tools.dotc.reporting + +import dotty.tools.DottyTest +import dotty.tools.dotc.rewrites.Rewrites +import dotty.tools.dotc.rewrites.Rewrites.ActionPatch +import dotty.tools.dotc.util.SourceFile + +import scala.annotation.tailrec +import scala.jdk.CollectionConverters.* +import scala.runtime.Scala3RunTime.assertFailed + +import org.junit.Assert._ +import org.junit.Test + +/** This is a test suite that is meant to test the actions attached to the + * diagnostic for a given code snippet. + */ +class CodeActionTest extends DottyTest: + + @Test def convertToFunctionValue = + checkCodeAction( + """|object Test: + | def x: Int = 3 + | val test = x _ + |""".stripMargin, + "Rewrite to function value", + """|object Test: + | def x: Int = 3 + | val test = (() => x) + |""".stripMargin + ) + + @Test def insertBracesForEmptyArgument = + checkCodeAction( + """|object Test: + | def foo(): Unit = () + | val x = foo + |""".stripMargin, + "Insert ()", + """|object Test: + | def foo(): Unit = () + | val x = foo() + |""".stripMargin + + ) + + @Test def removeRepeatModifier = + checkCodeAction( + """|final final class Test + |""".stripMargin, + """Remove repeated modifier: "final"""", + // TODO look into trying to remove the extra space that is left behind + """|final class Test + |""".stripMargin + + ) + + // Make sure we're not using the default reporter, which is the ConsoleReporter, + // meaning they will get reported in the test run and that's it. + private def newContext = + val rep = new StoreReporter(null) with UniqueMessagePositions with HideNonSensicalMessages + initialCtx.setReporter(rep).withoutColors + + private def checkCodeAction(code: String, title: String, expected: String) = + ctx = newContext + val source = SourceFile.virtual("test", code).content + val runCtx = checkCompile("typer", code) { (_, _) => () } + val diagnostics = runCtx.reporter.removeBufferedMessages + assertEquals(1, diagnostics.size) + + val diagnostic = diagnostics.head + val actions = diagnostic.msg.actions.toList + assertEquals(1, actions.size) + + // TODO account for more than 1 action + val action = actions.head + assertEquals(action.title, title) + val patches = action.patches.toList + if patches.nonEmpty then + patches.reduceLeft: (p1, p2) => + assert(p1.srcPos.span.end <= p2.srcPos.span.start, s"overlapping patches $p1 and $p2") + p2 + else assertFailed("Expected a patch attatched to this action, but it was empty") + + val result = patches.reverse.foldLeft(code): (newCode, patch) => + import scala.language.unsafeNulls + val start = newCode.substring(0, patch.srcPos.start) + val ending = newCode.substring(patch.srcPos.end, newCode.length) + start + patch.replacement + ending + + assertEquals(expected, result) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 54bc6ecadfe0..f22601346803 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -28,6 +28,6 @@ object Dependencies { "com.vladsch.flexmark" % "flexmark-ext-yaml-front-matter" % flexmarkVersion, ) - val newCompilerInterface = "org.scala-sbt" % "compiler-interface" % "1.8.0" + val newCompilerInterface = "org.scala-sbt" % "compiler-interface" % "1.9.0" val oldCompilerInterface = "org.scala-sbt" % "compiler-interface" % "1.3.5" } diff --git a/project/build.properties b/project/build.properties index 46e43a97ed86..40b3b8e7b655 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.2 +sbt.version=1.9.0 diff --git a/sbt-bridge/src/dotty/tools/xsbt/Action.java b/sbt-bridge/src/dotty/tools/xsbt/Action.java new file mode 100644 index 000000000000..2a1818fef78c --- /dev/null +++ b/sbt-bridge/src/dotty/tools/xsbt/Action.java @@ -0,0 +1,28 @@ +package dotty.tools.xsbt; + +import java.util.Optional; + +final public class Action implements xsbti.Action { + private final String _title; + private final Optional _description; + private final WorkspaceEdit _edit; + + public Action(String title, Optional description, WorkspaceEdit edit) { + super(); + this._title = title; + this._description = description; + this._edit = edit; + } + + public String title() { + return _title; + } + + public Optional description() { + return _description; + } + + public WorkspaceEdit edit() { + return _edit; + } +} diff --git a/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java b/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java index 25b934000144..dba1889b393f 100644 --- a/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java +++ b/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java @@ -3,11 +3,15 @@ */ package dotty.tools.xsbt; +import java.util.List; + import scala.Tuple2; import scala.collection.mutable.HashMap; +import scala.jdk.javaapi.CollectionConverters; import dotty.tools.dotc.core.Contexts.Context; import dotty.tools.dotc.reporting.AbstractReporter; +import dotty.tools.dotc.reporting.CodeAction; import dotty.tools.dotc.reporting.Diagnostic; import dotty.tools.dotc.reporting.Message; import dotty.tools.dotc.util.SourceFile; @@ -43,12 +47,13 @@ public void doReport(Diagnostic dia, Context ctx) { messageBuilder.append(message.message()); String diagnosticCode = String.valueOf(message.errorId().errorNumber()); boolean shouldExplain = Diagnostic.shouldExplain(dia, ctx); + List actions = CollectionConverters.asJava(message.actions(ctx)); if (shouldExplain && !message.explanation().isEmpty()) { rendered.append(explanation(message, ctx)); messageBuilder.append(System.lineSeparator()).append(explanation(message, ctx)); } - delegate.log(new Problem(position, messageBuilder.toString(), severity, rendered.toString(), diagnosticCode)); + delegate.log(new Problem(position, messageBuilder.toString(), severity, rendered.toString(), diagnosticCode, actions)); } private static Severity severityOf(int level) { diff --git a/sbt-bridge/src/dotty/tools/xsbt/Problem.java b/sbt-bridge/src/dotty/tools/xsbt/Problem.java index 29d64cc26c4a..9bde5ce76ebb 100644 --- a/sbt-bridge/src/dotty/tools/xsbt/Problem.java +++ b/sbt-bridge/src/dotty/tools/xsbt/Problem.java @@ -1,6 +1,16 @@ package dotty.tools.xsbt; +import java.util.List; import java.util.Optional; +import static java.util.stream.Collectors.toList; + +import dotty.tools.dotc.reporting.CodeAction; +import dotty.tools.dotc.rewrites.Rewrites.ActionPatch; +import dotty.tools.dotc.util.SourcePosition; + +import scala.jdk.javaapi.CollectionConverters; +import scala.jdk.javaapi.OptionConverters; + import xsbti.Position; import xsbti.Severity; @@ -10,14 +20,16 @@ final public class Problem implements xsbti.Problem { private final Severity _severity; private final Optional _rendered; private final String _diagnosticCode; + private final List _actions; - public Problem(Position position, String message, Severity severity, String rendered, String diagnosticCode) { + public Problem(Position position, String message, Severity severity, String rendered, String diagnosticCode, List actions) { super(); this._position = position; this._message = message; this._severity = severity; this._rendered = Optional.of(rendered); this._diagnosticCode = diagnosticCode; + this._actions = actions; } public String category() { @@ -56,6 +68,38 @@ public Optional diagnosticCode() { } } + public List actions() { + if (_actions.isEmpty()) { + return java.util.Collections.emptyList(); + } else { + // Same as with diagnosticCode, we need to ensure we don't create the actual + // Action until we are here to ensure that when using an older version of sbt/zinc + // with the new versions of the compiler, this doesn't blow up because this is + // never getting called. + return _actions + .stream() + .map(action -> new Action(action.title(), OptionConverters.toJava(action.description()), toWorkspaceEdit(CollectionConverters.asJava(action.patches())))) + .collect(toList()); + } + } + + private static WorkspaceEdit toWorkspaceEdit(List patches) { + return new WorkspaceEdit( + patches + .stream() + .map(patch -> new TextEdit(positionOf(patch.srcPos()), patch.replacement())) + .collect(toList()) + ); + } + + private static Position positionOf(SourcePosition pos) { + if (pos.exists()){ + return new PositionBridge(pos, pos.source()); + } else { + return PositionBridge.noPosition; + } + } + @Override public String toString() { return "Problem(" + _position + ", " + _message + ", " + _severity + ", " + _rendered + ", " + _diagnosticCode + ")"; diff --git a/sbt-bridge/src/dotty/tools/xsbt/TextEdit.java b/sbt-bridge/src/dotty/tools/xsbt/TextEdit.java new file mode 100644 index 000000000000..df717446b2f2 --- /dev/null +++ b/sbt-bridge/src/dotty/tools/xsbt/TextEdit.java @@ -0,0 +1,23 @@ +package dotty.tools.xsbt; + +import xsbti.Position; + +final public class TextEdit implements xsbti.TextEdit { + private final Position _position; + private final String _newText; + + public TextEdit(Position position, String newText) { + super(); + this._position = position; + this._newText = newText; + } + + public Position position() { + return _position; + } + + public String newText() { + return _newText; + } + +} diff --git a/sbt-bridge/src/dotty/tools/xsbt/WorkspaceEdit.java b/sbt-bridge/src/dotty/tools/xsbt/WorkspaceEdit.java new file mode 100644 index 000000000000..153de63e3765 --- /dev/null +++ b/sbt-bridge/src/dotty/tools/xsbt/WorkspaceEdit.java @@ -0,0 +1,20 @@ +package dotty.tools.xsbt; + +import java.util.List; + +import xsbti.TextEdit; + +final public class WorkspaceEdit implements xsbti.WorkspaceEdit { + + private final List _changes; + + public WorkspaceEdit(List changes) { + super(); + this._changes = changes; + } + + public List changes() { + return _changes; + } + +} diff --git a/sbt-test/compilerReporter/simple/Source.scala b/sbt-test/compilerReporter/simple/Source.scala index 6f06785990c3..fcfd8672475b 100644 --- a/sbt-test/compilerReporter/simple/Source.scala +++ b/sbt-test/compilerReporter/simple/Source.scala @@ -7,4 +7,7 @@ trait Wr { object Er { val a = er1 -} \ No newline at end of file + + def f: Int = 1 + val x = f _ +} diff --git a/sbt-test/compilerReporter/simple/project/Reporter.scala b/sbt-test/compilerReporter/simple/project/Reporter.scala index 6c3b60cebb3a..a22b5cfb904d 100644 --- a/sbt-test/compilerReporter/simple/project/Reporter.scala +++ b/sbt-test/compilerReporter/simple/project/Reporter.scala @@ -2,6 +2,8 @@ import sbt._ import Keys._ import KeyRanks.DTask +import scala.jdk.CollectionConverters.* + object Reporter { import xsbti.{Reporter, Problem, Position, Severity} @@ -27,27 +29,62 @@ object Reporter { check := (Compile / compile).failure.map(_ => { val problems = reporter.problems println(problems.toList) - assert(problems.size == 1) - // make sure position reported by zinc are proper - val mainProblem = problems.head + problems match { + case Array(err, warning) => + // Checking the error reported + val eline = err.position().line() + assert(eline.isPresent() == true) + assert(eline.get() == 9) + + val ediagnosticCode = err.diagnosticCode() + assert(ediagnosticCode.isPresent() == true) + val ecode = ediagnosticCode.get().code() + assert(ecode == "6") + + val epointer = err.position().pointer() + assert(epointer.isPresent() == true) + assert(epointer.get() == 10) + + assert(err.position.offset.isPresent) + + assert(err.severity == Severity.Error) // not found: er1, + + // Checking the warning reported + + val wline = warning.position().line() + assert(wline.isPresent() == true) + assert(wline.get() == 12) + + val wdiagnosticCode = warning.diagnosticCode() + assert(wdiagnosticCode.isPresent() == true) + val wcode = wdiagnosticCode.get().code() + assert(wcode == "99") + + val wpointer = warning.position().pointer() + assert(wpointer.isPresent() == true) + assert(wpointer.get() == 12) + + assert(warning.position.offset.isPresent) + + assert(warning.severity == Severity.Warn) // Only function types can be followed by _ but the current expression has type Int + + val actions = warning.actions().asScala.toList + + assert(actions.size == 1) + + val action = actions.head - val line = mainProblem.position().line() - assert(line.isPresent() == true) - assert(line.get() == 9) + assert(action.title() == "Rewrite to function value") - val diagnosticCode = mainProblem.diagnosticCode() - assert(diagnosticCode.isPresent() == true) - val code = diagnosticCode.get() - assert(diagnosticCode.get().code() == "6") + val edits = action.edit().changes().asScala.toList - val pointer = mainProblem.position().pointer() - assert(pointer.isPresent() == true) - assert(pointer.get() == 10) + assert(edits.size == 2) - assert(problems.forall(_.position.offset.isPresent)) + case somethingElse => + assert(false, s"Only expected to have a single error and a single warning, but instead got: ${somethingElse.toString}") - assert(problems.count(_.severity == Severity.Error) == 1) // not found: er1, + } }).value ) } diff --git a/tests/cmdTest-sbt-tests/sourcepath-with-inline-api-hash/project/build.properties b/tests/cmdTest-sbt-tests/sourcepath-with-inline-api-hash/project/build.properties index 46e43a97ed86..40b3b8e7b655 100644 --- a/tests/cmdTest-sbt-tests/sourcepath-with-inline-api-hash/project/build.properties +++ b/tests/cmdTest-sbt-tests/sourcepath-with-inline-api-hash/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.2 +sbt.version=1.9.0 diff --git a/tests/cmdTest-sbt-tests/sourcepath-with-inline/project/build.properties b/tests/cmdTest-sbt-tests/sourcepath-with-inline/project/build.properties index 46e43a97ed86..40b3b8e7b655 100644 --- a/tests/cmdTest-sbt-tests/sourcepath-with-inline/project/build.properties +++ b/tests/cmdTest-sbt-tests/sourcepath-with-inline/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.2 +sbt.version=1.9.0