Skip to content

Commit 5ce1ac9

Browse files
committed
Improvements to "did you mean ...?" scheme
1. Only show accessible members 2. Show several alternatives if they are at same distance. Unlike Scala 2, we do not show alternatives at larger distance. I fear that would produce more noise than signal. Fixes #17067
1 parent f2e2e3f commit 5ce1ac9

File tree

3 files changed

+59
-14
lines changed

3 files changed

+59
-14
lines changed

compiler/src/dotty/tools/dotc/reporting/DidYouMean.scala

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,21 +126,33 @@ object DidYouMean:
126126
def didYouMean(candidates: List[(Int, Binding)], proto: Type, prefix: String)(using Context): String =
127127

128128
def qualifies(b: Binding)(using Context): Boolean =
129-
proto match
130-
case _: SelectionProto => true
131-
case _ =>
132-
try !b.sym.isNoValue
133-
catch case ex: Exception => false
129+
try
130+
val valueOK = proto match
131+
case _: SelectionProto => true
132+
case _ => !b.sym.isNoValue
133+
val accessOK = b.sym.isAccessibleFrom(b.site)
134+
valueOK && accessOK
135+
catch case ex: Exception => false
136+
// exceptions might arise when completing (e.g. malformed class file, or cyclic reference)
134137

135138
def showName(name: Name, sym: Symbol)(using Context): String =
136139
if sym.is(ModuleClass) then s"${name.show}.type"
137140
else name.show
138141

142+
def alternatives(distance: Int, candidates: List[(Int, Binding)]): List[Binding] = candidates match
143+
case (d, b) :: rest if d == distance =>
144+
if qualifies(b) then b :: alternatives(distance, rest) else alternatives(distance, rest)
145+
case _ =>
146+
Nil
147+
139148
def recur(candidates: List[(Int, Binding)]): String = candidates match
140149
case (d, b) :: rest
141150
if d != 0 || b.sym.is(ModuleClass) => // Avoid repeating the same name in "did you mean"
142151
if qualifies(b) then
143-
s" - did you mean $prefix${showName(b.name, b.sym)}?"
152+
def hint(b: Binding) = prefix ++ showName(b.name, b.sym)
153+
val alts = alternatives(d, rest).map(hint).take(3)
154+
val suffix = if alts.isEmpty then "" else alts.mkString(" or perhaps ", " or ", "?")
155+
s" - did you mean ${hint(b)}?$suffix"
144156
else
145157
recur(rest)
146158
case _ => ""

tests/neg/i18682.check

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,35 @@
1616
| Not found: hellx - did you mean hello?
1717
|
1818
| longer explanation available when compiling with `-explain`
19-
-- [E008] Not Found Error: tests/neg/i18682.scala:13:12 ----------------------------------------------------------------
20-
13 |val _ = bar.Bap // error, App does shown as hint, too far away
19+
-- [E008] Not Found Error: tests/neg/i18682.scala:16:12 ----------------------------------------------------------------
20+
16 |val _ = bar.Bap // error, App does not show as hint, too far away
2121
| ^^^^^^^
2222
| value Bap is not a member of object Bar
23-
-- [E008] Not Found Error: tests/neg/i18682.scala:14:12 ----------------------------------------------------------------
24-
14 |val _ = bar.Bap() // error
23+
-- [E008] Not Found Error: tests/neg/i18682.scala:17:12 ----------------------------------------------------------------
24+
17 |val _ = bar.Bap() // error
2525
| ^^^^^^^
2626
| value Bap is not a member of object Bar - did you mean bar.Baz?
27-
-- [E006] Not Found Error: tests/neg/i18682.scala:16:8 -----------------------------------------------------------------
28-
16 |val _ = error // error, java.lang.Error does not show as hint, since it is not a value
27+
-- [E006] Not Found Error: tests/neg/i18682.scala:19:8 -----------------------------------------------------------------
28+
19 |val _ = error // error, java.lang.Error does not show as hint, since it is not a value
2929
| ^^^^^
3030
| Not found: error
3131
|
3232
| longer explanation available when compiling with `-explain`
33+
-- [E008] Not Found Error: tests/neg/i18682.scala:22:50 ----------------------------------------------------------------
34+
22 |val _ = "123".view.reverse.padTo(5, '0').iterator.reverse // error, no hint since `reversed` is not accessible
35+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
36+
| value reverse is not a member of Iterator[Char]
37+
-- [E006] Not Found Error: tests/neg/i18682.scala:27:8 -----------------------------------------------------------------
38+
27 |val _ = pool // error
39+
| ^^^^
40+
| Not found: pool - did you mean cool? or perhaps wool?
41+
|
42+
| longer explanation available when compiling with `-explain`
43+
-- [E008] Not Found Error: tests/neg/i18682.scala:29:12 ----------------------------------------------------------------
44+
29 |val _ = bar.poodle // error
45+
| ^^^^^^^^^^
46+
| value poodle is not a member of object Bar - did you mean bar.pool?
47+
-- [E008] Not Found Error: tests/neg/i18682.scala:31:12 ----------------------------------------------------------------
48+
31 |val _ = bar.ool // error
49+
| ^^^^^^^
50+
| value ool is not a member of object Bar - did you mean bar.cool? or perhaps bar.pool or bar.wool?

tests/neg/i18682.scala

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,24 @@ val _ = hellx // error
88
object Bar:
99
class Baz()
1010
object App
11+
def cool = 1
12+
def wool = 2
13+
def pool = 3
1114

1215
val bar = Bar
13-
val _ = bar.Bap // error, App does shown as hint, too far away
16+
val _ = bar.Bap // error, App does not show as hint, too far away
1417
val _ = bar.Bap() // error
1518

16-
val _ = error // error, java.lang.Error does not show as hint, since it is not a value
19+
val _ = error // error, java.lang.Error does not show as hint, since it is not a value
20+
21+
// #17067
22+
val _ = "123".view.reverse.padTo(5, '0').iterator.reverse // error, no hint since `reversed` is not accessible
23+
24+
val cool = "cool"
25+
val wool = "wool"
26+
27+
val _ = pool // error
28+
29+
val _ = bar.poodle // error
30+
31+
val _ = bar.ool // error

0 commit comments

Comments
 (0)