From c719da700c5728e4cb5efb1e4904f34c74ee00c7 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Mon, 19 Apr 2021 17:21:17 +0200 Subject: [PATCH] Support Scala 3 wildcard and renaming imports under -Xsource:3 Instead of: import foo._ One can now write: import foo.* and instead of: import foo.{bar => baz} One can now write: import foo.{bar as baz} As well as: import foo.bar as baz This will let us deprecate the old syntax in a future release of Scala 3 (it's currently only deprecated under `-source future`). See http://dotty.epfl.ch/docs/reference/changed-features/imports.html for details but note that unlike Scala 3 this commit does not implement support for: import java as j As that would require deeper changes in the compiler. --- .../scala/tools/nsc/ast/parser/Parsers.scala | 45 ++++++++++++------- .../scala/reflect/internal/StdNames.scala | 3 ++ test/files/neg/import-future.check | 4 ++ test/files/neg/import-future.scala | 27 +++++++++++ test/files/pos/import-future.scala | 25 +++++++++++ 5 files changed, 87 insertions(+), 17 deletions(-) create mode 100644 test/files/neg/import-future.check create mode 100644 test/files/neg/import-future.scala create mode 100644 test/files/pos/import-future.scala diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index e84248e4663b..f0356c7b00eb 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -2564,19 +2564,27 @@ self => def loop(expr: Tree): Tree = { expr setPos expr.pos.makeTransparent val selectors: List[ImportSelector] = in.token match { - case USCORE => List(wildImportSelector()) // import foo.bar._; - case LBRACE => importSelectors() // import foo.bar.{ x, y, z } - case _ => - val nameOffset = in.offset - val name = ident() - if (in.token == DOT) { - // import foo.bar.ident. and so create a select node and recurse. - val t = atPos(start, if (name == nme.ERROR) in.offset else nameOffset)(Select(expr, name)) - in.nextToken() - return loop(t) + case USCORE => + List(wildImportSelector()) // import foo.bar._ + case IDENTIFIER if currentRun.isScala3 && in.name == raw.STAR => + List(wildImportSelector()) // import foo.bar.* + case LBRACE => + importSelectors() // import foo.bar.{ x, y, z } + case _ => + if (settings.isScala3 && lookingAhead { isRawIdent && in.name == nme.as }) + List(importSelector()) // import foo.bar as baz + else { + val nameOffset = in.offset + val name = ident() + if (in.token == DOT) { + // import foo.bar.ident. and so create a select node and recurse. + val t = atPos(start, if (name == nme.ERROR) in.offset else nameOffset)(Select(expr, name)) + in.nextToken() + return loop(t) + } + // import foo.bar.Baz; + else List(makeImportSelector(name, nameOffset)) } - // import foo.bar.Baz; - else List(makeImportSelector(name, nameOffset)) } // reaching here means we're done walking. atPos(start)(Import(expr, selectors)) @@ -2619,17 +2627,20 @@ self => val bbq = in.token == BACKQUOTED_IDENT val name = wildcardOrIdent() var renameOffset = -1 - val rename = in.token match { - case ARROW => + + val rename = + if (in.token == ARROW || (settings.isScala3 && isRawIdent && in.name == nme.as)) { in.nextToken() renameOffset = in.offset if (name == nme.WILDCARD && !bbq) syntaxError(renameOffset, "Wildcard import cannot be renamed") wildcardOrIdent() - case _ if name == nme.WILDCARD && !bbq => null - case _ => + } + else if (name == nme.WILDCARD && !bbq) null + else { renameOffset = start name - } + } + ImportSelector(name, start, rename, renameOffset) } diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 1906a2f3028f..fc8581847966 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -658,6 +658,9 @@ trait StdNames { val long2Long: NameType = nameType("long2Long") val boolean2Boolean: NameType = nameType("boolean2Boolean") + // Scala 3 import syntax + val as: NameType = nameType("as") + // Compiler utilized names val AnnotatedType: NameType = nameType("AnnotatedType") diff --git a/test/files/neg/import-future.check b/test/files/neg/import-future.check new file mode 100644 index 000000000000..000601f45b7d --- /dev/null +++ b/test/files/neg/import-future.check @@ -0,0 +1,4 @@ +import-future.scala:15: error: not found: value unrelated + unrelated(1) // error + ^ +1 error diff --git a/test/files/neg/import-future.scala b/test/files/neg/import-future.scala new file mode 100644 index 000000000000..288fd3d0e240 --- /dev/null +++ b/test/files/neg/import-future.scala @@ -0,0 +1,27 @@ +// scalac: -Xsource:3 +// + +class D { + def *(y: Int): Int = y + def unrelated(y: Int): Int = y +} + +object Test { + val d = new D + + def one: Int = { + import d.`*` + + unrelated(1) // error + + *(1) + } + + def two: Int = { + import d.* + + unrelated(1) + + *(1) + } +} diff --git a/test/files/pos/import-future.scala b/test/files/pos/import-future.scala new file mode 100644 index 000000000000..cfaff804af02 --- /dev/null +++ b/test/files/pos/import-future.scala @@ -0,0 +1,25 @@ +// scalac: -Xsource:3 +// + +import java.io as jio +import scala.{collection as c} + +import c.mutable as mut +import mut.ArrayBuffer as Buf + +object O { + val x: jio.IOException = ??? + val y = Buf(1, 2, 3) + + type OString = String + def foo22(x: Int) = x +} + +class C { + import O.{ foo22 as foo, OString as OS } + println(foo(22)) + val s: OS = "" + + import mut.* + val ab = ArrayBuffer(1) +}