Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4692,6 +4692,10 @@ object Parsers {
def packaging(start: Int): Tree =
val pkg = qualId()
possibleTemplateStart()
if in.token != INDENT && in.token != LBRACE then
val prefix = "':' or "
val suffix = "\nNested package statements that are not at the beginning of the file require braces or ':' with an indented body."
syntaxErrorOrIncomplete(ExpectedTokenButFound(LBRACE, in.token, prefix = prefix, suffix = suffix), in.lastOffset)
val stats = inDefScopeBraces(topStatSeq(), rewriteWithColon = true)
makePackaging(start, pkg, stats)

Expand Down
22 changes: 18 additions & 4 deletions compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -243,14 +243,28 @@ trait MessageRendering {
given Level = Level(level)
given Offset = Offset(maxLineNumber.toString.length + 2)
val sb = StringBuilder()
val posString = posStr(pos, msg, diagnosticLevel(dia))
def adjust(pos: SourcePosition): SourcePosition =
if pos.span.isSynthetic
&& pos.span.isZeroExtent
&& pos.span.exists
&& pos.span.start == pos.source.length
&& pos.source(pos.span.start - 1) == '\n'
then
pos.withSpan(pos.span.shift(-1))
else
pos
val adjusted = adjust(pos)
val posString = posStr(adjusted, msg, diagnosticLevel(dia))
if (posString.nonEmpty) sb.append(posString).append(EOL)
if (pos.exists) {
val pos1 = pos.nonInlined
if (pos1.exists && pos1.source.file.exists) {
val (srcBefore, srcAfter, offset) = sourceLines(pos1)
val marker = positionMarker(pos1)
val err = errorMsg(pos1, msg.message)
val readjusted =
if pos1 == pos then adjusted
else adjust(pos1)
val (srcBefore, srcAfter, offset) = sourceLines(readjusted)
val marker = positionMarker(readjusted)
val err = errorMsg(readjusted, msg.message)
sb.append((srcBefore ::: marker :: err :: srcAfter).mkString(EOL))

if inlineStack.nonEmpty then
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1231,7 +1231,7 @@ extends ReferenceMsg(ForwardReferenceExtendsOverDefinitionID) {
|"""
}

class ExpectedTokenButFound(expected: Token, found: Token, prefix: String = "")(using Context)
class ExpectedTokenButFound(expected: Token, found: Token, prefix: String = "", suffix: String = "")(using Context)
extends SyntaxMsg(ExpectedTokenButFoundID) {

private def foundText = Tokens.showToken(found)
Expand All @@ -1240,7 +1240,7 @@ extends SyntaxMsg(ExpectedTokenButFoundID) {
val expectedText =
if (Tokens.isIdentifier(expected)) "an identifier"
else Tokens.showToken(expected)
i"""$prefix$expectedText expected, but $foundText found"""
i"""$prefix$expectedText expected, but $foundText found$suffix"""

def explain(using Context) =
if (Tokens.isIdentifier(expected) && Tokens.isKeyword(found))
Expand Down
73 changes: 41 additions & 32 deletions compiler/test/dotty/tools/vulpix/ParallelTesting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ import dotty.tools.vulpix.TestConfiguration.defaultOptions
* using this, you should be running your JUnit tests **sequentially**, as the
* test suite itself runs with a high level of concurrency.
*/
trait ParallelTesting extends RunnerOrchestration { self =>
import ParallelTesting._
trait ParallelTesting extends RunnerOrchestration:
import ParallelTesting.*

/** If the running environment supports an interactive terminal, each `Test`
* will be run with a progress bar and real time feedback
Expand Down Expand Up @@ -978,47 +978,47 @@ trait ParallelTesting extends RunnerOrchestration { self =>
case null => errorMap.put(key, 1)
case n => errorMap.put(key, n+1)
expectedErrors += 1
files.filter(isSourceFile).foreach { file =>
Using(Source.fromFile(file, StandardCharsets.UTF_8.name)) { source =>
source.getLines().zipWithIndex.foreach { case (line, lineNbr) =>
comment.findAllMatchIn(line).foreach { m =>
for file <- files if isSourceFile(file) do
Using.resource(Source.fromFile(file, StandardCharsets.UTF_8.name)): source =>
source.getLines().zipWithIndex.foreach: (line, lineNbr) =>
comment.findAllMatchIn(line).foreach: m =>
m.group(2) match
case prefix if m.group(1).isEmpty =>
val what = Option(prefix).getOrElse("")
echo(s"Warning: ${file.getCanonicalPath}:${lineNbr}: found `//${what}error` but expected `// ${what}error`, skipping comment")
case "nopos-" => bump("nopos")
case "anypos-" => bump("anypos")
case _ => bump(s"${file.getPath}:${lineNbr+1}")
}
}
}.get
}
case prefix if m.group(1).isEmpty =>
val what = Option(prefix).getOrElse("")
echo(s"Warning: ${file.getCanonicalPath}:${lineNbr}: found `//${what}error` but expected `// ${what}error`, skipping comment")
case "nopos-" => bump("nopos")
case "anypos-" => bump("anypos")
case _ => bump(s"${file.getPath}:${lineNbr+1}")
(errorMap, expectedErrors)
end getErrorMapAndExpectedCount

// return unfulfilled expected errors and unexpected diagnostics
// return unfulfilled expected errors and unexpected diagnostics.
// the errorMap of expected errors is drained and returned as unfulfilled.
// a diagnostic at EOF after NL is recorded at the preceding line,
// to obviate `anypos-error` in that case.
def getMissingExpectedErrors(errorMap: HashMap[String, Integer], reporterErrors: Iterator[Diagnostic]): (List[String], List[String]) =
val unexpected, unpositioned = ListBuffer.empty[String]
// For some reason, absolute paths leak from the compiler itself...
def relativize(path: String): String = path.split(JFile.separatorChar).dropWhile(_ != "tests").mkString(JFile.separator)
def seenAt(key: String): Boolean =
errorMap.get(key) match
case null => false
case 1 => errorMap.remove(key); true
case n => errorMap.put(key, n - 1); true
case null => false
case 1 => errorMap.remove(key); true
case n => errorMap.put(key, n - 1); true
def sawDiagnostic(d: Diagnostic): Unit =
d.pos.nonInlined match
case srcpos if srcpos.exists =>
val key = s"${relativize(srcpos.source.file.toString)}:${srcpos.line + 1}"
if !seenAt(key) then unexpected += key
case srcpos =>
if !seenAt("nopos") then unpositioned += relativize(srcpos.source.file.toString)
val srcpos = d.pos.nonInlined.adjustedAtEOF
val relatively = relativize(srcpos.source.file.toString)
if srcpos.exists then
val key = s"${relatively}:${srcpos.line + 1}"
if !seenAt(key) then unexpected += key
else
if !seenAt("nopos") then unpositioned += relatively

reporterErrors.foreach(sawDiagnostic)

errorMap.get("anypos") match
case n if n == unexpected.size => errorMap.remove("anypos") ; unexpected.clear()
case _ =>
if errorMap.get("anypos") == unexpected.size then
errorMap.remove("anypos")
unexpected.clear()

(errorMap.asScala.keys.toList, (unexpected ++ unpositioned).toList)
end getMissingExpectedErrors
Expand Down Expand Up @@ -1838,9 +1838,8 @@ trait ParallelTesting extends RunnerOrchestration { self =>
def isUserDebugging: Boolean =
val mxBean = ManagementFactory.getRuntimeMXBean
mxBean.getInputArguments.asScala.exists(_.contains("jdwp"))
}

object ParallelTesting {
object ParallelTesting:

def defaultOutputDir: String = "out"+JFile.separator

Expand All @@ -1855,4 +1854,14 @@ object ParallelTesting {
def isBestEffortTastyFile(f: JFile): Boolean =
f.getName.endsWith(".betasty")

}
extension (pos: SourcePosition)
private def adjustedAtEOF: SourcePosition =
if pos.span.isSynthetic
&& pos.span.isZeroExtent
&& pos.span.exists
&& pos.span.start == pos.source.length
&& pos.source(pos.span.start - 1) == '\n'
then
pos.withSpan(pos.span.shift(-1))
else
pos
9 changes: 9 additions & 0 deletions tests/neg/i23815.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- [E040] Syntax Error: tests/neg/i23815.scala:7:10 --------------------------------------------------------------------
7 |package t2 // error
| ^
| ':' or '{' expected, but 'end of statement' found
| Nested package statements that are not at the beginning of the file require braces or ':' with an indented body.
-- [E040] Syntax Error: tests/neg/i23815.scala:9:8 ---------------------------------------------------------------------
9 |// error
| ^
| '}' expected, but eof found
9 changes: 9 additions & 0 deletions tests/neg/i23815.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

package test6

package t1
trait T1

package t2 // error
trait T2
// error
4 changes: 2 additions & 2 deletions tests/neg/parser-stability-11.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package x0 {
case class x0 // error // error
}
package x0
package x0 // error
class x0 // error
// error
// error
Loading