From b41980334dfcb571d39dfcbd936e6b58d8c36179 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 19 Sep 2019 16:03:15 +0200 Subject: [PATCH 1/8] 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. --- .../dotty/tools/dotc/parsing/Scanners.scala | 41 +++++---- .../scala/tasty/reflect/TypeOrBoundsOps.scala | 2 +- .../fatal-warnings/indentLeft.scala | 8 ++ tests/neg/indentRight.scala | 8 ++ tests/pos/Labels.scala | 35 ++++---- tests/pos/i1442.scala | 12 +-- tests/pos/reference/union-types.scala | 38 ++++---- tests/pos/scoping1.scala | 2 +- tests/pos/simple-exceptions.scala | 2 +- tests/pos/spec-partialmap.scala | 4 +- tests/pos/spec-traits.scala | 2 +- tests/pos/t1164.scala | 8 +- tests/pos/t2591.scala | 6 +- tests/pos/t3278.scala | 2 +- tests/pos/t443.scala | 14 +-- tests/pos/t5508.scala | 6 +- tests/pos/t6335.scala | 2 +- tests/pos/t8230a.scala | 4 +- tests/pos/unapplyComplex.scala | 8 +- tests/run-staging/shonan-hmm-simple.scala | 35 ++++---- tests/run/Course-2002-08.scala | 6 +- tests/run/classTags.scala | 3 +- tests/run/coop-equality.scala | 4 +- tests/run/generic/Serialization.scala | 6 +- tests/run/lists-run.scala | 90 +++++++++---------- tests/run/llift.scala | 2 +- tests/run/serialization-new.scala | 4 +- tests/run/typeclass-derivation2.scala | 2 +- tests/run/typeclass-derivation2a.scala | 2 +- tests/run/typeclass-derivation2b.scala | 2 +- tests/run/virtpatmat_try.scala | 2 +- 31 files changed, 190 insertions(+), 172 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/indentLeft.scala create mode 100644 tests/neg/indentRight.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index ede520f7b809..292cc61561d0 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -492,31 +492,36 @@ object Scanners { indentPrefix = LBRACE case _ => } + def isMatchCatchFollow(prev: Token) = + nextWidth == lastWidth + && (prev == MATCH || prev == CATCH) + && token != CASE if (newlineIsSeparating && canEndStatTokens.contains(lastToken) && canStartStatTokens.contains(token) && !isLeadingInfixOperator()) insert(if (pastBlankLine) NEWLINES else NEWLINE, lineOffset) - else if (indentIsSignificant) - if (nextWidth < lastWidth - || nextWidth == lastWidth && (indentPrefix == MATCH || indentPrefix == CATCH) && token != CASE) - currentRegion match { - case r: Indented - if !r.isOutermost && - !isLeadingInfixOperator() && - !statCtdTokens.contains(lastToken) => - currentRegion = r.enclosing - insert(OUTDENT, offset) - handleEndMarkers(nextWidth) - case _ => - } - else if (lastWidth < nextWidth || - lastWidth == nextWidth && (lastToken == MATCH || lastToken == CATCH) && token == CASE) { - if (canStartIndentTokens.contains(lastToken)) { + else if indentIsSignificant then + if nextWidth < lastWidth + || nextWidth == lastWidth && (indentPrefix == MATCH || indentPrefix == CATCH) && token != CASE then + if !currentRegion.isOutermost && + !isLeadingInfixOperator() && + !statCtdTokens.contains(lastToken) then + currentRegion match + case r: Indented => + currentRegion = r.enclosing + insert(OUTDENT, offset) + handleEndMarkers(nextWidth) + case r: InBraces if token != RBRACE && !statCtdTokens.contains(token) => + ctx.warning("Line is indented too far to the left or a `}' is missing", + source.atSpan(Span(offset))) + case _ => + + else if lastWidth < nextWidth + || lastWidth == nextWidth && (lastToken == MATCH || lastToken == CATCH) && token == CASE then + if canStartIndentTokens.contains(lastToken) then currentRegion = Indented(nextWidth, Set(), lastToken, currentRegion) insert(INDENT, offset) - } - } else if (lastWidth != nextWidth) errorButContinue( i"""Incompatible combinations of tabs and spaces in indentation prefixes. diff --git a/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala b/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala index 22e7d7796cde..3abdcd637fe6 100644 --- a/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala +++ b/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala @@ -96,7 +96,7 @@ trait TypeOrBoundsOps extends Core { internal.matchTermRef(typeOrBounds).map(x => (x.qualifier, x.name)) } - object IsTypeRef { + object IsTypeRef { /** Matches any TypeRef and returns it */ def unapply(tpe: TypeOrBounds)(given ctx: Context): Option[TypeRef] = internal.matchTypeRef(tpe) diff --git a/tests/neg-custom-args/fatal-warnings/indentLeft.scala b/tests/neg-custom-args/fatal-warnings/indentLeft.scala new file mode 100644 index 000000000000..36734e354fed --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/indentLeft.scala @@ -0,0 +1,8 @@ +object Test { + + if (true) { + println("hi") + + println("!") // error: too far to the left +} +// error: too far to the left \ No newline at end of file diff --git a/tests/neg/indentRight.scala b/tests/neg/indentRight.scala new file mode 100644 index 000000000000..16db55ba2549 --- /dev/null +++ b/tests/neg/indentRight.scala @@ -0,0 +1,8 @@ +object Test { + + if (true) + println("hi") + } + + println("!") // error: expected a toplevel definition +} diff --git a/tests/pos/Labels.scala b/tests/pos/Labels.scala index 3dc7304d657b..539b09d0ebd4 100644 --- a/tests/pos/Labels.scala +++ b/tests/pos/Labels.scala @@ -1,21 +1,22 @@ object Labels { def main(args: Array[String]): Unit = { - var i = 10 - while(i>0) { - var j = 0 - while(j0) { + var j = 0 + while(j0) => println("one") - case t@2 => println("two" + t) - case _ => println("default") - } + def pattern(a: Int) = a match { + case 1 if (a>0) => println("one") + case t@2 => println("two" + t) + case _ => println("default") + } } diff --git a/tests/pos/i1442.scala b/tests/pos/i1442.scala index c5ce0bcc2305..e2eafd37833e 100644 --- a/tests/pos/i1442.scala +++ b/tests/pos/i1442.scala @@ -1,22 +1,22 @@ object Test1442 { final def sumMinimized[B](num: Numeric[B]): Int = { - var cse: scala.math.Numeric.type = null.asInstanceOf[scala.math.Numeric.type] - ({cse = scala.math.Numeric; num eq cse.IntIsIntegral} || + var cse: scala.math.Numeric.type = null.asInstanceOf[scala.math.Numeric.type] + ({cse = scala.math.Numeric; num eq cse.IntIsIntegral} || (num eq cse.BigDecimalAsIfIntegral)) 2 - } + } final def sum[B](implicit num: Numeric[B]): B = { // arithmetic series formula can be used for regular addition - var cse: scala.math.Numeric.type = null.asInstanceOf[scala.math.Numeric.type] + var cse: scala.math.Numeric.type = null.asInstanceOf[scala.math.Numeric.type] if ({cse = scala.math.Numeric; num eq cse.IntIsIntegral}|| (num eq cse.BigIntIsIntegral)|| (num eq cse.ShortIsIntegral)|| (num eq cse.ByteIsIntegral)|| (num eq cse.CharIsIntegral)|| (num eq cse.LongIsIntegral)|| - (num eq cse.BigDecimalIsFractional)) { + (num eq cse.BigDecimalIsFractional)) { null.asInstanceOf[B] } else null.asInstanceOf[B] - } + } } diff --git a/tests/pos/reference/union-types.scala b/tests/pos/reference/union-types.scala index 7366305ad1a1..f9e7612e0570 100644 --- a/tests/pos/reference/union-types.scala +++ b/tests/pos/reference/union-types.scala @@ -2,36 +2,36 @@ package unionTypes object t1 { -type Hash = Int + type Hash = Int -case class UserName(name: String) -case class Password(hash: Hash) + case class UserName(name: String) + case class Password(hash: Hash) -def help(id: UserName | Password) = { - val user = id match { - case UserName(name) => lookupName(name) - case Password(hash) => lookupPassword(hash) + def help(id: UserName | Password) = { + val user = id match { + case UserName(name) => lookupName(name) + case Password(hash) => lookupPassword(hash) + } } -} -def lookupName(name: String) = ??? -def lookupPassword(hash: Hash) = ??? + def lookupName(name: String) = ??? + def lookupPassword(hash: Hash) = ??? } object t2 { import t1._ -trait Admin -trait UserData + trait Admin + trait UserData -trait L { def lookup(admin: Admin): Object } + trait L { def lookup(admin: Admin): Object } -case class UserName(name: String) extends L { - def lookup(admin: Admin): UserData = ??? -} -case class Password(hash: Hash) extends L { - def lookup(admin: Admin): UserData = ??? -} + case class UserName(name: String) extends L { + def lookup(admin: Admin): UserData = ??? + } + case class Password(hash: Hash) extends L { + def lookup(admin: Admin): UserData = ??? + } } diff --git a/tests/pos/scoping1.scala b/tests/pos/scoping1.scala index 83ad1357ae86..788d8b484f1f 100644 --- a/tests/pos/scoping1.scala +++ b/tests/pos/scoping1.scala @@ -2,7 +2,7 @@ object This extends App { trait A { def foo(): Unit } - abstract class C { self: A => + abstract class C { self: A => def bar() = this.foo() } class D extends C with A { diff --git a/tests/pos/simple-exceptions.scala b/tests/pos/simple-exceptions.scala index 4572b9aa6b54..093e9ac703da 100644 --- a/tests/pos/simple-exceptions.scala +++ b/tests/pos/simple-exceptions.scala @@ -7,7 +7,7 @@ object Test { def main(args: Array[String]): Unit = { try { try { - Console.println("hi!") + Console.println("hi!") sys.error("xx") } finally Console.println("ho!") diff --git a/tests/pos/spec-partialmap.scala b/tests/pos/spec-partialmap.scala index 1e944c777043..f2001dc85ea8 100644 --- a/tests/pos/spec-partialmap.scala +++ b/tests/pos/spec-partialmap.scala @@ -4,8 +4,8 @@ import scala.collection.{Iterable,IterableOps}; trait PartialMap[@specialized A,@specialized B] extends PartialFunction[A,B] with Iterable[(A,B)] { // commenting out this declaration gives a different exception. /** Getter for all values for which the given key function returns true. */ - def apply(f : (A => Boolean)) : Iterator[B] = - for ((k,v) <- iterator; if f(k)) yield v + def apply(f : (A => Boolean)) : Iterator[B] = + for ((k,v) <- iterator; if f(k)) yield v // if this is commented, it compiles fine: def apply[This <: Iterable[A]](keys : IterableOps[A, Iterable, This]): Iterable[B] = keys.map(apply) diff --git a/tests/pos/spec-traits.scala b/tests/pos/spec-traits.scala index 759fd22677df..06adc3f18109 100644 --- a/tests/pos/spec-traits.scala +++ b/tests/pos/spec-traits.scala @@ -57,7 +57,7 @@ object AA } } - def foo[T](x: T) = { object A; false } + def foo[T](x: T) = { object A; false } } // issue 3325 diff --git a/tests/pos/t1164.scala b/tests/pos/t1164.scala index 7775b5e8647c..6e05357e2fcb 100644 --- a/tests/pos/t1164.scala +++ b/tests/pos/t1164.scala @@ -1,11 +1,11 @@ object test { - class Foo[a](val arg : a) + class Foo[a](val arg : a) - object Foo { + object Foo { def apply [a](arg : a, right :a) = new Foo[a](arg) def unapply [a](m : Foo[a]) = Some (m.arg) - } + } def matchAndGetArgFromFoo[a]( e:Foo[a]):a = {e match { case Foo(x) => x }} // Unapply node here will have type argument [a] instantiated to scala.Nothing: @@ -18,7 +18,7 @@ object test { // constructor type FunIntToA [a] = (Int) => a - class Bar[a] (var f: FunIntToA[a]) + class Bar[a] (var f: FunIntToA[a]) object Bar { def apply[a](f: FunIntToA[a]) = new Bar[a](f) diff --git a/tests/pos/t2591.scala b/tests/pos/t2591.scala index 59f7a02cc76e..15c0abf4a226 100644 --- a/tests/pos/t2591.scala +++ b/tests/pos/t2591.scala @@ -7,9 +7,9 @@ object Implicits { } object Test { - // should cause imp to be in scope so that the next expression type checks - // `import Implicits._` works - import Implicits.imp + // should cause imp to be in scope so that the next expression type checks + // `import Implicits._` works + import Implicits.imp (new A) : Int } diff --git a/tests/pos/t3278.scala b/tests/pos/t3278.scala index 254f4dc79268..5b0772f57640 100644 --- a/tests/pos/t3278.scala +++ b/tests/pos/t3278.scala @@ -20,7 +20,7 @@ object Test { } object Test2 { def main(args : Array[String]): Unit = { - args(0) += "a" + args(0) += "a" val a = new Test2 val f = new Foo a(f) = 1 //works diff --git a/tests/pos/t443.scala b/tests/pos/t443.scala index f1f7ec258642..9bc9df7e0ee9 100644 --- a/tests/pos/t443.scala +++ b/tests/pos/t443.scala @@ -3,12 +3,12 @@ object Test { def lookup(): Option[Tuple2[String, String]] = ((null: Option[Tuple2[String, String]]) : @unchecked) match { case Some((_, _)) => - if (true) - Some((null, null)) - else - lookup() match { - case Some(_) => Some(null) - case None => None - } + if (true) + Some((null, null)) + else + lookup() match { + case Some(_) => Some(null) + case None => None + } } } diff --git a/tests/pos/t5508.scala b/tests/pos/t5508.scala index 2b497580454e..75efa65923e5 100644 --- a/tests/pos/t5508.scala +++ b/tests/pos/t5508.scala @@ -13,7 +13,7 @@ object Base1 { private[this] var _st : Int = 0 def close : PartialFunction[Any,Any] = { case x : Int => - _st = identity(_st) + _st = identity(_st) } } } @@ -31,7 +31,7 @@ object Base2 { private[this] var _st : Int = 0 def close : PartialFunction[Any,Any] = { case x : Int => - _st = 1 + _st = 1 } } } @@ -41,7 +41,7 @@ class Base3 { private[this] var _st : Int = 0 def close : PartialFunction[Any,Any] = { case x : Int => - _st = 1 + _st = 1 } } } diff --git a/tests/pos/t6335.scala b/tests/pos/t6335.scala index eb052db1998a..1c0b846be372 100644 --- a/tests/pos/t6335.scala +++ b/tests/pos/t6335.scala @@ -21,5 +21,5 @@ object Test { "".yy - true.zz + true.zz } diff --git a/tests/pos/t8230a.scala b/tests/pos/t8230a.scala index dfbae51eeee2..00f8704f2d7d 100644 --- a/tests/pos/t8230a.scala +++ b/tests/pos/t8230a.scala @@ -15,8 +15,8 @@ object Test { object Okay { Arr("1") - import I.{ arrToTrav, longArrToTrav } - val x = foo(Arr("2")) + import I.{ arrToTrav, longArrToTrav } + val x = foo(Arr("2")) } object Fail { diff --git a/tests/pos/unapplyComplex.scala b/tests/pos/unapplyComplex.scala index 5261b70f41fd..d1d4d7d660c8 100644 --- a/tests/pos/unapplyComplex.scala +++ b/tests/pos/unapplyComplex.scala @@ -14,26 +14,26 @@ object ComplexRect { def unapply(z:Complex): Option[Complex] = { if (z.isInstanceOf[ComplexRect]) Some(z) else z match { case ComplexPolar(mod, arg) => - Some(new ComplexRect(mod*math.cos(arg), mod*math.sin(arg))) + Some(new ComplexRect(mod*math.cos(arg), mod*math.sin(arg))) } } } object ComplexPolar { def unapply(z:Complex): Option[Complex] = { if (z.isInstanceOf[ComplexPolar]) Some(z) else z match { case ComplexRect(re,im) => - Some(new ComplexPolar(math.sqrt(re*re + im*im), math.atan(re/im))) + Some(new ComplexPolar(math.sqrt(re*re + im*im), math.atan(re/im))) } } } object Test { def main(args:Array[String]) = { new ComplexRect(1,1) match { case ComplexPolar(mod,arg) => // z @ ??? - Console.println("mod"+mod+"arg"+arg) + Console.println("mod"+mod+"arg"+arg) } val Komplex = ComplexRect new ComplexPolar(math.sqrt(2),math.Pi / 4.0) match { case Komplex(re,im) => // z @ ??? - Console.println("re"+re+" im"+im) + Console.println("re"+re+" im"+im) } } } diff --git a/tests/run-staging/shonan-hmm-simple.scala b/tests/run-staging/shonan-hmm-simple.scala index 80cd92382eed..a820298dc0ae 100644 --- a/tests/run-staging/shonan-hmm-simple.scala +++ b/tests/run-staging/shonan-hmm-simple.scala @@ -2,29 +2,28 @@ import scala.quoted._ import scala.quoted.staging._ import scala.quoted.autolift.given -trait Ring[T] { +trait Ring[T] val zero: T val one: T val add: (x: T, y: T) => T val sub: (x: T, y: T) => T val mul: (x: T, y: T) => T -} +end Ring -class RingInt extends Ring[Int] { +class RingInt extends Ring[Int] val zero = 0 val one = 1 val add = (x, y) => x + y val sub = (x, y) => x - y val mul = (x, y) => x * y -} -class RingIntExpr(given QuoteContext) extends Ring[Expr[Int]] { + +class RingIntExpr(given QuoteContext) extends Ring[Expr[Int]] val zero = '{0} val one = '{1} val add = (x, y) => '{$x + $y} val sub = (x, y) => '{$x - $y} val mul = (x, y) => '{$x * $y} -} class RingComplex[U](u: Ring[U]) extends Ring[Complex[U]] { val zero = Complex(u.zero, u.zero) @@ -34,15 +33,14 @@ class RingComplex[U](u: Ring[U]) extends Ring[Complex[U]] { val mul = (x, y) => Complex(u.sub(u.mul(x.re, y.re), u.mul(x.im, y.im)), u.add(u.mul(x.re, y.im), u.mul(x.im, y.re))) } -sealed trait PV[T] { +sealed trait PV[T] def expr(given Liftable[T], QuoteContext): Expr[T] -} -case class Sta[T](x: T) extends PV[T] { + +case class Sta[T](x: T) extends PV[T] def expr(given Liftable[T], QuoteContext): Expr[T] = x -} -case class Dyn[T](x: Expr[T]) extends PV[T] { + +case class Dyn[T](x: Expr[T]) extends PV[T] def expr(given Liftable[T], QuoteContext): Expr[T] = x -} class RingPV[U: Liftable](u: Ring[U], eu: Ring[Expr[U]])(given QuoteContext) extends Ring[PV[U]] { val zero: PV[U] = Sta(u.zero) @@ -71,11 +69,10 @@ class RingPV[U: Liftable](u: Ring[U], eu: Ring[Expr[U]])(given QuoteContext) ext case class Complex[T](re: T, im: T) -object Complex { - implicit def isLiftable[T: Type: Liftable]: Liftable[Complex[T]] = new Liftable[Complex[T]] { +object Complex + implicit def isLiftable[T: Type: Liftable]: Liftable[Complex[T]] = new Liftable[Complex[T]] def toExpr(comp: Complex[T]) = '{Complex(${comp.re}, ${comp.im})} - } -} + case class Vec[Idx, T](size: Idx, get: Idx => T) { def map[U](f: T => U): Vec[Idx, U] = Vec(size, i => f(get(i))) @@ -99,17 +96,15 @@ class StaticVecOps[T] extends VecOps[Int, T] { } } -class ExprVecOps[T: Type](given QuoteContext) extends VecOps[Expr[Int], Expr[T]] { +class ExprVecOps[T: Type](given QuoteContext) extends VecOps[Expr[Int], Expr[T]] val reduce: ((Expr[T], Expr[T]) => Expr[T], Expr[T], Vec[Expr[Int], Expr[T]]) => Expr[T] = (plus, zero, vec) => '{ var sum = $zero var i = 0 - while (i < ${vec.size}) { + while i < ${vec.size} do sum = ${ plus('sum, vec.get('i)) } i += 1 - } sum } -} class Blas1[Idx, T](r: Ring[T], ops: VecOps[Idx, T]) { def dot(v1: Vec[Idx, T], v2: Vec[Idx, T]): T = ops.reduce(r.add, r.zero, v1.zipWith(v2, r.mul)) diff --git a/tests/run/Course-2002-08.scala b/tests/run/Course-2002-08.scala index fd69adf4ff10..f92436798ca9 100644 --- a/tests/run/Course-2002-08.scala +++ b/tests/run/Course-2002-08.scala @@ -517,9 +517,9 @@ abstract class CircuitSimulator() extends BasicCircuitSimulator() { def demux2(in: Wire, ctrl: List[Wire], out: List[Wire]) : Unit = { val ctrlN = ctrl.map(w => { val iw = new Wire(); inverter(w,iw); iw}); val w0 = new Wire(); - val w1 = new Wire(); - val w2 = new Wire(); - val w3 = new Wire(); + val w1 = new Wire(); + val w2 = new Wire(); + val w3 = new Wire(); andGate(in, ctrl(1), w3); andGate(in, ctrl(1), w2); diff --git a/tests/run/classTags.scala b/tests/run/classTags.scala index d0bd1c0eed30..da8d9826ada1 100644 --- a/tests/run/classTags.scala +++ b/tests/run/classTags.scala @@ -3,7 +3,8 @@ object Test { /* val a /* : Class[T] */ = classOf[T] // [Ljava/lang/String; println(a) -*/ val b /* : ClassTag[T] */ = reflect.classTag[T] // ClassTag(classOf[java.lang.String]) +*/ + val b /* : ClassTag[T] */ = reflect.classTag[T] // ClassTag(classOf[java.lang.String]) /* println(b) val d /* : ClassTag[T with U] */ = reflect.classTag[T with U] // ClassTag(classOf[java.lang.String]) diff --git a/tests/run/coop-equality.scala b/tests/run/coop-equality.scala index f32906cd8560..a50956f602a9 100644 --- a/tests/run/coop-equality.scala +++ b/tests/run/coop-equality.scala @@ -17,6 +17,6 @@ object Test extends App { val a = A("") val b = B("") - assert(a === b) - assert(!((a: ANY) === (b: ANY))) + assert(a === b) + assert(!((a: ANY) === (b: ANY))) } \ No newline at end of file diff --git a/tests/run/generic/Serialization.scala b/tests/run/generic/Serialization.scala index 211e801bd702..2aaceb9c6d47 100644 --- a/tests/run/generic/Serialization.scala +++ b/tests/run/generic/Serialization.scala @@ -92,10 +92,10 @@ object Serialization { ev1.write(x.fst, out) ev2.write(x.snd, out) } - def read(in: DataInputStream) = { - Prod(ev1.read(in), ev2.read(in)) + def read(in: DataInputStream) = { + Prod(ev1.read(in), ev2.read(in)) + } } - } implicit def IterableSerializable[I[X] <: Iterable[X], Elem](implicit ev1: IterableFactory[I], diff --git a/tests/run/lists-run.scala b/tests/run/lists-run.scala index 1edf3b24daaa..e157c1cebea7 100644 --- a/tests/run/lists-run.scala +++ b/tests/run/lists-run.scala @@ -69,10 +69,10 @@ min cardinality(ys, e))) }, "obey min cardinality") assert({ val intersection = xs intersect ys - val unconsumed = xs.foldLeft(intersection){(rest, e) => - if (! rest.isEmpty && e == rest.head) rest.tail else rest - } - unconsumed.isEmpty + val unconsumed = xs.foldLeft(intersection){(rest, e) => + if (! rest.isEmpty && e == rest.head) rest.tail else rest + } + unconsumed.isEmpty }, "maintain order") assert(xs == (xs intersect xs), "has the list as again intersection") @@ -87,47 +87,47 @@ object Test1 { val xs4 = List(2, 4, 6, 8) val xs5 = List(List(3, 4), List(3), List(4, 5)) - { - val n1 = xs1 count { e => e % 2 != 0 } - val n2 = xs4 count { e => e < 5 } - assert(4 == (n1 + n2), "check_count") - } - { - val b1 = xs1 exists { e => e % 2 == 0 } - val b2 = xs4 exists { e => e == 5 } - assert(!(b1 & b2), "check_exists") - } - { - val ys1 = xs1 filter { e => e % 2 == 0 } - val ys2 = xs4 filter { e => e < 5 } - assert(3 == ys1.length + ys2.length, "check_filter") - } - { - val n1 = xs1.foldLeft(0)((e1, e2) => e1 + e2) - val ys1 = xs4.foldLeft(List[Int]())((e1, e2) => e2 :: e1) - assert(10 == n1 + ys1.length, "check_foldLeft") - } - { - val b1 = xs1 forall { e => e < 10} - val b2 = xs4 forall { e => e % 2 == 0 } - assert(b1 & b2, "check_forall") - } - { - val ys1 = xs1 filterNot { e => e % 2 != 0 } - val ys2 = xs4 filterNot { e => e < 5 } - assert(3 == ys1.length + ys2.length, "check_remove") - } - { - val ys1 = xs1 zip xs2 - val ys2 = xs1 zip xs3 - assert(4 == ys1.length + ys2.length, "check_zip") - } - { - val ys1 = xs1.zipAll(xs2, 0, '_') - val ys2 = xs2.zipAll(xs1, '_', 0) - val ys3 = xs1.zipAll(xs3, 0, List(-1)) - assert(9 == ys1.length + ys2.length + ys3.length, "check_zipAll") - } + { + val n1 = xs1 count { e => e % 2 != 0 } + val n2 = xs4 count { e => e < 5 } + assert(4 == (n1 + n2), "check_count") + } + { + val b1 = xs1 exists { e => e % 2 == 0 } + val b2 = xs4 exists { e => e == 5 } + assert(!(b1 & b2), "check_exists") + } + { + val ys1 = xs1 filter { e => e % 2 == 0 } + val ys2 = xs4 filter { e => e < 5 } + assert(3 == ys1.length + ys2.length, "check_filter") + } + { + val n1 = xs1.foldLeft(0)((e1, e2) => e1 + e2) + val ys1 = xs4.foldLeft(List[Int]())((e1, e2) => e2 :: e1) + assert(10 == n1 + ys1.length, "check_foldLeft") + } + { + val b1 = xs1 forall { e => e < 10} + val b2 = xs4 forall { e => e % 2 == 0 } + assert(b1 & b2, "check_forall") + } + { + val ys1 = xs1 filterNot { e => e % 2 != 0 } + val ys2 = xs4 filterNot { e => e < 5 } + assert(3 == ys1.length + ys2.length, "check_remove") + } + { + val ys1 = xs1 zip xs2 + val ys2 = xs1 zip xs3 + assert(4 == ys1.length + ys2.length, "check_zip") + } + { + val ys1 = xs1.zipAll(xs2, 0, '_') + val ys2 = xs2.zipAll(xs1, '_', 0) + val ys3 = xs1.zipAll(xs3, 0, List(-1)) + assert(9 == ys1.length + ys2.length + ys3.length, "check_zipAll") + } } } diff --git a/tests/run/llift.scala b/tests/run/llift.scala index 60a1f4dcefc5..c0699eac705c 100644 --- a/tests/run/llift.scala +++ b/tests/run/llift.scala @@ -48,7 +48,7 @@ object Test { new C2().f3 } // } - /*new C1().*/f2 + /*new C1().*/f2 } def f1b(x: Int) = { diff --git a/tests/run/serialization-new.scala b/tests/run/serialization-new.scala index 793ec8184f48..92f027591831 100644 --- a/tests/run/serialization-new.scala +++ b/tests/run/serialization-new.scala @@ -482,8 +482,8 @@ class WithTransient extends Serializable { } object Test8 { - val x = new WithTransient - x.test + val x = new WithTransient + x.test try { val y:WithTransient = read(write(x)) y.test diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index 2f6d332225e1..aed502804720 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -253,7 +253,7 @@ object Eq { case _ => error("invalid call to eqlCases: one of Alts is not a subtype of T") } - case _: Unit => + case _: Unit => false } diff --git a/tests/run/typeclass-derivation2a.scala b/tests/run/typeclass-derivation2a.scala index b923a3a70c52..912f8342dfb6 100644 --- a/tests/run/typeclass-derivation2a.scala +++ b/tests/run/typeclass-derivation2a.scala @@ -240,7 +240,7 @@ object Eq { case _: (Shape.Case[alt, elems] *: alts1) => if (xm.ordinal == n) eqlElems[elems](xm, ym, 0) else eqlCases[alts1](xm, ym, n + 1) - case _: Unit => + case _: Unit => false } diff --git a/tests/run/typeclass-derivation2b.scala b/tests/run/typeclass-derivation2b.scala index e293f51f9e87..5a9144a66259 100644 --- a/tests/run/typeclass-derivation2b.scala +++ b/tests/run/typeclass-derivation2b.scala @@ -121,7 +121,7 @@ object Eq { 0) } else eqlCases[T, alts1](x, y, genSum, ord, n + 1) - case _: Unit => + case _: Unit => false } diff --git a/tests/run/virtpatmat_try.scala b/tests/run/virtpatmat_try.scala index f417488c687c..cccc383b87f0 100644 --- a/tests/run/virtpatmat_try.scala +++ b/tests/run/virtpatmat_try.scala @@ -20,7 +20,7 @@ object Test extends App { case _: Throwable => println("other") } - def simpleTry: Unit = { + def simpleTry: Unit = { try { bla } catch { From 12a9190267e156f994ae569396cb8410393dffa4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 19 Sep 2019 18:32:40 +0200 Subject: [PATCH 2/8] Issue warnings if code is indented too far to the right --- .../dotty/tools/dotc/parsing/Parsers.scala | 79 +++++++++++++------ .../dotty/tools/dotc/parsing/Scanners.scala | 14 ++-- .../src/dotty/tools/dotc/parsing/Tokens.scala | 2 + .../dotty/tools/dotc/plugins/Plugins.scala | 11 ++- .../dotty/tools/dotc/CompilationTests.scala | 3 +- tests/neg-custom-args/indentRight.scala | 25 ++++++ tests/neg/indentRight.scala | 8 -- 7 files changed, 96 insertions(+), 46 deletions(-) create mode 100644 tests/neg-custom-args/indentRight.scala delete mode 100644 tests/neg/indentRight.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ebe24cdaa395..b8d61a36aebc 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -585,10 +585,44 @@ object Parsers { try op finally in.adjustSepRegions(closing) } + /** Parse `body` while checking (under -noindent) that a `{` is not missing before it. + * This is done as follows: + * If the next token S is indented relative to the current region, + * and the end of `body` is followed by a new line and another statement, + * check that that other statement is indented less than S + */ + def subPart[T](body: () => T): T = in.currentRegion match + case r: InBraces if in.isAfterLineEnd => + val startIndentWidth = in.indentWidth(in.offset) + if r.indentWidth < startIndentWidth then + // Note: we can get here only if indentation is not significant + // If indentation is significant, we would see an as current token + // and the indent region would be Indented instead of InBraces. + // + // If indentation would be significant, an would be inserted here. + val t = body() + // Therefore, make sure there would be a matching + def nextIndentWidth = in.indentWidth(in.next.offset) + if (in.token == NEWLINE || in.token == NEWLINES) + && !(nextIndentWidth < startIndentWidth) + then + syntaxError( + if startIndentWidth <= nextIndentWidth then + i"""Line is indented too far to the right, or a `{' is missing before: + | + |$t""" + else + in.spaceTabMismatchMsg(startIndentWidth, nextIndentWidth), + in.next.offset + ) + t + else body() + case _ => body() + /* -------- REWRITES ----------------------------------------------------------- */ /** The last offset where a colon at the end of line would be required if a subsequent { ... } - * block would be converted to an indentation region. + * block would be converted to an indentation reg`ion. */ var possibleColonOffset: Int = -1 @@ -1606,13 +1640,6 @@ object Parsers { /* ----------- EXPRESSIONS ------------------------------------------------ */ - /** EqualsExpr ::= `=' Expr - */ - def equalsExpr(): Tree = { - accept(EQUALS) - expr() - } - def condExpr(altToken: Token): Tree = if (in.token == LPAREN) { var t: Tree = atSpan(in.offset) { Parens(inParens(exprInParens())) } @@ -1676,7 +1703,9 @@ object Parsers { */ val exprInParens: () => Tree = () => expr(Location.InParens) - def expr(): Tree = expr(Location.ElseWhere) + val expr: () => Tree = () => expr(Location.ElseWhere) + + def subExpr() = subPart(expr) def expr(location: Location.Value): Tree = { val start = in.offset @@ -1714,7 +1743,7 @@ object Parsers { atSpan(in.skipToken()) { val cond = condExpr(DO) newLinesOpt() - val body = expr() + val body = subExpr() WhileDo(cond, body) } } @@ -1753,7 +1782,7 @@ object Parsers { if (in.token == CATCH) { val span = in.offset in.nextToken() - (expr(), span) + (subExpr(), span) } else (EmptyTree, -1) @@ -1768,7 +1797,7 @@ object Parsers { } val finalizer = - if (in.token == FINALLY) { in.nextToken(); expr() } + if (in.token == FINALLY) { in.nextToken(); subExpr() } else { if (handler.isEmpty) warning( EmptyCatchAndFinallyBlock(body), @@ -1823,7 +1852,7 @@ object Parsers { case EQUALS => t match { case Ident(_) | Select(_, _) | Apply(_, _) => - atSpan(startOffset(t), in.skipToken()) { Assign(t, expr()) } + atSpan(startOffset(t), in.skipToken()) { Assign(t, subExpr()) } case _ => t } @@ -1870,8 +1899,8 @@ object Parsers { atSpan(start, in.skipToken()) { val cond = condExpr(THEN) newLinesOpt() - val thenp = expr() - val elsep = if (in.token == ELSE) { in.nextToken(); expr() } + val thenp = subExpr() + val elsep = if (in.token == ELSE) { in.nextToken(); subExpr() } else EmptyTree mkIf(cond, thenp, elsep) } @@ -2224,7 +2253,7 @@ object Parsers { else if (in.token == CASE) generator() else { val pat = pattern1() - if (in.token == EQUALS) atSpan(startOffset(pat), in.skipToken()) { GenAlias(pat, expr()) } + if (in.token == EQUALS) atSpan(startOffset(pat), in.skipToken()) { GenAlias(pat, subExpr()) } else generatorRest(pat, casePat = false) } @@ -2241,7 +2270,7 @@ object Parsers { if (casePat) GenCheckMode.FilterAlways else if (ctx.settings.strict.value) GenCheckMode.Check else GenCheckMode.FilterNow // filter for now, to keep backwards compat - GenFrom(pat, expr(), checkMode) + GenFrom(pat, subExpr(), checkMode) } /** ForExpr ::= `for' (`(' Enumerators `)' | `{' Enumerators `}') @@ -2313,12 +2342,12 @@ object Parsers { newLinesOpt() if (in.token == YIELD) { in.nextToken() - ForYield(enums, expr()) + ForYield(enums, subExpr()) } else if (in.token == DO) { if (rewriteToOldSyntax()) dropTerminator() in.nextToken() - ForDo(enums, expr()) + ForDo(enums, subExpr()) } else { if (!wrappedEnums) syntaxErrorOrIncomplete(YieldOrDoExpectedInForComprehension()) @@ -2758,7 +2787,7 @@ object Parsers { syntaxError(VarValParametersMayNotBeCallByName(name, mods.is(Mutable))) val tpt = paramType() val default = - if (in.token == EQUALS) { in.nextToken(); expr() } + if (in.token == EQUALS) { in.nextToken(); subExpr() } else EmptyTree if (impliedMods.mods.nonEmpty) impliedMods = impliedMods.withMods(Nil) // keep only flags, so that parameter positions don't overlap @@ -3003,7 +3032,7 @@ object Parsers { (lhs.toList forall (_.isInstanceOf[Ident]))) wildcardIdent() else - expr() + subExpr() } else EmptyTree lhs match { @@ -3043,7 +3072,7 @@ object Parsers { if (in.isScala2Mode) newLineOptWhenFollowedBy(LBRACE) val rhs = { if (!(in.token == LBRACE && scala2ProcedureSyntax(""))) accept(EQUALS) - atSpan(in.offset) { constrExpr() } + atSpan(in.offset) { subPart(constrExpr) } } makeConstructor(Nil, vparamss, rhs).withMods(mods).setComment(in.getDocComment(start)) } @@ -3076,7 +3105,7 @@ object Parsers { if (in.token == EQUALS) indentRegion(name) { in.nextToken() - expr() + subExpr() } else if (!tpt.isEmpty) EmptyTree @@ -3100,7 +3129,7 @@ object Parsers { /** ConstrExpr ::= SelfInvocation * | `{' SelfInvocation {semi BlockStat} `}' */ - def constrExpr(): Tree = + val constrExpr: () => Tree = () => if (in.isNestedStart) atSpan(in.offset) { inBracesOrIndented { @@ -3351,7 +3380,7 @@ object Parsers { if in.token == EQUALS && parents.length == 1 && parents.head.isType then in.nextToken() mods1 |= Final - DefDef(name, tparams, vparamss, parents.head, expr()) + DefDef(name, tparams, vparamss, parents.head, subExpr()) else //println(i"given $name $hasExtensionParams $hasGivenSig") possibleTemplateStart() diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 292cc61561d0..efa20d269d9d 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -512,8 +512,8 @@ object Scanners { currentRegion = r.enclosing insert(OUTDENT, offset) handleEndMarkers(nextWidth) - case r: InBraces if token != RBRACE && !statCtdTokens.contains(token) => - ctx.warning("Line is indented too far to the left or a `}' is missing", + case r: InBraces if !closingRegionTokens.contains(token) => + ctx.warning("Line is indented too far to the left, or a `}' is missing", source.atSpan(Span(offset))) case _ => @@ -523,10 +523,7 @@ object Scanners { currentRegion = Indented(nextWidth, Set(), lastToken, currentRegion) insert(INDENT, offset) else if (lastWidth != nextWidth) - errorButContinue( - i"""Incompatible combinations of tabs and spaces in indentation prefixes. - |Previous indent : $lastWidth - |Latest indent : $nextWidth""") + errorButContinue(spaceTabMismatchMsg(lastWidth, nextWidth)) currentRegion match { case Indented(curWidth, others, prefix, outer) if curWidth < nextWidth && !others.contains(nextWidth) => if (token == OUTDENT) @@ -540,6 +537,11 @@ object Scanners { } } + def spaceTabMismatchMsg(lastWidth: IndentWidth, nextWidth: IndentWidth) = + i"""Incompatible combinations of tabs and spaces in indentation prefixes. + |Previous indent : $lastWidth + |Latest indent : $nextWidth""" + def observeIndented(unless: BitSet, unlessSoftKW: TermName = EmptyTermName): Unit = if (indentSyntax && isAfterLineEnd && token != INDENT) { val newLineInserted = token == NEWLINE || token == NEWLINES diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index f9cc888268b9..92684c9c3293 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -263,6 +263,8 @@ object Tokens extends TokensCommon { final val statCtdTokens: BitSet = BitSet(THEN, ELSE, DO, CATCH, FINALLY, YIELD, MATCH) + final val closingRegionTokens = BitSet(RBRACE, CASE) | statCtdTokens + final val canStartIndentTokens: BitSet = statCtdTokens | BitSet(COLONEOL, EQUALS, ARROW, LARROW, WHILE, TRY, FOR) // `if` is excluded because it often comes after `else` which makes for awkward indentation rules TODO: try to do without the exception diff --git a/compiler/src/dotty/tools/dotc/plugins/Plugins.scala b/compiler/src/dotty/tools/dotc/plugins/Plugins.scala index 01fd0b6d4e6f..72c2b3bec543 100644 --- a/compiler/src/dotty/tools/dotc/plugins/Plugins.scala +++ b/compiler/src/dotty/tools/dotc/plugins/Plugins.scala @@ -96,12 +96,11 @@ trait Plugins { private var _plugins: List[Plugin] = _ def plugins(implicit ctx: Context): List[Plugin] = - if (_plugins == null) { - _plugins = loadPlugins - _plugins - } - else _plugins - + if (_plugins == null) { + _plugins = loadPlugins + _plugins + } + else _plugins /** A description of all the plugins that are loaded */ def pluginDescriptions: String = diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index bab516d8ca2a..ecf290c842c1 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -139,7 +139,8 @@ class CompilationTests extends ParallelTesting { compileFile("tests/neg-custom-args/i6300.scala", allowDeepSubtypes), compileFile("tests/neg-custom-args/infix.scala", defaultOptions.and("-strict", "-deprecation", "-Xfatal-warnings")), compileFile("tests/neg-custom-args/missing-alpha.scala", defaultOptions.and("-strict", "-deprecation", "-Xfatal-warnings")), - compileFile("tests/neg-custom-args/wildcards.scala", defaultOptions.and("-strict", "-deprecation", "-Xfatal-warnings")) + compileFile("tests/neg-custom-args/wildcards.scala", defaultOptions.and("-strict", "-deprecation", "-Xfatal-warnings")), + compileFile("tests/neg-custom-args/indentRight.scala", defaultOptions.and("-noindent", "-Xfatal-warnings")) ).checkExpectedErrors() } diff --git a/tests/neg-custom-args/indentRight.scala b/tests/neg-custom-args/indentRight.scala new file mode 100644 index 000000000000..5fefb400f956 --- /dev/null +++ b/tests/neg-custom-args/indentRight.scala @@ -0,0 +1,25 @@ +object Test { + + if (true) + println("ho") + println("hu") // error: Line is indented too far to the right + + if (true) + { println("ho") + } + println("hu") // error: Line is indented too far to the right + + while (true) + () + + for (x <- List()) // OK + { + + } + + if (true) // OK + println("hi") + } + + println("!") // error: expected a toplevel definition +} diff --git a/tests/neg/indentRight.scala b/tests/neg/indentRight.scala deleted file mode 100644 index 16db55ba2549..000000000000 --- a/tests/neg/indentRight.scala +++ /dev/null @@ -1,8 +0,0 @@ -object Test { - - if (true) - println("hi") - } - - println("!") // error: expected a toplevel definition -} From 59bb86b12011579d5ee5446819f86308df9ff238 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 19 Sep 2019 19:19:23 +0200 Subject: [PATCH 3/8] Turn misindent error into warning Also, fix two more tests --- .../dotty/tools/dotc/parsing/Parsers.scala | 2 +- compiler/test/debug/Gen.scala | 64 +++++++------- tests/patmat/patmatexhaust.scala | 88 +++++++++---------- 3 files changed, 77 insertions(+), 77 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index b8d61a36aebc..67bc9cc6521b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -606,7 +606,7 @@ object Parsers { if (in.token == NEWLINE || in.token == NEWLINES) && !(nextIndentWidth < startIndentWidth) then - syntaxError( + warning( if startIndentWidth <= nextIndentWidth then i"""Line is indented too far to the right, or a `{' is missing before: | diff --git a/compiler/test/debug/Gen.scala b/compiler/test/debug/Gen.scala index 179331492669..77a82473aa40 100755 --- a/compiler/test/debug/Gen.scala +++ b/compiler/test/debug/Gen.scala @@ -126,38 +126,38 @@ object Gen { |""".stripMargin }).mkString("\n\n") -s"""|#!/usr/bin/expect - | - |# log_user 1 - |# exp_internal 1 - |# set timeout 5 - | - |send_user "spawning job...\\n" - | - |spawn jdb -attach 5005 -sourcepath $source - | - |send_user "interacting...\\n" - | - |expect { - | "*VM Started*" { send_user "success - connected to server \\n" } - | timeout { - | send_user "timeout while waiting for: *VM Started*\\n" - | exit 1 - | } - |} - | - |send_user "setting breakpoints...\\n" - | - |# breakpoints - |$breakpoints - | - |# run - |send_user "run program...\\n" - |send "run\\r" - |expect "Breakpoint hit" - | - |# interactions - |$commands""".stripMargin + s"""|#!/usr/bin/expect + | + |# log_user 1 + |# exp_internal 1 + |# set timeout 5 + | + |send_user "spawning job...\\n" + | + |spawn jdb -attach 5005 -sourcepath $source + | + |send_user "interacting...\\n" + | + |expect { + | "*VM Started*" { send_user "success - connected to server \\n" } + | timeout { + | send_user "timeout while waiting for: *VM Started*\\n" + | exit 1 + | } + |} + | + |send_user "setting breakpoints...\\n" + | + |# breakpoints + |$breakpoints + | + |# run + |send_user "run program...\\n" + |send "run\\r" + |expect "Breakpoint hit" + | + |# interactions + |$commands""".stripMargin } def main(args: Array[String]): Unit = { diff --git a/tests/patmat/patmatexhaust.scala b/tests/patmat/patmatexhaust.scala index 832f97ad6120..8e6b3196cd5f 100644 --- a/tests/patmat/patmatexhaust.scala +++ b/tests/patmat/patmatexhaust.scala @@ -1,60 +1,60 @@ class TestSealedExhaustive { // compile only - sealed abstract class Foo + sealed abstract class Foo - case class Bar(x:Int) extends Foo - case object Baz extends Foo + case class Bar(x:Int) extends Foo + case object Baz extends Foo - def ma1(x:Foo) = x match { - case Bar(_) => // not exhaustive - } + def ma1(x:Foo) = x match { + case Bar(_) => // not exhaustive + } - def ma2(x:Foo) = x match { - case Baz => // not exhaustive - } + def ma2(x:Foo) = x match { + case Baz => // not exhaustive + } - sealed abstract class Mult - case class Kult(s:Mult) extends Mult - case class Qult() extends Mult + sealed abstract class Mult + case class Kult(s:Mult) extends Mult + case class Qult() extends Mult - def ma33(x:Kult) = x match { // exhaustive - case Kult(_) => // exhaustive - } + def ma33(x:Kult) = x match { // exhaustive + case Kult(_) => // exhaustive + } - def ma3(x:Mult) = (x,x) match { // not exhaustive - case (Kult(_), Qult()) => // Kult missing - //case (Kult(_), Kult(_)) => - case (Qult(), Kult(_)) => // Qult missing - //case (Qult(), Qult()) => - } + def ma3(x:Mult) = (x,x) match { // not exhaustive + case (Kult(_), Qult()) => // Kult missing + //case (Kult(_), Kult(_)) => + case (Qult(), Kult(_)) => // Qult missing + //case (Qult(), Qult()) => + } - def ma3u(x:Mult) = ((x,x) : @unchecked) match { // not exhaustive, but not checked! - case (Kult(_), Qult()) => - case (Qult(), Kult(_)) => - } + def ma3u(x:Mult) = ((x,x) : @unchecked) match { // not exhaustive, but not checked! + case (Kult(_), Qult()) => + case (Qult(), Kult(_)) => + } - sealed abstract class Deep + sealed abstract class Deep - case object Ga extends Deep - sealed class Gp extends Deep - case object Gu extends Gp + case object Ga extends Deep + sealed class Gp extends Deep + case object Gu extends Gp - def zma3(x:Deep) = x match { // exhaustive! - case _ => - } - def zma4(x:Deep) = x match { // exhaustive! - case Ga => - case _ => - } + def zma3(x:Deep) = x match { // exhaustive! + case _ => + } + def zma4(x:Deep) = x match { // exhaustive! + case Ga => + case _ => + } - def ma4(x:Deep) = x match { // missing cases: Gu, Gp which is not abstract so must be included - case Ga => - } + def ma4(x:Deep) = x match { // missing cases: Gu, Gp which is not abstract so must be included + case Ga => + } - def ma5(x:Deep) = x match { - case Gu => - case _ if 1 == 0 => - case Ga => - } + def ma5(x:Deep) = x match { + case Gu => + case _ if 1 == 0 => + case Ga => + } def ma6() = List(1,2) match { // give up case List(1,2) => From 46e1afe8b837bbd4af376a23dbe30eef1df1d2e8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 19 Sep 2019 19:45:42 +0200 Subject: [PATCH 4/8] Warning if code after an empty template body is indented Under -noindent, warn id code that follows an empty template is indented more deeply than the template header. Inserting braces automatically would change meaning in this case. Example: ```scala trait A case class B() extends A // error: Line is indented too far to the right case object C extends A // error: Line is indented too far to the right ``` --- .../dotty/tools/dotc/parsing/Parsers.scala | 31 ++++++--- compiler/test/debug/Gen.scala | 64 +++++++++---------- tests/neg-custom-args/indentRight.scala | 4 ++ 3 files changed, 59 insertions(+), 40 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 67bc9cc6521b..5659b81e8d0c 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -619,6 +619,16 @@ object Parsers { else body() case _ => body() + /** If indentation is not significant, check that this is not the start of a + * statement that's indented relative to the current region. + */ + def checkNextNotIndented(): Unit = in.currentRegion match + case r: InBraces if in.token == NEWLINE || in.token == NEWLINES => + val nextIndentWidth = in.indentWidth(in.next.offset) + if r.indentWidth < nextIndentWidth then + warning(i"Line is indented too far to the right, or a `{' is missing", in.next.offset) + case _ => + /* -------- REWRITES ----------------------------------------------------------- */ /** The last offset where a colon at the end of line would be required if a subsequent { ... } @@ -3460,22 +3470,27 @@ object Parsers { /** TemplateOpt = [Template] */ - def templateOpt(constr: DefDef): Template = { + def templateOpt(constr: DefDef): Template = possibleTemplateStart() - if (in.token == EXTENDS || isIdent(nme.derives)) + if in.token == EXTENDS || isIdent(nme.derives) then template(constr) else - if (in.isNestedStart) template(constr) - else Template(constr, Nil, Nil, EmptyValDef, Nil) - } + if in.isNestedStart then + template(constr) + else + checkNextNotIndented() + Template(constr, Nil, Nil, EmptyValDef, Nil) /** TemplateBody ::= [nl] `{' TemplateStatSeq `}' */ - def templateBodyOpt(constr: DefDef, parents: List[Tree], derived: List[Tree]): Template = { + def templateBodyOpt(constr: DefDef, parents: List[Tree], derived: List[Tree]): Template = val (self, stats) = - if (in.isNestedStart) templateBody() else (EmptyValDef, Nil) + if in.isNestedStart then + templateBody() + else + checkNextNotIndented() + (EmptyValDef, Nil) Template(constr, parents, derived, self, stats) - } def templateBody(): (ValDef, List[Tree]) = { val r = inDefScopeBraces { templateStatSeq() } diff --git a/compiler/test/debug/Gen.scala b/compiler/test/debug/Gen.scala index 77a82473aa40..46a047d20d83 100755 --- a/compiler/test/debug/Gen.scala +++ b/compiler/test/debug/Gen.scala @@ -126,38 +126,38 @@ object Gen { |""".stripMargin }).mkString("\n\n") - s"""|#!/usr/bin/expect - | - |# log_user 1 - |# exp_internal 1 - |# set timeout 5 - | - |send_user "spawning job...\\n" - | - |spawn jdb -attach 5005 -sourcepath $source - | - |send_user "interacting...\\n" - | - |expect { - | "*VM Started*" { send_user "success - connected to server \\n" } - | timeout { - | send_user "timeout while waiting for: *VM Started*\\n" - | exit 1 - | } - |} - | - |send_user "setting breakpoints...\\n" - | - |# breakpoints - |$breakpoints - | - |# run - |send_user "run program...\\n" - |send "run\\r" - |expect "Breakpoint hit" - | - |# interactions - |$commands""".stripMargin + s"""|#!/usr/bin/expect + | + |# log_user 1 + |# exp_internal 1 + |# set timeout 5 + | + |send_user "spawning job...\\n" + | + |spawn jdb -attach 5005 -sourcepath $source + | + |send_user "interacting...\\n" + | + |expect { + | "*VM Started*" { send_user "success - connected to server \\n" } + | timeout { + | send_user "timeout while waiting for: *VM Started*\\n" + | exit 1 + | } + |} + | + |send_user "setting breakpoints...\\n" + | + |# breakpoints + |$breakpoints + | + |# run + |send_user "run program...\\n" + |send "run\\r" + |expect "Breakpoint hit" + | + |# interactions + |$commands""".stripMargin } def main(args: Array[String]): Unit = { diff --git a/tests/neg-custom-args/indentRight.scala b/tests/neg-custom-args/indentRight.scala index 5fefb400f956..f852208d339d 100644 --- a/tests/neg-custom-args/indentRight.scala +++ b/tests/neg-custom-args/indentRight.scala @@ -17,6 +17,10 @@ object Test { } + trait A + case class B() extends A // error: Line is indented too far to the right + case object C extends A // error: Line is indented too far to the right + if (true) // OK println("hi") } From cf3b9b09f176f90f78f7900431b6278d7b7806f2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 19 Sep 2019 20:16:09 +0200 Subject: [PATCH 5/8] Fix indentation --- .../dotty/tools/languageserver/util/actions/SignatureHelp.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language-server/test/dotty/tools/languageserver/util/actions/SignatureHelp.scala b/language-server/test/dotty/tools/languageserver/util/actions/SignatureHelp.scala index bd82850dab35..a3c10c887c44 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/SignatureHelp.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/SignatureHelp.scala @@ -29,7 +29,7 @@ class SignatureHelp(override val marker: CodeMarker, activeSignature: Option[Int], activeParam: Int) extends ActionOnMarker { - val expectedSignatures = expected.map(DottyLanguageServer.signatureToSignatureInformation) + val expectedSignatures = expected.map(DottyLanguageServer.signatureToSignatureInformation) override def execute(): Exec[Unit] = { val results = server.signatureHelp(marker.toTextDocumentPositionParams).get() From 3e2ab41ec52322923d8fad7074684cebf2df8ef4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 19 Sep 2019 21:04:32 +0200 Subject: [PATCH 6/8] Fix indentation in test --- tests/run-staging/shonan-hmm-simple.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/run-staging/shonan-hmm-simple.scala b/tests/run-staging/shonan-hmm-simple.scala index a820298dc0ae..97ea80d29b35 100644 --- a/tests/run-staging/shonan-hmm-simple.scala +++ b/tests/run-staging/shonan-hmm-simple.scala @@ -71,8 +71,7 @@ case class Complex[T](re: T, im: T) object Complex implicit def isLiftable[T: Type: Liftable]: Liftable[Complex[T]] = new Liftable[Complex[T]] - def toExpr(comp: Complex[T]) = '{Complex(${comp.re}, ${comp.im})} - + def toExpr(comp: Complex[T]) = '{Complex(${comp.re}, ${comp.im})} case class Vec[Idx, T](size: Idx, get: Idx => T) { def map[U](f: T => U): Vec[Idx, U] = Vec(size, i => f(get(i))) From bdf9b81676f38ba54632b28c3e7f12a3ec5f8b13 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 19 Sep 2019 22:06:56 +0200 Subject: [PATCH 7/8] Fix test again --- tests/run-staging/shonan-hmm-simple.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/run-staging/shonan-hmm-simple.scala b/tests/run-staging/shonan-hmm-simple.scala index 97ea80d29b35..2fe2e7b98244 100644 --- a/tests/run-staging/shonan-hmm-simple.scala +++ b/tests/run-staging/shonan-hmm-simple.scala @@ -70,8 +70,9 @@ class RingPV[U: Liftable](u: Ring[U], eu: Ring[Expr[U]])(given QuoteContext) ext case class Complex[T](re: T, im: T) object Complex - implicit def isLiftable[T: Type: Liftable]: Liftable[Complex[T]] = new Liftable[Complex[T]] - def toExpr(comp: Complex[T]) = '{Complex(${comp.re}, ${comp.im})} + implicit def isLiftable[T: Type: Liftable]: Liftable[Complex[T]] = new Liftable[Complex[T]] { + def toExpr(comp: Complex[T]) = '{Complex(${comp.re}, ${comp.im})} + } case class Vec[Idx, T](size: Idx, get: Idx => T) { def map[U](f: T => U): Vec[Idx, U] = Vec(size, i => f(get(i))) From 25af394ed98d1cba7eedf3648d156f327c11491d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 20 Sep 2019 11:29:08 +0200 Subject: [PATCH 8/8] Polishings --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 4 ++-- compiler/src/dotty/tools/dotc/parsing/Scanners.scala | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 5659b81e8d0c..6e47b75a5495 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -604,7 +604,7 @@ object Parsers { // Therefore, make sure there would be a matching def nextIndentWidth = in.indentWidth(in.next.offset) if (in.token == NEWLINE || in.token == NEWLINES) - && !(nextIndentWidth < startIndentWidth) + && !(nextIndentWidth < startIndentWidth) then warning( if startIndentWidth <= nextIndentWidth then @@ -632,7 +632,7 @@ object Parsers { /* -------- REWRITES ----------------------------------------------------------- */ /** The last offset where a colon at the end of line would be required if a subsequent { ... } - * block would be converted to an indentation reg`ion. + * block would be converted to an indentation region. */ var possibleColonOffset: Int = -1 diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index efa20d269d9d..d6c3e39cec62 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -492,10 +492,6 @@ object Scanners { indentPrefix = LBRACE case _ => } - def isMatchCatchFollow(prev: Token) = - nextWidth == lastWidth - && (prev == MATCH || prev == CATCH) - && token != CASE if (newlineIsSeparating && canEndStatTokens.contains(lastToken) && canStartStatTokens.contains(token) &&