@@ -199,13 +199,78 @@ object messages {
199
199
case class TypeMismatch (found : Type , expected : Type , whyNoMatch : String = " " , implicitFailure : String = " " )(implicit ctx : Context )
200
200
extends Message (7 ) {
201
201
val kind = " Type Mismatch"
202
- private val (where, printCtx) = Formatting .disambiguateTypes(found, expected)
203
- private val (fnd, exp ) = Formatting .typeDiff (found, expected)(printCtx )
204
- val msg =
202
+ val msg = {
203
+ val (where, printCtx ) = Formatting .disambiguateTypes (found, expected)
204
+ val (fnd, exp) = Formatting .typeDiff(found, expected)(printCtx)
205
205
s """ |found: $fnd
206
206
|required: $exp
207
207
|
208
208
| $where""" .stripMargin + whyNoMatch + implicitFailure
209
+ }
210
+
211
+ val explanation = " "
212
+ }
213
+
214
+ case class NotAMember (site : Type , name : Name , selected : String )(implicit ctx : Context )
215
+ extends Message (8 ) {
216
+ val kind = " Member Not Found"
217
+
218
+ val msg = {
219
+ import core .Flags ._
220
+ val maxDist = 3
221
+ val decls = site.decls.flatMap { sym =>
222
+ if (sym.is(Synthetic | PrivateOrLocal ) || sym.isConstructor) Nil
223
+ else List ((sym.name.show, sym))
224
+ }
225
+
226
+ // Calculate Levenshtein distance
227
+ def distance (n1 : Iterable [_], n2 : Iterable [_]) =
228
+ n1.foldLeft(List .range(0 , n2.size)) { (prev, x) =>
229
+ (prev zip prev.tail zip n2).scanLeft(prev.head + 1 ) {
230
+ case (h, ((d, v), y)) => math.min(
231
+ math.min(h + 1 , v + 1 ),
232
+ if (x == y) d else d + 1
233
+ )
234
+ }
235
+ }.last
236
+
237
+ // Count number of wrong characters
238
+ def incorrectChars (x : (String , Int , Symbol )): (String , Symbol , Int ) = {
239
+ val (currName, _, sym) = x
240
+ val matching = name.show.zip(currName).foldLeft(0 ) {
241
+ case (acc, (x,y)) => if (x != y) acc + 1 else acc
242
+ }
243
+ (currName, sym, matching)
244
+ }
245
+
246
+ // Get closest match in `site`
247
+ val closest =
248
+ decls
249
+ .map { case (n, sym) => (n, distance(n, name.show), sym) }
250
+ .collect { case (n, dist, sym) if dist <= maxDist => (n, dist, sym) }
251
+ .groupBy(_._2).toList
252
+ .sortBy(_._1)
253
+ .headOption.map(_._2).getOrElse(Nil )
254
+ .map(incorrectChars).toList
255
+ .sortBy(_._3)
256
+ .take(1 ).map { case (n, sym, _) => (n, sym) }
257
+
258
+ val siteName = site match {
259
+ case site : NamedType => site.name.show
260
+ case site => i " $site"
261
+ }
262
+
263
+ val closeMember = closest match {
264
+ case (n, sym) :: Nil => hl """ - did you mean ` ${s " $siteName. $n" }`? """
265
+ case Nil => " "
266
+ case _ => assert(
267
+ false ,
268
+ " Could not single out one distinct member to match on input with"
269
+ )
270
+ }
271
+
272
+ ex " $selected ` $name` is not a member of $site$closeMember"
273
+ }
209
274
210
275
val explanation = " "
211
276
}
0 commit comments