@@ -2573,6 +2573,107 @@ class MissingImplicitArgument(
25732573 case ambi : AmbiguousImplicits => withoutDisambiguation()
25742574 case _ =>
25752575
2576+ /** Format `raw` implicitNotFound or implicitAmbiguous argument, replacing
2577+ * all occurrences of `${X}` where `X` is in `paramNames` with the
2578+ * corresponding shown type in `args`.
2579+ */
2580+ def userDefinedErrorString (raw : String , paramNames : List [String ], args : List [Type ])(using Context ): String =
2581+ def translate (name : String ): Option [String ] =
2582+ val idx = paramNames.indexOf(name)
2583+ if (idx >= 0 ) Some (i " ${args(idx)}" ) else None
2584+ """ \$\{\s*([^}\s]+)\s*\}""" .r.replaceAllIn(raw, (_ : Regex .Match ) match
2585+ case Regex .Groups (v) => quoteReplacement(translate(v).getOrElse(" " )).nn
2586+ )
2587+
2588+ /** @param rawMsg Message template with variables, e.g. "Variable A is ${A}"
2589+ * @param sym Symbol of the annotated type or of the method whose parameter was annotated
2590+ * @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int
2591+ */
2592+ def formatAnnotationMessage (rawMsg : String , sym : Symbol , substituteType : Type => Type )(using Context ): String =
2593+ val substitutableTypesSymbols = substitutableTypeSymbolsInScope(sym)
2594+ userDefinedErrorString(
2595+ rawMsg,
2596+ paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString),
2597+ args = substitutableTypesSymbols.map(_.typeRef).map(substituteType)
2598+ )
2599+
2600+ /** Extract a user defined error message from a symbol `sym`
2601+ * with an annotation matching the given class symbol `cls`.
2602+ */
2603+ def userDefinedMsg (sym : Symbol , cls : Symbol )(using Context ) =
2604+ for
2605+ ann <- sym.getAnnotation(cls)
2606+ msg <- ann.argumentConstantString(0 )
2607+ yield msg
2608+
2609+ def userDefinedImplicitNotFoundTypeMessageFor (sym : Symbol )(using Context ): Option [String ] =
2610+ for
2611+ rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot )
2612+ if Feature .migrateTo3 || sym != defn.Function1
2613+ // Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore
2614+ yield
2615+ val substituteType = (_ : Type ).asSeenFrom(pt, sym)
2616+ formatAnnotationMessage(rawMsg, sym, substituteType)
2617+
2618+ /** Extracting the message from a method parameter, e.g. in
2619+ *
2620+ * trait Foo
2621+ *
2622+ * def foo(implicit @annotation.implicitNotFound("Foo is missing") foo: Foo): Any = ???
2623+ */
2624+ def userDefinedImplicitNotFoundParamMessage (using Context ): Option [String ] =
2625+ paramSymWithMethodCallTree.flatMap: (sym, applTree) =>
2626+ userDefinedMsg(sym, defn.ImplicitNotFoundAnnot ).map: rawMsg =>
2627+ val fn = tpd.funPart(applTree)
2628+ val targs = tpd.typeArgss(applTree).flatten
2629+ val methodOwner = fn.symbol.owner
2630+ val methodOwnerType = tpd.qualifier(fn).tpe
2631+ val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType)
2632+ val methodTypeArgs = targs.map(_.tpe)
2633+ val substituteType = (_ : Type ).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs)
2634+ formatAnnotationMessage(rawMsg, sym.owner, substituteType)
2635+
2636+ def userDefinedImplicitNotFoundTypeMessage (using Context ): Option [String ] =
2637+ def recur (tp : Type ): Option [String ] = tp match
2638+ case tp : TypeRef =>
2639+ val sym = tp.symbol
2640+ userDefinedImplicitNotFoundTypeMessageFor(sym).orElse(recur(tp.info))
2641+ case tp : ClassInfo =>
2642+ tp.baseClasses.iterator
2643+ .map(userDefinedImplicitNotFoundTypeMessageFor)
2644+ .find(_.isDefined).flatten
2645+ case tp : TypeProxy =>
2646+ recur(tp.superType)
2647+ case tp : AndType =>
2648+ recur(tp.tp1).orElse(recur(tp.tp2))
2649+ case _ =>
2650+ None
2651+ recur(pt)
2652+
2653+ /** The implicitNotFound annotation on the parameter, or else on the type.
2654+ * implicitNotFound message strings starting with `explain=` are intended for
2655+ * additional explanations, not the message proper. The leading `explain=` is
2656+ * dropped in this case.
2657+ * @param explain The message is used for an additional explanation, not
2658+ * the message proper.
2659+ */
2660+ def userDefinedImplicitNotFoundMessage (explain : Boolean )(using Context ): Option [String ] =
2661+ val explainTag = " explain="
2662+ def filter (msg : Option [String ]) = msg match
2663+ case Some (str) =>
2664+ if str.startsWith(explainTag) then
2665+ if explain then Some (str.drop(explainTag.length)) else None
2666+ else if explain then None
2667+ else msg
2668+ case None => None
2669+ filter(userDefinedImplicitNotFoundParamMessage)
2670+ .orElse(filter(userDefinedImplicitNotFoundTypeMessage))
2671+
2672+ object AmbiguousImplicitMsg {
2673+ def unapply (search : SearchSuccess ): Option [String ] =
2674+ userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot )
2675+ }
2676+
25762677 def msg (using Context ): String =
25772678
25782679 def formatMsg (shortForm : String )(headline : String = shortForm) = arg match
@@ -2596,29 +2697,6 @@ class MissingImplicitArgument(
25962697 |But ${tpe.explanation}. """
25972698 case _ => headline
25982699
2599- /** Format `raw` implicitNotFound or implicitAmbiguous argument, replacing
2600- * all occurrences of `${X}` where `X` is in `paramNames` with the
2601- * corresponding shown type in `args`.
2602- */
2603- def userDefinedErrorString (raw : String , paramNames : List [String ], args : List [Type ]): String = {
2604- def translate (name : String ): Option [String ] = {
2605- val idx = paramNames.indexOf(name)
2606- if (idx >= 0 ) Some (i " ${args(idx)}" ) else None
2607- }
2608-
2609- """ \$\{\s*([^}\s]+)\s*\}""" .r.replaceAllIn(raw, (_ : Regex .Match ) match {
2610- case Regex .Groups (v) => quoteReplacement(translate(v).getOrElse(" " )).nn
2611- })
2612- }
2613-
2614- /** Extract a user defined error message from a symbol `sym`
2615- * with an annotation matching the given class symbol `cls`.
2616- */
2617- def userDefinedMsg (sym : Symbol , cls : Symbol ) = for {
2618- ann <- sym.getAnnotation(cls)
2619- msg <- ann.argumentConstantString(0 )
2620- } yield msg
2621-
26222700 def location (preposition : String ) = if (where.isEmpty) " " else s " $preposition $where"
26232701
26242702 def defaultAmbiguousImplicitMsg (ambi : AmbiguousImplicits ) =
@@ -2655,77 +2733,13 @@ class MissingImplicitArgument(
26552733 userDefinedErrorString(raw, params, args)
26562734 }
26572735
2658- /** @param rawMsg Message template with variables, e.g. "Variable A is ${A}"
2659- * @param sym Symbol of the annotated type or of the method whose parameter was annotated
2660- * @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int
2661- */
2662- def formatAnnotationMessage (rawMsg : String , sym : Symbol , substituteType : Type => Type ): String = {
2663- val substitutableTypesSymbols = substitutableTypeSymbolsInScope(sym)
2664-
2665- userDefinedErrorString(
2666- rawMsg,
2667- paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString),
2668- args = substitutableTypesSymbols.map(_.typeRef).map(substituteType)
2669- )
2670- }
2671-
2672- /** Extracting the message from a method parameter, e.g. in
2673- *
2674- * trait Foo
2675- *
2676- * def foo(implicit @annotation.implicitNotFound("Foo is missing") foo: Foo): Any = ???
2677- */
2678- def userDefinedImplicitNotFoundParamMessage : Option [String ] = paramSymWithMethodCallTree.flatMap { (sym, applTree) =>
2679- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot ).map { rawMsg =>
2680- val fn = tpd.funPart(applTree)
2681- val targs = tpd.typeArgss(applTree).flatten
2682- val methodOwner = fn.symbol.owner
2683- val methodOwnerType = tpd.qualifier(fn).tpe
2684- val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType)
2685- val methodTypeArgs = targs.map(_.tpe)
2686- val substituteType = (_ : Type ).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs)
2687- formatAnnotationMessage(rawMsg, sym.owner, substituteType)
2688- }
2689- }
2690-
26912736 /** Extracting the message from a type, e.g. in
26922737 *
26932738 * @annotation.implicitNotFound("Foo is missing")
26942739 * trait Foo
26952740 *
26962741 * def foo(implicit foo: Foo): Any = ???
26972742 */
2698- def userDefinedImplicitNotFoundTypeMessage : Option [String ] =
2699- def recur (tp : Type ): Option [String ] = tp match
2700- case tp : TypeRef =>
2701- val sym = tp.symbol
2702- userDefinedImplicitNotFoundTypeMessageFor(sym).orElse(recur(tp.info))
2703- case tp : ClassInfo =>
2704- tp.baseClasses.iterator
2705- .map(userDefinedImplicitNotFoundTypeMessageFor)
2706- .find(_.isDefined).flatten
2707- case tp : TypeProxy =>
2708- recur(tp.superType)
2709- case tp : AndType =>
2710- recur(tp.tp1).orElse(recur(tp.tp2))
2711- case _ =>
2712- None
2713- recur(pt)
2714-
2715- def userDefinedImplicitNotFoundTypeMessageFor (sym : Symbol ): Option [String ] =
2716- for
2717- rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot )
2718- if Feature .migrateTo3 || sym != defn.Function1
2719- // Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore
2720- yield
2721- val substituteType = (_ : Type ).asSeenFrom(pt, sym)
2722- formatAnnotationMessage(rawMsg, sym, substituteType)
2723-
2724- object AmbiguousImplicitMsg {
2725- def unapply (search : SearchSuccess ): Option [String ] =
2726- userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot )
2727- }
2728-
27292743 arg.tpe match
27302744 case ambi : AmbiguousImplicits =>
27312745 (ambi.alt1, ambi.alt2) match
@@ -2739,8 +2753,7 @@ class MissingImplicitArgument(
27392753 i """ No implicit search was attempted ${location(" for" )}
27402754 |since the expected type $target is not specific enough """
27412755 case _ =>
2742- val shortMessage = userDefinedImplicitNotFoundParamMessage
2743- .orElse(userDefinedImplicitNotFoundTypeMessage)
2756+ val shortMessage = userDefinedImplicitNotFoundMessage(explain = false )
27442757 .getOrElse(defaultImplicitNotFoundMessage)
27452758 formatMsg(shortMessage)()
27462759 end msg
@@ -2769,7 +2782,8 @@ class MissingImplicitArgument(
27692782 .orElse(noChainConversionsNote(ignoredConvertibleImplicits))
27702783 .getOrElse(ctx.typer.importSuggestionAddendum(pt))
27712784
2772- def explain (using Context ) = " "
2785+ def explain (using Context ) = userDefinedImplicitNotFoundMessage(explain = true )
2786+ .getOrElse(" " )
27732787end MissingImplicitArgument
27742788
27752789class CannotBeAccessed (tpe : NamedType , superAccess : Boolean )(using Context )
0 commit comments