message.scm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. ;;; User interface messages
  2. ;; Copyright (C) 2009-2012,2016,2018,2020-2021,2023 Free Software Foundation, Inc.
  3. ;;; This library is free software; you can redistribute it and/or modify it
  4. ;;; under the terms of the GNU Lesser General Public License as published by
  5. ;;; the Free Software Foundation; either version 3 of the License, or (at
  6. ;;; your option) any later version.
  7. ;;;
  8. ;;; This library is distributed in the hope that it will be useful, but
  9. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
  11. ;;; General Public License for more details.
  12. ;;;
  13. ;;; You should have received a copy of the GNU Lesser General Public License
  14. ;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;;;
  17. ;;; This module provide a simple interface to send messages to the user.
  18. ;;; TODO: Internationalize messages.
  19. ;;;
  20. ;;; Code:
  21. (define-module (system base message)
  22. #:use-module (srfi srfi-1)
  23. #:use-module (srfi srfi-9)
  24. #:use-module (ice-9 match)
  25. #:export (*current-warning-port*
  26. *current-warning-prefix*
  27. warning
  28. warning-type? warning-type-name warning-type-description
  29. warning-type-printer lookup-warning-type
  30. %warning-types))
  31. ;;;
  32. ;;; Source location
  33. ;;;
  34. (define (location-string loc)
  35. (define (format-loc file line column)
  36. (format #f "~a:~a:~a"
  37. (or file "<stdin>")
  38. (1+ line)
  39. column))
  40. (match loc
  41. (#(file line column)
  42. (format-loc file line column))
  43. ((? pair? loc)
  44. (format-loc (assoc-ref loc 'filename)
  45. (assoc-ref loc 'line)
  46. (assoc-ref loc 'column)))
  47. (_ "<unknown-location>")))
  48. ;;;
  49. ;;; Warnings
  50. ;;;
  51. ;; This name existed before %current-warning-port was introduced, but
  52. ;; otherwise it is a deprecated binding.
  53. (define *current-warning-port*
  54. ;; Can't play the identifier-syntax deprecation game in Guile 2.0, as
  55. ;; other modules might depend on this being a normal binding and not a
  56. ;; syntax binding.
  57. (parameter-fluid current-warning-port))
  58. (define *current-warning-prefix*
  59. ;; Prefix string when emitting a warning.
  60. (make-fluid ";;; "))
  61. (define-record-type <warning-type>
  62. (make-warning-type name description printer)
  63. warning-type?
  64. (name warning-type-name)
  65. (description warning-type-description)
  66. (printer warning-type-printer))
  67. (define %warning-types
  68. ;; List of known warning types.
  69. (map (lambda (args)
  70. (apply make-warning-type args))
  71. (let-syntax ((emit
  72. (lambda (s)
  73. (syntax-case s ()
  74. ((_ port fmt args ...)
  75. (string? (syntax->datum #'fmt))
  76. (with-syntax ((fmt
  77. (string-append "~a"
  78. (syntax->datum
  79. #'fmt))))
  80. #'(format port fmt
  81. (fluid-ref *current-warning-prefix*)
  82. args ...)))))))
  83. `((unsupported-warning ;; a "meta warning"
  84. "warn about unknown warning types"
  85. ,(lambda (port unused name)
  86. (emit port "warning: unknown warning type `~A'~%"
  87. name)))
  88. (unused-variable
  89. "report unused variables"
  90. ,(lambda (port loc name)
  91. (emit port "~A: warning: unused variable `~A'~%"
  92. loc name)))
  93. (unused-toplevel
  94. "report unused local top-level variables"
  95. ,(lambda (port loc name)
  96. (emit port "~A: warning: possibly unused local top-level variable `~A'~%"
  97. loc name)))
  98. (unused-module
  99. "report unused modules"
  100. ,(lambda (port loc name definitely-unused?)
  101. (if definitely-unused?
  102. (emit port "~A: warning: unused module ~a~%"
  103. loc name)
  104. (emit port "~A: warning: possibly unused module ~a~%"
  105. loc name))))
  106. (shadowed-toplevel
  107. "report shadowed top-level variables"
  108. ,(lambda (port loc name previous-loc)
  109. (emit port "~A: warning: shadows previous definition of `~A' at ~A~%"
  110. loc name
  111. (location-string previous-loc))))
  112. (unbound-variable
  113. "report possibly unbound variables"
  114. ,(lambda (port loc name)
  115. (emit port "~A: warning: possibly unbound variable `~A'~%"
  116. loc name)))
  117. (macro-use-before-definition
  118. "report possibly mis-use of macros before they are defined"
  119. ,(lambda (port loc name)
  120. (emit port "~A: warning: macro `~A' used before definition~%"
  121. loc name)))
  122. (use-before-definition
  123. "report uses of top-levels before they are defined"
  124. ,(lambda (port loc name)
  125. (emit port "~A: warning: `~A' used before definition~%"
  126. loc name)))
  127. (non-idempotent-definition
  128. "report names that can refer to imports on first load, but module definitions on second load"
  129. ,(lambda (port loc name)
  130. (emit port "~A: warning: non-idempotent binding for `~A'. When first loaded, value for `~A` comes from imported binding, but later module-local definition overrides it; any module reload would capture module-local binding rather than import.~%"
  131. loc name name)))
  132. (arity-mismatch
  133. "report procedure arity mismatches (wrong number of arguments)"
  134. ,(lambda (port loc name certain?)
  135. (if certain?
  136. (emit port
  137. "~A: warning: wrong number of arguments to `~A'~%"
  138. loc name)
  139. (emit port
  140. "~A: warning: possibly wrong number of arguments to `~A'~%"
  141. loc name))))
  142. (duplicate-case-datum
  143. "report a duplicate datum in a case expression"
  144. ,(lambda (port loc datum clause case-expr)
  145. (emit port
  146. "~A: warning: duplicate datum ~S in clause ~S of case expression ~S~%"
  147. loc datum clause case-expr)))
  148. (bad-case-datum
  149. "report a case datum that cannot be meaningfully compared using `eqv?'"
  150. ,(lambda (port loc datum clause case-expr)
  151. (emit port
  152. "~A: warning: datum ~S cannot be meaningfully compared using `eqv?' in clause ~S of case expression ~S~%"
  153. loc datum clause case-expr)))
  154. (format
  155. "report wrong number of arguments to `format'"
  156. ,(lambda (port loc . rest)
  157. (define (escape-newlines str)
  158. (list->string
  159. (string-fold-right (lambda (c r)
  160. (if (eq? c #\newline)
  161. (append '(#\\ #\n) r)
  162. (cons c r)))
  163. '()
  164. str)))
  165. (define (range min max)
  166. (cond ((eq? min 'any)
  167. (if (eq? max 'any)
  168. "any number" ;; can't happen
  169. (emit #f "up to ~a" max)))
  170. ((eq? max 'any)
  171. (emit #f "at least ~a" min))
  172. ((= min max) (number->string min))
  173. (else
  174. (emit #f "~a to ~a" min max))))
  175. (match rest
  176. (('simple-format fmt opt)
  177. (emit port
  178. "~A: warning: ~S: unsupported format option ~~~A, use (ice-9 format) instead~%"
  179. loc (escape-newlines fmt) opt))
  180. (('wrong-format-arg-count fmt min max actual)
  181. (emit port
  182. "~A: warning: ~S: wrong number of `format' arguments: expected ~A, got ~A~%"
  183. loc (escape-newlines fmt)
  184. (range min max) actual))
  185. (('syntax-error 'unterminated-iteration fmt)
  186. (emit port "~A: warning: ~S: unterminated iteration~%"
  187. loc (escape-newlines fmt)))
  188. (('syntax-error 'unterminated-conditional fmt)
  189. (emit port "~A: warning: ~S: unterminated conditional~%"
  190. loc (escape-newlines fmt)))
  191. (('syntax-error 'unexpected-semicolon fmt)
  192. (emit port "~A: warning: ~S: unexpected `~~;'~%"
  193. loc (escape-newlines fmt)))
  194. (('syntax-error 'unexpected-conditional-termination fmt)
  195. (emit port "~A: warning: ~S: unexpected `~~]'~%"
  196. loc (escape-newlines fmt)))
  197. (('wrong-port wrong-port)
  198. (emit port
  199. "~A: warning: ~S: wrong port argument~%"
  200. loc wrong-port))
  201. (('wrong-format-string fmt)
  202. (emit port
  203. "~A: warning: ~S: wrong format string~%"
  204. loc fmt))
  205. (('non-literal-format-string)
  206. (emit port
  207. "~A: warning: non-literal format string~%"
  208. loc))
  209. (('wrong-num-args count)
  210. (emit port
  211. "~A: warning: wrong number of arguments to `format'~%"
  212. loc))
  213. (else
  214. (emit port "~A: `format' warning~%" loc)))))))))
  215. (define (lookup-warning-type name)
  216. "Return the warning type NAME or `#f' if not found."
  217. (find (lambda (wt)
  218. (eq? name (warning-type-name wt)))
  219. %warning-types))
  220. (define (warning type location . args)
  221. "Emit a warning of type TYPE for source location LOCATION (a source
  222. property alist) using the data in ARGS."
  223. (let ((wt (lookup-warning-type type))
  224. (port (current-warning-port)))
  225. (if (warning-type? wt)
  226. (apply (warning-type-printer wt)
  227. port (location-string location)
  228. args)
  229. (format port "~A: unknown warning type `~A': ~A~%"
  230. (location-string location) type args))))