diff --git a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala index 7db5112b6674..012f6d1248fb 100644 --- a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala +++ b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala @@ -62,7 +62,7 @@ trait MessageRendering { } val syntax = - if (ctx.settings.color.value != "never") + if (ctx.settings.color.value != "never" && !ctx.isJava) SyntaxHighlighting.highlight(new String(pos.linesSlice)).toCharArray else pos.linesSlice val lines = linesFrom(syntax) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index fd16f0de5f3a..6872ca70d330 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -125,8 +125,8 @@ trait TypeAssigner { /** The type of the selection `tree`, where `qual1` is the typed qualifier part. */ def selectionType(tree: untpd.RefTree, qual1: Tree)(using Context): Type = - val qualType0 = qual1.tpe.widenIfUnstable val qualType = + val qualType0 = qual1.tpe.widenIfUnstable if !qualType0.hasSimpleKind && tree.name != nme.CONSTRUCTOR then // constructors are selected on type constructor, type arguments are passed afterwards errorType(em"$qualType0 takes type parameters", qual1.srcPos) @@ -199,7 +199,7 @@ trait TypeAssigner { /** Type assignment method. Each method takes as parameters * - an untpd.Tree to which it assigns a type, - * - typed child trees it needs to access to cpmpute that type, + * - typed child trees it needs to access to compute that type, * - any further information it needs to access to compute that type. */ def assignType(tree: untpd.Ident, tp: Type)(using Context): Ident = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index ce72aac596c0..0b1e9a047bf1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -986,20 +986,44 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer record("typedSelect") def typeSelectOnTerm(using Context): Tree = - val qual = typedExpr(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan)) if ctx.isJava then - javaSelection(qual) + // permitted selection depends on Java context (type or expression). + // we don't propagate (as a mode) whether a.b.m is a type name; OK since we only see type contexts. + // to allow correct selections, approximate by fallback for x.y: take x as class or (rooted) package. + def tryQualFallback(qual: untpd.Ident, name: Name)(using Context): Tree = + val qualTpe = + findRef(name.toTypeName, WildcardType, EmptyFlags, EmptyFlags, qual.srcPos) match + case tpe: NamedType if tpe.symbol.isClass => tpe + case _ => + val maybePackage = defn.RootPackage.info.member(name) + if maybePackage.exists then maybePackage.info else NoType + if qualTpe.exists then + javaSelection(assignType(cpy.Ident(qual)(name), qualTpe)) + else + errorTree(tree, em"no class or package to resolve `$name`") // just fail fallback + def tryQual(qual: untpd.Tree)(using Context): Tree = + javaSelection(typedExpr(qual, shallowSelectionProto(tree.name, pt, this, tree.nameSpan))) + tree.qualifier match + case qual @ Ident(name) => tryAlternatively(tryQual(qual))(tryQualFallback(qual, name)) + case qual => tryQual(qual) else + val qual = typedExpr(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan)) typedSelectWithAdapt(tree, pt, qual).withSpan(tree.span).computeNullable() def javaSelection(qual: Tree)(using Context) = - val tree1 = assignType(cpy.Select(tree)(qual, tree.name), qual) - tree1.tpe match - case moduleRef: TypeRef if moduleRef.symbol.is(ModuleClass, butNot = JavaDefined) => - // handle unmangling of module names (Foo$ -> Foo[ModuleClass]) - cpy.Select(tree)(qual, tree.name.unmangleClassName).withType(moduleRef) - case _ => - tree1 + qual match + case id @ Ident(name) if id.symbol.is(Package) && !id.symbol.owner.isRoot => + val rooted = defn.RootPackage.info.member(name) + val qual1 = if rooted.exists then assignType(cpy.Ident(id)(name), rooted.info) else qual + assignType(cpy.Select(tree)(qual1, tree.name), qual1) + case _ => + val tree1 = assignType(cpy.Select(tree)(qual, tree.name), qual) + tree1.tpe match + case moduleRef: TypeRef if moduleRef.symbol.is(ModuleClass, butNot = JavaDefined) => + // handle unmangling of module names (Foo$ -> Foo[ModuleClass]) + cpy.Select(tree)(qual, tree.name.unmangleClassName).withType(moduleRef) + case _ => + tree1 def tryJavaSelectOnType(using Context): Tree = tree.qualifier match { case sel @ Select(qual, name) => @@ -1016,17 +1040,14 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer errorTree(tree, em"cannot convert to type selection") // will never be printed due to fallback } - def selectWithFallback(fallBack: Context ?=> Tree) = - tryAlternatively(typeSelectOnTerm)(fallBack) - if (tree.qualifier.isType) { val qual1 = typedType(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan)) assignType(cpy.Select(tree)(qual1, tree.name), qual1) } else if (ctx.isJava && tree.name.isTypeName) - // SI-3120 Java uses the same syntax, A.B, to express selection from the - // value A and from the type A. We have to try both. - selectWithFallback(tryJavaSelectOnType) // !!! possibly exponential bcs of qualifier retyping + // scala/bug#3120 Java uses the same syntax, A.B, to express selection from the + // value A and from the type A. We have to try both. (possibly exponential bc of qualifier retyping) + tryAlternatively(typeSelectOnTerm)(tryJavaSelectOnType) else typeSelectOnTerm } diff --git a/tests/pos/t10350/Bar.scala b/tests/pos/t10350/Bar.scala new file mode 100644 index 000000000000..509d5af2b686 --- /dev/null +++ b/tests/pos/t10350/Bar.scala @@ -0,0 +1,6 @@ + +package bar + +object Bar { + def xxx(s: String): foo.Foo = foo.Foo.create(s) +} diff --git a/tests/pos/t10350/Baz.java b/tests/pos/t10350/Baz.java new file mode 100644 index 000000000000..c11c3875c3cb --- /dev/null +++ b/tests/pos/t10350/Baz.java @@ -0,0 +1,5 @@ + +package foo.java; + +interface Baz { +} diff --git a/tests/pos/t10350/Foo.java b/tests/pos/t10350/Foo.java new file mode 100644 index 000000000000..ea4bba7643e7 --- /dev/null +++ b/tests/pos/t10350/Foo.java @@ -0,0 +1,14 @@ + +package foo; + +public interface Foo { + static Foo create(java.lang.String v) { + return null; + } +} + +/* +5 | static Foo create(java.lang.String v) { + | ^^^^^^^^^ + | value lang is not a member of foo.java + */ diff --git a/tests/pos/t11788/Bar.scala b/tests/pos/t11788/Bar.scala new file mode 100644 index 000000000000..f3d61e4f468f --- /dev/null +++ b/tests/pos/t11788/Bar.scala @@ -0,0 +1,5 @@ +package p + +object Bar extends App { + println(new Foo().test()) +} diff --git a/tests/pos/t11788/Foo.java b/tests/pos/t11788/Foo.java new file mode 100644 index 000000000000..5b2224b6e00c --- /dev/null +++ b/tests/pos/t11788/Foo.java @@ -0,0 +1,11 @@ +package p; + +public class Foo { + private String java; + + // java is the rooted package, not the field + public java.lang.Integer test() { + //return Integer.valueOf(42); + throw null; + } +} diff --git a/tests/pos/t11788b/Bar.scala b/tests/pos/t11788b/Bar.scala new file mode 100644 index 000000000000..f3d61e4f468f --- /dev/null +++ b/tests/pos/t11788b/Bar.scala @@ -0,0 +1,5 @@ +package p + +object Bar extends App { + println(new Foo().test()) +} diff --git a/tests/pos/t11788b/Foo.java b/tests/pos/t11788b/Foo.java new file mode 100644 index 000000000000..960085bd453f --- /dev/null +++ b/tests/pos/t11788b/Foo.java @@ -0,0 +1,10 @@ +package p; + +public class Foo { + private String java; + + public java.lang.Integer test() { + //return Integer.valueOf(42); + throw null; + } +} diff --git a/tests/pos/t11788b/java.java b/tests/pos/t11788b/java.java new file mode 100644 index 000000000000..713e265b5e72 --- /dev/null +++ b/tests/pos/t11788b/java.java @@ -0,0 +1,8 @@ +package p; + +public class java { + public static class lang { + public static class Integer { + } + } +} diff --git a/tests/pos/t11788c/Bar.scala b/tests/pos/t11788c/Bar.scala new file mode 100644 index 000000000000..f3d61e4f468f --- /dev/null +++ b/tests/pos/t11788c/Bar.scala @@ -0,0 +1,5 @@ +package p + +object Bar extends App { + println(new Foo().test()) +} diff --git a/tests/pos/t11788c/Foo.java b/tests/pos/t11788c/Foo.java new file mode 100644 index 000000000000..c244f1f13554 --- /dev/null +++ b/tests/pos/t11788c/Foo.java @@ -0,0 +1,10 @@ +package p; + +public class Foo { + private String java; + + // java is class in scope, not the term member or package + public java.lang.Integer.Inner test() { + throw null; + } +} diff --git a/tests/pos/t11788c/java.java b/tests/pos/t11788c/java.java new file mode 100644 index 000000000000..3b5ff8e11cd2 --- /dev/null +++ b/tests/pos/t11788c/java.java @@ -0,0 +1,10 @@ +package p; + +public class java { + public static class lang { + public static class Integer { + public static class Inner { + } + } + } +}