Skip to content

Commit d0ec581

Browse files
som-snytttgodzik
authored andcommitted
Permit new with trivial end
[Cherry-picked f5bf57d]
1 parent 2068de5 commit d0ec581

File tree

4 files changed

+77
-23
lines changed

4 files changed

+77
-23
lines changed

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ object Trees {
319319

320320
import WithEndMarker.*
321321

322+
/** The span of the name in the end marker, or `NoSpan` if none.
323+
*/
322324
final def endSpan(using Context): Span =
323325
if hasEndMarker then
324326
val realName = srcName.stripModuleClassSuffix.lastPart
@@ -329,8 +331,8 @@ object Trees {
329331
/** The name in source code that represents this construct,
330332
* and is the name that the user must write to create a valid
331333
* end marker.
332-
* e.g. a constructor definition is terminated in the source
333-
* code by `end this`, so it's `srcName` should return `this`.
334+
* E.g., a constructor definition is terminated in the source
335+
* code by `end this`, so its `srcName` should return `this`.
334336
*/
335337
protected def srcName(using Context): Name
336338

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

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -340,43 +340,45 @@ object Parsers {
340340
if in.isNewLine then in.nextToken() else accept(SEMI)
341341

342342
/** Parse statement separators and end markers. Ensure that there is at least
343-
* one statement separator unless the next token terminates a statement´sequence.
344-
* @param stats the statements parsed to far
343+
* one statement separator unless the next token terminates a statement sequence.
344+
* @param stats the statements parsed so far
345345
* @param noPrevStat true if there was no immediately preceding statement parsed
346346
* @param what a string indicating what kind of statement is parsed
347347
* @param altEnd a token that is also considered as a terminator of the statement
348-
* sequence (the default `EOF` already assumes to terminate a statement
348+
* sequence (the default `EOF` is already assumed to terminate a statement
349349
* sequence).
350350
* @return true if the statement sequence continues, false if it terminates.
351351
*/
352352
def statSepOrEnd[T <: Tree](stats: ListBuffer[T], noPrevStat: Boolean = false, what: String = "statement", altEnd: Token = EOF): Boolean =
353+
inline def stopping = false
354+
inline def continuing = true
353355
def recur(sepSeen: Boolean, endSeen: Boolean): Boolean =
354356
if isStatSep then
355357
in.nextToken()
356-
recur(true, endSeen)
358+
recur(sepSeen = true, endSeen)
357359
else if in.token == END then
358360
if endSeen then syntaxError(em"duplicate end marker")
359361
checkEndMarker(stats)
360362
recur(sepSeen, endSeen = true)
361363
else if isStatSeqEnd || in.token == altEnd then
362-
false
364+
stopping
363365
else if sepSeen || endSeen then
364-
true
366+
continuing
365367
else
366368
val found = in.token
367369
val statFollows = mustStartStatTokens.contains(found)
368370
syntaxError(
369371
if noPrevStat then IllegalStartOfStatement(what, isModifier, statFollows)
370372
else em"end of $what expected but ${showToken(found)} found")
371-
if mustStartStatTokens.contains(found) then
372-
false // it's a statement that might be legal in an outer context
373+
if statFollows then
374+
stopping // it's a statement that might be legal in an outer context
373375
else
374376
in.nextToken() // needed to ensure progress; otherwise we might cycle forever
375377
skip()
376-
true
378+
continuing
377379

378380
in.observeOutdented()
379-
recur(false, false)
381+
recur(sepSeen = false, endSeen = false)
380382
end statSepOrEnd
381383

382384
def rewriteNotice(version: SourceVersion = `3.0-migration`, additionalOption: String = "") =
@@ -1441,15 +1443,23 @@ object Parsers {
14411443
if sourceVersion.isMigrating then
14421444
patch(source, Span(in.offset), " ")
14431445

1444-
def possibleTemplateStart(isNew: Boolean = false): Unit =
1446+
inline transparent def possibleTemplateStart(inline isNew: Boolean = false) =
1447+
inline if isNew then newTemplateStart() else (newTemplateStart(): Unit)
1448+
1449+
/** Return true on trivial end */
1450+
def newTemplateStart(): Boolean =
14451451
in.observeColonEOL(inTemplate = true)
14461452
if in.token == COLONeol then
1447-
if in.lookahead.token == END then in.token = NEWLINE
1453+
if in.lookahead.token == END then
1454+
in.token = NEWLINE
1455+
true
14481456
else
14491457
in.nextToken()
14501458
if in.token != LBRACE then acceptIndent()
1459+
false
14511460
else
14521461
newLineOptWhenFollowedBy(LBRACE)
1462+
false
14531463

14541464
def checkEndMarker[T <: Tree](stats: ListBuffer[T]): Unit =
14551465

@@ -2644,15 +2654,22 @@ object Parsers {
26442654
val parents =
26452655
if in.isNestedStart then Nil
26462656
else constrApps(exclude = COMMA)
2647-
possibleTemplateStart(isNew = true)
2648-
parents match {
2649-
case parent :: Nil if !in.isNestedStart =>
2650-
reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent)
2651-
case tkn if in.token == INDENT =>
2652-
New(templateBodyOpt(emptyConstructor, parents, Nil))
2653-
case _ =>
2654-
New(reposition(templateBodyOpt(emptyConstructor, parents, Nil)))
2655-
}
2657+
val colonized = possibleTemplateStart(isNew = true)
2658+
parents match
2659+
case parent :: Nil if !in.isNestedStart =>
2660+
reposition:
2661+
if colonized then New(Template(emptyConstructor, parents, derived = Nil, self = EmptyValDef, body = Nil))
2662+
else if parent.isType then ensureApplied(wrapNew(parent))
2663+
else parent
2664+
case parents =>
2665+
// With brace syntax, the last token consumed by a parser is }, but with indent syntax,
2666+
// the last token consumed by a parser is OUTDENT, which causes mismatching spans, so don't reposition.
2667+
val indented = in.token == INDENT
2668+
val body =
2669+
val bo = templateBodyOpt(emptyConstructor, parents, derived = Nil)
2670+
if !indented then reposition(bo) else bo
2671+
New(body)
2672+
end newExpr
26562673

26572674
/** ExprsInParens ::= ExprInParens {`,' ExprInParens}
26582675
* Bindings ::= Binding {`,' Binding}

tests/neg/i24250.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
class C
3+
val cc =
4+
new C
5+
end new // error
6+
val single =
7+
new C: end new // error // error

tests/pos/i24250.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
trait Foo
3+
val foo =
4+
new Foo:
5+
// comment
6+
end new
7+
val foo2 =
8+
new Foo:
9+
end new
10+
val foo3 =
11+
new Foo {
12+
}
13+
end new
14+
15+
class C
16+
val c =
17+
new C:
18+
end new
19+
val c2 =
20+
new C {
21+
}
22+
23+
class D:
24+
end D
25+
val d =
26+
new D:
27+
def more = ???
28+
end new

0 commit comments

Comments
 (0)