From 8e529b19d454a8823e5dad1d9b812d6fec6983b2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 16 Feb 2022 15:07:34 +0100 Subject: [PATCH] Add tests for error conditions --- .../src/dotty/tools/dotc/typer/Namer.scala | 5 ++- .../src/dotty/tools/dotc/typer/Typer.scala | 11 +++-- tests/neg/export-in-extension.check | 29 ++++++++++++++ tests/neg/export-in-extension.scala | 40 +++++++++++++++++++ tests/run/export-in-extension.scala | 9 +++++ 5 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 tests/neg/export-in-extension.check create mode 100644 tests/neg/export-in-extension.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 09bf1d26b1bb..f1566ead746d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1104,7 +1104,7 @@ class Namer { typer: Typer => else if sym.isAllOf(JavaModule) then Skip else if pathMethod.exists && mbr.isType then - No("cannot be exported as extension method") + No("is a type, so it cannot be exported as extension method") else Yes } @@ -1296,7 +1296,8 @@ class Namer { typer: Typer => process(stats1)(using ctx.importContext(stat, symbolOfTree(stat))) case (stat: ExtMethods) :: stats1 => for case exp: Export <- stat.methods do - processExport(exp, exportPathSym(exp.expr, stat)) + val pathSym = exportPathSym(exp.expr, stat) + if pathSym.exists then processExport(exp, pathSym) process(stats1) case stat :: stats1 => process(stats1) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 2558281d6529..b8a2d015cf16 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2565,10 +2565,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val selectors1 = typedSelectors(imp.selectors) assignType(cpy.Import(imp)(expr1, selectors1), sym) - def typedExport(exp: untpd.Export)(using Context): Export = - val expr1 = exp.expr.removeAttachment(TypedAhead).getOrElse(EmptyTree) - val selectors1 = typedSelectors(exp.selectors) - assignType(cpy.Export(exp)(expr1, selectors1)) + def typedExport(exp: untpd.Export)(using Context): Tree = + exp.expr.removeAttachment(TypedAhead) match + case Some(expr1) => + val selectors1 = typedSelectors(exp.selectors) + assignType(cpy.Export(exp)(expr1, selectors1)) + case _ => + errorTree(exp, em"exports are only allowed from objects and classes") def typedPackageDef(tree: untpd.PackageDef)(using Context): Tree = val pid1 = withMode(Mode.InPackageClauseName)(typedExpr(tree.pid, AnySelectionProto)) diff --git a/tests/neg/export-in-extension.check b/tests/neg/export-in-extension.check new file mode 100644 index 000000000000..43863ebf6ced --- /dev/null +++ b/tests/neg/export-in-extension.check @@ -0,0 +1,29 @@ +-- Error: tests/neg/export-in-extension.scala:14:13 -------------------------------------------------------------------- +14 | export c1.* // error + | ^^ + | export qualifier c1 is not a parameterless companion extension method +-- Error: tests/neg/export-in-extension.scala:19:22 -------------------------------------------------------------------- +19 | export cm.{bar, D} // error + | ^ + | no eligible member D at O.O2.cm + | O.O2.cm.D cannot be exported because it is a type, so it cannot be exported as extension method +-- Error: tests/neg/export-in-extension.scala:20:18 -------------------------------------------------------------------- +20 | export this.cm.baz // error + | ^^^^^^^ + | export qualifier must be a simple reference to a companion extension method +-- Error: tests/neg/export-in-extension.scala:24:13 -------------------------------------------------------------------- +24 | export missing.* // error + | ^^^^^^^ + | export qualifier missing is not a parameterless companion extension method +-- Error: tests/neg/export-in-extension.scala:28:13 -------------------------------------------------------------------- +28 | export cm.* // error + | ^^ + | export qualifier cm is not a parameterless companion extension method +-- Error: tests/neg/export-in-extension.scala:33:13 -------------------------------------------------------------------- +33 | export cm.* // error + | ^^ + | export qualifier cm is not a parameterless companion extension method +-- Error: tests/neg/export-in-extension.scala:38:13 -------------------------------------------------------------------- +38 | export cm.* // error + | ^^^^^^^^^^^ + | exports are only allowed from objects and classes diff --git a/tests/neg/export-in-extension.scala b/tests/neg/export-in-extension.scala new file mode 100644 index 000000000000..8bcab0e5ac8d --- /dev/null +++ b/tests/neg/export-in-extension.scala @@ -0,0 +1,40 @@ +object O: + + class C(x: Int): + def bar = x + def baz(y: Int) = x + y + val bam = x * x + def :: (y: Int) = x - y + class D + + val c1 = C(1) + + object O1: + extension (x: Int) + export c1.* // error + + object O2: + extension (x: Int) + private def cm = new C(x) + export cm.{bar, D} // error + export this.cm.baz // error + + object O3: + extension (x: Int) + export missing.* // error + + object O4: + extension (x: Int) + export cm.* // error + + object O5: + extension (x: Int) + private def cm(y: C) = C + export cm.* // error + + { + extension (x: Int) + private def cm = new C(x) + export cm.* // error + } + diff --git a/tests/run/export-in-extension.scala b/tests/run/export-in-extension.scala index 903e13e89269..26becc280ff3 100644 --- a/tests/run/export-in-extension.scala +++ b/tests/run/export-in-extension.scala @@ -13,6 +13,15 @@ object O: def succ2: Int = succ + 1 def ::: (y: Int) = x - y +object O2: + import O.C + extension (x: Int) + private def cm = new C(x) + export cm.{bar, baz, bam, ::} + def succ: Int = x + 1 + def succ2: Int = succ + 1 + def ::: (y: Int) = x - y + @main def Test = import O.* assert(3.succ2 == 5)