diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index d51f05e4ee0b..30f89c5c44d1 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -270,7 +270,7 @@ class Definitions { lazy val Any_getClass: TermSymbol = enterMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef.appliedTo(TypeBounds.empty)), Final) lazy val Any_isInstanceOf: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final) lazy val Any_asInstanceOf: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, _.paramRefs(0), Final) - lazy val Any_typeTest: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOfPM, _ => BooleanType, Final | Synthetic) + lazy val Any_typeTest: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOfPM, _ => BooleanType, Final | Synthetic | Artifact) // generated by pattern matcher, eliminated by erasure def AnyMethods: List[TermSymbol] = List(Any_==, Any_!=, Any_equals, Any_hashCode, diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index c48ef40fa6b8..966c95cad0fc 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -1,5 +1,7 @@ package dotty.tools.dotc.interactive +import java.nio.charset.Charset + import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.config.Printers.interactiv import dotty.tools.dotc.core.Contexts.{Context, NoContext} @@ -10,13 +12,13 @@ import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.Names.{Name, TermName} import dotty.tools.dotc.core.NameKinds.SimpleNameKind import dotty.tools.dotc.core.NameOps.NameDecorator -import dotty.tools.dotc.core.Symbols.{defn, NoSymbol, Symbol} +import dotty.tools.dotc.core.Symbols.{NoSymbol, Symbol, defn} import dotty.tools.dotc.core.Scopes import dotty.tools.dotc.core.StdNames.{nme, tpnme} import dotty.tools.dotc.core.TypeError -import dotty.tools.dotc.core.Types.{NameFilter, NamedType, Type, NoType} +import dotty.tools.dotc.core.Types.{NameFilter, NamedType, NoType, Type} import dotty.tools.dotc.printing.Texts._ -import dotty.tools.dotc.util.{NoSourcePosition, SourcePosition} +import dotty.tools.dotc.util.{NameTransformer, NoSourcePosition, SourcePosition} import scala.collection.mutable @@ -150,7 +152,8 @@ object Completion { nameToSymbols.map { case (name, symbols) => val typesFirst = symbols.sortWith((s1, s2) => s1.isType && !s2.isType) val desc = description(typesFirst) - Completion(name.toString, desc, typesFirst) + val label = NameTransformer.decodeIllegalChars(name.toString) + Completion(label, desc, typesFirst) } } @@ -207,11 +210,14 @@ object Completion { * considered. */ def addMemberCompletions(qual: Tree)(implicit ctx: Context): Unit = { - addAccessibleMembers(qual.tpe) - if (!mode.is(Mode.Import)) { - // Implicit conversions do not kick in when importing - implicitConversionTargets(qual)(ctx.fresh.setExploreTyperState()) - .foreach(addAccessibleMembers) + if (!qual.tpe.widenDealias.isBottomType) { + addAccessibleMembers(qual.tpe) + if (!mode.is(Mode.Import) && !qual.tpe.isRef(defn.NullClass)) { + // Implicit conversions do not kick in when importing + // and for `NullClass` they produce unapplicable completions (for unclear reasons) + implicitConversionTargets(qual)(ctx.fresh.setExploreTyperState()) + .foreach(addAccessibleMembers) + } } } diff --git a/compiler/src/dotty/tools/dotc/util/NameTransformer.scala b/compiler/src/dotty/tools/dotc/util/NameTransformer.scala index d489c1ed0e42..7fc724ad1cc1 100644 --- a/compiler/src/dotty/tools/dotc/util/NameTransformer.scala +++ b/compiler/src/dotty/tools/dotc/util/NameTransformer.scala @@ -55,6 +55,25 @@ object NameTransformer { else name } + /** Decode expanded characters starting with `$u`, followed by the character's unicode expansion. */ + def decodeIllegalChars(name: String): String = { + if (name.contains("$u")) { + val sb = new mutable.StringBuilder() + var i = 0 + while (i < name.length) { + if (i < name.length - 5 && name(i) == '$' && name(i + 1) == 'u') { + sb.append(Integer.valueOf(name.substring(i + 2, i + 6), 16).toChar) + i += 6 + } else { + sb.append(name(i)) + i += 1 + } + } + sb.result() + } + else name + } + /** Replace operator symbols by corresponding expansion strings. * * @param name the string to encode diff --git a/compiler/test/dotty/tools/repl/TabcompleteTests.scala b/compiler/test/dotty/tools/repl/TabcompleteTests.scala index 1bbbf499e737..9b426559fdbb 100644 --- a/compiler/test/dotty/tools/repl/TabcompleteTests.scala +++ b/compiler/test/dotty/tools/repl/TabcompleteTests.scala @@ -103,4 +103,25 @@ class TabcompleteTests extends ReplTest { assertEquals(comp.find(_.startsWith("<")), None) assert(!comp.contains("package")) } + + @Test def `null` = fromInitialState { implicit s => + val comp = tabComplete("null.") + assertEquals( + List("!=", "##", "==", "asInstanceOf", "clone", "eq", "equals", "finalize", "getClass", "hashCode", + "isInstanceOf", "ne", "notify", "notifyAll", "synchronized", "toString", "wait"), + comp.distinct.sorted) + } + + @Test def anyRef = fromInitialState { implicit s => + val comp = tabComplete("(null: AnyRef).") + assertEquals( + List("!=", "##", "+", "->", "==", "asInstanceOf", "clone", "ensuring", "eq", "equals", "finalize", "formatted", + "getClass", "hashCode", "isInstanceOf", "ne", "notify", "notifyAll", "synchronized", "toString", "wait", "→"), + comp.distinct.sorted) + } + + @Test def `???` = fromInitialState { implicit s => + val comp = tabComplete("???.") + assertEquals(Nil, comp) + } }