From b4401bcee541ded4170b2e7256f3a7fb46fafbba Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 19 Jan 2020 15:54:02 +0100 Subject: [PATCH] Fix #8032: Make import suggestions less chatty Two improvements: - Properly constraint union types as result types - Don't suggest members of Nothing --- .../dotty/tools/dotc/typer/ImportSuggestions.scala | 14 +++++++++++--- .../src/dotty/tools/dotc/typer/ProtoTypes.scala | 2 ++ .../src/dotty/tools/dotc/typer/TypeAssigner.scala | 1 + tests/neg/i8032.check | 9 +++++++++ tests/neg/i8032.scala | 3 +++ 5 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 tests/neg/i8032.check create mode 100644 tests/neg/i8032.scala diff --git a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala index bd8641bc49fa..a9c11c0314b0 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala @@ -148,9 +148,17 @@ trait ImportSuggestions with System.currentTimeMillis < deadLine && { given Context = ctx.fresh.setExploreTyperState() - pt match - case pt: ViewProto => pt.isMatchedBy(ref) - case _ => normalize(ref, pt) <:< pt + def test(pt: Type): Boolean = pt match + case ViewProto(argType, OrType(rt1, rt2)) => + // Union types do not constrain results, since comparison with a union + // type on the right might lose information. See ProtoTypes.disregardProto. + // To regain precision, test both sides separately. + test(ViewProto(argType, rt1)) || test(ViewProto(argType, rt2)) + case pt: ViewProto => + pt.isMatchedBy(ref) + case _ => + normalize(ref, pt) <:< pt + test(pt) } /** Test whether a full given term can be synthesized that matches diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 363fadb7d642..ec1a715497c5 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -61,6 +61,8 @@ object ProtoTypes { private def disregardProto(pt: Type)(implicit ctx: Context): Boolean = pt.dealias match { case _: OrType => true + // Don't constrain results with union types, since comparison with a union + // type on the right might commit too early into one side. case pt => pt.isRef(defn.UnitClass) } diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 34ea152e6f30..d0152379b4b0 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -283,6 +283,7 @@ trait TypeAssigner { |Note that `$name` is treated as an infix operator in Scala 3. |If you do not want that, insert a `;` or empty line in front |or drop any spaces behind the operator.""" + else if qualType.isBottomType then "" else var add = importSuggestionAddendum( ViewProto(qualType.widen, diff --git a/tests/neg/i8032.check b/tests/neg/i8032.check new file mode 100644 index 000000000000..e5fff3bbf8fc --- /dev/null +++ b/tests/neg/i8032.check @@ -0,0 +1,9 @@ +-- [E007] Type Mismatch Error: tests/neg/i8032.scala:1:15 -------------------------------------------------------------- +1 |val x: 1 | 2 = 3 // error + | ^ + | Found: (3 : Int) + | Required: (1 : Int) | (2 : Int) +-- [E008] Member Not Found Error: tests/neg/i8032.scala:3:12 ----------------------------------------------------------- +3 |val y = ???.map // error + | ^^^^^^^ + | value map is not a member of Nothing diff --git a/tests/neg/i8032.scala b/tests/neg/i8032.scala new file mode 100644 index 000000000000..a96acbadd987 --- /dev/null +++ b/tests/neg/i8032.scala @@ -0,0 +1,3 @@ +val x: 1 | 2 = 3 // error + +val y = ???.map // error \ No newline at end of file