diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Infer.scala | 25 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala | 19 | ||||
-rw-r--r-- | test/files/neg/bug4877.check | 22 | ||||
-rw-r--r-- | test/files/neg/bug4877.flags | 1 | ||||
-rw-r--r-- | test/files/neg/bug4877.scala | 22 |
5 files changed, 82 insertions, 7 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 495594da5e..b70f20ea89 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -229,7 +229,30 @@ trait Infer { } def typeErrorTree(tree: Tree, found: Type, req: Type): Tree = { - typeError(tree.pos, found, req) + // If the expected type is a refinement type, and the found type is a refinement or an anon + // class, we can greatly improve the error message by retyping the tree to recover the actual + // members present, then display along with the expected members. This is done here because + // this is the last point where we still have access to the original tree, rather than just + // the found/req types. + val foundType: Type = req.normalize match { + case RefinedType(parents, decls) if !decls.isEmpty && found.typeSymbol.isAnonOrRefinementClass => + val retyped = typer typed (tree.duplicate setType null) + val foundDecls = retyped.tpe.decls filter (sym => !sym.isConstructor && !sym.isSynthetic) + + if (foundDecls.isEmpty) found + else { + // The members arrive marked private, presumably because there was no + // expected type and so they're considered members of an anon class. + foundDecls foreach (_ resetFlag (PRIVATE | PROTECTED)) + // TODO: if any of the found parents match up with required parents after normalization, + // print the error so that they match. The major beneficiary there would be + // java.lang.Object vs. AnyRef. + refinedType(found.parents, found.typeSymbol.owner, foundDecls, tree.pos) + } + case _ => + found + } + typeError(tree.pos, foundType, req) setError(tree) } diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 2c424d17d7..6e0e78e8e2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -212,6 +212,13 @@ trait TypeDiagnostics { else if (sym.variance == -1) "contravariant" else "invariant" + // I think this should definitely be on by default, but I need to + // play with it a bit longer. For now it's behind -Xlint. + def explainAlias(tp: Type) = ( + if (!settings.lint.value || (tp eq tp.normalize)) "" + else " (which expands to)\n " + tp.normalize + ) + /** Look through the base types of the found type for any which * might have been valid subtypes if given conformant type arguments. * Examine those for situations where the type error would have been @@ -286,12 +293,12 @@ trait TypeDiagnostics { "" // no elaborable variance situation found } - def foundReqMsg(found: Type, req: Type): String = { - (withDisambiguation(List(), found, req) { - ";\n found : " + found.toLongString + existentialContext(found) + - "\n required: " + req + existentialContext(req) - }) + explainVariance(found, req) - } + def foundReqMsg(found: Type, req: Type): String = ( + withDisambiguation(Nil, found, req)( + ";\n found : " + found.toLongString + existentialContext(found) + explainAlias(found) + + "\n required: " + req + existentialContext(req) + explainAlias(req) + ) + explainVariance(found, req) + ) case class TypeDiag(tp: Type, sym: Symbol) extends Ordered[TypeDiag] { // save the name because it will be mutated until it has been diff --git a/test/files/neg/bug4877.check b/test/files/neg/bug4877.check new file mode 100644 index 0000000000..6970271c40 --- /dev/null +++ b/test/files/neg/bug4877.check @@ -0,0 +1,22 @@ +bug4877.scala:4: error: type mismatch; + found : java.lang.Object{def bar: Int} + required: AnyRef{def bar: String} + def foo: AnyRef { def bar: String } = new AnyRef { def bar = 42 } + ^ +bug4877.scala:6: error: type mismatch; + found : java.lang.Object{def bar(x: Int): java.lang.String} + required: AnyRef{def bar(x: Int): Int} + def foo3: AnyRef { def bar(x: Int): Int } = new AnyRef { def bar(x: Int) = "abc" } + ^ +bug4877.scala:7: error: type mismatch; + found : java.lang.Object with C{def bar(x: Int): Int} + required: C{def bar(x: Int): Int; def quux(x: Int): Int} + def foo4: C { def bar(x: Int): Int ; def quux(x: Int): Int } = new C { def bar(x: Int) = 5 } + ^ +bug4877.scala:17: error: type mismatch; + found : java.lang.Object{type Mom = String; def bar(x: Int): Int; def bippy(): List[Int]} + required: B.this.Bippy (which expands to) + AnyRef{type Mom; def bar(x: Int): this.Mom; def bippy(): List[this.Mom]} + val x: Bippy = new AnyRef { + ^ +four errors found diff --git a/test/files/neg/bug4877.flags b/test/files/neg/bug4877.flags new file mode 100644 index 0000000000..7ccd56103a --- /dev/null +++ b/test/files/neg/bug4877.flags @@ -0,0 +1 @@ +-Xlint
\ No newline at end of file diff --git a/test/files/neg/bug4877.scala b/test/files/neg/bug4877.scala new file mode 100644 index 0000000000..5d97877518 --- /dev/null +++ b/test/files/neg/bug4877.scala @@ -0,0 +1,22 @@ +trait C { } + +class A { + def foo: AnyRef { def bar: String } = new AnyRef { def bar = 42 } + def foo2: AnyRef { def bar: String } = new AnyRef { def bar = "abc" } + def foo3: AnyRef { def bar(x: Int): Int } = new AnyRef { def bar(x: Int) = "abc" } + def foo4: C { def bar(x: Int): Int ; def quux(x: Int): Int } = new C { def bar(x: Int) = 5 } +} + +class B { + type Bippy = { + type Mom + def bar(x: Int): Mom + def bippy(): List[Mom] + } + + val x: Bippy = new AnyRef { + type Mom = String + def bar(x: Int) = 55 + def bippy() = List(bar(55)) + } +}
\ No newline at end of file |