@@ -696,7 +696,8 @@ trait Implicits { self: Typer =>
696
696
}
697
697
698
698
/** 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.
700
701
*
701
702
* These roots are the smallest set of objects and packages that includes
702
703
*
@@ -798,6 +799,11 @@ trait Implicits { self: Typer =>
798
799
*
799
800
* 2. The _head matching_ given instances, that conform to the
800
801
* 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`.
801
807
*/
802
808
private def importSuggestions (pt : Type )(given ctx : Context ): (List [TermRef ], List [TermRef ]) =
803
809
val timer = new Timer ()
@@ -806,14 +812,14 @@ trait Implicits { self: Typer =>
806
812
/** Test whether the head of a given instance matches the expected type `pt`,
807
813
* ignoring any dependent implicit arguments.
808
814
*/
809
- def shallowTest (ref : TermRef )( given Context ) : Boolean =
815
+ def shallowTest (ref : TermRef ): Boolean =
810
816
System .currentTimeMillis < deadLine
811
817
&& (ref <:< pt)(given ctx .fresh.setExploreTyperState())
812
818
813
819
/** Test whether a full given term can be synthesized that matches
814
820
* the expected type `pt`.
815
821
*/
816
- def deepTest (ref : TermRef )( given Context ) : Boolean =
822
+ def deepTest (ref : TermRef ): Boolean =
817
823
System .currentTimeMillis < deadLine
818
824
&& {
819
825
val task = new TimerTask with
@@ -840,14 +846,37 @@ trait Implicits { self: Typer =>
840
846
}
841
847
end deepTest
842
848
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
+
843
861
try
844
- suggestionRoots
862
+ val roots = suggestionRoots
845
863
.filterNot(root => defn.RootImportTypes .exists(_.symbol == root.symbol))
846
864
// 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
847
873
.flatMap(_.implicitMembers.filter(shallowTest))
848
874
// filter whether the head of the implicit can match
849
875
.partition(deepTest)
850
876
// partition into full matches and head matches
877
+ match
878
+ case (Nil , partials) => (extensionImports, partials)
879
+ case givenImports => givenImports
851
880
catch
852
881
case ex : Throwable =>
853
882
if ctx.settings.Ydebug .value then
@@ -862,7 +891,7 @@ trait Implicits { self: Typer =>
862
891
* The addendum suggests given imports that might fix the problem.
863
892
* If there's nothing to suggest, an empty string is returned.
864
893
*/
865
- override def implicitSuggestionAddendum (pt : Type )(given ctx : Context ): String =
894
+ override def importSuggestionAddendum (pt : Type )(given ctx : Context ): String =
866
895
val (fullMatches, headMatches) =
867
896
importSuggestions(pt)(given ctx .fresh.setExploreTyperState())
868
897
implicits.println(i " suggestions for $pt in ${ctx.owner} = ( $fullMatches%, %, $headMatches%, %) " )
@@ -873,12 +902,14 @@ trait Implicits { self: Typer =>
873
902
s " import ${ctx.printer.toTextRef(ref).show}"
874
903
val suggestions = suggestedRefs
875
904
.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
880
911
}
881
- .map(_._2 )
912
+ .map((ref, str) => str )
882
913
.distinct // TermRefs might be different but generate the same strings
883
914
if suggestions.isEmpty then " "
884
915
else
@@ -891,7 +922,7 @@ trait Implicits { self: Typer =>
891
922
|
892
923
| $suggestions%\n%
893
924
"""
894
- end implicitSuggestionAddendum
925
+ end importSuggestionAddendum
895
926
896
927
/** Handlers to synthesize implicits for special types */
897
928
type SpecialHandler = (Type , Span ) => Context => Tree
@@ -1452,7 +1483,7 @@ trait Implicits { self: Typer =>
1452
1483
// example where searching for a nested type causes an infinite loop.
1453
1484
" "
1454
1485
1455
- def suggestedImports = implicitSuggestionAddendum (pt)
1486
+ def suggestedImports = importSuggestionAddendum (pt)
1456
1487
if normalImports.isEmpty then suggestedImports else normalImports
1457
1488
end hiddenImplicitsAddendum
1458
1489
0 commit comments