@@ -2562,6 +2562,106 @@ class MissingImplicitArgument(
25622562 case ambi : AmbiguousImplicits => withoutDisambiguation()
25632563 case _ =>
25642564
2565+ /** Format `raw` implicitNotFound or implicitAmbiguous argument, replacing
2566+ * all occurrences of `${X}` where `X` is in `paramNames` with the
2567+ * corresponding shown type in `args`.
2568+ */
2569+ def userDefinedErrorString (raw : String , paramNames : List [String ], args : List [Type ])(using Context ): String =
2570+ def translate (name : String ): Option [String ] =
2571+ val idx = paramNames.indexOf(name)
2572+ if (idx >= 0 ) Some (i " ${args(idx)}" ) else None
2573+ """ \$\{\s*([^}\s]+)\s*\}""" .r.replaceAllIn(raw, (_ : Regex .Match ) match
2574+ case Regex .Groups (v) => quoteReplacement(translate(v).getOrElse(" " )).nn
2575+ )
2576+
2577+ /** @param rawMsg Message template with variables, e.g. "Variable A is ${A}"
2578+ * @param sym Symbol of the annotated type or of the method whose parameter was annotated
2579+ * @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int
2580+ */
2581+ def formatAnnotationMessage (rawMsg : String , sym : Symbol , substituteType : Type => Type )(using Context ): String =
2582+ val substitutableTypesSymbols = substitutableTypeSymbolsInScope(sym)
2583+ userDefinedErrorString(
2584+ rawMsg,
2585+ paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString),
2586+ args = substitutableTypesSymbols.map(_.typeRef).map(substituteType)
2587+ )
2588+
2589+ /** Extract a user defined error message from a symbol `sym`
2590+ * with an annotation matching the given class symbol `cls`.
2591+ */
2592+ def userDefinedMsg (sym : Symbol , cls : Symbol )(using Context ) =
2593+ for
2594+ ann <- sym.getAnnotation(cls)
2595+ msg <- ann.argumentConstantString(0 )
2596+ yield msg
2597+
2598+ def userDefinedImplicitNotFoundTypeMessageFor (sym : Symbol )(using Context ): Option [String ] =
2599+ for
2600+ rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot )
2601+ if Feature .migrateTo3 || sym != defn.Function1
2602+ // Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore
2603+ yield
2604+ val substituteType = (_ : Type ).asSeenFrom(pt, sym)
2605+ formatAnnotationMessage(rawMsg, sym, substituteType)
2606+
2607+ /** Extracting the message from a method parameter, e.g. in
2608+ *
2609+ * trait Foo
2610+ *
2611+ * def foo(implicit @annotation.implicitNotFound("Foo is missing") foo: Foo): Any = ???
2612+ */
2613+ def userDefinedImplicitNotFoundParamMessage (using Context ): Option [String ] =
2614+ paramSymWithMethodCallTree.flatMap: (sym, applTree) =>
2615+ userDefinedMsg(sym, defn.ImplicitNotFoundAnnot ).map: rawMsg =>
2616+ val fn = tpd.funPart(applTree)
2617+ val targs = tpd.typeArgss(applTree).flatten
2618+ val methodOwner = fn.symbol.owner
2619+ val methodOwnerType = tpd.qualifier(fn).tpe
2620+ val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType)
2621+ val methodTypeArgs = targs.map(_.tpe)
2622+ val substituteType = (_ : Type ).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs)
2623+ formatAnnotationMessage(rawMsg, sym.owner, substituteType)
2624+
2625+ def userDefinedImplicitNotFoundTypeMessage (using Context ): Option [String ] =
2626+ def recur (tp : Type ): Option [String ] = tp match
2627+ case tp : TypeRef =>
2628+ val sym = tp.symbol
2629+ userDefinedImplicitNotFoundTypeMessageFor(sym).orElse(recur(tp.info))
2630+ case tp : ClassInfo =>
2631+ tp.baseClasses.iterator
2632+ .map(userDefinedImplicitNotFoundTypeMessageFor)
2633+ .find(_.isDefined).flatten
2634+ case tp : TypeProxy =>
2635+ recur(tp.superType)
2636+ case tp : AndType =>
2637+ recur(tp.tp1).orElse(recur(tp.tp2))
2638+ case _ =>
2639+ None
2640+ recur(pt)
2641+
2642+ /** The implicitNotFound annotation on the parameter, or else on the type.
2643+ * implicitNotFound message strings starting with `...` are intended for
2644+ * additional explanations, not the message proper. The leading `...` is
2645+ * dropped in this case.
2646+ * @param explain The message is used for an additional explanation, not
2647+ * the message proper.
2648+ */
2649+ def userDefinedImplicitNotFoundMessage (explain : Boolean )(using Context ): Option [String ] =
2650+ def filter (msg : Option [String ]) = msg match
2651+ case Some (str) =>
2652+ if str.startsWith(" ..." ) then
2653+ if explain then Some (str.drop(3 )) else None
2654+ else if explain then None
2655+ else msg
2656+ case None => None
2657+ filter(userDefinedImplicitNotFoundParamMessage)
2658+ .orElse(filter(userDefinedImplicitNotFoundTypeMessage))
2659+
2660+ object AmbiguousImplicitMsg {
2661+ def unapply (search : SearchSuccess ): Option [String ] =
2662+ userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot )
2663+ }
2664+
25652665 def msg (using Context ): String =
25662666
25672667 def formatMsg (shortForm : String )(headline : String = shortForm) = arg match
@@ -2585,29 +2685,6 @@ class MissingImplicitArgument(
25852685 |But ${tpe.explanation}. """
25862686 case _ => headline
25872687
2588- /** Format `raw` implicitNotFound or implicitAmbiguous argument, replacing
2589- * all occurrences of `${X}` where `X` is in `paramNames` with the
2590- * corresponding shown type in `args`.
2591- */
2592- def userDefinedErrorString (raw : String , paramNames : List [String ], args : List [Type ]): String = {
2593- def translate (name : String ): Option [String ] = {
2594- val idx = paramNames.indexOf(name)
2595- if (idx >= 0 ) Some (i " ${args(idx)}" ) else None
2596- }
2597-
2598- """ \$\{\s*([^}\s]+)\s*\}""" .r.replaceAllIn(raw, (_ : Regex .Match ) match {
2599- case Regex .Groups (v) => quoteReplacement(translate(v).getOrElse(" " )).nn
2600- })
2601- }
2602-
2603- /** Extract a user defined error message from a symbol `sym`
2604- * with an annotation matching the given class symbol `cls`.
2605- */
2606- def userDefinedMsg (sym : Symbol , cls : Symbol ) = for {
2607- ann <- sym.getAnnotation(cls)
2608- msg <- ann.argumentConstantString(0 )
2609- } yield msg
2610-
26112688 def location (preposition : String ) = if (where.isEmpty) " " else s " $preposition $where"
26122689
26132690 def defaultAmbiguousImplicitMsg (ambi : AmbiguousImplicits ) =
@@ -2644,77 +2721,13 @@ class MissingImplicitArgument(
26442721 userDefinedErrorString(raw, params, args)
26452722 }
26462723
2647- /** @param rawMsg Message template with variables, e.g. "Variable A is ${A}"
2648- * @param sym Symbol of the annotated type or of the method whose parameter was annotated
2649- * @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int
2650- */
2651- def formatAnnotationMessage (rawMsg : String , sym : Symbol , substituteType : Type => Type ): String = {
2652- val substitutableTypesSymbols = substitutableTypeSymbolsInScope(sym)
2653-
2654- userDefinedErrorString(
2655- rawMsg,
2656- paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString),
2657- args = substitutableTypesSymbols.map(_.typeRef).map(substituteType)
2658- )
2659- }
2660-
2661- /** Extracting the message from a method parameter, e.g. in
2662- *
2663- * trait Foo
2664- *
2665- * def foo(implicit @annotation.implicitNotFound("Foo is missing") foo: Foo): Any = ???
2666- */
2667- def userDefinedImplicitNotFoundParamMessage : Option [String ] = paramSymWithMethodCallTree.flatMap { (sym, applTree) =>
2668- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot ).map { rawMsg =>
2669- val fn = tpd.funPart(applTree)
2670- val targs = tpd.typeArgss(applTree).flatten
2671- val methodOwner = fn.symbol.owner
2672- val methodOwnerType = tpd.qualifier(fn).tpe
2673- val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType)
2674- val methodTypeArgs = targs.map(_.tpe)
2675- val substituteType = (_ : Type ).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs)
2676- formatAnnotationMessage(rawMsg, sym.owner, substituteType)
2677- }
2678- }
2679-
26802724 /** Extracting the message from a type, e.g. in
26812725 *
26822726 * @annotation.implicitNotFound("Foo is missing")
26832727 * trait Foo
26842728 *
26852729 * def foo(implicit foo: Foo): Any = ???
26862730 */
2687- def userDefinedImplicitNotFoundTypeMessage : Option [String ] =
2688- def recur (tp : Type ): Option [String ] = tp match
2689- case tp : TypeRef =>
2690- val sym = tp.symbol
2691- userDefinedImplicitNotFoundTypeMessageFor(sym).orElse(recur(tp.info))
2692- case tp : ClassInfo =>
2693- tp.baseClasses.iterator
2694- .map(userDefinedImplicitNotFoundTypeMessageFor)
2695- .find(_.isDefined).flatten
2696- case tp : TypeProxy =>
2697- recur(tp.superType)
2698- case tp : AndType =>
2699- recur(tp.tp1).orElse(recur(tp.tp2))
2700- case _ =>
2701- None
2702- recur(pt)
2703-
2704- def userDefinedImplicitNotFoundTypeMessageFor (sym : Symbol ): Option [String ] =
2705- for
2706- rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot )
2707- if Feature .migrateTo3 || sym != defn.Function1
2708- // Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore
2709- yield
2710- val substituteType = (_ : Type ).asSeenFrom(pt, sym)
2711- formatAnnotationMessage(rawMsg, sym, substituteType)
2712-
2713- object AmbiguousImplicitMsg {
2714- def unapply (search : SearchSuccess ): Option [String ] =
2715- userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot )
2716- }
2717-
27182731 arg.tpe match
27192732 case ambi : AmbiguousImplicits =>
27202733 (ambi.alt1, ambi.alt2) match
@@ -2728,8 +2741,7 @@ class MissingImplicitArgument(
27282741 i """ No implicit search was attempted ${location(" for" )}
27292742 |since the expected type $target is not specific enough """
27302743 case _ =>
2731- val shortMessage = userDefinedImplicitNotFoundParamMessage
2732- .orElse(userDefinedImplicitNotFoundTypeMessage)
2744+ val shortMessage = userDefinedImplicitNotFoundMessage(explain = false )
27332745 .getOrElse(defaultImplicitNotFoundMessage)
27342746 formatMsg(shortMessage)()
27352747 end msg
@@ -2758,7 +2770,8 @@ class MissingImplicitArgument(
27582770 .orElse(noChainConversionsNote(ignoredConvertibleImplicits))
27592771 .getOrElse(ctx.typer.importSuggestionAddendum(pt))
27602772
2761- def explain (using Context ) = " "
2773+ def explain (using Context ) = userDefinedImplicitNotFoundMessage(explain = true )
2774+ .getOrElse(" " )
27622775end MissingImplicitArgument
27632776
27642777class CannotBeAccessed (tpe : NamedType , superAccess : Boolean )(using Context )
0 commit comments