diff --git a/build.sbt b/build.sbt index e8f39ffea..9a07324dd 100644 --- a/build.sbt +++ b/build.sbt @@ -124,8 +124,9 @@ lazy val utilLogging = (project in internalPath / "util-logging") exclude[DirectMissingMethodProblem]("sbt.internal.util.SuccessEvent.copy*"), exclude[DirectMissingMethodProblem]("sbt.internal.util.TraceEvent.copy*"), exclude[DirectMissingMethodProblem]("sbt.internal.util.StringEvent.copy*"), - // Private final class constructor changed + // Private final class constructors changed exclude[DirectMissingMethodProblem]("sbt.util.InterfaceUtil#ConcretePosition.this"), + exclude[DirectMissingMethodProblem]("sbt.util.InterfaceUtil#ConcreteProblem.this"), ), ) .configure(addSbtIO) diff --git a/internal/util-interface/src/main/java/xsbti/Problem.java b/internal/util-interface/src/main/java/xsbti/Problem.java index db7f67b22..db61f2bde 100644 --- a/internal/util-interface/src/main/java/xsbti/Problem.java +++ b/internal/util-interface/src/main/java/xsbti/Problem.java @@ -3,10 +3,20 @@ */ package xsbti; +import java.util.Optional; + public interface Problem { String category(); Severity severity(); String message(); Position position(); -} \ No newline at end of file + + // Default value to avoid breaking binary compatibility + /** + * If present, the string shown to the user when displaying this Problem. + * Otherwise, the Problem will be shown in an implementation-defined way + * based on the values of its other fields. + */ + default Optional rendered() { return Optional.empty(); } +} diff --git a/internal/util-logging/src/main/contraband/interface.contra.txt b/internal/util-logging/src/main/contraband/interface.contra.txt index a42eb09cb..3b5ed4986 100644 --- a/internal/util-logging/src/main/contraband/interface.contra.txt +++ b/internal/util-logging/src/main/contraband/interface.contra.txt @@ -29,4 +29,5 @@ type Problem { severity: Severity! message: String! position: Position! + rendered: String } diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/codec/ProblemFormats.scala b/internal/util-logging/src/main/scala/sbt/internal/util/codec/ProblemFormats.scala index 9820289da..fb7583a5c 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/codec/ProblemFormats.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/codec/ProblemFormats.scala @@ -4,8 +4,8 @@ package sbt.internal.util.codec import xsbti.{ Problem, Severity, Position } -import sbt.util.InterfaceUtil.problem import _root_.sjsonnew.{ deserializationError, Builder, JsonFormat, Unbuilder } +import java.util.Optional trait ProblemFormats { self: SeverityFormats with PositionFormats with sjsonnew.BasicJsonProtocol => implicit lazy val ProblemFormat: JsonFormat[Problem] = new JsonFormat[Problem] { @@ -13,12 +13,20 @@ trait ProblemFormats { self: SeverityFormats with PositionFormats with sjsonnew. jsOpt match { case Some(js) => unbuilder.beginObject(js) - val category = unbuilder.readField[String]("category") - val severity = unbuilder.readField[Severity]("severity") - val message = unbuilder.readField[String]("message") - val position = unbuilder.readField[Position]("position") + val category0 = unbuilder.readField[String]("category") + val severity0 = unbuilder.readField[Severity]("severity") + val message0 = unbuilder.readField[String]("message") + val position0 = unbuilder.readField[Position]("position") + val rendered0 = unbuilder.readField[Optional[String]]("rendered") + unbuilder.endObject() - problem(category, position, message, severity) + new Problem { + override val category = category0 + override val position = position0 + override val message = message0 + override val severity = severity0 + override val rendered = rendered0 + } case None => deserializationError("Expected JsObject but found None") } @@ -29,6 +37,7 @@ trait ProblemFormats { self: SeverityFormats with PositionFormats with sjsonnew. builder.addField("severity", obj.severity) builder.addField("message", obj.message) builder.addField("position", obj.position) + builder.addField("rendered", obj.rendered) builder.endObject() } } diff --git a/internal/util-logging/src/main/scala/sbt/util/InterfaceUtil.scala b/internal/util-logging/src/main/scala/sbt/util/InterfaceUtil.scala index a1ae332d8..1de667f5b 100644 --- a/internal/util-logging/src/main/scala/sbt/util/InterfaceUtil.scala +++ b/internal/util-logging/src/main/scala/sbt/util/InterfaceUtil.scala @@ -89,8 +89,16 @@ object InterfaceUtil { endLine0, endColumn0) + @deprecated("Use the overload of this method with more arguments", "1.2.2") def problem(cat: String, pos: Position, msg: String, sev: Severity): Problem = - new ConcreteProblem(cat, pos, msg, sev) + problem(cat, pos, msg, sev, None) + + def problem(cat: String, + pos: Position, + msg: String, + sev: Severity, + rendered: Option[String]): Problem = + new ConcreteProblem(cat, pos, msg, sev, rendered) private final class ConcreteT2[A1, A2](a1: A1, a2: A2) extends T2[A1, A2] { val get1: A1 = a1 @@ -144,12 +152,14 @@ object InterfaceUtil { cat: String, pos: Position, msg: String, - sev: Severity + sev: Severity, + rendered0: Option[String] ) extends Problem { val category = cat val position = pos val message = msg val severity = sev + override val rendered = o2jo(rendered0) override def toString = s"[$severity] $pos: $message" } } diff --git a/internal/util-logging/src/main/scala/sbt/util/Logger.scala b/internal/util-logging/src/main/scala/sbt/util/Logger.scala index 37a043cfa..3e543b5ce 100644 --- a/internal/util-logging/src/main/scala/sbt/util/Logger.scala +++ b/internal/util-logging/src/main/scala/sbt/util/Logger.scala @@ -117,6 +117,7 @@ object Logger { sourceFile0 ) + @deprecated("Use InterfaceUtil.problem", "1.2.2") def problem(cat: String, pos: Position, msg: String, sev: Severity): Problem = InterfaceUtil.problem(cat, pos, msg, sev) }