@Override
 public void visitNode(Tree tree) {
   ListTree<TypeTree> thrownList = ((MethodTree) tree).throwsClauses();
   Set<String> reported = new HashSet<>();
   for (TypeTree typeTree : thrownList) {
     Type symbolType = typeTree.symbolType();
     String fullyQualifiedName = symbolType.fullyQualifiedName();
     if (!reported.contains(fullyQualifiedName)) {
       String superTypeName = isSubclassOfAny(symbolType, thrownList);
       if (superTypeName != null) {
         addIssue(
             typeTree,
             "Remove the declaration of thrown exception '"
                 + fullyQualifiedName
                 + "' which is a subclass of '"
                 + superTypeName
                 + "'.");
       } else if (symbolType.isSubtypeOf("java.lang.RuntimeException")) {
         addIssue(
             typeTree,
             "Remove the declaration of thrown exception '"
                 + fullyQualifiedName
                 + "' which is a runtime exception.");
       } else if (declaredMoreThanOnce(fullyQualifiedName, thrownList)) {
         addIssue(
             typeTree,
             "Remove the redundant '" + fullyQualifiedName + "' thrown exception declaration(s).");
       }
       reported.add(fullyQualifiedName);
     }
   }
 }
 private static String isSubclassOfAny(Type type, ListTree<TypeTree> thrownList) {
   for (TypeTree thrown : thrownList) {
     String name = thrown.symbolType().fullyQualifiedName();
     if (!type.is(name) && type.isSubtypeOf(name)) {
       return name;
     }
   }
   return null;
 }
 private static boolean isSubtypeOf(Type type, Type superType) {
   return type.isSubtypeOf(superType);
 }
 private static boolean isCloseable(Type type) {
   return type.isSubtypeOf(JAVA_IO_AUTO_CLOSEABLE) || type.isSubtypeOf(JAVA_IO_CLOSEABLE);
 }