I did some searching around and have come with with two solutions. Comments, Suggesstions, improvements are welcome, I've marked this answer as wiki. The first one is based on ScalaBeans and ParaNamer.
def valNamesWithAnnotations[T <: AnyRef](obj : T)(implicit m : Manifest[T]) : List[(String, List[java.lang.annotation.Annotation])] = {
val descriptor = descriptorOf(obj.getClass)
val c = descriptor.beanType.erasure
val constructor: Option[Constructor[_]] = {
if (c.getConstructors().isEmpty) None
else Some(c.getConstructors()(0).asInstanceOf[Constructor[_]])
}
val paranamer = new BytecodeReadingParanamer
val ctorParameterNames = constructor.map(paranamer.lookupParameterNames(_)).getOrElse(scala.Array[String]()).toList
val ctorParamAnnos = constructor.getOrElse(sys.error("Cannot find constructor entry for class " + c.getName)).getParameterAnnotations
val builder = List.newBuilder[(String, List[java.lang.annotation.Annotation])]
val paramIter = ctorParameterNames.iterator
val annoIter = ctorParamAnnos.iterator
while(paramIter.hasNext && annoIter.hasNext ) {
builder += ((paramIter.next, annoIter.next.toList))
}
builder.result
}
The second approach uses ScalaSignatures and is based on this SO answer:
def valNamesWithAnnotations[C: ClassManifest] : List[(String, List[java.lang.annotation.Annotation])] = {
val cls = classManifest[C].erasure
val ctors = cls.getConstructors
assert(ctors.size == 1, "Class " + cls.getName + " should have only one constructor")
val sig = ScalaSigParser.parse(cls).getOrElse(sys.error("No ScalaSig for class " + cls.getName + ", make sure it is a top-level case class"))
val classSymbol = sig.parseEntry(0).asInstanceOf[ClassSymbol]
assert(classSymbol.isCase, "Class " + cls.getName + " is not a case class")
val tableSize = sig.table.size
val ctorIndex = (1 until tableSize).find { i =>
sig.parseEntry(i) match {
case m @ MethodSymbol(SymbolInfo("<init>", owner, _, _, _, _), _) => owner match {
case sym: SymbolInfoSymbol if sym.index == 0 => true
case _ => false
}
case _ => false
}
}.getOrElse(sys.error("Cannot find constructor entry in ScalaSig for class " + cls.getName))
val paramsListBuilder = List.newBuilder[String]
for (i <- (ctorIndex + 1) until tableSize) {
sig.parseEntry(i) match {
case MethodSymbol(SymbolInfo(name, owner, _, _, _, _), _) => owner match {
case sym: SymbolInfoSymbol if sym.index == ctorIndex => paramsListBuilder += name
case _ =>
}
case _ =>
}
}
val paramAnnoArr = ctors(0).getParameterAnnotations
val builder = List.newBuilder[(String, List[java.lang.annotation.Annotation])]
val paramIter = paramsListBuilder.result.iterator
val annoIter = paramAnnoArr.iterator
while(paramIter.hasNext && annoIter.hasNext ) {
builder += ((paramIter.next, annoIter.next.toList))
}
builder.result
}