+(defmacro deep-compare ((left right) &body body)
+ (let ((block (gensym "BLOCK-")) (func (gensym "FUNC-"))
+ (l (gensym "LEFT-")) (r (gensym "RIGHT-")))
+ `(macrolet ((focus (expr &body body)
+ `(flet ((,',func (it) ,expr))
+ (let ((,',l (,',func ,',l))
+ (,',r (,',func ,',r)))
+ ,@body)))
+ (update (expr)
+ `(flet ((,',func (it) ,expr))
+ (psetf ,',l (,',func ,',l)
+ ,',r (,',func ,',r))))
+ (compare (expr)
+ `(cond ((let ((left ,',l) (right ,',r)) ,expr)
+ (return-from ,',block t))
+ ((let ((right ,',l) (left ,',r)) ,expr)
+ (return-from ,',block nil))))
+ (typesw (&rest clauses)
+ (labels ((iter (clauses)
+ (if (null clauses)
+ 'nil
+ (destructuring-bind (type &rest body)
+ (car clauses)
+ (if (eq type t)
+ `(progn ,@body)
+ `(if (typep ,',l ',type)
+ (if (typep ,',r ',type)
+ (progn ,@body)
+ (return-from ,',block t))
+ (if (typep ,',r ',type)
+ (return-from ,',block nil)
+ ,(iter (cdr clauses)))))))))
+ (iter clauses))))
+ (let ((,l ,left) (,r ,right))
+ (block ,block
+ ,@body)))))
+
+(defun order-specializers (la lb)
+ (deep-compare (la lb)
+ (loop (typesw (null (return nil)))
+ (focus (car it)
+ (typesw (eql-specializer
+ (focus (eql-specializer-object it)
+ (typesw (keyword
+ (compare (string< left right)))
+ (symbol
+ (focus (package-name (symbol-package it))
+ (compare (string< left right)))
+ (compare (string< left right)))
+ (t
+ (focus (with-output-to-string (out)
+ (prin1 it out)
+ (write-char #\nul))
+ (compare (string< left right)))))))
+ (class
+ (focus (class-name it)
+ (focus (package-name (symbol-package it))
+ (compare (string< left right)))
+ (compare (string< left right))))
+ (t
+ (error "unexpected things"))))
+ (update (cdr it)))))
+
+(defun analyse-generic-functions (package)
+ (setf package (find-package package))
+ (flet ((function-name-core (name)
+ (typecase name
+ (symbol (values name t))
+ ((cons (eql setf) t) (values (cadr name) t))
+ (t (values nil nil)))))
+ (let ((methods (make-hash-table))
+ (functions (make-hash-table))
+ (externs (make-hash-table)))
+ (dolist (symbol (list-exported-symbols package))
+ (setf (gethash symbol externs) t))
+ (dolist (symbol (list-exported-symbols package))
+ (flet ((dofunc (func)
+ (when (typep func 'generic-function)
+ (setf (gethash func functions) t)
+ (dolist (method (generic-function-methods func))
+ (setf (gethash method methods) t)))))
+ (dofunc (and (fboundp symbol) (fdefinition symbol)))
+ (dofunc (ignore-errors (fdefinition (list 'setf symbol)))))
+ (when (eq (symbol-package symbol) package)
+ (let ((class (find-class symbol nil)))
+ (when class
+ (dolist
+ (func (specializer-direct-generic-functions class))
+ (multiple-value-bind (name knownp)
+ (function-name-core (generic-function-name func))
+ (when (and knownp
+ (or (not (eq (symbol-package name) package))
+ (gethash name externs)))
+ (setf (gethash func functions) t)
+ (dolist (method (specializer-direct-methods class))
+ (setf (gethash method methods) t)))))))))
+ (let ((funclist nil))
+ (maphash (lambda (func value)
+ (declare (ignore value))
+ (push func funclist))
+ functions)
+ (setf funclist (sort funclist
+ (lambda (a b)
+ (let ((core-a (function-name-core a))
+ (core-b (function-name-core b)))
+ (if (eq core-a core-b)
+ (and (atom a) (consp b))
+ (string< core-a core-b))))
+ :key #'generic-function-name))
+ (dolist (function funclist)
+ (let ((name (generic-function-name function)))
+ (etypecase name
+ (symbol
+ (format t "~A~%" (pretty-symbol-name name package)))
+ ((cons (eql setf) t)
+ (format t "(setf ~A)~%"
+ (pretty-symbol-name (cadr name) package)))))
+ (dolist (method (sort (copy-list
+ (generic-function-methods function))
+ #'order-specializers
+ :key #'method-specializers))
+ (when (gethash method methods)
+ (format t "~2T~{~A~^ ~}~%"
+ (mapcar
+ (lambda (spec)
+ (etypecase spec
+ (class
+ (let ((name (class-name spec)))
+ (if (eq name t) "t"
+ (pretty-symbol-name name package))))
+ (eql-specializer
+ (let ((obj (eql-specializer-object spec)))
+ (format nil "(eql ~A)"
+ (if (symbolp obj)
+ (pretty-symbol-name obj package)
+ obj))))))
+ (method-specializers method))))))))))
+
+(defun check-slot-names (package)
+ (setf package (find-package package))
+ (let* ((symbols (list-exported-symbols package))
+ (classes (mapcan (lambda (symbol)
+ (when (eq (symbol-package symbol) package)
+ (let ((class (find-class symbol nil)))
+ (and class (list class)))))
+ symbols))
+ (offenders (mapcan
+ (lambda (class)
+ (let* ((slot-names
+ (mapcar #'slot-definition-name
+ (class-direct-slots class)))
+ (exported (remove-if
+ (lambda (sym)
+ (or (not (symbol-package sym))
+ (and (not (exported-symbol-p
+ sym))
+ (eq (symbol-package sym)
+ package))))
+ slot-names)))
+ (and exported
+ (list (cons (class-name class)
+ exported)))))
+ classes))
+ (bad-words (remove-duplicates (mapcan (lambda (list)
+ (copy-list (cdr list)))
+ offenders))))
+ (values offenders bad-words)))
+