@@ -695,15 +695,10 @@ trait Implicits { self: Typer =>
695
695
}
696
696
}
697
697
698
- /** A helper class to find imports of givens that might fix a type error.
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
700
*
700
- * suggestions(p).search
701
- *
702
- * returns a list of TermRefs that refer to implicits or givens
703
- * that satisfy predicate `p`.
704
- *
705
- * The search algorithm looks for givens in the smallest set of objects
706
- * and packages that includes
701
+ * These roots are the smallest set of objects and packages that includes
707
702
*
708
703
* - any object that is a defined in an enclosing scope,
709
704
* - any object that is a member of an enclosing class,
@@ -712,11 +707,23 @@ trait Implicits { self: Typer =>
712
707
* - any object or package from which something is imported in an enclosing scope,
713
708
* - any package that is nested in a searched package, provided
714
709
* the package was accessed in some way previously.
710
+ *
711
+ * Excluded from the root set are:
712
+ *
713
+ * - Objects that contain `$`s in their name. These have to
714
+ * be omitted since they might be inner Java class files which
715
+ * cannot be read by the ClassfileParser without crashing.
716
+ * - Any members of static parts of Java classes.
717
+ * - Any members of the empty package. These should be
718
+ * skipped since the empty package often contains unrelated junk files
719
+ * that should not be used for suggestions.
720
+ * - Any members of the java or java.lang packages. These are
721
+ * skipped as an optimization, since they won't contain implicits anyway.
715
722
*/
716
- private class Suggestions ( pt : Type ) with
717
- private val seen = mutable.Set [TermRef ]()
723
+ private def suggestionRoots ( given Context ) =
724
+ val seen = mutable.Set [TermRef ]()
718
725
719
- private def lookInside (root : Symbol )(given Context ): Boolean =
726
+ def lookInside (root : Symbol )(given Context ): Boolean =
720
727
if root.is(Package ) then root.isTerm && root.isCompleted
721
728
else ! root.name.is(FlatName )
722
729
&& ! root.name.lastPart.contains('$' )
@@ -732,7 +739,7 @@ trait Implicits { self: Typer =>
732
739
}
733
740
}
734
741
735
- private def rootsStrictlyIn (ref : Type )(given Context ): List [TermRef ] =
742
+ def rootsStrictlyIn (ref : Type )(given Context ): List [TermRef ] =
736
743
val site = ref.widen
737
744
val refSym = site.typeSymbol
738
745
val nested =
@@ -751,18 +758,18 @@ trait Implicits { self: Typer =>
751
758
.flatMap(rootsIn)
752
759
.toList
753
760
754
- private def rootsIn (ref : TermRef )(given Context ): List [TermRef ] =
761
+ def rootsIn (ref : TermRef )(given Context ): List [TermRef ] =
755
762
if seen.contains(ref) then Nil
756
763
else
757
764
implicits.println(i " search for suggestions in ${ref.symbol.fullName}" )
758
765
seen += ref
759
766
ref :: rootsStrictlyIn(ref)
760
767
761
- private def rootsOnPath (tp : Type )(given Context ): List [TermRef ] = tp match
768
+ def rootsOnPath (tp : Type )(given Context ): List [TermRef ] = tp match
762
769
case ref : TermRef => rootsIn(ref) ::: rootsOnPath(ref.prefix)
763
770
case _ => Nil
764
771
765
- private def roots (given ctx : Context ): List [TermRef ] =
772
+ def recur (given ctx : Context ): List [TermRef ] =
766
773
if ctx.owner.exists then
767
774
val defined =
768
775
if ctx.owner.isClass then
@@ -778,76 +785,86 @@ trait Implicits { self: Typer =>
778
785
else ctx.importInfo.sym.info match
779
786
case ImportType (expr) => rootsOnPath(expr.tpe)
780
787
case _ => Nil
781
- defined ++ imported ++ roots (given ctx .outer)
788
+ defined ++ imported ++ recur (given ctx .outer)
782
789
else Nil
783
790
784
- def search (given ctx : Context ): (List [TermRef ], List [TermRef ]) =
785
- val timer = new Timer ()
786
- val deadLine = System .currentTimeMillis() + suggestImplicitTimeOut
791
+ recur
792
+ end suggestionRoots
787
793
788
- /** Test whether the head of a given instance matches the expected type `pt`,
789
- * ignoring any dependent implicit arguments.
790
- */
791
- def shallowTest (ref : TermRef )(given Context ): Boolean =
792
- System .currentTimeMillis < deadLine
793
- && (ref <:< pt)(given ctx .fresh.setExploreTyperState())
794
+ /** Given an expected type `pt`, return two lists of TermRefs:
795
+ *
796
+ * 1. The _fully matching_ given instances that can be completed
797
+ * to a full synthesized given term that matches the expected type `pt`.
798
+ *
799
+ * 2. The _head matching_ given instances, that conform to the
800
+ * expected type `pt`, ignoring any dependent implicit arguments.
801
+ */
802
+ private def importSuggestions (pt : Type )(given ctx : Context ): (List [TermRef ], List [TermRef ]) =
803
+ val timer = new Timer ()
804
+ val deadLine = System .currentTimeMillis() + suggestImplicitTimeOut
794
805
795
- /** Test whether a full given term can be synthesized that matches
796
- * the expected type `pt`.
797
- */
798
- def deepTest (ref : TermRef )(given Context ): Boolean =
799
- System .currentTimeMillis < deadLine
800
- && {
801
- val task = new TimerTask with
802
- def run () =
803
- println(i " Cancelling test of $ref when making suggestions for error in ${ctx.source}" )
804
- ctx.run.isCancelled = true
805
- val span = ctx.owner.sourcePos.span
806
- val (expectedType, argument, kind) = pt match
807
- case ViewProto (argType, resType) =>
808
- (resType,
809
- untpd.Ident (ref.name).withSpan(span).withType(argType),
810
- if hasExtMethod(ref, resType) then Candidate .Extension
811
- else Candidate .Conversion )
812
- case _ =>
813
- (pt, EmptyTree , Candidate .Value )
814
- val candidate = Candidate (ref, kind, 0 )
815
- try
816
- timer.schedule(task, testOneImplicitTimeOut)
817
- typedImplicit(candidate, expectedType, argument, span)(
818
- given ctx .fresh.setExploreTyperState()).isSuccess
819
- finally
820
- task.cancel()
821
- ctx.run.isCancelled = false
822
- }
823
- end deepTest
824
-
825
- try
826
- roots
827
- .filterNot(root => defn.RootImportTypes .exists(_.symbol == root.symbol))
828
- // don't suggest things that are imported by default
829
- .flatMap(_.implicitMembers.filter(shallowTest))
830
- // filter whether the head of the implicit can match
831
- .partition(deepTest)
832
- // partition into full matches and head matches
833
- catch
834
- case ex : Throwable =>
835
- if ctx.settings.Ydebug .value then
836
- println(" caught exception when searching for suggestions" )
837
- ex.printStackTrace()
838
- (Nil , Nil )
839
- finally timer.cancel()
840
- end search
841
- end Suggestions
806
+ /** Test whether the head of a given instance matches the expected type `pt`,
807
+ * ignoring any dependent implicit arguments.
808
+ */
809
+ def shallowTest (ref : TermRef )(given Context ): Boolean =
810
+ System .currentTimeMillis < deadLine
811
+ && (ref <:< pt)(given ctx .fresh.setExploreTyperState())
812
+
813
+ /** Test whether a full given term can be synthesized that matches
814
+ * the expected type `pt`.
815
+ */
816
+ def deepTest (ref : TermRef )(given Context ): Boolean =
817
+ System .currentTimeMillis < deadLine
818
+ && {
819
+ val task = new TimerTask with
820
+ def run () =
821
+ println(i " Cancelling test of $ref when making suggestions for error in ${ctx.source}" )
822
+ ctx.run.isCancelled = true
823
+ val span = ctx.owner.sourcePos.span
824
+ val (expectedType, argument, kind) = pt match
825
+ case ViewProto (argType, resType) =>
826
+ (resType,
827
+ untpd.Ident (ref.name).withSpan(span).withType(argType),
828
+ if hasExtMethod(ref, resType) then Candidate .Extension
829
+ else Candidate .Conversion )
830
+ case _ =>
831
+ (pt, EmptyTree , Candidate .Value )
832
+ val candidate = Candidate (ref, kind, 0 )
833
+ try
834
+ timer.schedule(task, testOneImplicitTimeOut)
835
+ typedImplicit(candidate, expectedType, argument, span)(
836
+ given ctx .fresh.setExploreTyperState()).isSuccess
837
+ finally
838
+ task.cancel()
839
+ ctx.run.isCancelled = false
840
+ }
841
+ end deepTest
842
+
843
+ try
844
+ suggestionRoots
845
+ .filterNot(root => defn.RootImportTypes .exists(_.symbol == root.symbol))
846
+ // don't suggest things that are imported by default
847
+ .flatMap(_.implicitMembers.filter(shallowTest))
848
+ // filter whether the head of the implicit can match
849
+ .partition(deepTest)
850
+ // partition into full matches and head matches
851
+ catch
852
+ case ex : Throwable =>
853
+ if ctx.settings.Ydebug .value then
854
+ println(" caught exception when searching for suggestions" )
855
+ ex.printStackTrace()
856
+ (Nil , Nil )
857
+ finally timer.cancel()
858
+ end importSuggestions
842
859
843
860
/** An addendum to an error message where the error might be fixed
844
861
* by some implicit value of type `pt` that is however not found.
845
862
* The addendum suggests given imports that might fix the problem.
846
863
* If there's nothing to suggest, an empty string is returned.
847
864
*/
848
- override def implicitSuggestionsFor (pt : Type )(given ctx : Context ): String =
865
+ override def implicitSuggestionAddendum (pt : Type )(given ctx : Context ): String =
849
866
val (fullMatches, headMatches) =
850
- Suggestions (pt).search (given ctx .fresh.setExploreTyperState())
867
+ importSuggestions (pt)(given ctx .fresh.setExploreTyperState())
851
868
implicits.println(i " suggestions for $pt in ${ctx.owner} = ( $fullMatches%, %, $headMatches%, %) " )
852
869
val (suggestedRefs, help) =
853
870
if fullMatches.nonEmpty then (fullMatches, " fix" )
@@ -874,7 +891,7 @@ trait Implicits { self: Typer =>
874
891
|
875
892
| $suggestions%\n%
876
893
"""
877
- end implicitSuggestionsFor
894
+ end implicitSuggestionAddendum
878
895
879
896
/** Handlers to synthesize implicits for special types */
880
897
type SpecialHandler = (Type , Span ) => Context => Tree
@@ -1435,7 +1452,7 @@ trait Implicits { self: Typer =>
1435
1452
// example where searching for a nested type causes an infinite loop.
1436
1453
" "
1437
1454
1438
- def suggestedImports = implicitSuggestionsFor (pt)
1455
+ def suggestedImports = implicitSuggestionAddendum (pt)
1439
1456
if normalImports.isEmpty then suggestedImports else normalImports
1440
1457
end hiddenImplicitsAddendum
1441
1458
0 commit comments