throws cleanup (#117)

* remove addException code

* switch to using AnnotationSpec for Throws

* fix PR concerns

* make sure overrides are handled correctly
diff --git a/src/main/java/com/squareup/kotlinpoet/FunSpec.kt b/src/main/java/com/squareup/kotlinpoet/FunSpec.kt
index 56088d2..a73ff50 100644
--- a/src/main/java/com/squareup/kotlinpoet/FunSpec.kt
+++ b/src/main/java/com/squareup/kotlinpoet/FunSpec.kt
@@ -299,18 +299,6 @@
     fun addParameter(name: String, type: KClass<*>, vararg modifiers: KModifier)
         = addParameter(name, type.asTypeName(), *modifiers)
 
-    fun addExceptions(exceptions: Iterable<TypeName>) = apply {
-      this.exceptions += exceptions
-    }
-
-    fun addException(exception: TypeName) = apply {
-      exceptions += exception
-    }
-
-    fun addException(exception: Type) = addException(exception.asTypeName())
-
-    fun addException(exception: KClass<*>) = addException(exception.asTypeName())
-
     fun addCode(format: String, vararg args: Any) = apply {
       code.add(format, *args)
     }
@@ -413,8 +401,11 @@
             .build()
       }
 
-      for (thrownType in method.thrownTypes) {
-        funBuilder.addException(thrownType.asTypeName())
+      if(method.thrownTypes.isNotEmpty()) {
+        val throwsValueString = method.thrownTypes.joinToString { "%T::class" }
+        funBuilder.addAnnotation(AnnotationSpec.builder(Throws::class)
+            .addMember("value", throwsValueString, *method.thrownTypes.toTypedArray())
+            .build())
       }
 
       return funBuilder
diff --git a/src/test/java/com/squareup/kotlinpoet/FunSpecTest.kt b/src/test/java/com/squareup/kotlinpoet/FunSpecTest.kt
index 2baba19..1d64e55 100644
--- a/src/test/java/com/squareup/kotlinpoet/FunSpecTest.kt
+++ b/src/test/java/com/squareup/kotlinpoet/FunSpecTest.kt
@@ -87,9 +87,9 @@
     val methodElement = getOnlyElement(methodsIn(classElement.enclosedElements))
     val funSpec = FunSpec.overriding(methodElement).build()
     assertThat(funSpec.toString()).isEqualTo("""
+        |@kotlin.jvm.Throws(java.io.IOException::class, java.lang.SecurityException::class)
         |protected override fun <T : java.lang.Runnable & java.io.Closeable> everything(arg0: java.lang.String,
-        |    arg1: java.util.List<out T>): java.lang.Runnable throws java.io.IOException,
-        |    java.lang.SecurityException {
+        |    arg1: java.util.List<out T>): java.lang.Runnable {
         |}
         |""".trimMargin())
   }
@@ -111,7 +111,8 @@
     var exec = findFirst(methods, "call")
     var funSpec = FunSpec.overriding(exec, classType, types).build()
     assertThat(funSpec.toString()).isEqualTo("""
-        |override fun call(): java.lang.Integer throws java.lang.Exception {
+        |@kotlin.jvm.Throws(java.lang.Exception::class)
+        |override fun call(): java.lang.Integer {
         |}
         |""".trimMargin())
     exec = findFirst(methods, "compareTo")
@@ -281,19 +282,5 @@
     assertThat(a.hashCode()).isEqualTo(b.hashCode())
   }
 
-  @Test fun duplicateExceptionsIgnored() {
-    val ioException = IOException::class.asClassName()
-    val timeoutException = TimeoutException::class.asClassName()
-    val funSpec = FunSpec.builder("duplicateExceptions")
-        .addException(ioException)
-        .addException(timeoutException)
-        .addException(timeoutException)
-        .addException(ioException)
-        .build()
-    assertThat(funSpec.exceptions).isEqualTo(Arrays.asList(ioException, timeoutException))
-    assertThat(funSpec.toBuilder().addException(ioException).build().exceptions)
-        .isEqualTo(Arrays.asList(ioException, timeoutException))
-  }
-
   private fun whenMock(any: Any?) = Mockito.`when`(any)
 }
diff --git a/src/test/java/com/squareup/kotlinpoet/TypeSpecTest.kt b/src/test/java/com/squareup/kotlinpoet/TypeSpecTest.kt
index 3e99242..617d793 100644
--- a/src/test/java/com/squareup/kotlinpoet/TypeSpecTest.kt
+++ b/src/test/java/com/squareup/kotlinpoet/TypeSpecTest.kt
@@ -474,36 +474,49 @@
     val taco = TypeSpec.classBuilder("Taco")
         .addModifiers(KModifier.ABSTRACT)
         .addFun(FunSpec.builder("throwOne")
-            .addException(IOException::class)
+            .addAnnotation(AnnotationSpec.builder(Throws::class)
+                .addMember("value", "%T::class", IOException::class.asClassName())
+                .build())
             .build())
         .addFun(FunSpec.builder("throwTwo")
-            .addException(IOException::class)
-            .addException(ClassName(tacosPackage, "SourCreamException"))
+            .addAnnotation(AnnotationSpec.builder(Throws::class)
+                .addMember("value", "%T::class, %T::class",
+                    IOException::class.asClassName(), ClassName(tacosPackage, "SourCreamException"))
+                .build())
             .build())
         .addFun(FunSpec.builder("abstractThrow")
             .addModifiers(KModifier.ABSTRACT)
-            .addException(IOException::class)
+            .addAnnotation(AnnotationSpec.builder(Throws::class)
+                .addMember("value", "%T::class", IOException::class.asClassName())
+                .build())
             .build())
         .addFun(FunSpec.builder("nativeThrow")
             .addModifiers(KModifier.EXTERNAL)
-            .addException(IOException::class)
+            .addAnnotation(AnnotationSpec.builder(Throws::class)
+                .addMember("value", "%T::class", IOException::class.asClassName())
+                .build())
             .build())
         .build()
     assertThat(toString(taco)).isEqualTo("""
         |package com.squareup.tacos
         |
         |import java.io.IOException
+        |import kotlin.jvm.Throws
         |
         |abstract class Taco {
-        |  fun throwOne() throws IOException {
+        |  @Throws(IOException::class)
+        |  fun throwOne() {
         |  }
         |
-        |  fun throwTwo() throws IOException, SourCreamException {
+        |  @Throws(IOException::class, SourCreamException::class)
+        |  fun throwTwo() {
         |  }
         |
-        |  abstract fun abstractThrow() throws IOException
+        |  @Throws(IOException::class)
+        |  abstract fun abstractThrow()
         |
-        |  external fun nativeThrow() throws IOException
+        |  @Throws(IOException::class)
+        |  external fun nativeThrow()
         |}
         |""".trimMargin())
   }