Skip to content

Commit b419803

Browse files
committed
Issue warnings if code is indented too far to the left
In a brace-delimited region, if there would be an "unindent" between one line and the next, a warning is now issued that states: Line is indented too far to the left or a `}' is missing This is very helpful in finding missing closing braces. The tests are fixed to avoid the warning except for triple-quoted-expr.scala. The latter has a """ at the start of a line.
1 parent 16d507d commit b419803

31 files changed

+190
-172
lines changed

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

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -492,31 +492,36 @@ object Scanners {
492492
indentPrefix = LBRACE
493493
case _ =>
494494
}
495+
def isMatchCatchFollow(prev: Token) =
496+
nextWidth == lastWidth
497+
&& (prev == MATCH || prev == CATCH)
498+
&& token != CASE
495499
if (newlineIsSeparating &&
496500
canEndStatTokens.contains(lastToken) &&
497501
canStartStatTokens.contains(token) &&
498502
!isLeadingInfixOperator())
499503
insert(if (pastBlankLine) NEWLINES else NEWLINE, lineOffset)
500-
else if (indentIsSignificant)
501-
if (nextWidth < lastWidth
502-
|| nextWidth == lastWidth && (indentPrefix == MATCH || indentPrefix == CATCH) && token != CASE)
503-
currentRegion match {
504-
case r: Indented
505-
if !r.isOutermost &&
506-
!isLeadingInfixOperator() &&
507-
!statCtdTokens.contains(lastToken) =>
508-
currentRegion = r.enclosing
509-
insert(OUTDENT, offset)
510-
handleEndMarkers(nextWidth)
511-
case _ =>
512-
}
513-
else if (lastWidth < nextWidth ||
514-
lastWidth == nextWidth && (lastToken == MATCH || lastToken == CATCH) && token == CASE) {
515-
if (canStartIndentTokens.contains(lastToken)) {
504+
else if indentIsSignificant then
505+
if nextWidth < lastWidth
506+
|| nextWidth == lastWidth && (indentPrefix == MATCH || indentPrefix == CATCH) && token != CASE then
507+
if !currentRegion.isOutermost &&
508+
!isLeadingInfixOperator() &&
509+
!statCtdTokens.contains(lastToken) then
510+
currentRegion match
511+
case r: Indented =>
512+
currentRegion = r.enclosing
513+
insert(OUTDENT, offset)
514+
handleEndMarkers(nextWidth)
515+
case r: InBraces if token != RBRACE && !statCtdTokens.contains(token) =>
516+
ctx.warning("Line is indented too far to the left or a `}' is missing",
517+
source.atSpan(Span(offset)))
518+
case _ =>
519+
520+
else if lastWidth < nextWidth
521+
|| lastWidth == nextWidth && (lastToken == MATCH || lastToken == CATCH) && token == CASE then
522+
if canStartIndentTokens.contains(lastToken) then
516523
currentRegion = Indented(nextWidth, Set(), lastToken, currentRegion)
517524
insert(INDENT, offset)
518-
}
519-
}
520525
else if (lastWidth != nextWidth)
521526
errorButContinue(
522527
i"""Incompatible combinations of tabs and spaces in indentation prefixes.

library/src/scala/tasty/reflect/TypeOrBoundsOps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ trait TypeOrBoundsOps extends Core {
9696
internal.matchTermRef(typeOrBounds).map(x => (x.qualifier, x.name))
9797
}
9898

99-
object IsTypeRef {
99+
object IsTypeRef {
100100
/** Matches any TypeRef and returns it */
101101
def unapply(tpe: TypeOrBounds)(given ctx: Context): Option[TypeRef] =
102102
internal.matchTypeRef(tpe)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test {
2+
3+
if (true) {
4+
println("hi")
5+
6+
println("!") // error: too far to the left
7+
}
8+
// error: too far to the left

tests/neg/indentRight.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test {
2+
3+
if (true)
4+
println("hi")
5+
}
6+
7+
println("!") // error: expected a toplevel definition
8+
}

tests/pos/Labels.scala

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
object Labels {
22
def main(args: Array[String]): Unit = {
3-
var i = 10
4-
while(i>0) {
5-
var j = 0
6-
while(j<i) {
7-
println(j +" " + i)
8-
j = j + 1
9-
}
10-
i = i - 1}
11-
pattern(1)
12-
pattern(2)
13-
pattern(3)
14-
}
3+
var i = 10
4+
while(i>0) {
5+
var j = 0
6+
while(j<i) {
7+
println(j +" " + i)
8+
j = j + 1
9+
}
10+
i = i - 1
11+
}
12+
pattern(1)
13+
pattern(2)
14+
pattern(3)
15+
}
1516

16-
def pattern(a: Int) = a match {
17-
case 1 if (a>0) => println("one")
18-
case t@2 => println("two" + t)
19-
case _ => println("default")
20-
}
17+
def pattern(a: Int) = a match {
18+
case 1 if (a>0) => println("one")
19+
case t@2 => println("two" + t)
20+
case _ => println("default")
21+
}
2122
}

tests/pos/i1442.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
object Test1442 {
22
final def sumMinimized[B](num: Numeric[B]): Int = {
3-
var cse: scala.math.Numeric.type = null.asInstanceOf[scala.math.Numeric.type]
4-
({cse = scala.math.Numeric; num eq cse.IntIsIntegral} ||
3+
var cse: scala.math.Numeric.type = null.asInstanceOf[scala.math.Numeric.type]
4+
({cse = scala.math.Numeric; num eq cse.IntIsIntegral} ||
55
(num eq cse.BigDecimalAsIfIntegral))
66
2
7-
}
7+
}
88

99
final def sum[B](implicit num: Numeric[B]): B = {
1010
// arithmetic series formula can be used for regular addition
11-
var cse: scala.math.Numeric.type = null.asInstanceOf[scala.math.Numeric.type]
11+
var cse: scala.math.Numeric.type = null.asInstanceOf[scala.math.Numeric.type]
1212
if ({cse = scala.math.Numeric; num eq cse.IntIsIntegral}||
1313
(num eq cse.BigIntIsIntegral)||
1414
(num eq cse.ShortIsIntegral)||
1515
(num eq cse.ByteIsIntegral)||
1616
(num eq cse.CharIsIntegral)||
1717
(num eq cse.LongIsIntegral)||
18-
(num eq cse.BigDecimalIsFractional)) {
18+
(num eq cse.BigDecimalIsFractional)) {
1919
null.asInstanceOf[B]
2020
} else null.asInstanceOf[B]
21-
}
21+
}
2222
}

tests/pos/reference/union-types.scala

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,36 @@ package unionTypes
22

33
object t1 {
44

5-
type Hash = Int
5+
type Hash = Int
66

7-
case class UserName(name: String)
8-
case class Password(hash: Hash)
7+
case class UserName(name: String)
8+
case class Password(hash: Hash)
99

10-
def help(id: UserName | Password) = {
11-
val user = id match {
12-
case UserName(name) => lookupName(name)
13-
case Password(hash) => lookupPassword(hash)
10+
def help(id: UserName | Password) = {
11+
val user = id match {
12+
case UserName(name) => lookupName(name)
13+
case Password(hash) => lookupPassword(hash)
14+
}
1415
}
15-
}
1616

17-
def lookupName(name: String) = ???
18-
def lookupPassword(hash: Hash) = ???
17+
def lookupName(name: String) = ???
18+
def lookupPassword(hash: Hash) = ???
1919

2020
}
2121

2222
object t2 {
2323
import t1._
2424

25-
trait Admin
26-
trait UserData
25+
trait Admin
26+
trait UserData
2727

28-
trait L { def lookup(admin: Admin): Object }
28+
trait L { def lookup(admin: Admin): Object }
2929

30-
case class UserName(name: String) extends L {
31-
def lookup(admin: Admin): UserData = ???
32-
}
33-
case class Password(hash: Hash) extends L {
34-
def lookup(admin: Admin): UserData = ???
35-
}
30+
case class UserName(name: String) extends L {
31+
def lookup(admin: Admin): UserData = ???
32+
}
33+
case class Password(hash: Hash) extends L {
34+
def lookup(admin: Admin): UserData = ???
35+
}
3636

3737
}

tests/pos/scoping1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ object This extends App {
22
trait A {
33
def foo(): Unit
44
}
5-
abstract class C { self: A =>
5+
abstract class C { self: A =>
66
def bar() = this.foo()
77
}
88
class D extends C with A {

tests/pos/simple-exceptions.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ object Test {
77
def main(args: Array[String]): Unit = {
88
try {
99
try {
10-
Console.println("hi!")
10+
Console.println("hi!")
1111
sys.error("xx")
1212
}
1313
finally Console.println("ho!")

tests/pos/spec-partialmap.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import scala.collection.{Iterable,IterableOps};
44
trait PartialMap[@specialized A,@specialized B] extends PartialFunction[A,B] with Iterable[(A,B)] {
55
// commenting out this declaration gives a different exception.
66
/** Getter for all values for which the given key function returns true. */
7-
def apply(f : (A => Boolean)) : Iterator[B] =
8-
for ((k,v) <- iterator; if f(k)) yield v
7+
def apply(f : (A => Boolean)) : Iterator[B] =
8+
for ((k,v) <- iterator; if f(k)) yield v
99

1010
// if this is commented, it compiles fine:
1111
def apply[This <: Iterable[A]](keys : IterableOps[A, Iterable, This]): Iterable[B] = keys.map(apply)

0 commit comments

Comments
 (0)