diff options
author | Simon Ochsenreither <simon@ochsenreither.de> | 2012-09-17 13:45:43 +0200 |
---|---|---|
committer | Simon Ochsenreither <simon@ochsenreither.de> | 2012-09-21 18:20:35 +0200 |
commit | 990b3c7682d9b0655518e20274673aade75dbed0 (patch) | |
tree | 5850654506a86fdf3ec63446d7b2008cbe6c4c09 | |
parent | b0a4d536482c6582bafb383a30f553862aceb00f (diff) | |
download | scala-990b3c7682d9b0655518e20274673aade75dbed0.tar.gz scala-990b3c7682d9b0655518e20274673aade75dbed0.tar.bz2 scala-990b3c7682d9b0655518e20274673aade75dbed0.zip |
SI-6380 #1 Add @throws[Exception]
This change allows an additional notation of the @throws annotation:
Old-style: @throws(classOf[Exception])
New-style: @throws[Exception]
The optional String argument moves @throws in line with @deprecated,
@migration, etc. and prevents confusion caused by the default inheritance
of ScalaDoc comments and the non-inheritance of annotations.
Before: /** This method does ...
* @throws IllegalArgumentException if `a` is less than 0. */
@throws(classOf[IllegalArgumentException])
def foo(a: Int) = ...
Now: /** This method does ... */
@throws[IllegalArgumentException]("if `a` is less than 0")
def foo(a: Int) = ...
ScalaDoc @throws tags remain supported for cases where documentation of
thrown exceptions is needed, but are not supposed to be added to the
exception attribute of the class file.
In this commit the necessary compiler support is added.
The code to extract exceptions from annotations is now shared instead
of being duplicated all over the place.
The change is completely source and binary compatible, except that the code
is now enforcing that the type thrown is a subtype of Throwable as mandated
by the JVM spec instead of allowing something like @throws(classOf[String]).
Not in this commit:
- ScalaDoc support to add the String argument to ScalaDoc's exception list
- Adaption of the library
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala | 11 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala | 5 | ||||
-rw-r--r-- | src/library/scala/throws.scala | 6 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/AnnotationInfos.scala | 21 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Definitions.scala | 2 | ||||
-rw-r--r-- | test/files/run/t6380.check | 7 | ||||
-rw-r--r-- | test/files/run/t6380.scala | 20 |
7 files changed, 57 insertions, 15 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 28966eef08..475a0f86ee 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -835,15 +835,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters { * * The contents of that attribute are determined by the `String[] exceptions` argument to ASM's ClassVisitor.visitMethod() * This method returns such list of internal names. - * */ - def getExceptions(excs: List[AnnotationInfo]): List[String] = { - for (AnnotationInfo(tp, List(exc), _) <- excs.distinct if tp.typeSymbol == ThrowsClass) - yield { - val Literal(const) = exc - javaName(const.typeValue.typeSymbol) - } - } + def getExceptions(excs: List[AnnotationInfo]): List[String] = + for (ThrownException(exc) <- excs.distinct) + yield javaName(exc) /** Whether an annotation should be emitted as a Java annotation * .initialize: if 'annot' is read from pickle, atp might be un-initialized diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index 62c281b82f..cb6156c59c 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -606,11 +606,10 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with // put some random value; the actual number is determined at the end buf putShort 0xbaba.toShort - for (AnnotationInfo(tp, List(exc), _) <- excs.distinct if tp.typeSymbol == ThrowsClass) { - val Literal(const) = exc + for (ThrownException(exc) <- excs.distinct) { buf.putShort( cpool.addClass( - javaName(const.typeValue.typeSymbol)).shortValue) + javaName(exc)).shortValue) nattr += 1 } diff --git a/src/library/scala/throws.scala b/src/library/scala/throws.scala index 0aa0d31c9f..02dffb00b0 100644 --- a/src/library/scala/throws.scala +++ b/src/library/scala/throws.scala @@ -14,7 +14,7 @@ package scala * {{{ * class Reader(fname: String) { * private val in = new BufferedReader(new FileReader(fname)) - * @throws(classOf[IOException]) + * @throws[IOException]("if the file doesn't exist") * def read() = in.read() * } * }}} @@ -23,4 +23,6 @@ package scala * @version 1.0, 19/05/2006 * @since 2.1 */ -class throws(clazz: Class[_]) extends scala.annotation.StaticAnnotation +class throws[T <: Throwable](cause: String = "") extends scala.annotation.StaticAnnotation { + def this(clazz: Class[T]) = this() +} diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index 3bd7f4f4fa..46e4329b2e 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -30,7 +30,7 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => /** Symbols of any @throws annotations on this symbol. */ def throwsAnnotations(): List[Symbol] = annotations collect { - case AnnotationInfo(tp, Literal(Constant(tpe: Type)) :: Nil, _) if tp.typeSymbol == ThrowsClass => tpe.typeSymbol + case ThrownException(exc) => exc } /** Tests for, get, or remove an annotation */ @@ -325,4 +325,23 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => implicit val AnnotationTag = ClassTag[AnnotationInfo](classOf[AnnotationInfo]) object UnmappableAnnotation extends CompleteAnnotationInfo(NoType, Nil, Nil) + + /** Extracts symbol of thrown exception from AnnotationInfo. + * + * Supports both “old-style” `@throws(classOf[Exception])` + * as well as “new-stye” `@throws[Exception]("cause")` annotations. + */ + object ThrownException { + def unapply(ann: AnnotationInfo): Option[Symbol] = + ann match { + case AnnotationInfo(tpe, _, _) if tpe.typeSymbol != ThrowsClass => + None + // old-style: @throws(classOf[Exception]) (which is throws[T](classOf[Exception])) + case AnnotationInfo(_, List(Literal(Constant(tpe: Type))), _) => + Some(tpe.typeSymbol) + // new-style: @throws[Exception], @throws[Exception]("cause") + case AnnotationInfo(TypeRef(_, _, args), _, _) => + Some(args.head.typeSymbol) + } + } } diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 48a658192b..9eb20eb6ae 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -948,7 +948,7 @@ trait Definitions extends api.StandardDefinitions { lazy val ScalaNoInlineClass = requiredClass[scala.noinline] lazy val SerialVersionUIDAttr = requiredClass[scala.SerialVersionUID] lazy val SpecializedClass = requiredClass[scala.specialized] - lazy val ThrowsClass = requiredClass[scala.throws] + lazy val ThrowsClass = requiredClass[scala.throws[_]] lazy val TransientAttr = requiredClass[scala.transient] lazy val UncheckedClass = requiredClass[scala.unchecked] lazy val UnspecializedClass = requiredClass[scala.annotation.unspecialized] diff --git a/test/files/run/t6380.check b/test/files/run/t6380.check new file mode 100644 index 0000000000..912525ed66 --- /dev/null +++ b/test/files/run/t6380.check @@ -0,0 +1,7 @@ +List(class java.lang.Exception) +List(class java.lang.Throwable) +List(class java.lang.RuntimeException) +List(class java.lang.IllegalArgumentException, class java.util.NoSuchElementException) +List(class java.lang.IndexOutOfBoundsException, class java.lang.IndexOutOfBoundsException) +List(class java.lang.IllegalStateException, class java.lang.IllegalStateException) +List(class java.lang.NullPointerException, class java.lang.NullPointerException) diff --git a/test/files/run/t6380.scala b/test/files/run/t6380.scala new file mode 100644 index 0000000000..0e264d9175 --- /dev/null +++ b/test/files/run/t6380.scala @@ -0,0 +1,20 @@ +object Test extends App { + classOf[Foo].getDeclaredMethods().sortBy(_.getName).map(_.getExceptionTypes.sortBy(_.getName).toList).toList.foreach(println) +} + +class Foo { + @throws[Exception] + def bar1 = ??? + @throws[Throwable]("always") + def bar2 = ??? + @throws(classOf[RuntimeException]) + def bar3 = ??? + @throws[IllegalArgumentException] @throws[NoSuchElementException] + def bar4 = ??? + @throws(classOf[IndexOutOfBoundsException]) @throws(classOf[IndexOutOfBoundsException]) + def bar5 = ??? + @throws[IllegalStateException]("Cause") @throws[IllegalStateException] + def bar6 = ??? + @throws[NullPointerException]("Cause A") @throws[NullPointerException]("Cause B") + def bar7 = ??? +}
\ No newline at end of file |