Skip to content

Commit f5bf57d

Browse files
committed
Permit new with trivial end
1 parent 25401d0 commit f5bf57d

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
@@ -323,6 +323,8 @@ object Trees {
323323

324324
import WithEndMarker.*
325325

326+
/** The span of the name in the end marker, or `NoSpan` if none.
327+
*/
326328
final def endSpan(using Context): Span =
327329
if hasEndMarker then
328330
val realName = srcName.stripModuleClassSuffix.lastPart
@@ -333,8 +335,8 @@ object Trees {
333335
/** The name in source code that represents this construct,
334336
* and is the name that the user must write to create a valid
335337
* end marker.
336-
* e.g. a constructor definition is terminated in the source
337-
* code by `end this`, so it's `srcName` should return `this`.
338+
* E.g., a constructor definition is terminated in the source
339+
* code by `end this`, so its `srcName` should return `this`.
338340
*/
339341
protected def srcName(using Context): Name
340342

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

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

351351
/** Parse statement separators and end markers. Ensure that there is at least
352-
* one statement separator unless the next token terminates a statement´sequence.
353-
* @param stats the statements parsed to far
352+
* one statement separator unless the next token terminates a statement sequence.
353+
* @param stats the statements parsed so far
354354
* @param noPrevStat true if there was no immediately preceding statement parsed
355355
* @param what a string indicating what kind of statement is parsed
356356
* @param altEnd a token that is also considered as a terminator of the statement
357-
* sequence (the default `EOF` already assumes to terminate a statement
357+
* sequence (the default `EOF` is already assumed to terminate a statement
358358
* sequence).
359359
* @return true if the statement sequence continues, false if it terminates.
360360
*/
361361
def statSepOrEnd[T <: Tree](stats: ListBuffer[T], noPrevStat: Boolean = false, what: String = "statement", altEnd: Token = EOF): Boolean =
362+
inline def stopping = false
363+
inline def continuing = true
362364
def recur(sepSeen: Boolean, endSeen: Boolean): Boolean =
363365
if isStatSep then
364366
in.nextToken()
365-
recur(true, endSeen)
367+
recur(sepSeen = true, endSeen)
366368
else if in.token == END then
367369
if endSeen then syntaxError(em"duplicate end marker")
368370
checkEndMarker(stats)
369371
recur(sepSeen, endSeen = true)
370372
else if isStatSeqEnd || in.token == altEnd then
371-
false
373+
stopping
372374
else if sepSeen || endSeen then
373-
true
375+
continuing
374376
else
375377
val found = in.token
376378
val statFollows = mustStartStatTokens.contains(found)
377379
syntaxError(
378380
if noPrevStat then IllegalStartOfStatement(what, isModifier, statFollows)
379381
else em"end of $what expected but ${showToken(found)} found")
380-
if mustStartStatTokens.contains(found) then
381-
false // it's a statement that might be legal in an outer context
382+
if statFollows then
383+
stopping // it's a statement that might be legal in an outer context
382384
else
383385
in.nextToken() // needed to ensure progress; otherwise we might cycle forever
384386
skip()
385-
true
387+
continuing
386388

387389
in.observeOutdented()
388-
recur(false, false)
390+
recur(sepSeen = false, endSeen = false)
389391
end statSepOrEnd
390392

391393
def rewriteNotice(version: SourceVersion = `3.0-migration`, additionalOption: String = "") =
@@ -1566,15 +1568,23 @@ object Parsers {
15661568
if MigrationVersion.Scala2to3.needsPatch then
15671569
patch(source, Span(in.offset), " ")
15681570

1569-
def possibleTemplateStart(isNew: Boolean = false): Unit =
1571+
inline transparent def possibleTemplateStart(inline isNew: Boolean = false) =
1572+
inline if isNew then newTemplateStart() else (newTemplateStart(): Unit)
1573+
1574+
/** Return true on trivial end */
1575+
def newTemplateStart(): Boolean =
15701576
in.observeColonEOL(inTemplate = true)
15711577
if in.token == COLONeol then
1572-
if in.lookahead.token == END then in.token = NEWLINE
1578+
if in.lookahead.token == END then
1579+
in.token = NEWLINE
1580+
true
15731581
else
15741582
in.nextToken()
15751583
if in.token != LBRACE then acceptIndent()
1584+
false
15761585
else
15771586
newLineOptWhenFollowedBy(LBRACE)
1587+
false
15781588

15791589
def checkEndMarker[T <: Tree](stats: ListBuffer[T]): Unit =
15801590

@@ -2915,15 +2925,22 @@ object Parsers {
29152925
val parents =
29162926
if in.isNestedStart then Nil
29172927
else constrApps(exclude = COMMA)
2918-
possibleTemplateStart(isNew = true)
2919-
parents match {
2920-
case parent :: Nil if !in.isNestedStart =>
2921-
reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent)
2922-
case tkn if in.token == INDENT =>
2923-
New(templateBodyOpt(emptyConstructor, parents, Nil))
2924-
case _ =>
2925-
New(reposition(templateBodyOpt(emptyConstructor, parents, Nil)))
2926-
}
2928+
val colonized = possibleTemplateStart(isNew = true)
2929+
parents match
2930+
case parent :: Nil if !in.isNestedStart =>
2931+
reposition:
2932+
if colonized then New(Template(emptyConstructor, parents, derived = Nil, self = EmptyValDef, body = Nil))
2933+
else if parent.isType then ensureApplied(wrapNew(parent))
2934+
else parent
2935+
case parents =>
2936+
// With brace syntax, the last token consumed by a parser is }, but with indent syntax,
2937+
// the last token consumed by a parser is OUTDENT, which causes mismatching spans, so don't reposition.
2938+
val indented = in.token == INDENT
2939+
val body =
2940+
val bo = templateBodyOpt(emptyConstructor, parents, derived = Nil)
2941+
if !indented then reposition(bo) else bo
2942+
New(body)
2943+
end newExpr
29272944

29282945
/** ExprsInParens ::= ExprInParens {`,' ExprInParens}
29292946
* | NamedExprInParens {‘,’ NamedExprInParens}

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)