Skip to content

Commit 7dec935

Browse files
committed
Emit a friendly error when scala-xml is missing
This is similar to what Scala 2 does.
1 parent 4bb88c3 commit 7dec935

File tree

5 files changed

+52
-4
lines changed

5 files changed

+52
-4
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ class Definitions {
223223
lazy val Sys_errorR: TermRef = SysPackage.moduleClass.requiredMethodRef(nme.error)
224224
def Sys_error(implicit ctx: Context): Symbol = Sys_errorR.symbol
225225

226+
lazy val ScalaXmlPackageClass: Symbol = ctx.getPackageClassIfDefined("scala.xml")
227+
226228
lazy val CompiletimePackageObjectRef: TermRef = ctx.requiredModuleRef("scala.compiletime.package")
227229
lazy val CompiletimePackageObject: Symbol = CompiletimePackageObjectRef.symbol.moduleClass
228230
lazy val Compiletime_errorR: TermRef = CompiletimePackageObjectRef.symbol.requiredMethodRef(nme.error)

compiler/src/dotty/tools/dotc/core/Symbols.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,15 @@ trait Symbols { this: Context =>
383383
.requiredSymbol("class", name, generateStubs = false)(_.isClass)
384384
}
385385

386+
/** Get ClassSymbol if package is either defined in current compilation run
387+
* or present on classpath.
388+
* Returns NoSymbol otherwise. */
389+
def getPackageClassIfDefined(path: PreName): Symbol = {
390+
val name = path.toTypeName
391+
base.staticRef(name, isPackage = true, generateStubs = false)
392+
.requiredSymbol("package", name, generateStubs = false)(_ is PackageClass)
393+
}
394+
386395
def requiredModule(path: PreName): TermSymbol = {
387396
val name = path.toTermName
388397
base.staticRef(name).requiredSymbol("object", name)(_ is Module).asTerm

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ package parsing
55
import scala.annotation.internal.sharable
66
import scala.collection.mutable.ListBuffer
77
import scala.collection.immutable.BitSet
8-
import util.{ SourceFile, SourcePosition }
8+
import util.{ SourceFile, SourcePosition, NoSourcePosition }
99
import Tokens._
1010
import Scanners._
1111
import xml.MarkupParsers.MarkupParser
@@ -473,8 +473,21 @@ object Parsers {
473473

474474
/* -------------- XML ---------------------------------------------------- */
475475

476-
/** the markup parser */
477-
lazy val xmlp: xml.MarkupParsers.MarkupParser = new MarkupParser(this, true)
476+
/** The markup parser.
477+
* The first time this lazy val is accessed, we assume we were trying to parse an XML literal.
478+
* The current position is recorded for later error reporting if it turns out
479+
* that we don't have scala-xml on the compilation classpath.
480+
*/
481+
lazy val xmlp: xml.MarkupParsers.MarkupParser = {
482+
myFirstXmlPos = source.atSpan(Span(in.offset))
483+
new MarkupParser(this, true)
484+
}
485+
486+
/** The position of the first XML literal encountered while parsing,
487+
* NoSourcePosition if there were no XML literals.
488+
*/
489+
def firstXmlPos: SourcePosition = myFirstXmlPos
490+
private[this] var myFirstXmlPos: SourcePosition = NoSourcePosition
478491

479492
object symbXMLBuilder extends xml.SymbolicXMLBuilder(this, true) // DEBUG choices
480493

compiler/src/dotty/tools/dotc/typer/FrontEnd.scala

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import parsing.Parsers.Parser
1111
import config.Config
1212
import config.Printers.{typr, default}
1313
import util.Stats._
14+
import util.{ SourcePosition, NoSourcePosition }
1415
import scala.util.control.NonFatal
1516
import ast.Trees._
1617

@@ -41,13 +42,27 @@ class FrontEnd extends Phase {
4142

4243
def parse(implicit ctx: Context): Unit = monitor("parsing") {
4344
val unit = ctx.compilationUnit
45+
46+
var firstXmlPos: SourcePosition = NoSourcePosition
47+
4448
unit.untpdTree =
4549
if (unit.isJava) new JavaParser(unit.source).parse()
46-
else new Parser(unit.source).parse()
50+
else {
51+
val p = new Parser(unit.source)
52+
val tree = p.parse()
53+
firstXmlPos = p.firstXmlPos
54+
tree
55+
}
56+
4757
val printer = if (ctx.settings.Xprint.value.contains("parser")) default else typr
4858
printer.println("parsed:\n" + unit.untpdTree.show)
4959
if (Config.checkPositions)
5060
unit.untpdTree.checkPos(nonOverlapping = !unit.isJava && !ctx.reporter.hasErrors)
61+
62+
if (firstXmlPos.exists && !defn.ScalaXmlPackageClass.exists)
63+
ctx.error("""To support XML literals, your project must depend on scala-xml.
64+
|See https://github.com/scala/scala-xml for more information.""".stripMargin,
65+
firstXmlPos)
5166
}
5267

5368
def enterSyms(implicit ctx: Context): Unit = monitor("indexing") {

tests/neg/xml.check

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-- Error: tests/neg/xml.scala:2:10 -------------------------------------------------------------------------------------
2+
2 | val a = <foo>bla</foo> // error // error
3+
| ^
4+
| To support XML literals, your project must depend on scala-xml.
5+
| See https://github.com/scala/scala-xml for more information.
6+
-- [E008] Member Not Found Error: tests/neg/xml.scala:2:11 -------------------------------------------------------------
7+
2 | val a = <foo>bla</foo> // error // error
8+
| ^
9+
| value xml is not a member of scala - did you mean scala.&?

0 commit comments

Comments
 (0)