diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 8b3a0b7a550f..55e2473bc405 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -2588,7 +2588,8 @@ class JSCodeGen()(implicit ctx: Context) { sym.info.paramNamess.flatten.zip(sym.info.paramInfoss.flatten) val wereRepeated = ctx.atPhase(ctx.elimRepeatedPhase) { - val list = for ((name, tpe) <- paramNamesAndTypes) + val list = + for ((name, tpe) <- paramNamesAndTypes) yield (name -> tpe.isRepeatedParam) list.toMap } diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 0aecf0920973..b0582dd855db 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -52,6 +52,7 @@ class ScalaSettings extends Settings.SettingGroup { val oldSyntax: Setting[Boolean] = BooleanSetting("-old-syntax", "Require `(...)` around conditions") val indent: Setting[Boolean] = BooleanSetting("-indent", "allow significant indentation") val noindent: Setting[Boolean] = BooleanSetting("-noindent", "require classical {...} syntax, indentation is not significant") + val YindentColons: Setting[Boolean] = BooleanSetting("-Yindent-colons", "allow colons at ends-of-lines to start indentation blocks") /** Decompiler settings */ val printTasty: Setting[Boolean] = BooleanSetting("-print-tasty", "Prints the raw tasty.") withAbbreviation "--print-tasty" diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index e10232327085..48f246f14cfe 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -735,7 +735,7 @@ object Parsers { } }) canRewrite &= (in.isAfterLineEnd || statCtdTokens.contains(in.token)) // test (5) - if (canRewrite) { + if (canRewrite && (!colonRequired || in.colonSyntax)) { val openingPatchStr = if (!colonRequired) "" else if (testChar(startOpening - 1, Chars.isOperatorPart(_))) " :" @@ -1218,7 +1218,7 @@ object Parsers { } def possibleTemplateStart(): Unit = { - in.observeIndented() + in.observeIndented(noIndentTemplateTokens, nme.derives) newLineOptWhenFollowedBy(LBRACE) } @@ -1636,8 +1636,11 @@ object Parsers { if (rewriteToOldSyntax()) revertToParens(t) in.nextToken() } - else if (rewriteToNewSyntax(t.span)) - dropParensOrBraces(t.span.start, s"${tokenString(altToken)}") + else { + in.observeIndented(noIndentAfterConditionTokens) + if (rewriteToNewSyntax(t.span)) + dropParensOrBraces(t.span.start, s"${tokenString(altToken)}") + } t } else { @@ -2296,6 +2299,7 @@ object Parsers { dropParensOrBraces(start, if (in.token == YIELD || in.token == DO) "" else "do") } } + in.observeIndented(noIndentAfterEnumeratorTokens) res } else { @@ -3539,15 +3543,14 @@ object Parsers { /** TemplateOpt = [Template] */ - def templateOpt(constr: DefDef): Template = - possibleBracesStart() + def templateOpt(constr: DefDef): Template = { + possibleTemplateStart() if (in.token == EXTENDS || isIdent(nme.derives)) template(constr) - else { - possibleTemplateStart() + else if (in.isNestedStart) template(constr) else Template(constr, Nil, Nil, EmptyValDef, Nil) - } + } /** TemplateBody ::= [nl] `{' TemplateStatSeq `}' */ diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 84ecf16878fa..ede520f7b809 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -152,12 +152,15 @@ object Scanners { val rewriteNoIndent = ctx.settings.noindent.value && rewrite val noindentSyntax = - ctx.settings.noindent.value || - ctx.settings.oldSyntax.value || - isScala2Mode + ctx.settings.noindent.value + || ctx.settings.oldSyntax.value + || isScala2Mode val indentSyntax = - (if (Config.defaultIndent) !noindentSyntax else ctx.settings.indent.value) || - rewriteNoIndent + (if (Config.defaultIndent) !noindentSyntax else ctx.settings.indent.value) + || rewriteNoIndent + val colonSyntax = + ctx.settings.YindentColons.value + || rewriteNoIndent if (rewrite) { val s = ctx.settings @@ -532,10 +535,11 @@ object Scanners { } } - def observeIndented(): Unit = + def observeIndented(unless: BitSet, unlessSoftKW: TermName = EmptyTermName): Unit = if (indentSyntax && isAfterLineEnd && token != INDENT) { val newLineInserted = token == NEWLINE || token == NEWLINES val nextOffset = if (newLineInserted) next.offset else offset + val nextToken = if (newLineInserted) next.token else token val nextWidth = indentWidth(nextOffset) val lastWidth = currentRegion match { case r: Indented => r.width @@ -543,7 +547,9 @@ object Scanners { case _ => nextWidth } - if (lastWidth < nextWidth) { + if (lastWidth < nextWidth + && !unless.contains(nextToken) + && (unlessSoftKW.isEmpty || token != IDENTIFIER || name != unlessSoftKW)) { currentRegion = Indented(nextWidth, Set(), COLONEOL, currentRegion) if (!newLineInserted) next.copyFrom(this) offset = nextOffset @@ -590,7 +596,7 @@ object Scanners { lookahead() val atEOL = isAfterLineEnd reset() - if (atEOL) token = COLONEOL + if (colonSyntax && atEOL) token = COLONEOL case EOF | RBRACE => currentRegion match { case r: Indented if !r.isOutermost => diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index 0e83f37aef1e..8294b8d4254d 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -281,6 +281,10 @@ object Tokens extends TokensCommon { */ final val startParamOrGivenTypeTokens: BitSet = startParamTokens | BitSet(GIVEN, ERASED) + final val noIndentTemplateTokens = BitSet(EXTENDS) + final val noIndentAfterConditionTokens = BitSet(THEN, DO) + final val noIndentAfterEnumeratorTokens = BitSet(YIELD, DO) + final val scala3keywords = BitSet(ENUM, ERASED, GIVEN, IMPLIED) final val softModifierNames = Set(nme.inline, nme.opaque) diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index bd3549012860..1e75b2106244 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -2174,7 +2174,8 @@ object messages { def symLocation(sym: Symbol) = { val lineDesc = if (sym.span.exists && sym.span != sym.owner.span) - s" at line ${sym.sourcePos.line + 1}" else "" + s" at line ${sym.sourcePos.line + 1}" + else "" i"in ${sym.owner}${lineDesc}" } val clashDescription = diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index 2dd7af23f804..6914f59fb1b6 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -254,7 +254,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => def setters(mixin: ClassSymbol): List[Tree] = for (setter <- mixin.info.decls.filter(setr => setr.isSetter && !wasOneOf(setr, Deferred))) - yield transformFollowing(DefDef(mkForwarderSym(setter.asTerm), unitLiteral.withSpan(cls.span))) + yield transformFollowing(DefDef(mkForwarderSym(setter.asTerm), unitLiteral.withSpan(cls.span))) def mixinForwarders(mixin: ClassSymbol): List[Tree] = for (meth <- mixin.info.decls.toList if needsMixinForwarder(meth)) @@ -263,7 +263,6 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => transformFollowing(polyDefDef(mkForwarderSym(meth.asTerm, Bridge), forwarderRhsFn(meth))) } - cpy.Template(impl)( constr = if (cls.is(Trait)) cpy.DefDef(impl.constr)(vparamss = Nil :: Nil) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index f92b15ee6fe5..70ed4b4143c4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -890,8 +890,8 @@ trait Applications extends Compatibility { case err: ErrorType => cpy.Apply(tree)(fun1, proto.unforcedTypedArgs).withType(err) case TryDynamicCallType => typedDynamicApply(tree, pt) case _ => - if originalProto.isDropped then fun1 - else if fun1.symbol == defn.Compiletime_summonFrom then + if (originalProto.isDropped) fun1 + else if (fun1.symbol == defn.Compiletime_summonFrom) // Special handling of `summonFrom { ... }`. // We currently cannot use a macro for that since unlike other inline methods // summonFrom needs to expand lazily. For instance, in diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 96aea1c65745..0ff79a9ab558 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1252,7 +1252,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { |""".stripMargin ctx.error(msg, inlinedFrom.sourcePos) EmptyTree - } else { + } + else { val evaluatedSplice = Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)(ctx1) val inlinedNormailizer = new TreeMap { @@ -1270,10 +1271,10 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { /** Return the set of symbols that are refered at level -1 by the tree and defined in the current run. * This corresponds to the symbols that will need to be interpreted. */ - private def macroDependencies(tree: Tree)(implicit ctx: Context) = { + private def macroDependencies(tree: Tree)(implicit ctx: Context) = new TreeAccumulator[Set[Symbol]] { private[this] var level = -1 - override def apply(syms: Set[Symbol], tree: tpd.Tree)(implicit ctx: Context): Set[Symbol] = { + override def apply(syms: Set[Symbol], tree: tpd.Tree)(implicit ctx: Context): Set[Symbol] = if (level != -1) foldOver(syms, tree) else tree match { case tree: RefTree if level == -1 && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => @@ -1289,8 +1290,6 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { case _ => foldOver(syms, tree) } - } }.apply(Set.empty, tree) - } } diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index bab7932762a8..80f30874763f 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -216,7 +216,7 @@ trait QuotesAndSplices { super.transform(tree) case tdef: TypeDef if tdef.symbol.hasAnnotation(defn.InternalQuoted_patternBindHoleAnnot) => transformTypeBindingTypeDef(tdef, typePatBuf) - case tree @ AppliedTypeTree(tpt, args) => + case tree @ AppliedTypeTree(tpt, args) => val args1: List[Tree] = args.zipWithConserve(tpt.tpe.typeParams.map(_.paramVariance)) { (arg, v) => arg.tpe match { case _: TypeBounds => transform(arg) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index c68eff6ea7be..bab516d8ca2a 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -56,7 +56,8 @@ class CompilationTests extends ParallelTesting { "tests/neg-custom-args/fatal-warnings/xfatalWarnings.scala", defaultOptions.and("-nowarn", "-Xfatal-warnings") ), - compileFile("tests/pos-special/typeclass-scaling.scala", defaultOptions.and("-Xmax-inlines", "40")) + compileFile("tests/pos-special/typeclass-scaling.scala", defaultOptions.and("-Xmax-inlines", "40")), + compileFile("tests/pos-special/indent-colons.scala", defaultOptions.and("-Yindent-colons")) ).checkCompile() } diff --git a/tests/neg/endmarkers.scala b/tests/neg/endmarkers.scala index 40a2a731b2cf..3f6f41627f31 100644 --- a/tests/neg/endmarkers.scala +++ b/tests/neg/endmarkers.scala @@ -1,6 +1,6 @@ object Test - locally: + locally { var x = 0 while x < 10 do x += 1 end while // error: end of statement expected but while found // error: not found: end @@ -9,7 +9,8 @@ object Test x += 1 x < 10 do () - end while // error: misaligned end marker + end while // error: misaligned end marker // error: not found : end + } // error: ';' expected, but '}' found def f(x: Int): Int = val y = @@ -23,22 +24,12 @@ object Test val z = 22 x + y + z - end f // error: misaligned end marker + end f def g = "!" val xs = List(1, 2, 3) - xs.map: - x => - val y = x * x - y * y - - xs.map: - x => - val y = x * x - y + y - println(f(2) + g) (new Test2).foo @@ -57,10 +48,11 @@ class Test2 def foo = 1 object x - new Test2: + new Test2 { override def foo = 2 end new // error: end of statement expected but new found // error: not found: end - def bar = 2 // error: ';' expected, but unindent found + } // error: ';' expected, but '}' found + def bar = 2 end Test2 // error: misaligned end marker end Test2 @@ -102,9 +94,11 @@ class Coder(words: List[String]) /** Invert the mnemonics map to give a map from chars 'A' ... 'Z' to '2' ... '9' */ private val charCode0: Map[Char, Char] = mnemonics - .withFilter: + .withFilter { case (digit, str) => true case _ => false - .flatMap: + } + .flatMap { case (digit, str) => str map (ltr => ltr -> digit) + } end Coder // error: The start of this line does not match any of the previous indentation widths. \ No newline at end of file diff --git a/tests/neg/i4373b.scala b/tests/neg/i4373b.scala index a3d8f3850eff..297fcd76ff08 100644 --- a/tests/neg/i4373b.scala +++ b/tests/neg/i4373b.scala @@ -1,5 +1,5 @@ // ==> 05bef7805687ba94da37177f7568e3ba7da1f91c.scala <== class x0 { x1: // error - x0 | _ + x0 | _ // error // error \ No newline at end of file diff --git a/tests/pos-special/indent-colons.scala b/tests/pos-special/indent-colons.scala new file mode 100644 index 000000000000..ef9af5044174 --- /dev/null +++ b/tests/pos-special/indent-colons.scala @@ -0,0 +1,118 @@ +object Test + + locally: + var x = 0 + while x < 10 do x += 1 + val f = 10 + while + x += 1 + x < 10 + do () + + def f(x: Int): Int = + val y = + if x > 0 then + println("hello") + 22 + else + println("world") + 33 + val z = 22 + x + y + z + end f + + def g = "!" + + val xs = List(1, 2, 3) + + xs.map: + x => + val y = x * x + y * y + + xs.map: + x => + val y = x * x + y + y + + xs.map { x => + val y = x * x + if y >= 0 then + val z = y + y + println(z) + y + 1 + } + + println(f(2) + g) + + (new Test2).foo + (new Test3).foo + + var x = 1 + while + x += 1 + val y = x + println(y) + x < 10 + do () + +class Test2 + self => + def foo = 1 + + val x = + new Test2: + override def foo = 2 + end new + end x +end Test2 + +class Test3 + self => + def foo = 1 + +import collection.mutable.HashMap + +class Coder(words: List[String]) + + class Foo + println() + end Foo + + class Bar + + (2 -> "ABC", new ArrowAssoc('3') -> "DEF") + + private val mnemonics = Map( + '2' -> "ABC", '3' -> "DEF", '4' -> "GHI", '5' -> "JKL", + '6' -> "MNO", '7' -> "PQRS", '8' -> "TUV", '9' -> "WXYZ") + + ('1', "1") match + case (digit, str) => true + case _ => false + + ('1', "1") match + case (digit, str) => true + case _ => false + + try List(1, 2, 3) match + case x :: xs => println(x) + case Nil => println("Nil") + catch + case ex: java.io.IOException => println(ex) + case ex: Throwable => + throw ex + end try + + /** Invert the mnemonics map to give a map from chars 'A' ... 'Z' to '2' ... '9' */ + private val charCode0: Map[Char, Char] = + mnemonics + .withFilter: + case (digit, str) => true + case _ => false + .flatMap: + case (digit, str) => str map (ltr => ltr -> digit) +end Coder + +object Test22 + def foo: Int = 22 \ No newline at end of file diff --git a/tests/pos/Coder.scala b/tests/pos/Coder.scala index 6eb1ad55ab95..8a36200ed9b1 100644 --- a/tests/pos/Coder.scala +++ b/tests/pos/Coder.scala @@ -15,14 +15,16 @@ class Coder(words: List[String]) { } /** Invert the mnemonics map to give a map from chars 'A' ... 'Z' to '2' ... '9' */ - private val charCode0: Map[Char, Char] = mnemonics withFilter { - case (digit, str) => true - case _ => false - } flatMap { x$1 => - x$1 match { - case (digit, str) => str map (ltr => ltr -> digit) + private val charCode0: Map[Char, Char] = mnemonics + .withFilter { + case (digit, str) => true + case _ => false + } + .flatMap { x$1 => + x$1 match { + case (digit, str) => str map (ltr => ltr -> digit) + } } - } private val charCode: Map[Char, Char] = for ((digit, str) <- mnemonics; ltr <- str) yield ltr -> digit @@ -42,12 +44,12 @@ class Coder(words: List[String]) { splitPoint <- 1 to number.length word <- wordsForNum(number take splitPoint) rest <- encode(number drop splitPoint) - } yield word :: rest + } + yield word :: rest }.toSet /** Maps a number to a list of all word phrases that can represent it */ def translate(number: String): Set[String] = encode(number) map (_ mkString " ") - } object Coder { diff --git a/tests/pos/i7217.scala b/tests/pos/i7217.scala index 1c2a92c731b7..0a177a1da88c 100644 --- a/tests/pos/i7217.scala +++ b/tests/pos/i7217.scala @@ -1,4 +1,4 @@ -class Foo(p1: String, p2: String): +class Foo(p1: String, p2: String) def this(p1: String) = this(p1, "blah") val x = { Foo("blah", "hah") } diff --git a/tests/pos/indent.scala b/tests/pos/indent.scala index ef9af5044174..e77f16b79134 100644 --- a/tests/pos/indent.scala +++ b/tests/pos/indent.scala @@ -1,6 +1,6 @@ object Test - locally: + locally { var x = 0 while x < 10 do x += 1 val f = 10 @@ -8,6 +8,7 @@ object Test x += 1 x < 10 do () + } def f(x: Int): Int = val y = @@ -25,15 +26,17 @@ object Test val xs = List(1, 2, 3) - xs.map: + xs.map { x => val y = x * x y * y + } - xs.map: + xs.map { x => val y = x * x y + y + } xs.map { x => val y = x * x @@ -61,9 +64,9 @@ class Test2 def foo = 1 val x = - new Test2: + new Test2 { override def foo = 2 - end new + } end x end Test2 @@ -107,11 +110,13 @@ class Coder(words: List[String]) /** Invert the mnemonics map to give a map from chars 'A' ... 'Z' to '2' ... '9' */ private val charCode0: Map[Char, Char] = mnemonics - .withFilter: + .withFilter { case (digit, str) => true case _ => false - .flatMap: + } + .flatMap { case (digit, str) => str map (ltr => ltr -> digit) + } end Coder object Test22 diff --git a/tests/pos/indent4.scala b/tests/pos/indent4.scala new file mode 100644 index 000000000000..50eef34ff3e7 --- /dev/null +++ b/tests/pos/indent4.scala @@ -0,0 +1,24 @@ +object testindent + + if (true) + val x = 1 + println(x) + + while (false) + val x = 1 + println(x) + + for (x <- List(1, 2, 3)) + val y = x + println(y) + + for { x <- List(1, 2, 3) } + val y = x + println(y) + + for { + x <- List(1, 2, 3) + } + val y = x + println(y) +