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