diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index 2954ce7da916..622c30a22644 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -41,7 +41,8 @@ class MissingType(pre: Type, name: Name) extends TypeError { } } -class RecursionOverflow(val op: String, details: => String, val previous: Throwable, val weight: Int) extends TypeError { +class RecursionOverflow(val op: String, details: => String, val previous: Throwable, val weight: Int) +extends TypeError { def explanation: String = s"$op $details" diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 86d63c72ac47..25c4c8fc91c3 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -430,21 +430,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case tpe => tpe } ) - case Import(expr, selectors) => - val exprTpe = expr.tpe - val seen = mutable.Set.empty[Name] - - def checkIdent(sel: untpd.ImportSelector): Unit = - if !exprTpe.member(sel.name).exists - && !exprTpe.member(sel.name.toTypeName).exists then - report.error(NotAMember(exprTpe, sel.name, "value"), sel.imported.srcPos) - if seen.contains(sel.name) then - report.error(ImportRenamedTwice(sel.imported), sel.imported.srcPos) - seen += sel.name - - for sel <- selectors do - if !sel.isWildcard then checkIdent(sel) - super.transform(tree) case Typed(Ident(nme.WILDCARD), _) => withMode(Mode.Pattern)(super.transform(tree)) // The added mode signals that bounds in a pattern need not diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 313d238181e1..0d93e83432fe 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1429,6 +1429,27 @@ trait Checking { em"""Implementation restriction: cannot generate CanThrow capability for this kind of catch. |CanThrow capabilities can only be generated $req.""", pat.srcPos) + + /** (1) Check that every named import selector refers to a type or value member of the + * qualifier type. + * (2) Check that no import selector is renamed more than once. + */ + def checkImportSelectors(qualType: Type, selectors: List[untpd.ImportSelector])(using Context): Unit = + val seen = mutable.Set.empty[Name] + + def checkIdent(sel: untpd.ImportSelector): Unit = + if sel.name != nme.ERROR + && !qualType.member(sel.name).exists + && !qualType.member(sel.name.toTypeName).exists + then + report.error(NotAMember(qualType, sel.name, "value"), sel.imported.srcPos) + if seen.contains(sel.name) then + report.error(ImportRenamedTwice(sel.imported), sel.imported.srcPos) + seen += sel.name + + for sel <- selectors do + if !sel.isWildcard then checkIdent(sel) + end checkImportSelectors } trait ReChecking extends Checking { @@ -1466,4 +1487,5 @@ trait NoChecking extends ReChecking { override def checkMembersOK(tp: Type, pos: SrcPos)(using Context): Type = tp override def checkInInlineContext(what: String, pos: SrcPos)(using Context): Unit = () override def checkValidInfix(tree: untpd.InfixOp, meth: Symbol)(using Context): Unit = () + override def checkImportSelectors(qualType: Type, selectors: List[untpd.ImportSelector])(using Context): Unit = () } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 9a62ac480c30..529f22b54cc2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2606,6 +2606,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val expr1 = typedImportQualifier(imp, typedExpr(_, _)(using ctx.withOwner(sym))) checkLegalImportPath(expr1) val selectors1 = typedSelectors(imp.selectors) + checkImportSelectors(expr1.tpe, selectors1) assignType(cpy.Import(imp)(expr1, selectors1), sym) def typedExport(exp: untpd.Export)(using Context): Tree = diff --git a/tests/neg-custom-args/jdk-9-app.check b/tests/neg-custom-args/jdk-9-app.check index 0eacc7510766..484e525aafa4 100644 --- a/tests/neg-custom-args/jdk-9-app.check +++ b/tests/neg-custom-args/jdk-9-app.check @@ -1,3 +1,7 @@ +-- [E008] Not Found Error: tests/neg-custom-args/jdk-9-app.scala:1:17 -------------------------------------------------- +1 |import java.lang.ProcessHandle // error: not a member + | ^^^^^^^^^^^^^ + | value ProcessHandle is not a member of java.lang -- [E006] Not Found Error: tests/neg-custom-args/jdk-9-app.scala:4:10 -------------------------------------------------- 4 | println(ProcessHandle.current().pid()) // error: not found | ^^^^^^^^^^^^^ diff --git a/tests/neg-custom-args/jdk-9-app.scala b/tests/neg-custom-args/jdk-9-app.scala index 3dfd55332492..5709da34c466 100644 --- a/tests/neg-custom-args/jdk-9-app.scala +++ b/tests/neg-custom-args/jdk-9-app.scala @@ -1,4 +1,4 @@ -import java.lang.ProcessHandle +import java.lang.ProcessHandle // error: not a member object Jdk9App extends App { println(ProcessHandle.current().pid()) // error: not found diff --git a/tests/neg/deriving.scala b/tests/neg/deriving.scala index 4d8d0f001cec..af44e97dbff5 100644 --- a/tests/neg/deriving.scala +++ b/tests/neg/deriving.scala @@ -1,4 +1,4 @@ -import reflect.Generic +import reflect.Generic // error: Generic is not a member of reflect sealed trait A derives Generic // error: cannot take shape, it has anonymous or inaccessible subclasses diff --git a/tests/neg/i15468.scala b/tests/neg/i15468.scala new file mode 100644 index 000000000000..8518216fb75d --- /dev/null +++ b/tests/neg/i15468.scala @@ -0,0 +1,6 @@ +object Test { + val s = "" + import s.{length => x, size => y} // error: size is not a member of s + locally(x) // ok + locally(size) // error: Not found: y +} diff --git a/tests/neg/i6056.scala b/tests/neg/i6056.scala index dc836e319770..ad68616eecc2 100644 --- a/tests/neg/i6056.scala +++ b/tests/neg/i6056.scala @@ -1,5 +1,5 @@ object i0{ - import i0.i0 // error + import i0.i0 // error // error def i0={ import _ // error import // error diff --git a/tests/neg/implicitDefs.scala b/tests/neg/implicitDefs.scala index fbf91ee5c70d..7302a8a06ae0 100644 --- a/tests/neg/implicitDefs.scala +++ b/tests/neg/implicitDefs.scala @@ -1,6 +1,6 @@ package test -import Predef.{any2stringadd as _, StringAdd as _, *} +import Predef.{any2stringadd as _, *} object implicitDefs { diff --git a/tests/neg/language-import.scala b/tests/neg/language-import.scala index 8f498f091f75..12ea8f60459d 100644 --- a/tests/neg/language-import.scala +++ b/tests/neg/language-import.scala @@ -3,7 +3,7 @@ object a: import l.noAutoTupling // error import l.experimental.genericNumberLiterals // error val scala = c - import scala.language.noAutoTupling // error + import scala.language.noAutoTupling // error // error val language = b import language.experimental.genericNumberLiterals // error diff --git a/tests/neg/parser-stability-23.scala b/tests/neg/parser-stability-23.scala index a27d79d5cc3e..d63059288b63 100644 --- a/tests/neg/parser-stability-23.scala +++ b/tests/neg/parser-stability-23.scala @@ -1,3 +1,3 @@ object i0 { - import Ordering.{ implicitly as } (true: Boolean) match { case _: i1 as true } // error // error + import Ordering.{ implicitly as } (true: Boolean) match { case _: i1 as true } // error // error // error } diff --git a/tests/neg/rootImplicits.scala b/tests/neg/rootImplicits.scala index f3cfd4b8a42b..cf63d02e2211 100644 --- a/tests/neg/rootImplicits.scala +++ b/tests/neg/rootImplicits.scala @@ -1,6 +1,6 @@ package test -import Predef.{any2stringadd as _, StringAdd as _, *} +import Predef.{any2stringadd as _, *} object rootImplicits {