From fc043bfb2e1c8fd0a73b87a4c955e3e09f6bf8c0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 8 Feb 2016 14:35:53 +0100 Subject: Add checking for leaking private definitions First version. Fixes #997. --- src/dotty/tools/dotc/core/Flags.scala | 3 ++ src/dotty/tools/dotc/core/Types.scala | 9 ++++++ src/dotty/tools/dotc/transform/PostTyper.scala | 44 ++++++++++++++++++++++--- test/dotc/tests.scala | 2 ++ tests/neg/i997.scala | 45 ++++++++++++++++++++++++++ 5 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 tests/neg/i997.scala diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 02b341649..d1f562f72 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -543,6 +543,9 @@ object Flags { /** A lazy or deferred value */ final val LazyOrDeferred = Lazy | Deferred + /** A synthetic or private definition */ + final val SyntheticOrPrivate = Synthetic | Private + /** A type parameter or type parameter accessor */ final val TypeParamOrAccessor = TypeParam | TypeParamAccessor diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 8595da640..16287c827 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1014,6 +1014,12 @@ object Types { case _ => List() } + /** The full parent types, including all type arguments */ + def parentsWithArgs(implicit ctx: Context): List[Type] = this match { + case tp: TypeProxy => tp.underlying.parents + case _ => List() + } + /** The first parent of this type, AnyRef if list of parents is empty */ def firstParent(implicit ctx: Context): TypeRef = parents match { case p :: _ => p @@ -2780,6 +2786,9 @@ object Types { parentsCache } + override def parentsWithArgs(implicit ctx: Context): List[Type] = + parents.map(p => typeRef.baseTypeWithArgs(p.symbol)) + /** The parent types with all type arguments */ def instantiatedParents(implicit ctx: Context): List[Type] = parents mapConserve { pref => diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index 14edaa7b5..b4b01d1f3 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -127,8 +127,42 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran private def transformAnnot(annot: Annotation)(implicit ctx: Context): Annotation = annot.derivedAnnotation(transformAnnot(annot.tree)) - private def transformAnnots(tree: MemberDef)(implicit ctx: Context): Unit = - tree.symbol.transformAnnotations(transformAnnot) + private def transformMemberDef(tree: MemberDef)(implicit ctx: Context): Unit = { + val sym = tree.symbol + sym.transformAnnotations(transformAnnot) + type Errors = List[(String, Position)] + val notPrivate = new TypeAccumulator[Errors] { + def boundary(sym: Symbol): Symbol = + if (sym.is(Private)) sym.owner + else if (sym.privateWithin.exists) sym.privateWithin + else if (sym.is(Package)) sym + else boundary(sym.owner) + def apply(errors: Errors, tp: Type): Errors = tp match { + case tp: NamedType => + val errors1 = + if (tp.symbol.is(Private) && + !boundary(sym).isContainedIn(boundary(tp.symbol))) { + //println(i"sym = $sym, ref = ${tp.symbol}, symb = ${boundary(sym)}, refb = ${boundary(tp.symbol)}") + (d"non-private $sym refers to private ${tp.symbol}\n in its type signature ${sym.info}", tree.pos) :: errors + } + else foldOver(errors, tp) + if ((errors1 ne errors) && tp.info.isAlias) { + val errors2 = apply(errors, tp.info.bounds.hi) + if (errors2 eq errors) errors2 + else errors1 + } + else errors1 + case tp: ClassInfo => + (apply(errors, tp.prefix) /: tp.typeRef.parentsWithArgs)(apply) + case _ => + foldOver(errors, tp) + } + } + if (!sym.is(SyntheticOrPrivate) && sym.owner.isClass) { + val errors = notPrivate(Nil, sym.info) + errors.foreach { case (msg, pos) => ctx.errorOrMigrationWarning(msg, pos) } + } + } private def transformSelect(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = { val qual = tree.qualifier @@ -172,10 +206,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran } finally parentNews = saved case tree: DefDef => - transformAnnots(tree) + transformMemberDef(tree) superAcc.wrapDefDef(tree)(super.transform(tree).asInstanceOf[DefDef]) case tree: TypeDef => - transformAnnots(tree) + transformMemberDef(tree) val sym = tree.symbol val tree1 = if (sym.isClass) tree @@ -185,7 +219,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran } super.transform(tree1) case tree: MemberDef => - transformAnnots(tree) + transformMemberDef(tree) super.transform(tree) case tree: New if !inJavaAnnot && !parentNews.contains(tree) => Checking.checkInstantiable(tree.tpe, tree.pos) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 421846ca2..573436733 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -165,6 +165,8 @@ class tests extends CompilerTest { @Test def neg_i803 = compileFile(negDir, "i803", xerrors = 2) @Test def neg_i866 = compileFile(negDir, "i866", xerrors = 2) @Test def neg_i974 = compileFile(negDir, "i974", xerrors = 2) + @Test def neg_i997 = compileFile(negDir, "i997a", xerrors = 15) + @Test def neg_i997a = compileFile(negDir, "i997a", xerrors = 2) @Test def neg_i1050 = compileFile(negDir, "i1050", List("-strict"), xerrors = 11) @Test def neg_i1050a = compileFile(negDir, "i1050a", xerrors = 2) @Test def neg_i1050c = compileFile(negDir, "i1050c", xerrors = 8) diff --git a/tests/neg/i997.scala b/tests/neg/i997.scala new file mode 100644 index 000000000..bfcf5637e --- /dev/null +++ b/tests/neg/i997.scala @@ -0,0 +1,45 @@ +class C { + + private type T = E + private val p: C = new C + + def f1(x: T): Unit = () // error + def f2(x: p.D): Unit = () // error + + val v1: T = ??? // error + val v2: p.D = ??? // error + + type U1[X <: T] // error + type U2 = T // error + + private class E { + def f1ok(x: T): Unit = () // ok + def f2ok(x: p.D): Unit = () // ok + + val v1ok: T = ??? // ok + val v2ok: p.D = ??? // ok + + type U1ok[X <: T] //ok + type U2ok = T //ok + } + + class D extends E { // error + def f1(x: T): Unit = () // error + def f2(x: p.D): Unit = () // error + + val v1: T = ??? // error + val v2: p.D = ??? // error + + type U1[X <: T] // error + type U2 = T // error + } + + class F(x: T) // error + + class G private (x: T) // ok + + private trait U + + class H extends U // error + +} -- cgit v1.2.3