aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-02-08 14:35:53 +0100
committerMartin Odersky <odersky@gmail.com>2016-02-19 16:57:35 +0100
commitfc043bfb2e1c8fd0a73b87a4c955e3e09f6bf8c0 (patch)
tree1f2813884bfcd8fff4e558f6a61aa40b750b613e
parenteb1908a9f2c61895cabe70c0ac0ebbe8ef14fcea (diff)
downloaddotty-fc043bfb2e1c8fd0a73b87a4c955e3e09f6bf8c0.tar.gz
dotty-fc043bfb2e1c8fd0a73b87a4c955e3e09f6bf8c0.tar.bz2
dotty-fc043bfb2e1c8fd0a73b87a4c955e3e09f6bf8c0.zip
Add checking for leaking private definitions
First version. Fixes #997.
-rw-r--r--src/dotty/tools/dotc/core/Flags.scala3
-rw-r--r--src/dotty/tools/dotc/core/Types.scala9
-rw-r--r--src/dotty/tools/dotc/transform/PostTyper.scala44
-rw-r--r--test/dotc/tests.scala2
-rw-r--r--tests/neg/i997.scala45
5 files changed, 98 insertions, 5 deletions
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
+
+}