Permalink
Browse files

print-warnings task for Scala 2.10+ to avoid needing to rerun 'compil…

…e' to see deprecation/unchecked warnings
  • Loading branch information...
1 parent 9049c6c commit 1cbb7ce93cd9e54a0b008663cdf31c99b06f81b1 @harrah committed Mar 17, 2012
@@ -9,15 +9,15 @@ package sbt
// Original author: Martin Odersky
import xsbti.{Maybe,Position,Problem,Reporter,Severity}
+ import java.io.File
import java.util.EnumMap
import scala.collection.mutable
import LoggerReporter._
+ import Logger.{m2o,o2m,position,problem}
import Severity.{Error,Info => SInfo,Warn}
object LoggerReporter
{
- def m2o[S](m: Maybe[S]): Option[S] = if(m.isDefined) Some(m.get) else None
-
final class PositionKey(pos: Position)
{
def offset = pos.offset
@@ -49,7 +49,7 @@ class LoggerReporter(maximumErrors: Int, log: Logger) extends xsbti.Reporter
{
val positions = new mutable.HashMap[PositionKey, Severity]
val count = new EnumMap[Severity, Int](classOf[Severity])
- private val allProblems = new mutable.ListBuffer[Problem]
+ private[this] val allProblems = new mutable.ListBuffer[Problem]
reset()
@@ -63,7 +63,7 @@ class LoggerReporter(maximumErrors: Int, log: Logger) extends xsbti.Reporter
}
def hasWarnings = count.get(Warn) > 0
def hasErrors = count.get(Error) > 0
- def problems = allProblems.toArray
+ def problems: Array[Problem] = allProblems.toArray
def printSummary()
{
@@ -126,13 +126,6 @@ class LoggerReporter(maximumErrors: Int, log: Logger) extends xsbti.Reporter
case _ => display(pos, msg, severity)
}
}
- def problem(pos: Position, msg: String, sev: Severity): Problem =
- new Problem
- {
- val position = pos
- val message = msg
- val severity = sev
- }
def testAndLog(pos: Position, severity: Severity): Boolean =
{
@@ -13,12 +13,13 @@ trait Analysis
val stamps: Stamps
val apis: APIs
val relations: Relations
+ val infos: SourceInfos
def ++(other: Analysis): Analysis
def -- (sources: Iterable[File]): Analysis
- def copy(stamps: Stamps = stamps, apis: APIs = apis, relations: Relations = relations): Analysis
+ def copy(stamps: Stamps = stamps, apis: APIs = apis, relations: Relations = relations, infos: SourceInfos = infos): Analysis
- def addSource(src: File, api: Source, stamp: Stamp, internalDeps: Iterable[File]): Analysis
+ def addSource(src: File, api: Source, stamp: Stamp, internalDeps: Iterable[File], info: SourceInfo): Analysis
def addBinaryDep(src: File, dep: File, className: String, stamp: Stamp): Analysis
def addExternalDep(src: File, dep: String, api: Source): Analysis
def addProduct(src: File, product: File, stamp: Stamp, name: String): Analysis
@@ -28,45 +29,48 @@ trait Analysis
object Analysis
{
- lazy val Empty: Analysis = new MAnalysis(Stamps.empty, APIs.empty, Relations.empty)
+ lazy val Empty: Analysis = new MAnalysis(Stamps.empty, APIs.empty, Relations.empty, SourceInfos.empty)
def summary(a: Analysis): String =
{
val (j, s) = a.apis.allInternalSources.partition(_.getName.endsWith(".java"))
val c = a.stamps.allProducts
val ext = a.apis.allExternals
val jars = a.relations.allBinaryDeps.filter(_.getName.endsWith(".jar"))
+ val unreportedCount = a.infos.allInfos.values.map(_.unreportedProblems.size).sum
val sections =
counted("Scala source", "", "s", s.size) ++
counted("Java source", "", "s", j.size) ++
counted("class", "", "es", c.size) ++
counted("external source dependenc", "y", "ies", ext.size) ++
- counted("binary dependenc", "y", "ies", jars.size)
+ counted("binary dependenc", "y", "ies", jars.size) ++
+ counted("unreported warning", "", "s", unreportedCount)
sections.mkString("Analysis: ", ", ", "")
}
}
-private class MAnalysis(val stamps: Stamps, val apis: APIs, val relations: Relations) extends Analysis
+private class MAnalysis(val stamps: Stamps, val apis: APIs, val relations: Relations, val infos: SourceInfos) extends Analysis
{
- def ++ (o: Analysis): Analysis = new MAnalysis(stamps ++ o.stamps, apis ++ o.apis, relations ++ o.relations)
+ def ++ (o: Analysis): Analysis = new MAnalysis(stamps ++ o.stamps, apis ++ o.apis, relations ++ o.relations, infos ++ o.infos)
def -- (sources: Iterable[File]): Analysis =
{
val newRelations = relations -- sources
def keep[T](f: (Relations, T) => Set[_]): T => Boolean = file => !f(newRelations, file).isEmpty
val newAPIs = apis.removeInternal(sources).filterExt( keep(_ usesExternal _) )
val newStamps = stamps.filter( keep(_ produced _), sources, keep(_ usesBinary _))
- new MAnalysis(newStamps, newAPIs, newRelations)
+ val newInfos = infos -- sources
+ new MAnalysis(newStamps, newAPIs, newRelations, newInfos)
}
- def copy(stamps: Stamps, apis: APIs, relations: Relations): Analysis = new MAnalysis(stamps, apis, relations)
+ def copy(stamps: Stamps, apis: APIs, relations: Relations, infos: SourceInfos): Analysis = new MAnalysis(stamps, apis, relations, infos)
- def addSource(src: File, api: Source, stamp: Stamp, internalDeps: Iterable[File]): Analysis =
- copy( stamps.markInternalSource(src, stamp), apis.markInternalSource(src, api), relations.addInternalSrcDeps(src, internalDeps) )
+ def addSource(src: File, api: Source, stamp: Stamp, internalDeps: Iterable[File], info: SourceInfo): Analysis =
+ copy( stamps.markInternalSource(src, stamp), apis.markInternalSource(src, api), relations.addInternalSrcDeps(src, internalDeps), infos.add(src, info) )
def addBinaryDep(src: File, dep: File, className: String, stamp: Stamp): Analysis =
- copy( stamps.markBinary(dep, className, stamp), apis, relations.addBinaryDep(src, dep) )
+ copy( stamps.markBinary(dep, className, stamp), apis, relations.addBinaryDep(src, dep), infos )
def addExternalDep(src: File, dep: String, depAPI: Source): Analysis =
- copy( stamps, apis.markExternalAPI(dep, depAPI), relations.addExternalDep(src, dep) )
+ copy( stamps, apis.markExternalAPI(dep, depAPI), relations.addExternalDep(src, dep), infos )
def addProduct(src: File, product: File, stamp: Stamp, name: String): Analysis =
- copy( stamps.markProduct(product, stamp), apis, relations.addProduct(src, product, name) )
+ copy( stamps.markProduct(product, stamp), apis, relations.addProduct(src, product, name), infos )
}
@@ -5,6 +5,8 @@ package sbt
package inc
import xsbti.api.{Source, SourceAPI}
+import xsbti.{Position,Problem,Severity}
+import Logger.{m2o, problem}
import java.io.File
object IncrementalCompile
@@ -44,6 +46,8 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
import collection.mutable.{HashMap, HashSet, ListBuffer, Map, Set}
private[this] val apis = new HashMap[File, (Int, SourceAPI)]
+ private[this] val unreporteds = new HashMap[File, ListBuffer[Problem]]
+ private[this] val reporteds = new HashMap[File, ListBuffer[Problem]]
private[this] val binaryDeps = new HashMap[File, Set[File]]
// source file to set of generated (class file, class name)
private[this] val classes = new HashMap[File, Set[(File, String)]]
@@ -58,6 +62,14 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
private def add[A,B](map: Map[A,Set[B]], a: A, b: B): Unit =
map.getOrElseUpdate(a, new HashSet[B]) += b
+ def problem(pos: Position, msg: String, severity: Severity, reported: Boolean): Unit =
+ {
+ for(source <- m2o(pos.sourceFile)) {
+ val map = if(reported) reporteds else unreporteds
+ map.getOrElseUpdate(source, ListBuffer.empty) += Logger.problem(pos, msg, severity)
+ }
+ }
+
def sourceDependency(dependsOn: File, source: File) = if(source != dependsOn) add(sourceDeps, source, dependsOn)
def externalBinaryDependency(binary: File, className: String, source: File)
{
@@ -120,8 +132,10 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
// TODO store this in Relations, rather than Source.
val hasMacro: Boolean = macroSources.contains(src)
val s = new xsbti.api.Source(compilation, hash, api._2, api._1, hasMacro)
- a.addSource(src, s, stamp, sourceDeps.getOrElse(src, Nil: Iterable[File]))
+ val info = SourceInfos.makeInfo(getOrNil(reporteds, src), getOrNil(unreporteds, src))
+ a.addSource(src, s, stamp, sourceDeps.getOrElse(src, Nil: Iterable[File]), info)
}
+ def getOrNil[A,B](m: collection.Map[A, Seq[B]], a: A): Seq[B] = m.get(a).toList.flatten
def addExternals(base: Analysis): Analysis = (base /: extSrcDeps) { case (a, (source, name, api)) => a.addExternalDep(source, name, api) }
def addAll[A,B](base: Analysis, m: Map[A, Set[B]])( f: (Analysis, A, B) => Analysis): Analysis =
@@ -0,0 +1,37 @@
+package sbt
+package inc
+
+ import xsbti.Problem
+
+ import java.io.File
+
+trait SourceInfo
+{
+ def reportedProblems: Seq[Problem]
+ def unreportedProblems: Seq[Problem]
+}
+trait SourceInfos
+{
+ def ++(o: SourceInfos): SourceInfos
+ def add(file: File, info: SourceInfo): SourceInfos
+ def --(files: Iterable[File]): SourceInfos
+ def get(file: File): SourceInfo
+ def allInfos: Map[File, SourceInfo]
+}
+object SourceInfos
+{
+ def empty: SourceInfos = make(Map.empty)
+ def make(m: Map[File, SourceInfo]): SourceInfos = new MSourceInfos(m)
+
+ val emptyInfo: SourceInfo = makeInfo(Nil, Nil)
+ def makeInfo(reported: Seq[Problem], unreported: Seq[Problem]): SourceInfo =
+ new MSourceInfo(reported, unreported)
+}
+private final class MSourceInfos(val allInfos: Map[File, SourceInfo]) extends SourceInfos
+{
+ def ++(o: SourceInfos) = new MSourceInfos(allInfos ++ o.allInfos)
+ def --(sources: Iterable[File]) = new MSourceInfos(allInfos -- sources)
+ def add(file: File, info: SourceInfo) = new MSourceInfos(allInfos + ((file, info)))
+ def get(file:File) = allInfos.getOrElse(file, SourceInfos.emptyInfo)
+}
+private final class MSourceInfo(val reportedProblems: Seq[Problem], val unreportedProblems: Seq[Problem]) extends SourceInfo
@@ -3,7 +3,7 @@
*/
package xsbt
-import xsbti.{AnalysisCallback,Logger,Problem,Reporter}
+import xsbti.{AnalysisCallback,Logger,Problem,Reporter,Severity}
import scala.tools.nsc.{Phase, SubComponent}
import Log.debug
@@ -17,10 +17,10 @@ class CompilerInterface
val settings = new Settings(Log.settingsError(log))
val command = Command(args.toList, settings)
- val reporter = DelegatingReporter(settings, delegate)
- def noErrors = !reporter.hasErrors && command.ok
+ val dreporter = DelegatingReporter(settings, delegate)
+ def noErrors = !dreporter.hasErrors && command.ok
- object compiler extends Global(command.settings, reporter)
+ object compiler extends Global(command.settings, dreporter)
{
object dummy // temporary fix for #4426
object sbtAnalyzer extends
@@ -65,23 +65,39 @@ class CompilerInterface
meth.setAccessible(true)
meth.invoke(this).asInstanceOf[List[SubComponent]]
}
+ def logUnreportedWarnings(seq: List[(Position,String)]): Unit = // Scala 2.10.x and later
+ {
+ for( (pos, msg) <- seq) yield
+ callback.problem(dreporter.convert(pos), msg, Severity.Warn, false)
+ }
+ def logUnreportedWarnings(count: Boolean): Unit = () // for source compatibility with Scala 2.8.x
+ def logUnreportedWarnings(count: Int): Unit = () // for source compatibility with Scala 2.9.x
+ }
+ def processUnreportedWarnings(run: compiler.Run)
+ {
+ implicit def listToBoolean[T](l: List[T]): Boolean = error("source compatibility only, should never be called")
+ implicit def listToInt[T](l: List[T]): Int = error("source compatibility only, should never be called")
+ compiler.logUnreportedWarnings(run.deprecationWarnings)
+ compiler.logUnreportedWarnings(run.uncheckedWarnings)
}
if(command.shouldStopWithInfo)
{
- reporter.info(null, command.getInfoMessage(compiler), true)
+ dreporter.info(null, command.getInfoMessage(compiler), true)
throw new InterfaceCompileFailed(args, Array(), "Compiler option supplied that disabled actual compilation.")
}
if(noErrors)
{
val run = new compiler.Run
debug(log, args.mkString("Calling Scala compiler with arguments (CompilerInterface):\n\t", "\n\t", ""))
run compile command.files
+ processUnreportedWarnings(run)
+ dreporter.problems foreach { p => callback.problem(p.position, p.message, p.severity, true) }
}
- reporter.printSummary()
+ dreporter.printSummary()
if(!noErrors)
{
debug(log, "Compilation failed (CompilerInterface)")
- throw new InterfaceCompileFailed(args, reporter.problems, "Compilation failed")
+ throw new InterfaceCompileFailed(args, dreporter.problems, "Compilation failed")
}
}
}
@@ -37,7 +37,7 @@ private final class DelegatingReporter(warnFatal: Boolean, delegate: xsbti.Repor
val severity = if(warnFatal && rawSeverity == WARNING) ERROR else rawSeverity
delegate.log(convert(pos), msg, convert(severity))
}
- private[this] def convert(posIn: Position): xsbti.Position =
+ def convert(posIn: Position): xsbti.Position =
{
val pos =
posIn match
@@ -5,9 +5,11 @@ package sbt
package inc
import xsbti.api.Source
+ import xsbti.{Position,Problem,Severity}
import java.io.File
import sbinary._
import DefaultProtocol._
+ import Logger.{m2o, position, problem}
object AnalysisFormats
{
@@ -40,8 +42,26 @@ object AnalysisFormats
}
}
- implicit def analysisFormat(implicit stampsF: Format[Stamps], apisF: Format[APIs], relationsF: Format[Relations]): Format[Analysis] =
- asProduct3( Analysis.Empty.copy _)( a => (a.stamps, a.apis, a.relations))(stampsF, apisF, relationsF)
+ implicit def analysisFormat(implicit stampsF: Format[Stamps], apisF: Format[APIs], relationsF: Format[Relations], infosF: Format[SourceInfos]): Format[Analysis] =
+ asProduct4( Analysis.Empty.copy _)( a => (a.stamps, a.apis, a.relations, a.infos))(stampsF, apisF, relationsF, infosF)
+
+ implicit def infosFormat(implicit infoF: Format[Map[File, SourceInfo]]): Format[SourceInfos] =
+ wrap[SourceInfos, Map[File, SourceInfo]]( _.allInfos, SourceInfos.make _)
+
+ implicit def infoFormat(implicit infoF: Format[Problem]): Format[SourceInfo] =
+ wrap[SourceInfo, (Seq[Problem],Seq[Problem])](si => (si.reportedProblems, si.unreportedProblems), { case (a,b) => SourceInfos.makeInfo(a,b)})
+
+ implicit def problemFormat(implicit posF: Format[Position], msgF: Format[String], sevF: Format[Severity]): Format[Problem] =
+ asProduct3(problem _)( p => (p.position, p.message, p.severity))
+
+ implicit def positionFormat: Format[Position] =
+ asProduct7( position _ )( p => (m2o(p.line), p.lineContent, m2o(p.offset), m2o(p.pointer), m2o(p.pointerSpace), m2o(p.sourcePath), m2o(p.sourceFile)))
+
+ implicit val fileOptionFormat: Format[Option[File]] = optionsAreFormat[File](fileFormat)
+ implicit val integerFormat: Format[Integer] = wrap[Integer, Int](_.toInt, Integer.valueOf)
+ implicit val severityFormat: Format[Severity] =
+ wrap[Severity, Byte]( _.ordinal.toByte, b => Severity.values.apply(b.toInt) )
+
implicit def setupFormat(implicit outDirF: Format[File], optionF: Format[CompileOptions], compilerVersion: Format[String], orderF: Format[CompileOrder.Value]): Format[CompileSetup] =
asProduct4[CompileSetup, File, CompileOptions, String, CompileOrder.Value]( (a,b,c,d) => new CompileSetup(a,b,c,d) )(s => (s.outputDirectory, s.options, s.compilerVersion, s.order))(outDirF, optionF, compilerVersion, orderF)
@@ -24,4 +24,7 @@
public void endSource(File sourcePath);
/** Called when the public API of a source file is extracted. */
public void api(File sourceFile, xsbti.api.SourceAPI source);
+ /** Provides problems discovered during compilation. These may be reported (logged) or unreported.
+ * Unreported problems are usually unreported because reporting was not enabled via a command line switch. */
+ public void problem(Position pos, String msg, Severity severity, boolean reported);
}
@@ -20,4 +20,5 @@ class TestCallback extends AnalysisCallback
def endSource(source: File) { endedSources += source }
def api(source: File, sourceAPI: xsbti.api.SourceAPI) { apis += ((source, sourceAPI)) }
+ def problem(pos: xsbti.Position, message: String, severity: xsbti.Severity, reported: Boolean) {}
}
View
@@ -211,6 +211,7 @@ object Defaults extends BuildCommon
initialCommands in GlobalScope :== "",
cleanupCommands in GlobalScope :== "",
compile <<= compileTask tag(Tags.Compile, Tags.CPU),
+ printWarnings <<= printWarningsTask,
compileIncSetup <<= compileIncSetupTask,
console <<= consoleTask,
consoleQuick <<= consoleQuickTask,
@@ -562,6 +563,12 @@ object Defaults extends BuildCommon
Compiler.inputs(classes +: data(cp), srcs, classes, optsPair._1, optsPair._2, maxErr, order)(cs, incSetup, s.log)
})
}
+ def printWarningsTask: Initialize[Task[Unit]] =
+ (streams, compile, maxErrors) map { (s, analysis, max) =>
+ val problems = analysis.infos.allInfos.values.flatMap(i => i.reportedProblems++ i.unreportedProblems)
+ val reporter = new LoggerReporter(max, s.log)
+ problems foreach { p => reporter.display(p.position, p.message, p.severity) }
+ }
def sbtPluginExtra(m: ModuleID, sbtV: String, scalaV: String): ModuleID =
m.extra(CustomPomParser.SbtVersionKey -> sbtV, CustomPomParser.ScalaVersionKey -> scalaV).copy(crossVersion = CrossVersion.Disabled)
View
@@ -141,6 +141,7 @@ object Keys
val classpathOptions = SettingKey[ClasspathOptions]("classpath-options", "Configures handling of Scala classpaths.")
val definedSbtPlugins = TaskKey[Set[String]]("defined-sbt-plugins", "The set of names of Plugin implementations defined by this project.")
val sbtPlugin = SettingKey[Boolean]("sbt-plugin", "If true, enables adding sbt as a dependency and auto-generation of the plugin descriptor file.")
+ val printWarnings = TaskKey[Unit]("print-warnings", "Shows warnings from compilation, including ones that weren't printed initially.")
val clean = TaskKey[Unit]("clean", "Deletes files produced by the build, such as generated sources, compiled classes, and task caches.")
val console = TaskKey[Unit]("console", "Starts the Scala interpreter with the project classes on the classpath.")
Oops, something went wrong.

0 comments on commit 1cbb7ce

Please sign in to comment.