diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 4195267ef1cc..8cc0750de53c 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -137,6 +137,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { val rename: TermName = renamed match case Ident(rename: TermName) => rename case _ => name + + def isUnimport = rename == nme.WILDCARD } case class Number(digits: String, kind: NumberKind)(implicit @constructorOnly src: SourceFile) extends TermTree diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index fd297b26aa26..726cdc7131c4 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -135,7 +135,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case JavaSymbolIsNotAValueID // errorNumber: 119 case DoubleDefinitionID // errorNumber: 120 case MatchCaseOnlyNullWarningID // errorNumber: 121 - case ImportRenamedTwiceID // errorNumber: 122 + case ImportedTwiceID // errorNumber: 122 case TypeTestAlwaysDivergesID // errorNumber: 123 case TermMemberNeedsNeedsResultTypeForImplicitSearchID // errorNumber: 124 case ClassCannotExtendEnumID // errorNumber: 125 @@ -198,6 +198,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case NotConstantID // errorNumber: 182 case ClosureCannotHaveInternalParameterDependenciesID // errorNumber: 183 case MatchTypeNoCasesID // errorNumber: 184 + case UnimportedAndImportedID // errorNumber: 185 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 65299684f4e9..970c44d54cbc 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2234,8 +2234,15 @@ extends NamingMsg(DoubleDefinitionID) { def explain(using Context) = "" } -class ImportRenamedTwice(ident: untpd.Ident)(using Context) extends SyntaxMsg(ImportRenamedTwiceID) { - def msg(using Context) = s"${ident.show} is renamed twice on the same import line." +class ImportedTwice(sel: Name)(using Context) extends SyntaxMsg(ImportedTwiceID) { + def msg(using Context) = s"${sel.show} is imported twice on the same import line." + def explain(using Context) = "" +} + +class UnimportedAndImported(sel: Name, isImport: Boolean)(using Context) extends SyntaxMsg(UnimportedAndImportedID) { + def msg(using Context) = + val otherStr = if isImport then "and imported" else "twice" + s"${sel.show} is unimported $otherStr on the same import line." def explain(using Context) = "" } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index e3b2d119950d..7ca99df2bea3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1511,7 +1511,8 @@ trait Checking { * (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] + val originals = mutable.Set.empty[Name] + val targets = mutable.Set.empty[Name] def checkIdent(sel: untpd.ImportSelector): Unit = if sel.name != nme.ERROR @@ -1519,9 +1520,14 @@ trait Checking { && !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 + if sel.isUnimport then + if originals.contains(sel.name) then + report.error(UnimportedAndImported(sel.name, targets.contains(sel.name)), sel.imported.srcPos) + else + if targets.contains(sel.rename) then + report.error(ImportedTwice(sel.rename), sel.renamed.orElse(sel.imported).srcPos) + targets += sel.rename + originals += sel.name if !ctx.compilationUnit.isJava then for sel <- selectors do diff --git a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala index b5be2daf873b..4d8d5ada46a1 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -109,7 +109,7 @@ class ImportInfo(symf: Context ?=> Symbol, else if sel.rename != sel.name then myExcluded = myExcluded.nn + sel.name - if sel.rename != nme.WILDCARD then + if !sel.isUnimport then myForwardMapping = myForwardMapping.uncheckedNN.updated(sel.name, sel.rename) myReverseMapping = myReverseMapping.uncheckedNN.updated(sel.rename, sel.name) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index e6162627efc7..44787dcfeedc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1309,7 +1309,7 @@ class Namer { typer: Typer => if sel.isWildcard then addWildcardForwarders(seen, sel.span) else - if sel.rename != nme.WILDCARD then + if !sel.isUnimport then addForwardersNamed(sel.name, sel.rename, sel.span) addForwarders(sels1, sel.name :: seen) case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 2c5b7d0c9678..51787cb5004a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -325,7 +325,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer fail(em"reference to `$name` is ambiguous; it is imported twice") found - if selector.rename == termName && selector.rename != nme.WILDCARD then + if selector.rename == termName && !selector.isUnimport then val memberName = if selector.name == termName then name else if name.isTypeName then selector.name.toTypeName diff --git a/tests/neg/i18082.check b/tests/neg/i18082.check new file mode 100644 index 000000000000..46fc1909c4e1 --- /dev/null +++ b/tests/neg/i18082.check @@ -0,0 +1,20 @@ +-- [E122] Syntax Error: tests/neg/i18082.scala:5:23 -------------------------------------------------------------------- +5 |import O.{a as b, b as b} // error + | ^ + | b is imported twice on the same import line. +-- [E122] Syntax Error: tests/neg/i18082.scala:6:18 -------------------------------------------------------------------- +6 |import O.{a as b, b} // error + | ^ + | b is imported twice on the same import line. +-- [E122] Syntax Error: tests/neg/i18082.scala:7:13 -------------------------------------------------------------------- +7 |import O.{a, a} // error + | ^ + | a is imported twice on the same import line. +-- [E185] Syntax Error: tests/neg/i18082.scala:10:18 ------------------------------------------------------------------- +10 |import O.{a as _, a as _} // error + | ^ + | a is unimported twice on the same import line. +-- [E185] Syntax Error: tests/neg/i18082.scala:11:13 ------------------------------------------------------------------- +11 |import O.{a, a as _} // error + | ^ + | a is unimported and imported on the same import line. diff --git a/tests/neg/i18082.scala b/tests/neg/i18082.scala new file mode 100644 index 000000000000..4d97c389181d --- /dev/null +++ b/tests/neg/i18082.scala @@ -0,0 +1,12 @@ +object O { val a = 1; val b = 2 } + +import O.{a as b, a} // OK +import O.{a as b, b as a} // OK +import O.{a as b, b as b} // error +import O.{a as b, b} // error +import O.{a, a} // error + +import O.{a as _, b as _} // ok +import O.{a as _, a as _} // error +import O.{a, a as _} // error + diff --git a/tests/neg/i3745c.scala b/tests/neg/i3745c.scala deleted file mode 100644 index 40daf0072e05..000000000000 --- a/tests/neg/i3745c.scala +++ /dev/null @@ -1 +0,0 @@ -import scala.collection.{ Seq as A, Seq as B } // error: Seq is renamed twice