From 5bc83b90a4190f6f50519a00660bdb44781d863e Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Thu, 18 Dec 2025 15:50:46 +0100 Subject: [PATCH] Error tree on outdent is zero extent Recovery on syntax error may consume remaining input, with an OUTDENT at EOF, so that the parent tree does not actually extend to EOF. Do not report spurious `def ` as ambiguous. [Cherry-picked 85c0527be946c1be8ce297a347a2132c7e2bb5bb][modified] --- .../dotty/tools/dotc/parsing/Parsers.scala | 4 +++- .../dotty/tools/dotc/parsing/Scanners.scala | 2 +- .../src/dotty/tools/dotc/typer/Checking.scala | 2 +- tests/neg/i23729.check | 14 ++++++++++++++ tests/neg/i23729.scala | 19 +++++++++++++++++++ 5 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 tests/neg/i23729.check create mode 100644 tests/neg/i23729.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ea7006eaef8d..bd867cb1b78e 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -403,7 +403,9 @@ object Parsers { false } - def errorTermTree(start: Offset): Tree = atSpan(Span(start, in.offset)) { unimplementedExpr } + def errorTermTree(start: Offset): Tree = + val end = if in.token == OUTDENT then start else in.offset + atSpan(Span(start, end)) { unimplementedExpr } private var inFunReturnType = false private def fromWithinReturnType[T](body: => T): T = { diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 1992ab32c9c6..2436f6ae3af2 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -305,7 +305,7 @@ object Scanners { println(s"\nSTART SKIP AT ${sourcePos().line + 1}, $this in $currentRegion") var noProgress = 0 // Defensive measure to ensure we always get out of the following while loop - // even if source file is weirly formatted (i.e. we never reach EOF) + // even if source file is weirdly formatted (i.e. we never reach EOF) var prevOffset = offset while !atStop && noProgress < 3 do nextToken() diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 8b6e875ab6ff..b5878eae2ee0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1175,7 +1175,7 @@ trait Checking { typr.println(i"check no double declarations $cls") def checkDecl(decl: Symbol): Unit = { - for (other <- seen(decl.name) if !decl.isAbsent() && !other.isAbsent()) { + for (other <- seen(decl.name) if decl.name != nme.ERROR && !decl.isAbsent() && !other.isAbsent()) { typr.println(i"conflict? $decl $other") def javaFieldMethodPair = decl.is(JavaDefined) && other.is(JavaDefined) && diff --git a/tests/neg/i23729.check b/tests/neg/i23729.check new file mode 100644 index 000000000000..2f067c42c6c1 --- /dev/null +++ b/tests/neg/i23729.check @@ -0,0 +1,14 @@ +-- [E018] Syntax Error: tests/neg/i23729.scala:17:33 ------------------------------------------------------------------- +17 | def start: List[Direction] = match self // error syntax + | ^^^^^ + | expression expected but match found + | + | longer explanation available when compiling with `-explain` +-- [E040] Syntax Error: tests/neg/i23729.scala:18:6 -------------------------------------------------------------------- +18 | case Empty => Nil // error poor recovery + | ^^^^ + | 'def' expected, but 'case' found +-- [E040] Syntax Error: tests/neg/i23729.scala:19:6 -------------------------------------------------------------------- +19 | case Node(_, l, _) => l.start :+ Left // error poor recovery + | ^^^^ + | 'def' expected, but 'case' found diff --git a/tests/neg/i23729.scala b/tests/neg/i23729.scala new file mode 100644 index 000000000000..b575cbc0c35f --- /dev/null +++ b/tests/neg/i23729.scala @@ -0,0 +1,19 @@ + +trait Collection[Self, Element]: + type Index + extension (self: Self) + def start: Index + +sealed trait Tree[+T] +object Tree: + case object Empty extends Tree[Nothing] + case class Node[+T](value: T, lhs: Tree[T], rhs: Tree[T]) extends Tree[T] + +enum Direction: + case Left, Right, Here +given [T]: Collection[Tree[T], T] with + type Index = List[Direction] + extension (self: Tree[T]) + def start: List[Direction] = match self // error syntax + case Empty => Nil // error poor recovery + case Node(_, l, _) => l.start :+ Left // error poor recovery