@@ -2573,6 +2573,107 @@ class MissingImplicitArgument(
2573
2573
case ambi : AmbiguousImplicits => withoutDisambiguation()
2574
2574
case _ =>
2575
2575
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
+
2576
2677
def msg (using Context ): String =
2577
2678
2578
2679
def formatMsg (shortForm : String )(headline : String = shortForm) = arg match
@@ -2596,29 +2697,6 @@ class MissingImplicitArgument(
2596
2697
|But ${tpe.explanation}. """
2597
2698
case _ => headline
2598
2699
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
-
2622
2700
def location (preposition : String ) = if (where.isEmpty) " " else s " $preposition $where"
2623
2701
2624
2702
def defaultAmbiguousImplicitMsg (ambi : AmbiguousImplicits ) =
@@ -2655,77 +2733,13 @@ class MissingImplicitArgument(
2655
2733
userDefinedErrorString(raw, params, args)
2656
2734
}
2657
2735
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
-
2691
2736
/** Extracting the message from a type, e.g. in
2692
2737
*
2693
2738
* @annotation.implicitNotFound("Foo is missing")
2694
2739
* trait Foo
2695
2740
*
2696
2741
* def foo(implicit foo: Foo): Any = ???
2697
2742
*/
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
-
2729
2743
arg.tpe match
2730
2744
case ambi : AmbiguousImplicits =>
2731
2745
(ambi.alt1, ambi.alt2) match
@@ -2739,8 +2753,7 @@ class MissingImplicitArgument(
2739
2753
i """ No implicit search was attempted ${location(" for" )}
2740
2754
|since the expected type $target is not specific enough """
2741
2755
case _ =>
2742
- val shortMessage = userDefinedImplicitNotFoundParamMessage
2743
- .orElse(userDefinedImplicitNotFoundTypeMessage)
2756
+ val shortMessage = userDefinedImplicitNotFoundMessage(explain = false )
2744
2757
.getOrElse(defaultImplicitNotFoundMessage)
2745
2758
formatMsg(shortMessage)()
2746
2759
end msg
@@ -2769,7 +2782,8 @@ class MissingImplicitArgument(
2769
2782
.orElse(noChainConversionsNote(ignoredConvertibleImplicits))
2770
2783
.getOrElse(ctx.typer.importSuggestionAddendum(pt))
2771
2784
2772
- def explain (using Context ) = " "
2785
+ def explain (using Context ) = userDefinedImplicitNotFoundMessage(explain = true )
2786
+ .getOrElse(" " )
2773
2787
end MissingImplicitArgument
2774
2788
2775
2789
class CannotBeAccessed (tpe : NamedType , superAccess : Boolean )(using Context )
0 commit comments