Skip to content

Commit decdff4

Browse files
Merge pull request #5744 from dotty-staging/fix-#4535
Fix #4535: Handle Null and Nothing in completions
2 parents b68a7ac + d4ddb49 commit decdff4

File tree

4 files changed

+56
-10
lines changed

4 files changed

+56
-10
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ class Definitions {
270270
lazy val Any_getClass: TermSymbol = enterMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef.appliedTo(TypeBounds.empty)), Final)
271271
lazy val Any_isInstanceOf: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final)
272272
lazy val Any_asInstanceOf: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, _.paramRefs(0), Final)
273-
lazy val Any_typeTest: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOfPM, _ => BooleanType, Final | Synthetic)
273+
lazy val Any_typeTest: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOfPM, _ => BooleanType, Final | Synthetic | Artifact)
274274
// generated by pattern matcher, eliminated by erasure
275275

276276
def AnyMethods: List[TermSymbol] = List(Any_==, Any_!=, Any_equals, Any_hashCode,

compiler/src/dotty/tools/dotc/interactive/Completion.scala

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package dotty.tools.dotc.interactive
22

3+
import java.nio.charset.Charset
4+
35
import dotty.tools.dotc.ast.Trees._
46
import dotty.tools.dotc.config.Printers.interactiv
57
import dotty.tools.dotc.core.Contexts.{Context, NoContext}
@@ -10,13 +12,13 @@ import dotty.tools.dotc.core.Flags._
1012
import dotty.tools.dotc.core.Names.{Name, TermName}
1113
import dotty.tools.dotc.core.NameKinds.SimpleNameKind
1214
import dotty.tools.dotc.core.NameOps.NameDecorator
13-
import dotty.tools.dotc.core.Symbols.{defn, NoSymbol, Symbol}
15+
import dotty.tools.dotc.core.Symbols.{NoSymbol, Symbol, defn}
1416
import dotty.tools.dotc.core.Scopes
1517
import dotty.tools.dotc.core.StdNames.{nme, tpnme}
1618
import dotty.tools.dotc.core.TypeError
17-
import dotty.tools.dotc.core.Types.{NameFilter, NamedType, Type, NoType}
19+
import dotty.tools.dotc.core.Types.{NameFilter, NamedType, NoType, Type}
1820
import dotty.tools.dotc.printing.Texts._
19-
import dotty.tools.dotc.util.{NoSourcePosition, SourcePosition}
21+
import dotty.tools.dotc.util.{NameTransformer, NoSourcePosition, SourcePosition}
2022

2123
import scala.collection.mutable
2224

@@ -150,7 +152,8 @@ object Completion {
150152
nameToSymbols.map { case (name, symbols) =>
151153
val typesFirst = symbols.sortWith((s1, s2) => s1.isType && !s2.isType)
152154
val desc = description(typesFirst)
153-
Completion(name.toString, desc, typesFirst)
155+
val label = NameTransformer.decodeIllegalChars(name.toString)
156+
Completion(label, desc, typesFirst)
154157
}
155158
}
156159

@@ -207,11 +210,14 @@ object Completion {
207210
* considered.
208211
*/
209212
def addMemberCompletions(qual: Tree)(implicit ctx: Context): Unit = {
210-
addAccessibleMembers(qual.tpe)
211-
if (!mode.is(Mode.Import)) {
212-
// Implicit conversions do not kick in when importing
213-
implicitConversionTargets(qual)(ctx.fresh.setExploreTyperState())
214-
.foreach(addAccessibleMembers)
213+
if (!qual.tpe.widenDealias.isBottomType) {
214+
addAccessibleMembers(qual.tpe)
215+
if (!mode.is(Mode.Import) && !qual.tpe.isRef(defn.NullClass)) {
216+
// Implicit conversions do not kick in when importing
217+
// and for `NullClass` they produce unapplicable completions (for unclear reasons)
218+
implicitConversionTargets(qual)(ctx.fresh.setExploreTyperState())
219+
.foreach(addAccessibleMembers)
220+
}
215221
}
216222
}
217223

compiler/src/dotty/tools/dotc/util/NameTransformer.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,25 @@ object NameTransformer {
5555
else name
5656
}
5757

58+
/** Decode expanded characters starting with `$u`, followed by the character's unicode expansion. */
59+
def decodeIllegalChars(name: String): String = {
60+
if (name.contains("$u")) {
61+
val sb = new mutable.StringBuilder()
62+
var i = 0
63+
while (i < name.length) {
64+
if (i < name.length - 5 && name(i) == '$' && name(i + 1) == 'u') {
65+
sb.append(Integer.valueOf(name.substring(i + 2, i + 6), 16).toChar)
66+
i += 6
67+
} else {
68+
sb.append(name(i))
69+
i += 1
70+
}
71+
}
72+
sb.result()
73+
}
74+
else name
75+
}
76+
5877
/** Replace operator symbols by corresponding expansion strings.
5978
*
6079
* @param name the string to encode

compiler/test/dotty/tools/repl/TabcompleteTests.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,25 @@ class TabcompleteTests extends ReplTest {
103103
assertEquals(comp.find(_.startsWith("<")), None)
104104
assert(!comp.contains("package"))
105105
}
106+
107+
@Test def `null` = fromInitialState { implicit s =>
108+
val comp = tabComplete("null.")
109+
assertEquals(
110+
List("!=", "##", "==", "asInstanceOf", "clone", "eq", "equals", "finalize", "getClass", "hashCode",
111+
"isInstanceOf", "ne", "notify", "notifyAll", "synchronized", "toString", "wait"),
112+
comp.distinct.sorted)
113+
}
114+
115+
@Test def anyRef = fromInitialState { implicit s =>
116+
val comp = tabComplete("(null: AnyRef).")
117+
assertEquals(
118+
List("!=", "##", "+", "->", "==", "asInstanceOf", "clone", "ensuring", "eq", "equals", "finalize", "formatted",
119+
"getClass", "hashCode", "isInstanceOf", "ne", "notify", "notifyAll", "synchronized", "toString", "wait", ""),
120+
comp.distinct.sorted)
121+
}
122+
123+
@Test def `???` = fromInitialState { implicit s =>
124+
val comp = tabComplete("???.")
125+
assertEquals(Nil, comp)
126+
}
106127
}

0 commit comments

Comments
 (0)