diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index f7743dddda4e..64b8a91e9096 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -281,6 +281,7 @@ private sealed trait YSettings: val Yscala2Unpickler: Setting[String] = StringSetting("-Yscala2-unpickler", "", "Control where we may get Scala 2 symbols from. This is either \"always\", \"never\", or a classpath.", "always") val YnoImports: Setting[Boolean] = BooleanSetting("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.") + val Yimports: Setting[List[String]] = MultiStringSetting("-Yimports", helpArg="", "Custom root imports. If set, none of scala.*, java.lang.*, or Predef.* will be imported unless explicitly included.") val YnoGenericSig: Setting[Boolean] = BooleanSetting("-Yno-generic-signatures", "Suppress generation of generic signatures for Java.") val YnoPredef: Setting[Boolean] = BooleanSetting("-Yno-predef", "Compile without importing Predef.") val Yskip: Setting[List[String]] = PhasesSetting("-Yskip", "Skip") diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 36aaef8e8f47..bad796df2bd5 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1558,12 +1558,21 @@ class Definitions { private val PredefImportFns: RootRef = RootRef(() => ScalaPredefModule.termRef, isPredef=true) - @tu private lazy val JavaRootImportFns: List[RootRef] = - if ctx.settings.YnoImports.value then Nil - else JavaImportFns + @tu private lazy val YimportsImportFns: List[RootRef] = ctx.settings.Yimports.value.map { name => + val denot = + getModuleIfDefined(name).suchThat(_.is(Module)) `orElse` + getPackageClassIfDefined(name).suchThat(_.is(Package)) + if !denot.exists then + report.error(s"error: bad preamble import $name") + val termRef = denot.symbol.termRef + RootRef(() => termRef) + } + + @tu private lazy val JavaRootImportFns: List[RootRef] = JavaImportFns @tu private lazy val ScalaRootImportFns: List[RootRef] = - if ctx.settings.YnoImports.value then Nil + if !ctx.settings.Yimports.isDefault then YimportsImportFns + else if ctx.settings.YnoImports.value then Nil else if ctx.settings.YnoPredef.value then ScalaImportFns else ScalaImportFns :+ PredefImportFns diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 500cc6cbe17f..20daa2d24406 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -151,9 +151,6 @@ class CompilationTests { compileFile("tests/neg-custom-args/ovlazy.scala", scala2CompatMode.and("-Xfatal-warnings")), compileFile("tests/neg-custom-args/newline-braces.scala", scala2CompatMode.and("-Xfatal-warnings")), compileFile("tests/neg-custom-args/autoTuplingTest.scala", defaultOptions.andLanguageFeature("noAutoTupling")), - compileFile("tests/neg-custom-args/nopredef.scala", defaultOptions.and("-Yno-predef")), - compileFile("tests/neg-custom-args/noimports.scala", defaultOptions.and("-Yno-imports")), - compileFile("tests/neg-custom-args/noimports2.scala", defaultOptions.and("-Yno-imports")), compileFile("tests/neg-custom-args/i1650.scala", allowDeepSubtypes), compileFile("tests/neg-custom-args/i3882.scala", allowDeepSubtypes), compileFile("tests/neg-custom-args/i4372.scala", allowDeepSubtypes), diff --git a/tests/neg/missing-import.scala b/tests/neg/missing-import.scala new file mode 100644 index 000000000000..8af26030435a --- /dev/null +++ b/tests/neg/missing-import.scala @@ -0,0 +1,3 @@ +class annotation extends Annotation // error +val s: String = "str" +val regex: Regex = s.r // error diff --git a/tests/neg/noimports-additional.scala b/tests/neg/noimports-additional.scala new file mode 100644 index 000000000000..e726db5b9b0a --- /dev/null +++ b/tests/neg/noimports-additional.scala @@ -0,0 +1,4 @@ +// scalac: -Yno-imports -Yimports:scala.annotation,scala.util.matching +class annotation extends Annotation +val s: String = "str" // error +val regex: Regex = new Regex("str") diff --git a/tests/neg-custom-args/noimports.scala b/tests/neg/noimports.scala similarity index 70% rename from tests/neg-custom-args/noimports.scala rename to tests/neg/noimports.scala index 6cef8dee8843..720d111757cd 100644 --- a/tests/neg-custom-args/noimports.scala +++ b/tests/neg/noimports.scala @@ -1,3 +1,4 @@ +// scalac: -Yno-imports object Test { val t: Int = 1 // error: not found Int } diff --git a/tests/neg-custom-args/noimports2.scala b/tests/neg/noimports2.scala similarity index 74% rename from tests/neg-custom-args/noimports2.scala rename to tests/neg/noimports2.scala index b75f1361ddb9..deee773c35c6 100644 --- a/tests/neg-custom-args/noimports2.scala +++ b/tests/neg/noimports2.scala @@ -1,3 +1,4 @@ +// scalac: -Yno-imports object Test { assert("asdf" == "asdf") // error: not found assert } diff --git a/tests/neg/nopredef-additional.scala b/tests/neg/nopredef-additional.scala new file mode 100644 index 000000000000..0b6a71ca7c53 --- /dev/null +++ b/tests/neg/nopredef-additional.scala @@ -0,0 +1,4 @@ +// scalac: -Yno-predef -Yimports:java.lang,scala.annotation,scala.util.matching +class annotation extends Annotation +val s: String = "str" +val regex: Regex = s.r // error diff --git a/tests/neg/nopredef.scala b/tests/neg/nopredef.scala index 0a22e200805a..fa9a344772a6 100644 --- a/tests/neg/nopredef.scala +++ b/tests/neg/nopredef.scala @@ -1,5 +1,4 @@ -import Predef.{assert as _} - +// scalac: -Yno-predef object Test { assert("asdf" == "asdf") // error: not found assert } diff --git a/tests/neg-custom-args/nopredef.scala b/tests/neg/unimport-Predef-assert.scala similarity index 70% rename from tests/neg-custom-args/nopredef.scala rename to tests/neg/unimport-Predef-assert.scala index b75f1361ddb9..0a22e200805a 100644 --- a/tests/neg-custom-args/nopredef.scala +++ b/tests/neg/unimport-Predef-assert.scala @@ -1,3 +1,5 @@ +import Predef.{assert as _} + object Test { assert("asdf" == "asdf") // error: not found assert } diff --git a/tests/neg/yimports-custom.check b/tests/neg/yimports-custom.check new file mode 100644 index 000000000000..6ed2eb8b1df3 --- /dev/null +++ b/tests/neg/yimports-custom.check @@ -0,0 +1,7 @@ + +-- [E006] Not Found Error: tests/neg/yimports-custom/C_2.scala:5:16 ---------------------------------------------------- +5 | def greet() = println("hello, world!") // error + | ^^^^^^^ + | Not found: println + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/yimports-custom/C_2.scala b/tests/neg/yimports-custom/C_2.scala new file mode 100644 index 000000000000..6ba25ad2963c --- /dev/null +++ b/tests/neg/yimports-custom/C_2.scala @@ -0,0 +1,6 @@ +// scalac: -Yimports:hello.world.minidef + +class C { + val v: Numb = Magic + def greet() = println("hello, world!") // error +} diff --git a/tests/neg/yimports-custom/minidef_1.scala b/tests/neg/yimports-custom/minidef_1.scala new file mode 100644 index 000000000000..5d18d0a39584 --- /dev/null +++ b/tests/neg/yimports-custom/minidef_1.scala @@ -0,0 +1,7 @@ + +package hello.world + +object minidef { + type Numb = Int + final val Magic = 42 +} diff --git a/tests/neg/yimports-nojava.check b/tests/neg/yimports-nojava.check new file mode 100644 index 000000000000..8aef6786ca21 --- /dev/null +++ b/tests/neg/yimports-nojava.check @@ -0,0 +1,12 @@ +-- [E006] Not Found Error: tests/neg/yimports-nojava.scala:5:16 -------------------------------------------------------- +5 | def g() = new Integer(42) // error + | ^^^^^^^ + | Not found: type Integer + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg/yimports-nojava.scala:6:16 -------------------------------------------------------- +6 | def sleep() = Thread.sleep(42000L) // error + | ^^^^^^ + | Not found: Thread + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/yimports-nojava.scala b/tests/neg/yimports-nojava.scala new file mode 100644 index 000000000000..35233e37a775 --- /dev/null +++ b/tests/neg/yimports-nojava.scala @@ -0,0 +1,7 @@ +// scalac: -Yimports:scala,scala.Predef + +trait T { + def f() = println("hello, world!") + def g() = new Integer(42) // error + def sleep() = Thread.sleep(42000L) // error +} diff --git a/tests/neg/yimports-nosuch.check b/tests/neg/yimports-nosuch.check new file mode 100644 index 000000000000..5a77d7f8d016 --- /dev/null +++ b/tests/neg/yimports-nosuch.check @@ -0,0 +1,2 @@ +error: bad preamble import skala +error: bad preamble import scala.Predeff diff --git a/tests/neg/yimports-nosuch.scala b/tests/neg/yimports-nosuch.scala new file mode 100644 index 000000000000..431daf39a180 --- /dev/null +++ b/tests/neg/yimports-nosuch.scala @@ -0,0 +1,5 @@ +// scalac: -Yimports:skala,scala.Predeff +// +class C +// nopos-error +// nopos-error diff --git a/tests/neg/yimports-order.check b/tests/neg/yimports-order.check new file mode 100644 index 000000000000..b49503f75e01 --- /dev/null +++ b/tests/neg/yimports-order.check @@ -0,0 +1,16 @@ +-- [E006] Not Found Error: tests/neg/yimports-order.scala:9:16 --------------------------------------------------------- +9 | def f() = Map("hello" -> "world") // error // error + | ^^^ + | Not found: Map + | + | longer explanation available when compiling with `-explain` +-- [E008] Not Found Error: tests/neg/yimports-order.scala:9:28 --------------------------------------------------------- +9 | def f() = Map("hello" -> "world") // error // error + | ^^^^^^^^^^ + | value -> is not a member of String +-- [E006] Not Found Error: tests/neg/yimports-order.scala:10:16 -------------------------------------------------------- +10 | def g() = println(f()) // error + | ^^^^^^^ + | Not found: println + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/yimports-order.scala b/tests/neg/yimports-order.scala new file mode 100644 index 000000000000..9cba91385b8a --- /dev/null +++ b/tests/neg/yimports-order.scala @@ -0,0 +1,13 @@ + +package top { + package middle { + class C { + def c() = println("hello, world") + } + import Predef.{Map => _} + object Test { + def f() = Map("hello" -> "world") // error // error + def g() = println(f()) // error + } + } +} diff --git a/tests/neg/yimports-predef.check b/tests/neg/yimports-predef.check new file mode 100644 index 000000000000..eb8881e04223 --- /dev/null +++ b/tests/neg/yimports-predef.check @@ -0,0 +1,4 @@ +-- [E008] Not Found Error: tests/neg/yimports-predef.scala:6:21 -------------------------------------------------------- +6 | def f[A](x: A) = x + 42 // error + | ^^^ + | value + is not a member of A diff --git a/tests/neg/yimports-predef.scala b/tests/neg/yimports-predef.scala new file mode 100644 index 000000000000..8bfe89b08cd8 --- /dev/null +++ b/tests/neg/yimports-predef.scala @@ -0,0 +1,7 @@ +// scalac: -Yimports:scala,scala.Predef +// +import Predef.{any2stringadd => _, _} + +class classic { + def f[A](x: A) = x + 42 // error +} diff --git a/tests/neg/yimports-stable.check b/tests/neg/yimports-stable.check new file mode 100644 index 000000000000..c5bfd914ae07 --- /dev/null +++ b/tests/neg/yimports-stable.check @@ -0,0 +1,14 @@ + +error: bad preamble import hello.world.potions +-- [E006] Not Found Error: tests/neg/yimports-stable/C_2.scala:4:9 ----------------------------------------------------- +4 | val v: Numb = magic // error // error + | ^^^^ + | Not found: type Numb + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg/yimports-stable/C_2.scala:4:16 ---------------------------------------------------- +4 | val v: Numb = magic // error // error + | ^^^^^ + | Not found: magic + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/yimports-stable/C_2.scala b/tests/neg/yimports-stable/C_2.scala new file mode 100644 index 000000000000..0b97775f1a01 --- /dev/null +++ b/tests/neg/yimports-stable/C_2.scala @@ -0,0 +1,7 @@ +// scalac: -Yimports:scala,scala.Predef,hello.world.potions +// +class C { + val v: Numb = magic // error // error + def greet() = println("hello, world!") +} +// nopos-error diff --git a/tests/neg/yimports-stable/minidef_1.scala b/tests/neg/yimports-stable/minidef_1.scala new file mode 100644 index 000000000000..b3ea7445df24 --- /dev/null +++ b/tests/neg/yimports-stable/minidef_1.scala @@ -0,0 +1,11 @@ + +package hello + +trait stuff { + type Numb = Int + val magic = 42 +} + +object world { + val potions = new stuff {} +} diff --git a/tests/pending/neg/yimports-custom-b.check b/tests/pending/neg/yimports-custom-b.check new file mode 100644 index 000000000000..d046a1d8f6cc --- /dev/null +++ b/tests/pending/neg/yimports-custom-b.check @@ -0,0 +1,10 @@ + +C_2.scala:8: error: not found: type Numb + val v: Numb = Answer + ^ +-- [E006] Not Found Error: tests/neg/yimports-custom-b/C_2.scala:9:16 -------------------------------------------------- +9 | def greet() = println("hello, world!") // error + | ^^^^^^^ + | Not found: println + | + | longer explanation available when compiling with `-explain` diff --git a/tests/pending/neg/yimports-custom-b/C_2.scala b/tests/pending/neg/yimports-custom-b/C_2.scala new file mode 100644 index 000000000000..8da798e80b0d --- /dev/null +++ b/tests/pending/neg/yimports-custom-b/C_2.scala @@ -0,0 +1,10 @@ +// scalac: -Yimports:hello.world.minidef + +import hello.{world => hw} +import hw.minidef.{Magic => Answer} + +// Finds the answer, but dumb to forget Numb +class C { + val v: Numb = Answer // error + def greet() = println("hello, world!") // error +} diff --git a/tests/pending/neg/yimports-custom-b/minidef_1.scala b/tests/pending/neg/yimports-custom-b/minidef_1.scala new file mode 100644 index 000000000000..befc137b6ab6 --- /dev/null +++ b/tests/pending/neg/yimports-custom-b/minidef_1.scala @@ -0,0 +1,8 @@ +// scalac: -Yimports:scala + +package hello.world + +object minidef { + type Numb = Int + final val Magic = 42 +} diff --git a/tests/pending/neg/yimports-masked.check b/tests/pending/neg/yimports-masked.check new file mode 100644 index 000000000000..ae715313392a --- /dev/null +++ b/tests/pending/neg/yimports-masked.check @@ -0,0 +1,10 @@ + +C_2.scala:11: error: not found: type Numb + val v: Numb = Answer + ^ +-- [E006] Not Found Error: tests/neg/yimports-masked/C_2.scala:12:18 --------------------------------------------------- +12 | def greet() = println("hello, world!") // error + | ^^^^^^^ + | Not found: println + | + | longer explanation available when compiling with `-explain` diff --git a/tests/pending/neg/yimports-masked/C_2.scala b/tests/pending/neg/yimports-masked/C_2.scala new file mode 100644 index 000000000000..1b6c736bad7b --- /dev/null +++ b/tests/pending/neg/yimports-masked/C_2.scala @@ -0,0 +1,14 @@ +// scalac: -Yimports:scala,hello.world.minidef + +// import at top level or top of package disables implicit import. +// the import can appear at any statement position, here, end of package. +// Update: with new trick, the import has to be completed before usages. + +import hello.world.minidef.{Magic => Answer} + +package p { + class C { + val v: Numb = Answer // error + def greet() = println("hello, world!") // error + } +} diff --git a/tests/pending/neg/yimports-masked/minidef_1.scala b/tests/pending/neg/yimports-masked/minidef_1.scala new file mode 100644 index 000000000000..5d18d0a39584 --- /dev/null +++ b/tests/pending/neg/yimports-masked/minidef_1.scala @@ -0,0 +1,7 @@ + +package hello.world + +object minidef { + type Numb = Int + final val Magic = 42 +} diff --git a/tests/pos/multiple-additional-imports.scala b/tests/pos/multiple-additional-imports.scala new file mode 100644 index 000000000000..a86c7e8fc342 --- /dev/null +++ b/tests/pos/multiple-additional-imports.scala @@ -0,0 +1,5 @@ +// scalac: -Yimports:scala,java.lang,scala.Predef,scala.annotation,scala.util.matching + +class annotation extends Annotation +val s: String = "str" +val regex: Regex = s.r diff --git a/tests/pos/single-additional-import.scala b/tests/pos/single-additional-import.scala new file mode 100644 index 000000000000..d8ca5b54e05e --- /dev/null +++ b/tests/pos/single-additional-import.scala @@ -0,0 +1,2 @@ +// scalac: -Yimports:scala.annotation +class annotation extends Annotation