Skip to content

move 'return outside method definition' to error class #3173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/core/PhantomErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package dotty.tools.dotc.core

import dotty.tools.dotc.ast.tpd._
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.Symbols.defn
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.core.Types.Type

/** Phantom erasure erases:
Expand All @@ -27,4 +27,7 @@ object PhantomErasure {
/** Returns the default erased tree for a phantom parameter ref */
def erasedParameterRef(implicit ctx: Context): Tree = ref(defn.ErasedPhantom_UNIT)

/** Is it a pure term inserted by the phantom erasure? */
def isErasedPhantom(sym: Symbol)(implicit ctx: Context): Boolean = sym eq defn.ErasedPhantom_UNIT

}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ public enum ErrorMessageID {
ExpectedStartOfTopLevelDefinitionID,
MissingReturnTypeWithReturnStatementID,
NoReturnFromInlineID,
ReturnOutsideMethodDefinitionID,
;

public int errorNumber() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package reporting
package diagnostic

import dotc.core._
import Contexts.Context
import Contexts.{Context, NoContext}
import Decorators._
import Symbols._
import Names._
Expand Down Expand Up @@ -1747,4 +1747,14 @@ object messages {
|returned from a method.
|"""
}

case class ReturnOutsideMethodDefinition(owner: Symbol)(implicit ctx: Context)
extends Message(ReturnOutsideMethodDefinitionID) {
val kind = "Syntax"
val msg = hl"${"return"} outside method definition"
val explanation =
hl"""You used ${"return"} in ${owner}.
|${"return"} is a keyword and may only be used within method declarations.
|"""
}
}
21 changes: 13 additions & 8 deletions compiler/src/dotty/tools/dotc/transform/localopt/Simplify.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import core.NameOps._
import transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo}
import config.Printers.simplify
import ast.tpd
import dotty.tools.dotc.core.PhantomErasure

import scala.annotation.tailrec

Expand Down Expand Up @@ -62,6 +63,7 @@ class Simplify extends MiniPhaseTransform with IdentityDenotTransformer {
new Devalify ::
new Jumpjump ::
new DropGoodCasts ::
new DropNoEffects(this) ::
new ConstantFold(this) ::
Nil

Expand Down Expand Up @@ -174,13 +176,16 @@ object Simplify {
}

def isImmutableAccessor(t: Tree)(implicit ctx: Context): Boolean = {
val isImmutableGetter = t.symbol.isGetter && !t.symbol.is(Mutable | Lazy)
val isCaseAccessor = t.symbol.is(CaseAccessor) && !t.symbol.is(Mutable | Lazy)
val isProductAccessor = t.symbol.exists &&
t.symbol.owner.derivesFrom(defn.ProductClass) &&
t.symbol.owner.is(CaseClass) &&
t.symbol.name.isSelectorName &&
!t.symbol.info.decls.exists(_.is(Mutable | Lazy)) // Conservatively covers case class A(var x: Int)
isImmutableGetter || isCaseAccessor || isProductAccessor
val sym = t.symbol
val isImmutableGetter = sym.isGetter && !sym.is(Mutable | Lazy)
val isCaseAccessor = sym.is(CaseAccessor) && !sym.is(Mutable | Lazy)
val isProductAccessor = sym.exists &&
sym.owner.derivesFrom(defn.ProductClass) &&
sym.owner.is(CaseClass) &&
sym.name.isSelectorName &&
!sym.info.decls.exists(_.is(Mutable | Lazy)) // Conservatively covers case class A(var x: Int)
val isErasedPhantom = PhantomErasure.isErasedPhantom(sym)

isImmutableGetter || isCaseAccessor || isProductAccessor || isErasedPhantom
}
}
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -979,8 +979,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
def enclMethInfo(cx: Context): (Tree, Type) = {
val owner = cx.owner
if (cx == NoContext || owner.isType) {
ctx.error("return outside method definition", tree.pos)
if (owner.isType) {
ctx.error(ReturnOutsideMethodDefinition(owner), tree.pos)
(EmptyTree, WildcardType)
}
else if (owner != cx.outer.owner && owner.isRealMethod) {
Expand Down
5 changes: 2 additions & 3 deletions compiler/test/dotc/comptest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ object comptest extends ParallelTesting {
def isInteractive = true
def testFilter = None

implicit val defaultOutputDir: String = "."

val posDir = "./tests/pos/"
val negDir = "./tests/neg/"
val dotcDir = "./src/dotty/"
Expand All @@ -26,6 +24,7 @@ object comptest extends ParallelTesting {
dotcDir + "tools/dotc/core/Types.scala",
dotcDir + "tools/dotc/ast/Trees.scala"
),
TestFlags("", Array("-Ylog:frontend", "-Xprompt"))
TestFlags("", Array("-Ylog:frontend", "-Xprompt")),
outDirectory = "."
)
}
17 changes: 10 additions & 7 deletions compiler/test/dotty/tools/dotc/CompilationTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import dotty.tools.io.JFile


class CompilationTests extends ParallelTesting {
import ParallelTesting._
import TestConfiguration._
import CompilationTests._

Expand Down Expand Up @@ -68,7 +69,6 @@ class CompilationTests extends ParallelTesting {
compileFilesInDir("../tests/pos-special/strawman-collections", defaultOptions) +
compileFile("../scala2-library/src/library/scala/collection/immutable/IndexedSeq.scala", defaultOptions) +
compileFile("../scala2-library/src/library/scala/collection/parallel/mutable/ParSetLike.scala", defaultOptions) +
compileFile("../tests/pos/t2171.scala", defaultOptimised) +
compileList(
"parSetSubset",
List(
Expand Down Expand Up @@ -159,6 +159,7 @@ class CompilationTests extends ParallelTesting {

@Test def compileNeg: Unit = {
compileShallowFilesInDir("../tests/neg", defaultOptions) +
compileShallowFilesInDir("../tests/neg/no-optimise", defaultOptions) +
compileFile("../tests/neg/customArgs/typers.scala", allowDoubleBindings) +
compileFile("../tests/neg/customArgs/overrideClass.scala", scala2Mode) +
compileFile("../tests/neg/customArgs/autoTuplingTest.scala", defaultOptions.and("-language:noAutoTupling")) +
Expand Down Expand Up @@ -186,12 +187,7 @@ class CompilationTests extends ParallelTesting {

@Test def runAll: Unit = {
compileFilesInDir("../tests/run", defaultOptions) +
compileFile("../tests/run/i3018.scala", defaultOptimised) +
compileFile("../tests/run/blame_eye_triple_eee-double.scala", defaultOptimised) +
compileFile("../tests/run/blame_eye_triple_eee-float.scala", defaultOptimised) +
compileFile("../tests/run/run-bug4840.scala", defaultOptimised) +
compileFile("../tests/run/optimizer-array-load.scala", defaultOptimised) +
compileFile("../tests/run/constant-optimization.scala", defaultOptimised)
compileFilesInDir("../tests/run-no-optimise", defaultOptions)
}.checkRuns()

// Pickling Tests ------------------------------------------------------------
Expand Down Expand Up @@ -298,6 +294,13 @@ class CompilationTests extends ParallelTesting {
tests.foreach(_.delete())
}

@Test def testOptimised: Unit = {
val outputDir = defaultOutputDir + "optimised/"
compileFilesInDir("../tests/pos", defaultOptimised, outputDir).checkCompile()
compileFilesInDir("../tests/run", defaultOptimised, outputDir).checkRuns()
compileShallowFilesInDir("../tests/neg", defaultOptimised, outputDir).checkExpectedErrors()
}

private val (compilerSources, backendSources, backendJvmSources) = {
val compilerDir = Paths.get("../compiler/src")
val compilerSources0 = sources(Files.walk(compilerDir))
Expand Down
1 change: 1 addition & 0 deletions compiler/test/dotty/tools/dotc/LinkOptimiseTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import scala.concurrent.duration._
import scala.collection.JavaConverters._

class LinkOptimiseTests extends ParallelTesting {
import ParallelTesting._
import TestConfiguration._
import LinkOptimiseTests._

Expand Down
14 changes: 14 additions & 0 deletions compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -985,4 +985,18 @@ class ErrorMessagesTests extends ErrorMessagesTest {
val NoReturnFromInline(method) :: Nil = messages
assertEquals("method usesReturn", method.show)
}

@Test def returnOutsideMethodDefinition =
checkMessagesAfter("frontend") {
"""object A {
| return 5
|}
""".stripMargin
}.expect { (ictx, messages) =>
implicit val ctx: Context = ictx
assertMessageCount(1, messages)
val ReturnOutsideMethodDefinition(owner) :: Nil = messages
assertEquals("object A", owner.show)
}

}
13 changes: 8 additions & 5 deletions compiler/test/dotty/tools/vulpix/ParallelTesting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1057,7 +1057,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
}

/** Compiles a single file from the string path `f` using the supplied flags */
def compileFile(f: String, flags: TestFlags)(implicit outDirectory: String): CompilationTest = {
def compileFile(f: String, flags: TestFlags, outDirectory: String = defaultOutputDir): CompilationTest = {
val callingMethod = getCallingMethod()
val sourceFile = new JFile(f)
val parent = sourceFile.getParentFile
Expand Down Expand Up @@ -1087,7 +1087,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
* By default, files are compiled in alphabetical order. An optional seed
* can be used for randomization.
*/
def compileDir(f: String, flags: TestFlags, randomOrder: Option[Int] = None)(implicit outDirectory: String): CompilationTest = {
def compileDir(f: String, flags: TestFlags, randomOrder: Option[Int] = None, outDirectory: String = defaultOutputDir): CompilationTest = {
val callingMethod = getCallingMethod()
val outDir = outDirectory + callingMethod + "/"
val sourceDir = new JFile(f)
Expand Down Expand Up @@ -1116,7 +1116,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
* `testName` since files can be in separate directories and or be otherwise
* dissociated
*/
def compileList(testName: String, files: List[String], flags: TestFlags, callingMethod: String = getCallingMethod())(implicit outDirectory: String): CompilationTest = {
def compileList(testName: String, files: List[String], flags: TestFlags, callingMethod: String = getCallingMethod(), outDirectory: String = defaultOutputDir): CompilationTest = {
val outDir = outDirectory + callingMethod + "/" + testName + "/"

// Directories in which to compile all containing files with `flags`:
Expand Down Expand Up @@ -1147,7 +1147,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
* - Directories can have an associated check-file, where the check file has
* the same name as the directory (with the file extension `.check`)
*/
def compileFilesInDir(f: String, flags: TestFlags)(implicit outDirectory: String): CompilationTest = {
def compileFilesInDir(f: String, flags: TestFlags, outDirectory: String = defaultOutputDir): CompilationTest = {
val callingMethod = getCallingMethod()
val outDir = outDirectory + callingMethod + "/"
val sourceDir = new JFile(f)
Expand All @@ -1167,7 +1167,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
* sub-directories and as such, does **not** perform separate compilation
* tests.
*/
def compileShallowFilesInDir(f: String, flags: TestFlags)(implicit outDirectory: String): CompilationTest = {
def compileShallowFilesInDir(f: String, flags: TestFlags, outDirectory: String = defaultOutputDir): CompilationTest = {
val callingMethod = getCallingMethod()
val outDir = outDirectory + callingMethod + "/"
val sourceDir = new JFile(f)
Expand All @@ -1185,6 +1185,9 @@ trait ParallelTesting extends RunnerOrchestration { self =>
}

object ParallelTesting {

def defaultOutputDir: String = "../out/"

def isSourceFile(f: JFile): Boolean = {
val name = f.getName
name.endsWith(".scala") || name.endsWith(".java")
Expand Down
1 change: 0 additions & 1 deletion compiler/test/dotty/tools/vulpix/TestConfiguration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package tools
package vulpix

object TestConfiguration {
implicit val defaultOutputDir: String = "../out/"

val noCheckOptions = Array(
"-pagewidth", "120",
Expand Down
7 changes: 7 additions & 0 deletions doc-tool/resources/_layouts/main.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,12 @@
}
});
</script>

<script>
((window.gitter = {}).chat = {}).options = {
room: 'lampepfl/dotty'
};
</script>
<script src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer></script>
</body>
</html>
4 changes: 4 additions & 0 deletions doc-tool/resources/css/dottydoc.css
Original file line number Diff line number Diff line change
Expand Up @@ -270,3 +270,7 @@ aside.success {
border-left: 3px solid #36bf1d;
background-color: #ebfddd;
}

.gitter-open-chat-button {
background-color: gray;
}
2 changes: 1 addition & 1 deletion project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,7 @@ object Build {


sbtPlugin := true,
version := "0.1.5",
version := "0.1.6-SNAPSHOT",
ScriptedPlugin.scriptedSettings,
ScriptedPlugin.sbtTestDirectory := baseDirectory.value / "sbt-test",
ScriptedPlugin.scriptedBufferLog := false,
Expand Down
59 changes: 32 additions & 27 deletions sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dotty.tools.sbtplugin

import sbt._
import sbt.Def.Initialize
import sbt.Keys._
import java.io._
import java.lang.ProcessBuilder
Expand Down Expand Up @@ -196,34 +197,38 @@ object DottyIDEPlugin extends AutoPlugin {
origState
}

private def projectConfigTask(config: Configuration): Initialize[Task[Option[ProjectConfig]]] = Def.task {
if ((sources in config).value.isEmpty) None
else {
// Not needed to generate the config, but this guarantees that the
// generated config is usable by an IDE without any extra compilation
// step.
val _ = (compile in config).value

val id = s"${thisProject.value.id}/${config.name}"
val compilerVersion = (scalaVersion in config).value
val compilerArguments = (scalacOptions in config).value
val sourceDirectories = (unmanagedSourceDirectories in config).value ++ (managedSourceDirectories in config).value
val depClasspath = Attributed.data((dependencyClasspath in config).value)
val classDir = (classDirectory in config).value

Some(new ProjectConfig(
id,
compilerVersion,
compilerArguments.toArray,
sourceDirectories.toArray,
depClasspath.toArray,
classDir
))
}
}

override def projectSettings: Seq[Setting[_]] = Seq(
// Use Def.derive so `projectConfig` is only defined in the configurations where the
// tasks/settings it depends on are defined.
Def.derive(projectConfig := {
if (sources.value.isEmpty) None
else {
// Not needed to generate the config, but this guarantees that the
// generated config is usable by an IDE without any extra compilation
// step.
val _ = compile.value

val id = s"${thisProject.value.id}/${configuration.value.name}"
val compilerVersion = scalaVersion.value
val compilerArguments = scalacOptions.value
val sourceDirectories = unmanagedSourceDirectories.value ++ managedSourceDirectories.value
val depClasspath = Attributed.data(dependencyClasspath.value)
val classDir = classDirectory.value

Some(new ProjectConfig(
id,
compilerVersion,
compilerArguments.toArray,
sourceDirectories.toArray,
depClasspath.toArray,
classDir
))
}
})
// TODO: It would be better to use Def.derive to define projectConfig in
// every configuration where the keys it depends on exist, however this
// currently breaks aggregated tasks: https://github.com/sbt/sbt/issues/3580
projectConfig in Compile := projectConfigTask(Compile).value,
projectConfig in Test := projectConfigTask(Test).value
)

override def buildSettings: Seq[Setting[_]] = Seq(
Expand Down
File renamed without changes.
File renamed without changes.