Skip to content

Commit b1c5f0b

Browse files
authored
Merge pull request #11636 from dotty-staging/change-skipping
Improve skipping on syntax errors
2 parents 2ad7a13 + 8942ce5 commit b1c5f0b

File tree

15 files changed

+57
-103
lines changed

15 files changed

+57
-103
lines changed

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

Lines changed: 9 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,6 @@ object Parsers {
3838

3939
case class OpInfo(operand: Tree, operator: Ident, offset: Offset)
4040

41-
class ParensCounters {
42-
private var parCounts = new Array[Int](lastParen - firstParen)
43-
44-
def count(tok: Token): Int = parCounts(tok - firstParen)
45-
def change(tok: Token, delta: Int): Unit = parCounts(tok - firstParen) += delta
46-
def nonePositive: Boolean = parCounts forall (_ <= 0)
47-
}
48-
4941
enum Location(val inParens: Boolean, val inPattern: Boolean, val inArgs: Boolean):
5042
case InParens extends Location(true, false, false)
5143
case InArgs extends Location(true, false, true)
@@ -174,8 +166,6 @@ object Parsers {
174166

175167
val in: Scanner = new Scanner(source)
176168

177-
val openParens: ParensCounters = new ParensCounters
178-
179169
/** This is the general parse entry point.
180170
* Overridden by ScriptParser
181171
*/
@@ -262,55 +252,14 @@ object Parsers {
262252
}
263253

264254
/** Skip on error to next safe point.
265-
* Safe points are:
266-
* - Closing braces, provided they match an opening brace before the error point.
267-
* - Closing parens and brackets, provided they match an opening parent or bracket
268-
* before the error point and there are no intervening other kinds of parens.
269-
* - Semicolons and newlines, provided there are no intervening braces.
270-
* - Definite statement starts on new lines, provided they are not more indented
271-
* than the last known statement start before the error point.
272-
*/
273-
protected def skip(): Unit = {
274-
val skippedParens = new ParensCounters
275-
while (true) {
276-
(in.token: @switch) match {
277-
case EOF =>
278-
return
279-
case SEMI | NEWLINE | NEWLINES =>
280-
if (skippedParens.count(LBRACE) == 0) return
281-
case RBRACE =>
282-
if (openParens.count(LBRACE) > 0 && skippedParens.count(LBRACE) == 0)
283-
return
284-
skippedParens.change(LBRACE, -1)
285-
case RPAREN =>
286-
if (openParens.count(LPAREN) > 0 && skippedParens.nonePositive)
287-
return
288-
skippedParens.change(LPAREN, -1)
289-
case RBRACKET =>
290-
if (openParens.count(LBRACKET) > 0 && skippedParens.nonePositive)
291-
return
292-
skippedParens.change(LBRACKET, -1)
293-
case OUTDENT =>
294-
if (openParens.count(INDENT) > 0 && skippedParens.count(INDENT) == 0)
295-
return
296-
skippedParens.change(INDENT, -1)
297-
case LBRACE =>
298-
skippedParens.change(LBRACE, +1)
299-
case LPAREN =>
300-
skippedParens.change(LPAREN, +1)
301-
case LBRACKET=>
302-
skippedParens.change(LBRACKET, +1)
303-
case INDENT =>
304-
skippedParens.change(INDENT, +1)
305-
case _ =>
306-
if (mustStartStat &&
307-
in.isAfterLineEnd &&
308-
isLeqIndented(in.offset, lastStatOffset max 0))
309-
return
310-
}
255+
*/
256+
protected def skip(): Unit =
257+
val lastRegion = in.currentRegion
258+
def atStop =
259+
in.token == EOF
260+
|| skipStopTokens.contains(in.token) && (in.currentRegion eq lastRegion)
261+
while !atStop do
311262
in.nextToken()
312-
}
313-
}
314263

315264
def warning(msg: Message, sourcePos: SourcePosition): Unit =
316265
report.warning(msg, sourcePos)
@@ -557,15 +506,9 @@ object Parsers {
557506

558507
/* -------- COMBINATORS -------------------------------------------------------- */
559508

560-
def enclosed[T](tok: Token, body: => T): T = {
509+
def enclosed[T](tok: Token, body: => T): T =
561510
accept(tok)
562-
openParens.change(tok, 1)
563-
try body
564-
finally {
565-
accept(tok + 1)
566-
openParens.change(tok, -1)
567-
}
568-
}
511+
try body finally accept(tok + 1)
569512

570513
def inParens[T](body: => T): T = enclosed(LPAREN, body)
571514
def inBraces[T](body: => T): T = enclosed(LBRACE, body)
@@ -1431,7 +1374,6 @@ object Parsers {
14311374
functionRest(Nil)
14321375
}
14331376
else {
1434-
openParens.change(LPAREN, 1)
14351377
imods = modifiers(funTypeArgMods)
14361378
val paramStart = in.offset
14371379
val ts = funArgType() match {
@@ -1443,7 +1385,6 @@ object Parsers {
14431385
case t =>
14441386
funArgTypesRest(t, funArgType)
14451387
}
1446-
openParens.change(LPAREN, -1)
14471388
accept(RPAREN)
14481389
if isValParamList || in.token == ARROW || in.token == CTXARROW then
14491390
functionRest(ts)
@@ -2151,14 +2092,12 @@ object Parsers {
21512092
if in.token == RPAREN then
21522093
Nil
21532094
else
2154-
openParens.change(LPAREN, 1)
21552095
var mods1 = mods
21562096
if in.token == ERASED then mods1 = addModifier(mods1)
21572097
try
21582098
commaSeparated(() => binding(mods1))
21592099
finally
21602100
accept(RPAREN)
2161-
openParens.change(LPAREN, -1)
21622101
else {
21632102
val start = in.offset
21642103
val name = bindingName()
@@ -2506,7 +2445,6 @@ object Parsers {
25062445
val enums =
25072446
if (leading == LBRACE || leading == LPAREN && followingIsEnclosedGenerators()) {
25082447
in.nextToken()
2509-
openParens.change(leading, 1)
25102448
val res =
25112449
if (leading == LBRACE || in.token == CASE)
25122450
enumerators()
@@ -2516,7 +2454,6 @@ object Parsers {
25162454
if (in.token == RPAREN || pats.length > 1) {
25172455
wrappedEnums = false
25182456
accept(RPAREN)
2519-
openParens.change(LPAREN, -1)
25202457
atSpan(start) { makeTupleOrParens(pats) } // note: alternatives `|' need to be weeded out by typer.
25212458
}
25222459
else pats.head
@@ -2525,7 +2462,6 @@ object Parsers {
25252462
if (wrappedEnums) {
25262463
val closingOnNewLine = in.isAfterLineEnd
25272464
accept(leading + 1)
2528-
openParens.change(leading, -1)
25292465
def hasMultiLineEnum =
25302466
res.exists { t =>
25312467
val pos = t.sourcePos

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,6 +1383,22 @@ object Scanners {
13831383
private def useOuterWidth(): Unit =
13841384
if enclosing.knownWidth == null then enclosing.useOuterWidth()
13851385
knownWidth = enclosing.knownWidth
1386+
1387+
private def delimiter = this match
1388+
case _: InString => "}(in string)"
1389+
case InParens(LPAREN, _) => ")"
1390+
case InParens(LBRACKET, _) => "]"
1391+
case _: InBraces => "}"
1392+
case _: InCase => "=>"
1393+
case _: Indented => "UNDENT"
1394+
1395+
/** Show open regions as list of lines with decreasing indentations */
1396+
def visualize: String =
1397+
indentWidth.toPrefix
1398+
+ delimiter
1399+
+ outer.match
1400+
case null => ""
1401+
case next: Region => "\n" + next.visualize
13861402
end Region
13871403

13881404
case class InString(multiLine: Boolean, outer: Region) extends Region

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,5 +287,7 @@ object Tokens extends TokensCommon {
287287

288288
final val endMarkerTokens = identifierTokens | BitSet(IF, WHILE, FOR, MATCH, TRY, NEW, THROW, GIVEN, VAL, THIS)
289289

290+
final val skipStopTokens = BitSet(SEMI, NEWLINE, NEWLINES, RBRACE, RPAREN, RBRACKET, OUTDENT)
291+
290292
final val softModifierNames = Set(nme.inline, nme.opaque, nme.open, nme.transparent, nme.infix)
291293
}

tests/neg/errpos.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
object Test {
22
val x = // error: expression expected
3-
val y = 2 // error: ';' expected
3+
val y = 2
44

55
val z = // error: expression expected
66

77
// ...
8-
val a = 3 // error: ';' expected
8+
val a = 3
99

1010
val b = type // error: expression expected (on "type")
1111

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
object Test {
22
extension [T] (t: T) {
3-
val v: T = ??? // error // error
3+
val v: T = ??? // error
44
}
55
}

tests/neg/i10546.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ object test:
22
def times(num : Int)(block : => Unit) : Unit = ()
33
times(10): println("ah") // error: end of statement expected but '(' found // error
44

5-
def foo: Set(Int) = Set(1) // error: end of statement expected but '(' found // error // error
5+
def foo: Set(Int) = Set(1)

tests/neg/i4373.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class A4 extends _ with Base // error
1616
object Test {
1717
type T1 = _ // error
1818
type T2 = _[Int] // error // error
19-
type T3 = _ { type S } // error
19+
type T3 = _ { type S } // error // error
2020
type T4 = [X] =>> _ // error
2121

2222
// Open questions:

tests/neg/i4934.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ object Main {
88

99
object Foo {
1010
val a = ""); // error: `}` expected but `)` found
11-
} // error: eof expected
11+
}

tests/neg/i5004.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
object i0 {
22
1 match {
3-
def this(): Int // error // error
4-
def this() // error
5-
} // error
3+
def this(): Int // error
4+
def this()
5+
}
66
}

tests/neg/i5032b.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
class a@
2-
class b@ // error // error
2+
class b@ // error

tests/neg/i7751.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
val a = Some(a=a,)=> // error // error
2-
val a = Some(x=y,)=> // error
2+
val a = Some(x=y,)=>

tests/neg/i8731.scala

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,17 @@ object test:
33
3 match
44
case 3 => ???
55
end match
6-
case _ => () // error: missing parameter type
6+
case _ => () // error: expected start of definition
77
end match
88

99
if 3 == 3 then
1010
()
1111
end if
1212
else // error: illegal start of definition
1313
()
14-
end if
14+
end if // error: misaligned end marker
1515

1616
class Test {
1717
val test = 3
18-
end Test // error: misaligned end marker
19-
}
20-
21-
while
22-
3 == 3
23-
end while // error: `do` expected
24-
do ()
25-
26-
for
27-
a <- Seq()
28-
end for // error: `yield` or `do` expected
29-
do ()
18+
end Test // error: misaligned end marker
19+
} // error: eof expected, but unindent found

tests/neg/i8731a.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
object test:
2+
while
3+
3 == 3
4+
end while // error: `do` expected
5+
do ()
6+
7+
for
8+
a <- Seq()
9+
end for // error: `yield` or `do` expected
10+
do () // error: expression expected but eof found

tests/neg/i9328.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ case class Foo()
1414

1515
case class Bar(
1616
} { ; // error
17-
object Bar { // error
17+
object Bar {
1818
class Foo(a: Int) extends AnyVal
1919
Foo()
2020
}
21-
type Bar // error
21+
type Bar

tests/neg/indent.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ object Test {
44
val y3 =
55
if (1) max 10 gt 0 // error: end of statement expected but integer literal found // error // error // error
66
1
7-
else // error: end of statement expected but 'else' found
8-
2 // error
9-
}
7+
else
8+
2
9+
} // error

0 commit comments

Comments
 (0)