summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-01-02 11:48:22 -0800
committerPaul Phillips <paulp@improving.org>2012-01-09 21:31:45 -0800
commit2064372659156e2637eac4d092321818a30abb41 (patch)
treebaa2394fadad02a6426c076f249f6856eeadeb4c /src
parent820491ed6376e9f8f8a8102387495113dce55444 (diff)
downloadscala-2064372659156e2637eac4d092321818a30abb41.tar.gz
scala-2064372659156e2637eac4d092321818a30abb41.tar.bz2
scala-2064372659156e2637eac4d092321818a30abb41.zip
Better error reporting regarding main methods.
Now notices most things which look like main methods and says something useful if they aren't usable as entry points. Closes SI-4749.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/reflect/internal/Definitions.scala17
-rw-r--r--src/compiler/scala/reflect/internal/transform/Erasure.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala68
3 files changed, 54 insertions, 33 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala
index d3af8e2623..cc6ba32f9c 100644
--- a/src/compiler/scala/reflect/internal/Definitions.scala
+++ b/src/compiler/scala/reflect/internal/Definitions.scala
@@ -443,15 +443,11 @@ trait Definitions extends reflect.api.StandardDefinitions {
def isStringAddition(sym: Symbol) = sym == String_+ || sym == StringAdd_+
def isArrowAssoc(sym: Symbol) = ArrowAssocClass.tpe.decls.toList contains sym
- // The given symbol is a method with the right signature to be a runnable java program.
- def isJavaMainMethod(sym: Symbol) = sym.tpe match {
- case MethodType(param :: Nil, restpe) if restpe.typeSymbol == UnitClass =>
- param.tpe match {
- case TypeRef(_, ArrayClass, arg :: Nil) => arg.typeSymbol == StringClass
- case _ => false
- }
- case _ => false
- }
+ // The given symbol is a method with the right name and signature to be a runnable java program.
+ def isJavaMainMethod(sym: Symbol) = (sym.name == nme.main) && (sym.info match {
+ case MethodType(p :: Nil, restpe) => isArrayOfSymbol(p.tpe, StringClass) && restpe.typeSymbol == UnitClass
+ case _ => false
+ })
// The given class has a main method.
def hasJavaMainMethod(sym: Symbol): Boolean =
(sym.tpe member nme.main).alternatives exists isJavaMainMethod
@@ -595,6 +591,9 @@ trait Definitions extends reflect.api.StandardDefinitions {
def byNameType(arg: Type) = appliedType(ByNameParamClass.typeConstructor, List(arg))
def iteratorOfType(tp: Type) = appliedType(IteratorClass.typeConstructor, List(tp))
+ lazy val StringArray = arrayType(StringClass.tpe)
+ lazy val ObjectArray = arrayType(ObjectClass.tpe)
+
def ClassType(arg: Type) =
if (phase.erasedTypes || forMSIL) ClassClass.tpe
else appliedType(ClassClass.typeConstructor, List(arg))
diff --git a/src/compiler/scala/reflect/internal/transform/Erasure.scala b/src/compiler/scala/reflect/internal/transform/Erasure.scala
index c8cb6febfa..1e0ba17e15 100644
--- a/src/compiler/scala/reflect/internal/transform/Erasure.scala
+++ b/src/compiler/scala/reflect/internal/transform/Erasure.scala
@@ -75,7 +75,7 @@ trait Erasure {
case TypeRef(pre, sym, args) =>
if (sym == ArrayClass)
if (unboundedGenericArrayLevel(tp) == 1) ObjectClass.tpe
- else if (args.head.typeSymbol.isBottomClass) arrayType(ObjectClass.tpe)
+ else if (args.head.typeSymbol.isBottomClass) ObjectArray
else typeRef(apply(pre), sym, args map this)
else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass || sym == NotNullClass) erasedTypeRef(ObjectClass)
else if (sym == UnitClass) erasedTypeRef(BoxedUnitClass)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index 03d1bc3ad2..4ec3ef5ae6 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -30,13 +30,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
import global._
import icodes._
import icodes.opcodes._
- import definitions.{
- AnyClass, ObjectClass, ThrowsClass, ThrowableClass, ClassfileAnnotationClass,
- SerializableClass, StringClass, ClassClass, FunctionClass,
- DeprecatedAttr, SerializableAttr, SerialVersionUIDAttr, VolatileAttr,
- TransientAttr, CloneableAttr, RemoteAttr, JavaCloneableClass,
- RemoteInterfaceClass, RemoteExceptionClass, hasJavaMainMethod
- }
+ import definitions._
val phaseName = "jvm"
@@ -68,10 +62,10 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
def isJavaEntryPoint(clasz: IClass) = {
val sym = clasz.symbol
- def fail(msg: String) = {
+ def fail(msg: String, pos: Position = sym.pos) = {
clasz.cunit.warning(sym.pos,
- sym.name + " has a main method, but " + sym.fullName('.') + " will not be a runnable program.\n" +
- " " + msg + ", which means no static forwarder can be generated.\n"
+ sym.name + " has a main method with parameter type Array[String], but " + sym.fullName('.') + " will not be a runnable program.\n" +
+ " Reason: " + msg
// TODO: make this next claim true, if possible
// by generating valid main methods as static in module classes
// not sure what the jvm allows here
@@ -79,19 +73,47 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
)
false
}
- sym.hasModuleFlag && hasJavaMainMethod(sym) && {
- // At this point we've seen a module with a main method, so if this
- // doesn't turn out to be a valid entry point, issue a warning.
- val companion = sym.linkedClassOfClass
- if (companion.isTrait)
- fail("Its companion is a trait")
- else if (hasJavaMainMethod(companion) && !(sym isSubClass companion))
- fail("Its companion contains its own main method")
- // this is only because forwarders aren't smart enough yet
- else if (companion.tpe.member(nme.main) != NoSymbol)
- fail("Its companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)")
- else
- true
+ def failNoForwarder(msg: String) = {
+ fail(msg + ", which means no static forwarder can be generated.\n")
+ }
+ val possibles = if (sym.hasModuleFlag) (sym.tpe nonPrivateMember nme.main).alternatives else Nil
+ val hasApproximate = possibles exists { m =>
+ m.info match {
+ case MethodType(p :: Nil, _) => p.tpe.typeSymbol == ArrayClass
+ case _ => false
+ }
+ }
+ // At this point it's a module with a main-looking method, so either
+ // succeed or warn that it isn't.
+ hasApproximate && {
+ // Before erasure so we can identify generic mains.
+ atPhase(currentRun.erasurePhase) {
+ val companion = sym.linkedClassOfClass
+ val companionMain = companion.tpe.member(nme.main)
+
+ if (hasJavaMainMethod(companion))
+ failNoForwarder("companion contains its own main method")
+ else if (companion.tpe.member(nme.main) != NoSymbol)
+ // this is only because forwarders aren't smart enough yet
+ failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)")
+ else if (companion.isTrait)
+ failNoForwarder("companion is a trait")
+ // Now either succeeed, or issue some additional warnings for things which look like
+ // attempts to be java main methods.
+ else possibles exists { m =>
+ m.info match {
+ case PolyType(_, _) =>
+ fail("main methods cannot be generic.")
+ case MethodType(params, res) =>
+ if (res.typeSymbol :: params exists (_.isAbstractType))
+ fail("main methods cannot refer to type parameters or abstract types.", m.pos)
+ else
+ isJavaMainMethod(m) || fail("main method must have exact signature (Array[String])Unit", m.pos)
+ case tp =>
+ fail("don't know what this is: " + tp, m.pos)
+ }
+ }
+ }
}
}