@@ -2562,6 +2562,106 @@ class MissingImplicitArgument(
2562
2562
case ambi : AmbiguousImplicits => withoutDisambiguation()
2563
2563
case _ =>
2564
2564
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
+
2565
2665
def msg (using Context ): String =
2566
2666
2567
2667
def formatMsg (shortForm : String )(headline : String = shortForm) = arg match
@@ -2585,29 +2685,6 @@ class MissingImplicitArgument(
2585
2685
|But ${tpe.explanation}. """
2586
2686
case _ => headline
2587
2687
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
-
2611
2688
def location (preposition : String ) = if (where.isEmpty) " " else s " $preposition $where"
2612
2689
2613
2690
def defaultAmbiguousImplicitMsg (ambi : AmbiguousImplicits ) =
@@ -2644,77 +2721,13 @@ class MissingImplicitArgument(
2644
2721
userDefinedErrorString(raw, params, args)
2645
2722
}
2646
2723
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
-
2680
2724
/** Extracting the message from a type, e.g. in
2681
2725
*
2682
2726
* @annotation.implicitNotFound("Foo is missing")
2683
2727
* trait Foo
2684
2728
*
2685
2729
* def foo(implicit foo: Foo): Any = ???
2686
2730
*/
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
-
2718
2731
arg.tpe match
2719
2732
case ambi : AmbiguousImplicits =>
2720
2733
(ambi.alt1, ambi.alt2) match
@@ -2728,8 +2741,7 @@ class MissingImplicitArgument(
2728
2741
i """ No implicit search was attempted ${location(" for" )}
2729
2742
|since the expected type $target is not specific enough """
2730
2743
case _ =>
2731
- val shortMessage = userDefinedImplicitNotFoundParamMessage
2732
- .orElse(userDefinedImplicitNotFoundTypeMessage)
2744
+ val shortMessage = userDefinedImplicitNotFoundMessage(explain = false )
2733
2745
.getOrElse(defaultImplicitNotFoundMessage)
2734
2746
formatMsg(shortMessage)()
2735
2747
end msg
@@ -2758,7 +2770,8 @@ class MissingImplicitArgument(
2758
2770
.orElse(noChainConversionsNote(ignoredConvertibleImplicits))
2759
2771
.getOrElse(ctx.typer.importSuggestionAddendum(pt))
2760
2772
2761
- def explain (using Context ) = " "
2773
+ def explain (using Context ) = userDefinedImplicitNotFoundMessage(explain = true )
2774
+ .getOrElse(" " )
2762
2775
end MissingImplicitArgument
2763
2776
2764
2777
class CannotBeAccessed (tpe : NamedType , superAccess : Boolean )(using Context )
0 commit comments