1.0.30.20: less DEFGENERIC clobbers FTYPE STYLE-WARNINGS
[sbcl.git] / src / code / describe.lisp
1 ;;;; the DESCRIBE system
2
3 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; more information.
5 ;;;;
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
11
12 ;;; SB-IMPL, not SB!IMPL, since we're built in warm load.
13 (in-package "SB-IMPL")
14
15 ;;;; Utils, move elsewhere.
16
17 (defun class-name-or-class (class)
18   (let ((name (class-name class)))
19     (if (eq class (find-class name nil))
20         name
21         class)))
22
23 (defun fun-name (x)
24   (if (typep x 'generic-function)
25       (sb-pcl:generic-function-name x)
26       (%fun-name x)))
27
28 ;;; Prints X on a single line, limiting output length by *PRINT-RIGHT-MARGIN*
29 ;;; -- good for printing object parts, etc.
30 (defun prin1-to-line (x &key (columns 1) (reserve 0))
31   (let* ((line (write-to-string x :escape t :readably nil :lines 2 :circle t))
32          (p (position #\newline line))
33          (limit (truncate (- *print-right-margin* reserve) columns)))
34     (flet ((trunc (&optional end)
35              (let ((line-end (- limit 2)))
36                (with-output-to-string (s)
37                  (write-string line s :end (if end
38                                                (min end line-end)
39                                                line-end))
40                  (write-string ".." s)))))
41       (cond (p
42              (trunc p))
43             ((> (length line) limit)
44              (trunc))
45             (t
46              line)))))
47
48 (defun describe (object &optional (stream-designator *standard-output*))
49   #+sb-doc
50   "Print a description of OBJECT to STREAM-DESIGNATOR."
51   (let ((stream (out-synonym-of stream-designator))
52         (*print-right-margin* (or *print-right-margin* 72)))
53     ;; Until sbcl-0.8.0.x, we did
54     ;;   (FRESH-LINE STREAM)
55     ;;   (PPRINT-LOGICAL-BLOCK (STREAM NIL)
56     ;;     ...
57     ;; here. However, ANSI's specification of DEFUN DESCRIBE,
58     ;;   DESCRIBE exists as an interface primarily to manage argument
59     ;;   defaulting (including conversion of arguments T and NIL into
60     ;;   stream objects) and to inhibit any return values from
61     ;;   DESCRIBE-OBJECT.
62     ;; doesn't mention either FRESH-LINEing or PPRINT-LOGICAL-BLOCKing,
63     ;; and the example of typical DESCRIBE-OBJECT behavior in ANSI's
64     ;; specification of DESCRIBE-OBJECT will work poorly if we do them
65     ;; here. (The example method for DESCRIBE-OBJECT does its own
66     ;; FRESH-LINEing, which is a physical directive which works poorly
67     ;; inside a pretty-printer logical block.)
68     (describe-object object stream)
69     ;; We don't TERPRI here either (any more since sbcl-0.8.0.x), because
70     ;; again ANSI's specification of DESCRIBE doesn't mention it and
71     ;; ANSI's example of DESCRIBE-OBJECT does its own final TERPRI.
72     (values)))
73 \f
74 ;;;; DESCRIBE-OBJECT
75 ;;;;
76 ;;;; Style guide:
77 ;;;;
78 ;;;; * Each interesting class has a primary method of its own.
79 ;;;;
80 ;;;; * Output looks like
81 ;;;;
82 ;;;;    object-self-string
83 ;;;;      [object-type-string]
84 ;;;;
85 ;;;;    Block1:
86 ;;;;      Sublabel1: text
87 ;;;;      Sublabel2: text
88 ;;;;
89 ;;;;    Block2:
90 ;;;;      ...
91 ;;;;
92 ;;;; * The newline policy that gets the whitespace right is for
93 ;;;;   each block to both start and end with a newline.
94
95 (defgeneric object-self-string (x))
96
97 (defmethod object-self-string (x)
98   (prin1-to-line x))
99
100 (defmethod object-self-string ((x symbol))
101   (let ((*package* (find-package :keyword)))
102     (prin1-to-string x)))
103
104 (defgeneric object-type-string (x))
105
106 (defmethod object-type-string (x)
107   (let ((type (class-name-or-class (class-of x))))
108     (if (symbolp type)
109         (string-downcase type)
110         (prin1-to-string type))))
111
112 (defmethod object-type-string ((x cons))
113   (if (listp (cdr x)) "list" "cons"))
114
115 (defmethod object-type-string ((x hash-table))
116   "hash-table")
117
118 (defmethod object-type-string ((x condition))
119   "condition")
120
121 (defmethod object-type-string ((x structure-object))
122   "structure-object")
123
124 (defmethod object-type-string ((x standard-object))
125   "standard-object")
126
127 (defmethod object-type-string ((x function))
128   (typecase x
129     (simple-fun "compiled function")
130     (closure "compiled closure")
131     #+sb-eval
132     (sb-eval:interpreted-function
133      "interpreted function")
134     (generic-function
135      "generic-function")
136     (t
137      "funcallable-instance")))
138
139 (defmethod object-type-string ((x stream))
140   "stream")
141
142 (defmethod object-type-string ((x sb-gray:fundamental-stream))
143   "gray stream")
144
145 (defmethod object-type-string ((x package))
146   "package")
147
148 (defmethod object-type-string ((x array))
149   (cond ((or (stringp x) (bit-vector-p x))
150          (format nil "~@[simple-~*~]~A"
151                  (typep x 'simple-array)
152                  (typecase x
153                    (base-string "base-string")
154                    (string "string")
155                    (t "bit-vector"))))
156         (t
157          (if (simple-vector-p x)
158              "simple-vector"
159              (format nil "~@[simple ~*~]~@[specialized ~*~]~:[array~;vector~]"
160                      (typep x 'simple-array)
161                      (neq t (array-element-type x))
162                      (vectorp x))))))
163
164 (defmethod object-type-string ((x character))
165   (typecase x
166     (standard-char "standard-char")
167     (base-char "base-char")
168     (t "character")))
169
170 (defgeneric describe-object (x stream))
171
172 (defvar *in-describe* nil)
173
174 (defmethod describe-object :around (x s)
175   (cond (*in-describe*
176          (call-next-method))
177         (t
178          (format s "~&~A~%  [~A]~%"
179                  (object-self-string x)
180                  (object-type-string x))
181          (pprint-logical-block (s nil)
182            (call-next-method x s)))))
183
184 ;;; Catch-all.
185 (defmethod describe-object ((x t) s)
186   (values))
187
188 (defmethod describe-object ((x cons) s)
189   (describe-function x nil s))
190
191 (defmethod describe-object ((x function) s)
192   (describe-function nil x s))
193
194 (defmethod describe-object ((x class) s)
195   (describe-class nil x s)
196   (describe-instance x s))
197
198 (defmethod describe-object ((x sb-pcl::slot-object) s)
199   (describe-instance x s))
200
201 (defmethod describe-object ((x character) s)
202   (format s "~%:_Char-code: ~S" (char-code x))
203   (format s "~%:_Char-name: ~A~%_" (char-name x)))
204
205 (defmethod describe-object ((x array) s)
206   (format s "~%Element-type: ~S" (array-element-type x))
207   (if (vectorp x)
208       (if (array-has-fill-pointer-p x)
209           (format s "~%Fill-pointer: ~S~%Size: ~S"
210                   (fill-pointer x)
211                   (array-total-size x))
212           (format s "~%Length: ~S" (length x)))
213       (format s "~%Dimensions: ~S" (array-dimensions x)))
214   (let ((*print-array* nil))
215     (unless (typep x 'simple-array)
216       (format s "~%Adjustable: ~A" (if (adjustable-array-p x) "yes" "no"))
217       (multiple-value-bind (to offset) (array-displacement x)
218         (if (format s "~%Displaced-to: ~A~%Displaced-offset: ~S"
219                     (prin1-to-line to)
220                     offset)
221             (format s "~%Displaced: no"))))
222     (when (and (not (array-displacement x)) (array-header-p x))
223       (format s "~%Storage vector: ~A"
224               (prin1-to-line (array-storage-vector x))))
225     (terpri s)))
226
227 (defmethod describe-object ((x hash-table) s)
228   ;; Don't print things which are already apparent from the printed representation
229   ;; -- COUNT, TEST, and WEAKNESS
230   (format s "~%Occupancy: ~,1F" (float (/ (hash-table-count x) (hash-table-size x))))
231   (format s "~%Rehash-threshold: ~S" (hash-table-rehash-threshold x))
232   (format s "~%Rehash-size: ~S" (hash-table-rehash-size x))
233   (format s "~%Size: ~S" (hash-table-size x))
234   (format s "~%Synchronized: ~A" (if (hash-table-synchronized-p x) "yes" "no"))
235   (terpri s))
236
237 (defmethod describe-object ((symbol symbol) stream)
238   ;; Describe the value cell.
239   (let* ((kind (info :variable :kind symbol))
240          (wot (ecase kind
241                 (:special "a special variable")
242                 (:macro "a symbol macro")
243                 (:constant "a constant variable")
244                 (:global "a global variable")
245                 (:unknown "an undefined variable")
246                 (:alien "an alien variable"))))
247     (when (or (not (eq :unknown kind)) (boundp symbol))
248       (pprint-logical-block (stream nil)
249         (format stream "~%~A names ~A:" symbol wot)
250         (pprint-indent :block 2 stream)
251         (when (eq (info :variable :where-from symbol) :declared)
252           (format stream "~@:_Declared type: ~S"
253                   (type-specifier (info :variable :type symbol))))
254         (cond
255           ((eq kind :alien)
256            (let ((info (info :variable :alien-info symbol)))
257              (format stream "~@:_Value: ~S" (eval symbol))
258              (format stream "~@:_Type: ~S"
259                      (sb-alien-internals:unparse-alien-type
260                       (sb-alien::heap-alien-info-type info)))
261              (format stream "~@:_Address: #x~8,'0X"
262                      (sap-int (eval (sb-alien::heap-alien-info-sap-form info))))))
263           ((eq kind :macro)
264            (let ((expansion (info :variable :macro-expansion symbol)))
265              (format stream "~@:_Expansion: ~S" expansion)))
266           ((boundp symbol)
267            (format stream "~:@_Value: ~S" (symbol-value symbol)))
268           ((not (eq kind :unknown))
269            (format stream "~:@_Currently unbound.")))
270         (describe-documentation symbol 'variable stream)
271         (terpri stream))))
272
273   ;; TODO: We could grovel over all packages looking for and
274   ;; reporting other phenomena, e.g. IMPORT and SHADOW, or
275   ;; availability in some package even after (SYMBOL-PACKAGE SYMBOL) has
276   ;; been set to NIL.
277   ;;
278   ;; TODO: It might also be nice to describe (find-package symbol)
279   ;; if one exists. Maybe not all the exports, etc, but the package
280   ;; documentation.
281   (describe-function symbol nil stream)
282   (describe-class symbol nil stream)
283
284   ;; Type specifier
285   (let* ((kind (info :type :kind symbol))
286          (fun (case kind
287                 (:defined
288                  (or (info :type :expander symbol) t))
289                 (:primitive
290                  (or (info :type :translator symbol) t)))))
291     (when fun
292       (pprint-newline :mandatory stream)
293       (pprint-logical-block (stream nil)
294         (pprint-indent :block 2 stream)
295         (format stream "~A names a ~@[primitive~* ~]type-specifier:"
296                 symbol
297                 (eq kind :primitive))
298         (describe-documentation symbol 'type stream (eq t fun))
299         (unless (eq t fun)
300           (describe-lambda-list (if (eq :primitive kind)
301                                     (%fun-lambda-list fun)
302                                     (info :type :lambda-list symbol))
303                                 stream)
304           (when (eq (%fun-fun fun) (%fun-fun (constant-type-expander t)))
305             (format stream "~@:_Expansion: ~S" (funcall fun (list symbol))))))
306       (terpri stream)))
307
308   ;; Print out properties.
309   (let ((plist (symbol-plist symbol)))
310     (when plist
311       (pprint-logical-block (stream nil)
312         (format stream "~%Symbol-plist:")
313         (pprint-indent :block 2 stream)
314         (sb-pcl::doplist (key value) plist
315           (format stream "~@:_~A -> ~A"
316                   (prin1-to-line key :columns 2 :reserve 5)
317                   (prin1-to-line value :columns 2 :reserve 5))))
318       (terpri stream))))
319
320 (defmethod describe-object ((package package) stream)
321   (describe-documentation package t stream)
322   (flet ((humanize (list)
323            (sort (mapcar (lambda (x)
324                            (if (packagep x)
325                                (package-name x)
326                                x))
327                          list)
328                  #'string<))
329          (out (label list)
330            (describe-stuff label list stream :escape nil)))
331     (let ((implemented (humanize (package-implemented-by-list package)))
332           (implements (humanize (package-implements-list package)))
333           (nicks (humanize (package-nicknames package)))
334           (uses (humanize (package-use-list package)))
335           (used (humanize (package-used-by-list package)))
336           (shadows (humanize (package-shadowing-symbols package)))
337           (this (list (package-name package)))
338           (exports nil))
339       (do-external-symbols (ext package)
340         (push ext exports))
341       (setf exports (humanize exports))
342       (when (package-locked-p package)
343         (format stream "~@:_Locked."))
344       (when (set-difference implemented this :test #'string=)
345         (out "Implemented-by-list" implemented))
346       (when (set-difference implements this :test #'string=)
347         (out "Implements-list" implements))
348       (out "Nicknames" nicks)
349       (out "Use-list" uses)
350       (out "Used-by-list" used)
351       (out "Shadows" shadows)
352       (out "Exports" exports)
353       (format stream "~@:_~S internal symbols."
354               (package-internal-symbol-count package))))
355   (terpri stream))
356 \f
357 ;;;; Helpers to deal with shared functionality
358
359 (defun describe-class (name class stream)
360   (let* ((by-name (not class))
361          (name (if class (class-name class) name))
362          (class (if class class (find-class name nil))))
363     (when class
364       (let ((metaclass-name (class-name (class-of class))))
365         (pprint-logical-block (stream nil)
366           (when by-name
367             (format stream "~%~A names the ~(~A~) ~S:"
368                     name
369                     metaclass-name
370                     class)
371             (pprint-indent :block 2 stream))
372           (describe-documentation class t stream)
373           (when (sb-mop:class-finalized-p class)
374             (describe-stuff "Class precedence-list"
375                             (mapcar #'class-name-or-class (sb-mop:class-precedence-list class))
376                             stream))
377           (describe-stuff "Direct superclasses"
378                           (mapcar #'class-name-or-class (sb-mop:class-direct-superclasses class))
379                           stream)
380           (let ((subs (mapcar #'class-name-or-class (sb-mop:class-direct-subclasses class))))
381             (if subs
382                 (describe-stuff "Direct subclasses" subs stream)
383                 (format stream "~@:_No subclasses.")))
384           (unless (sb-mop:class-finalized-p class)
385             (format stream "~@:_Not yet finalized."))
386           (if (eq 'structure-class metaclass-name)
387               (let* ((dd (find-defstruct-description name))
388                      (slots (dd-slots dd)))
389                 (if slots
390                     (format stream "~@:_Slots:~:{~@:_  ~S~
391                                     ~@:_    Type: ~A ~@[~A~]~
392                                     ~@:_    Initform: ~S~}"
393                             (mapcar (lambda (dsd)
394                                       (list
395                                        (dsd-name dsd)
396                                        (dsd-type dsd)
397                                        (unless (eq t (dsd-raw-type dsd))
398                                          "(unboxed)")
399                                        (dsd-default dsd)))
400                                     slots))
401                     (format stream "~@:_No slots.")))
402               (let ((slots (sb-mop:class-direct-slots class)))
403                 (if slots
404                     (format stream "~@:_Direct slots:~:{~@:_  ~S~
405                                     ~@[~@:_    Type: ~S~]~
406                                     ~@[~@:_    Allocation: ~S~]~
407                                     ~@[~@:_    Initargs: ~{~S~^, ~}~]~
408                                     ~@[~@:_    Initform: ~S~]~
409                                     ~@[~@:_    Readers: ~{~S~^, ~}~]~
410                                     ~@[~@:_    Writers: ~{~S~^, ~}~]~
411                                     ~@[~@:_    Documentation:~@:_     ~@<~@;~A~:>~]~}"
412                             (mapcar (lambda (slotd)
413                                       (list (sb-mop:slot-definition-name slotd)
414                                             (let ((type (sb-mop:slot-definition-type slotd)))
415                                               (unless (eq t type) type))
416                                             (let ((alloc (sb-mop:slot-definition-allocation slotd)))
417                                               (unless (eq :instance alloc) alloc))
418                                             (sb-mop:slot-definition-initargs slotd)
419                                             (sb-mop:slot-definition-initform slotd)
420                                             (sb-mop:slot-definition-readers slotd)
421                                             (sb-mop:slot-definition-writers slotd)
422                                             ;; FIXME: does this get the prefix right?
423                                             (quiet-doc slotd t)))
424                                     slots))
425                     (format stream "~@:_No direct slots."))))
426           (pprint-newline :mandatory stream))))))
427
428 (defun describe-instance (object stream)
429   (let* ((class (class-of object))
430          (slotds (sb-mop:class-slots class))
431          (max-slot-name-length 0)
432          (plist nil))
433
434     ;; Figure out a good width for the slot-name column.
435     (flet ((adjust-slot-name-length (name)
436              (setf max-slot-name-length
437                    (max max-slot-name-length (length (symbol-name name))))))
438       (dolist (slotd slotds)
439         (adjust-slot-name-length (sb-mop:slot-definition-name slotd))
440         (push slotd (getf plist (sb-mop:slot-definition-allocation slotd))))
441       (setf max-slot-name-length  (min (+ max-slot-name-length 3) 30)))
442
443     ;; Now that we know the width, we can print.
444     (flet ((describe-slot (name value)
445              (format stream "~%  ~A~VT = ~A" name max-slot-name-length
446                      (prin1-to-line value))))
447       (sb-pcl::doplist (allocation slots) plist
448         (format stream "~%Slots with ~S allocation:" allocation)
449         (dolist (slotd (nreverse slots))
450           (describe-slot
451            (sb-mop:slot-definition-name slotd)
452            (sb-pcl::slot-value-or-default object (sb-mop:slot-definition-name slotd))))))
453     (unless slotds
454       (format stream "~@:_No slots."))
455     (terpri stream)))
456
457 (defun quiet-doc (object type)
458   (handler-bind ((warning #'muffle-warning))
459     (documentation object type)))
460
461 (defun describe-documentation (object type stream &optional undoc newline)
462   (let ((doc (quiet-doc object type)))
463     (cond (doc
464            (format stream "~@:_Documentation:~@:_")
465            (pprint-logical-block (stream nil :per-line-prefix "  ")
466              (princ doc stream)))
467           (undoc
468            (format stream "~@:_(undocumented)")))
469     (when newline
470       (pprint-newline :mandatory stream))))
471
472 (defun describe-stuff (label list stream &key (escape t))
473   (when list
474     (if escape
475         (format stream "~@:_~A:~@<~;~{ ~S~^,~:_~}~;~:>" label list)
476         (format stream "~@:_~A:~@<~;~{ ~A~^,~:_~}~;~:>" label list))))
477
478 (defun describe-lambda-list (lambda-list stream)
479   (format stream "~@:_Lambda-list: ~:A" lambda-list))
480
481 (defun describe-function-source (function stream)
482   (if (compiled-function-p function)
483       (let* ((code (fun-code-header (%fun-fun function)))
484              (info (sb-kernel:%code-debug-info code)))
485         (when info
486           (let ((source (sb-c::debug-info-source info)))
487             (when source
488               (let ((namestring (sb-c::debug-source-namestring source)))
489                 ;; This used to also report the times the source was created
490                 ;; and compiled, but that seems more like noise than useful
491                 ;; information -- but FWIW that are to be had as
492                 ;; SB-C::DEBUG-SOUCE-CREATED/COMPILED.
493                 (cond (namestring
494                        (format stream "~@:_Source file: ~A" namestring))
495                       ((sb-di:debug-source-form source)
496                        (format stream "~@:_Source form:~@:_  ~S"
497                                (sb-di:debug-source-form source)))
498                       (t (bug "Don't know how to use a DEBUG-SOURCE without ~
499                                a namestring or a form."))))))))
500       #+sb-eval
501       (let ((source (sb-eval:interpreted-function-source-location function)))
502         (when source
503           (let ((namestring (sb-c:definition-source-location-namestring source)))
504             (when namestring
505               (format stream "~@:_Source file: ~A" namestring)))))))
506
507 (defun describe-function (name function stream)
508   (let ((name (if function (fun-name function) name)))
509     (if (not (or function (and (legal-fun-name-p name) (fboundp name))))
510         ;; Not defined, but possibly the type is declared, or we have
511         ;; compiled calls to it.
512         (when (legal-fun-name-p name)
513           (multiple-value-bind (from sure) (info :function :where-from name)
514             (when (or (eq :declared from) (and sure (eq :assumed from)))
515               (pprint-logical-block (stream nil)
516                 (format stream "~%~A names an undefined function" name)
517                 (pprint-indent :block 2 stream)
518                 (format stream "~@:_~:(~A~) type: ~S"
519                         from
520                         (type-specifier (info :function :type name)))))))
521         ;; Defined.
522         (multiple-value-bind (fun what lambda-list ftype from inline
523                                   methods)
524             (cond ((and (not function) (symbolp name) (special-operator-p name))
525                    (let ((fun (symbol-function name)))
526                      (values fun "a special operator" (%fun-lambda-list fun))))
527                   ((and (not function) (symbolp name) (macro-function name))
528                    (let ((fun (macro-function name)))
529                      (values fun "a macro" (%fun-lambda-list fun))))
530                   (t
531                    (let ((fun (or function (fdefinition name))))
532                      (multiple-value-bind (ftype from)
533                          (if function
534                              (values (%fun-type function) "Derived")
535                              (let ((ctype (info :function :type name)))
536                                (values (when ctype (type-specifier ctype))
537                                        (when ctype
538                                          ;; Ensure lazy pickup of information
539                                          ;; from methods.
540                                          (sb-c::maybe-update-info-for-gf name)
541                                          (ecase (info :function :where-from name)
542                                            (:declared "Declared")
543                                            ;; This is hopefully clearer to users
544                                            ((:defined-method :defined) "Derived"))))))
545                        (if (typep fun 'generic-function)
546                            (values fun
547                                    "a generic function"
548                                    (sb-mop:generic-function-lambda-list fun)
549                                    ftype
550                                    from
551                                    nil
552                                    (or (sb-mop:generic-function-methods fun)
553                                        :none))
554                            (values fun
555                                    (if (compiled-function-p fun)
556                                        "a compiled function"
557                                        "an interpreted function")
558                                    (%fun-lambda-list fun)
559                                    ftype
560                                    from
561                                    (unless function
562                                      (cons
563                                       (info :function :inlinep name)
564                                       (info :function :inline-expansion-designator name)))))))))
565           (pprint-logical-block (stream nil)
566             (unless function
567               (format stream "~%~A names ~A:" name what)
568               (pprint-indent :block 2 stream))
569             (describe-lambda-list lambda-list stream)
570             (when (and ftype from)
571               (format stream "~@:_~A type: ~S" from ftype))
572             (describe-documentation name 'function stream)
573             (when (car inline)
574               (format stream "~@:_Inline proclamation: ~A (~:[no ~;~]inline expansion available)"
575                       (car inline)
576                       (cdr inline)))
577             (when methods
578               (format stream "~@:_Method-combination: ~S"
579                       (sb-pcl::method-combination-type-name
580                        (sb-pcl:generic-function-method-combination fun)))
581               (cond ((eq :none methods)
582                      (format stream "~@:_No methods."))
583                     (t
584                      (pprint-newline :mandatory stream)
585                      (pprint-logical-block (stream nil)
586                        (format stream "Methods:")
587                        (dolist (method methods)
588                          (pprint-indent :block 2 stream)
589                          (format stream "~@:_(~A ~{~S ~}~:S)"
590                                  name
591                                  (method-qualifiers method)
592                                  (sb-pcl::unparse-specializers fun (sb-mop:method-specializers method)))
593                          (pprint-indent :block 4 stream)
594                          (describe-documentation method t stream nil))))))
595             (describe-function-source fun stream)
596             (terpri stream)))))
597   (unless function
598     (awhen (and (legal-fun-name-p name) (compiler-macro-function name))
599       (pprint-logical-block (stream nil)
600         (format stream "~@:_~A has a compiler-macro:" name)
601         (pprint-indent :block 2 stream)
602         (describe-documentation it t stream)
603         (describe-function-source it stream))
604       (terpri stream))
605     (when (and (consp name) (eq 'setf (car name)) (not (cddr name)))
606       (let* ((name2 (second name))
607              (inverse (info :setf :inverse name2))
608              (expander (info :setf :expander name2)))
609         (cond (inverse
610                (pprint-logical-block (stream nil)
611                  (format stream "~&~A has setf-expansion: ~S"
612                          name inverse)
613                  (pprint-indent :block 2 stream)
614                  (describe-documentation name2 'setf stream))
615                (terpri stream))
616               (expander
617                (pprint-logical-block (stream nil)
618                  (format stream "~&~A has a complex setf-expansion:"
619                          name)
620                  (pprint-indent :block 2 stream)
621                  (describe-documentation name2 'setf stream t))
622                (terpri stream)))))
623     (when (symbolp name)
624       (describe-function `(setf ,name) nil stream))))