@@ -696,7 +696,8 @@ trait Implicits { self: Typer =>
696696 }
697697
698698 /** A list of TermRefs referring to the roots where suggestions for
699- * imports of givens that might fix a type error are searched.
699+ * imports of givens or extension methods that might fix a type error
700+ * are searched.
700701 *
701702 * These roots are the smallest set of objects and packages that includes
702703 *
@@ -798,6 +799,11 @@ trait Implicits { self: Typer =>
798799 *
799800 * 2. The _head matching_ given instances, that conform to the
800801 * expected type `pt`, ignoring any dependent implicit arguments.
802+ *
803+ * If there are no fully matching given instances under (1), and `pt` is
804+ * a view prototype of a selection of the form `T ?=>? { name: ... }`,
805+ * return instead a list of all possible references to extension methods named
806+ * `name` that are applicable to `T`.
801807 */
802808 private def importSuggestions (pt : Type )(given ctx : Context ): (List [TermRef ], List [TermRef ]) =
803809 val timer = new Timer ()
@@ -806,14 +812,14 @@ trait Implicits { self: Typer =>
806812 /** Test whether the head of a given instance matches the expected type `pt`,
807813 * ignoring any dependent implicit arguments.
808814 */
809- def shallowTest (ref : TermRef )( given Context ) : Boolean =
815+ def shallowTest (ref : TermRef ): Boolean =
810816 System .currentTimeMillis < deadLine
811817 && (ref <:< pt)(given ctx .fresh.setExploreTyperState())
812818
813819 /** Test whether a full given term can be synthesized that matches
814820 * the expected type `pt`.
815821 */
816- def deepTest (ref : TermRef )( given Context ) : Boolean =
822+ def deepTest (ref : TermRef ): Boolean =
817823 System .currentTimeMillis < deadLine
818824 && {
819825 val task = new TimerTask with
@@ -840,14 +846,37 @@ trait Implicits { self: Typer =>
840846 }
841847 end deepTest
842848
849+ /** Optionally, an extension method reference `site.name` that is
850+ * applicable to `argType`.
851+ */
852+ def extensionMethod (site : TermRef , name : TermName , argType : Type ): Option [TermRef ] =
853+ site.member(name)
854+ .alternatives
855+ .map(mbr => TermRef (site, mbr.symbol))
856+ .filter(ref =>
857+ ref.symbol.is(Extension )
858+ && isApplicableMethodRef(ref, argType :: Nil , WildcardType ))
859+ .headOption
860+
843861 try
844- suggestionRoots
862+ val roots = suggestionRoots
845863 .filterNot(root => defn.RootImportTypes .exists(_.symbol == root.symbol))
846864 // don't suggest things that are imported by default
865+
866+ def extensionImports = pt match
867+ case ViewProto (argType, SelectionProto (name : TermName , _, _, _)) =>
868+ roots.flatMap(extensionMethod(_, name, argType))
869+ case _ =>
870+ Nil
871+
872+ roots
847873 .flatMap(_.implicitMembers.filter(shallowTest))
848874 // filter whether the head of the implicit can match
849875 .partition(deepTest)
850876 // partition into full matches and head matches
877+ match
878+ case (Nil , partials) => (extensionImports, partials)
879+ case givenImports => givenImports
851880 catch
852881 case ex : Throwable =>
853882 if ctx.settings.Ydebug .value then
@@ -862,7 +891,7 @@ trait Implicits { self: Typer =>
862891 * The addendum suggests given imports that might fix the problem.
863892 * If there's nothing to suggest, an empty string is returned.
864893 */
865- override def implicitSuggestionAddendum (pt : Type )(given ctx : Context ): String =
894+ override def importSuggestionAddendum (pt : Type )(given ctx : Context ): String =
866895 val (fullMatches, headMatches) =
867896 importSuggestions(pt)(given ctx .fresh.setExploreTyperState())
868897 implicits.println(i " suggestions for $pt in ${ctx.owner} = ( $fullMatches%, %, $headMatches%, %) " )
@@ -873,12 +902,14 @@ trait Implicits { self: Typer =>
873902 s " import ${ctx.printer.toTextRef(ref).show}"
874903 val suggestions = suggestedRefs
875904 .zip(suggestedRefs.map(importString))
876- .filter(_._2.contains('.' ))
877- .sortWith { (rs1, rs2) => // sort by specificity first, alphabetically second
878- val diff = compare(rs1._1, rs2._1)
879- diff > 0 || diff == 0 && rs1._2 < rs2._2
905+ .filter((ref, str) => str.contains('.' ))
906+ .sortWith { (x, y) =>
907+ // sort by specificity first, alphabetically second
908+ val ((ref1, str1), (ref2, str2)) = (x, y)
909+ val diff = compare(ref1, ref2)
910+ diff > 0 || diff == 0 && str1 < str2
880911 }
881- .map(_._2 )
912+ .map((ref, str) => str )
882913 .distinct // TermRefs might be different but generate the same strings
883914 if suggestions.isEmpty then " "
884915 else
@@ -891,7 +922,7 @@ trait Implicits { self: Typer =>
891922 |
892923 | $suggestions%\n%
893924 """
894- end implicitSuggestionAddendum
925+ end importSuggestionAddendum
895926
896927 /** Handlers to synthesize implicits for special types */
897928 type SpecialHandler = (Type , Span ) => Context => Tree
@@ -1452,7 +1483,7 @@ trait Implicits { self: Typer =>
14521483 // example where searching for a nested type causes an infinite loop.
14531484 " "
14541485
1455- def suggestedImports = implicitSuggestionAddendum (pt)
1486+ def suggestedImports = importSuggestionAddendum (pt)
14561487 if normalImports.isEmpty then suggestedImports else normalImports
14571488 end hiddenImplicitsAddendum
14581489
0 commit comments